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 && (