fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: persist elapsed timer across session switches - #484

Merged
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset
Apr 15, 2026
Merged

fix: persist elapsed timer across session switches#484
op7418 merged 2 commits into
op7418:mainfrom
KevinYoung-Kw:fix/timer-reset

Conversation

@KevinYoung-Kw

@KevinYoung-KwKevinYoung-Kw commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

CodePilot 版本: v0.50.1

问题描述

在侧边栏切换 Session 后再切回来,底部流式回复的计时器会从 0s 重新开始计数。期望它应该基于真实的流累计时长展示,不因切换而重置。

根因

ElapsedTimerStreamingMessage.tsx 里 mount 时用本地 Date.now() 记录开始时间。切换 Session 导致 ChatView(连带 StreamingMessage)unmount/remount,计时器就归零了。

修复

stream-session-managerSessionStreamSnapshot 已包含 startedAt 字段,记录流的真实开始时间。现在把它从组件树顶层一路透传到底层:

ChatViewMessageListStreamingMessageStreamingStatusBarElapsedTimer

ElapsedTimer 改为基于传入的 startedAt 计算 elapsed,组件 remount 后仍能恢复正确的累计时长。

改动文件

文件改动
src/components/chat/ChatView.tsxstreamSnapshot.startedAt 传给 MessageList
src/components/chat/MessageList.tsx新增 startedAt prop 并透传给 StreamingMessage
src/components/chat/StreamingMessage.tsxStreamingStatusBar / ElapsedTimer 接收 startedAt 并基于它计算

验证方式

  • 进入某个 Session 开始流式回复,等底部计时器走几秒
  • 切换到另一个 Session,再切回来
  • 计时器应从之前的累计时间继续,而不是从 0 重置
  • 单 Session 内的正常流式行为无变化
  • npm run test 通过(typecheck + 单元测试)

修复前后对比

切换会话后,计时器重新归零修复录像 - 计时器正确显示

**问题**
切换 Session 后,`StreamingMessage` 底部的计时器会从 0 重新开始计数。根因是 `ElapsedTimer` 组件在 mount 时用 `Date.now()` 本地初始化开始时间,Session 切换导致组件 remount 后时间重置。
**修复**
- `stream-session-manager` 的 `SessionStreamSnapshot` 已包含 `startedAt` 字段,记录流的真实开始时间
- 将 `startedAt` 从 `ChatView` → `MessageList` → `StreamingMessage` → `StreamingStatusBar` → `ElapsedTimer` 逐级透传
- `ElapsedTimer` 改为基于传入的 `startedAt` 计算 elapsed,组件 remount 后仍能恢复真实累计时长
**影响范围**
仅影响流式响应状态下的底部计时器显示,不改变任何持久化逻辑或计时行为。
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercelBot commented Apr 15, 2026

Copy link
Copy Markdown

@KevinYoung-Kw is attempting to deploy a commit to the op7418's projects Team on Vercel.

A member of the Team first needs to authorize it.

@op7418
op7418 merged commit 93c71c2 into op7418:mainApr 15, 2026
1 check failed
@op7418

Copy link
Copy Markdown
Owner

已合并 🎉 根因定位 + 修法都很干净(把本来就存在的 startedAt 透传到底层就够了,不用改数据模型)。只有一个小 nit:MessageList.tsx 里 startedAt! 是非空断言,理论上当 streamSnapshot 为 null 时会传 undefined 到 ElapsedTimer,变成 NaN 显示。不过实际场景下 isStreaming=true 保证 streamSnapshot 非空,不是阻塞项,先这样。

感谢贡献,下次发版带上。

op7418 added a commit that referenced this pull request Apr 15, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (#461/#478/#476/#457/#470/#474)
- OpenAI OAuth 403/网络失败自动重试 (#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (#465/#466/#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (#480/#484)
- 选 slash 命令清空已输入文本 (#479/#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@KevinYoung-Kw
KevinYoung-Kw deleted the fix/timer-reset branch April 16, 2026 10:29
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
切 session 后计时器归零的问题。根因:ElapsedTimer mount 时用 Date.now() 记录开始时间,session 切换导致 ChatView/StreamingMessage unmount/remount,计时器归零。
修法:把 stream-session-manager 里本来就有的 startedAt 从 ChatView 透传到 ElapsedTimer(MessageList → StreamingMessage → StreamingStatusBar → ElapsedTimer),组件 remount 后基于 startedAt 恢复正确累计时长。
Closesop7418#480.
Co-Authored-By: Kevin Young <kevinyoung-kw@example>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
Fixes:
- cc-switch 切换 provider 被默默改路由 (op7418#461/op7418#478/op7418#476/op7418#457/op7418#470/op7418#474)
- OpenAI OAuth 403/网络失败自动重试 (op7418#464)
- Electron 端口稳定化修复 localStorage 配置全丢 (op7418#465/op7418#466/op7418#477)
- v0.49.0+ 长对话 AI_MissingToolResultsError 回归
- Turbopack CJS↔ESM interop 导致 streamClaudeSdk is not a function
- 内置 MCP 自动批准 (Memory/Widget/Notify 等 7 个)
- Claude Code executable not found on Windows (SDK bundle 修复)
- 切换会话后计时器归零 (op7418#480/op7418#484)
- 选 slash 命令清空已输入文本 (op7418#479/op7418#486)
- Skills 弹窗误触发路径 + 多选 + badge 去描述
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@KevinYoung-Kw@op7418