diff --git a/src/components/chat/ChatView.tsx b/src/components/chat/ChatView.tsx index 5155df93..6f4d8fc5 100644 --- a/src/components/chat/ChatView.tsx +++ b/src/components/chat/ChatView.tsx @@ -651,6 +651,7 @@ export function ChatView({ sessionId, initialMessages = [], initialHasMore = fal onLoadMore={loadEarlierMessages} rewindPoints={rewindPoints} sessionId={sessionId} + startedAt={streamSnapshot?.startedAt} isAssistantProject={isAssistantProject} assistantName={assistantName} /> diff --git a/src/components/chat/MessageList.tsx b/src/components/chat/MessageList.tsx index ff8c3840..3122403e 100644 --- a/src/components/chat/MessageList.tsx +++ b/src/components/chat/MessageList.tsx @@ -172,6 +172,7 @@ interface MessageListProps { /** SDK rewind points — only emitted for visible prompt-level user messages (not tool results or auto-triggers), mapped by position */ rewindPoints?: RewindPoint[]; sessionId?: string; + startedAt?: number; /** Whether this is an assistant workspace project */ isAssistantProject?: boolean; /** Assistant name for avatar display */ @@ -193,6 +194,7 @@ export function MessageList({ onLoadMore, rewindPoints = [], sessionId, + startedAt, isAssistantProject, assistantName, }: MessageListProps) { @@ -315,6 +317,7 @@ export function MessageList({ content={streamingContent} isStreaming={isStreaming} sessionId={sessionId} + startedAt={startedAt!} toolUses={toolUses} toolResults={toolResults} streamingToolOutput={streamingToolOutput} diff --git a/src/components/chat/StreamingMessage.tsx b/src/components/chat/StreamingMessage.tsx index 849d5317..5b8c243a 100644 --- a/src/components/chat/StreamingMessage.tsx +++ b/src/components/chat/StreamingMessage.tsx @@ -106,6 +106,7 @@ interface StreamingMessageProps { content: string; isStreaming: boolean; sessionId?: string; + startedAt: number; toolUses?: ToolUseInfo[]; toolResults?: ToolResultInfo[]; streamingToolOutput?: string; @@ -196,17 +197,20 @@ function ThinkingPhaseLabel() { return {text}; } -function ElapsedTimer() { - const [elapsed, setElapsed] = useState(0); - const startRef = useRef(0); +function ElapsedTimer({ startedAt }: { startedAt: number }) { + const [elapsed, setElapsed] = useState(() => Math.floor((Date.now() - startedAt) / 1000)); + + // Reset elapsed when the stream start time changes (e.g. new turn or session switch) + useEffect(() => { + setElapsed(Math.floor((Date.now() - startedAt) / 1000)); + }, [startedAt]); useEffect(() => { - startRef.current = Date.now(); const interval = setInterval(() => { - setElapsed(Math.floor((Date.now() - startRef.current) / 1000)); + setElapsed(Math.floor((Date.now() - startedAt) / 1000)); }, 1000); return () => clearInterval(interval); - }, []); + }, [startedAt]); const mins = Math.floor(elapsed / 60); const secs = elapsed % 60; @@ -218,7 +222,7 @@ function ElapsedTimer() { ); } -function StreamingStatusBar({ statusText, onForceStop }: { statusText?: string; onForceStop?: () => void }) { +function StreamingStatusBar({ statusText, onForceStop, startedAt }: { statusText?: string; onForceStop?: () => void; startedAt: number }) { const displayText = statusText || 'Thinking'; // Parse elapsed seconds from statusText like "Running bash... (45s)" @@ -241,7 +245,7 @@ function StreamingStatusBar({ statusText, onForceStop }: { statusText?: string; )} | - + {isCritical && onForceStop && (