History

Reset stuck send phase after turns settle

- Keep the send timer alive while a send is active - Fallback-reset sendPhase when a turn settles after a missed transition

Julius Marminge committed 7 months ago

Commit 9a9be391 parent a60daa11 file · +15 −2Browse files

Unified · Split

apps/web/src/components/ChatView.tsx+15−2
@@ -2152,14 +2152,14 @@ export default function ChatView({ threadId }: ChatViewProps) { · 2151 unmodified lines
2152 : "local";2152 : "local";
2153 2153
2154 useEffect(() => {2154 useEffect(() => {
2155 if (phase !== "running") return;2155 if (phase !== "running" && sendPhase === "idle") return;
2156 const timer = window.setInterval(() => {2156 const timer = window.setInterval(() => {
2157 setNowTick(Date.now());2157 setNowTick(Date.now());
2158 }, 1000);2158 }, 1000);
2159 return () => {2159 return () => {
2160 window.clearInterval(timer);2160 window.clearInterval(timer);
2161 };2161 };
2162 }, [phase]);2162 }, [phase, sendPhase]);
2163 2163
2164 const beginSendPhase = useCallback((nextPhase: Exclude<SendPhase, "idle">) => {2164 const beginSendPhase = useCallback((nextPhase: Exclude<SendPhase, "idle">) => {
2165 setSendStartedAt((current) => current ?? new Date().toISOString());2165 setSendStartedAt((current) => current ?? new Date().toISOString());
@@ -2192,6 +2192,19 @@ export default function ChatView({ threadId }: ChatViewProps) { · 26 unmodified lines
2192 sendPhase,2192 sendPhase,
2193 ]);2193 ]);
2194 2194
2195 // Fallback: if the "running" phase transition was missed (e.g. due to React
2196 // batching two snapshots, a WebSocket reconnect, or a very fast turn), the
2197 // primary auto-reset above never fires and sendPhase stays stuck. Detect
2198 // this by checking whether the turn has settled while sendPhase is still
2199 // non-idle — the turn completing is a definitive signal that "sending" is
2200 // over.
2201 useEffect(() => {
2202 if (sendPhase === "idle") return;
2203 if (phase !== "running" && phase !== "connecting" && latestTurnSettled) {
2204 resetSendPhase();
2205 }
2206 }, [latestTurnSettled, phase, resetSendPhase, sendPhase]);
2207
2195 useEffect(() => {2208 useEffect(() => {
2196 if (!activeThreadId) return;2209 if (!activeThreadId) return;
2197 const previous = terminalOpenByThreadRef.current[activeThreadId] ?? false;2210 const previous = terminalOpenByThreadRef.current[activeThreadId] ?? false;