Stage 333: PR #2018 — fix(stop): refresh button after chat/start stream id by @rhelmer

This commit is contained in:
nesquena-hermes
2026-05-10 18:16:59 +00:00
2 changed files with 50 additions and 0 deletions
+5
View File
@@ -210,6 +210,7 @@ async function send(){
startClarifyPolling(activeSid);
_fetchYoloState(activeSid); // sync YOLO pill with backend state
S.activeStreamId = null; // will be set after stream starts
if(typeof updateSendBtn==='function') updateSendBtn();
// Set provisional title from user message immediately so session appears
// in the sidebar right away with a meaningful name (server may refine later)
@@ -243,6 +244,7 @@ async function send(){
profile:S.activeProfile||S.session.profile||'default',
attachments:uploaded.length?uploaded:undefined
})});
if(startData.effective_model && S.session){
S.session.model=startData.effective_model;
S.session.model_provider=startData.effective_model_provider||S.session.model_provider||null;
@@ -259,6 +261,9 @@ async function send(){
}
streamId=startData.stream_id;
S.activeStreamId = streamId;
// setBusy(true) already ran with activeStreamId=null; refresh now that we
// have a stream id so the primary button can switch to Stop (see getComposerPrimaryAction).
if(typeof updateSendBtn==='function') updateSendBtn();
if(S.session&&typeof startData.pending_started_at==='number'){
S.session.pending_started_at=startData.pending_started_at;
}
+45
View File
@@ -207,6 +207,51 @@ class TestBusySendButton:
"boot.js should wire btnSend to handleComposerPrimaryAction(), not directly to send()"
)
def test_send_refreshes_primary_button_after_clearing_active_stream_id(self):
"""send() must call updateSendBtn after resetting activeStreamId for a new turn.
getComposerPrimaryAction maps to Stop only when S.activeStreamId is set; after
nulling the id, btnSend must refresh so a stale Stop icon cannot linger until
the next composer input event.
"""
send_start = MESSAGES_JS.find("async function send(")
assert send_start >= 0, "send() not found in messages.js"
send_end = MESSAGES_JS.find("const LIVE_STREAMS={}", send_start)
assert send_end > send_start, "could not find end of send() body"
send_body = MESSAGES_JS[send_start:send_end]
marker = "S.activeStreamId = null; // will be set after stream starts"
mpos = send_body.find(marker)
assert mpos >= 0, "send() must reset activeStreamId before chat/start"
window = send_body[mpos : mpos + 200]
assert "updateSendBtn" in window, (
"send() must call updateSendBtn() after clearing activeStreamId "
"so btnSend state matches the pending-start phase"
)
def test_send_refreshes_primary_button_after_chat_start_stream_id(self):
"""send() must call updateSendBtn in the chat/start try block after assigning streamId.
setBusy(true) already ran updateSendBtn while activeStreamId was still null, so the
Stop affordance did not appear until something else (e.g. typing) called
updateSendBtn again.
"""
send_start = MESSAGES_JS.find("async function send(")
assert send_start >= 0, "send() not found in messages.js"
send_end = MESSAGES_JS.find("const LIVE_STREAMS={}", send_start)
assert send_end > send_start, "could not find end of send() body"
send_body = MESSAGES_JS[send_start:send_end]
assign = "S.activeStreamId = streamId;"
apos = send_body.find(assign)
assert apos >= 0, "send() must assign S.activeStreamId from startData"
after_assign = send_body[apos:]
end_try = after_assign.find(" }catch(e){")
assert end_try > 0, "send() outer try/catch not found after stream id assign"
try_after_assign = after_assign[:end_try]
assert "updateSendBtn" in try_after_assign, (
"send() must call updateSendBtn() in the chat/start try block after assigning "
"streamId so the primary button switches to Stop without waiting for composer input"
)
class TestSendBusyBranchDispatch:
"""send()'s busy block must read window._busyInputMode and branch accordingly."""