Compare commits
1 Commits
82699f7a20
...
feat/198-i
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b146fd24d |
@@ -183,11 +183,11 @@ export default function ChatThread({
|
||||
// deliberately switches chat / starts a new chat (the parent remounts this via
|
||||
// `key`), but it SURVIVES in-place new-chat id adoption (no remount), so a
|
||||
// message queued during a brand-new chat's first turn is not lost. On a normal
|
||||
// Stop / disconnect / error the queue is intentionally preserved (onFinish still
|
||||
// fires on every terminal outcome, but only a CLEAN finish flushes it) so the
|
||||
// user decides what to do with the pending messages. The one exception is a
|
||||
// deliberate "Send now", which also calls stop() and then flushes the promoted
|
||||
// head via the abort branch of onFinish (see sendNow / onFinish below).
|
||||
// Stop / disconnect / error the queue is intentionally preserved (onFinish DOES
|
||||
// fire on those — see the abort/disconnect/error branches below — but it leaves
|
||||
// the queue intact) so the user decides what to do with the pending messages.
|
||||
// The one exception is a deliberate "Send now" (which itself calls stop()): its
|
||||
// abort branch in onFinish flushes the message it promoted to the head.
|
||||
const [queued, setQueued] = useState<QueuedMessage[]>([]);
|
||||
// Mirror the queue in a ref so the `onFinish` flush always reads the latest
|
||||
// queue without a stale closure; `setQueue` updates BOTH the ref and the state.
|
||||
@@ -319,6 +319,13 @@ export default function ChatThread({
|
||||
// Keep the flush helper pointed at the latest sendMessage instance.
|
||||
sendMessageRef.current = sendMessage;
|
||||
|
||||
// Mirror the live turn status in a ref so event handlers (sendNow) branch on the
|
||||
// CURRENT status rather than a value captured in a stale render closure — a turn
|
||||
// can finish between render and click, and arming the interrupt refs against a
|
||||
// no-op stop() would leave them set to leak into a later, unrelated Stop.
|
||||
const statusRef = useRef(status);
|
||||
statusRef.current = status;
|
||||
|
||||
// EARLY chat-id adoption (#174): the server streams the authoritative chat id
|
||||
// on the assistant message metadata at the `start` chunk (message.metadata.
|
||||
// chatId — see adopt-chat-id.ts / chatStreamMetadata). Forward it to the parent
|
||||
@@ -350,27 +357,16 @@ export default function ChatThread({
|
||||
|
||||
const isStreaming = status === "submitted" || status === "streaming";
|
||||
|
||||
// Mirror the live `status` in a ref (updated every render) so event handlers
|
||||
// fired from a stale render — notably `sendNow` clicked in the sub-frame window
|
||||
// after a turn finished but before the re-render — branch on the CURRENT status,
|
||||
// not the value captured in their closure. Prevents arming the interrupt/flush
|
||||
// refs when nothing is actually streaming (see sendNow).
|
||||
const statusRef = useRef(status);
|
||||
statusRef.current = status;
|
||||
|
||||
// "Send now" on a queued message: interrupt the current turn and immediately
|
||||
// send THIS message. Any other queued messages stay queued and flush normally
|
||||
// after the new turn finishes.
|
||||
const sendNow = useCallback(
|
||||
(id: string) => {
|
||||
// Branch on the LIVE status (statusRef), not the closure-captured
|
||||
// `isStreaming`: if the turn finished between this render and the click,
|
||||
// treat it as not-streaming and send immediately, instead of arming a
|
||||
// flush/interrupt that no abort will ever consume (the stop() below would be
|
||||
// a no-op and the flags would leak into a later turn).
|
||||
const liveStatus = statusRef.current;
|
||||
// Branch on the LIVE status (statusRef), not the closure-captured isStreaming:
|
||||
// the turn may have finished between render and click, in which case stop()
|
||||
// is a no-op and arming the interrupt refs would strand them for a later turn.
|
||||
const liveStreaming =
|
||||
liveStatus === "submitted" || liveStatus === "streaming";
|
||||
statusRef.current === "submitted" || statusRef.current === "streaming";
|
||||
if (liveStreaming) {
|
||||
// Promote the chosen message to the head so the existing onFinish→flushNext
|
||||
// sends exactly it, then interrupt: the abort triggers onFinish below.
|
||||
|
||||
Reference in New Issue
Block a user