Files
Jyotisha/PROGRESS-chat-streaming-ux-20260901.md
T
Jesse_Chen eeb82dfdc4 docs(chat): record streaming-ux progress, block task 3 on batch three, align DESIGN.md
DESIGN.md gains the streaming-states table, the thinking-collapse and
label-swap motion rows, the 17px body correction and the single live
marker rule; task 3 is registered in BLOCKED.md until the home split
batch three lands; PROGRESS lists every touched assertion with its
former value.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
2026-09-02 04:29:01 +00:00

12 KiB
Raw Blame History

PROGRESS · Agent 聊天流式体验与双会话面统一(2026-09-01)

工作树:.worktrees/streaming-ux-20260901 分支:codex/streaming-ux-20260901 基线:origin/staging @ fbb80fa3(任务书写 80e77361,开工时远端已多一条任务书本身的 docs commit)。

未改 .gitea/workflows/**。未合并 staging,未碰 main。未手写 useCallback / useMemorectification-agentic-chat.tsx 既有的 useCallback 原样保留)。未动服务端事件语义;校正 thinking.deltastream-mapping.ts 公开边界的丢弃原样保留。page.tsx 一行未动。

任务 状态 commit BUG
0 · 流式按帧合并 + 匀速吐字 + Markdown 前缀记忆 完成 dd4e3b37 BUG-473
1 · 结算不闪、时间线开合过渡、流式可折叠、queued 行 完成 69d19e0f BUG-474、BUG-475
2 · 校正接入同一套时间线、删三条死路径、移除 thinking-orbs 完成 cc17d971 BUG-476
3 · 校正复用 ChatComposer / 统一滚动跟随 / JumpToLatest / 900px 未做,见 BLOCKED.md BUG-476/475 未占号
4 · DESIGN.md 与 0/1/2 对应的条目已改;与 3 对应的条目随 3 延后 本文件同 commit

任务 0 · 流式渲染

改动:src/lib/stream-frame-buffer.ts(新)、src/lib/chat-markdown-split.ts(新)、src/components/chat-message-content.tsxsrc/components/chat-message-row.tsxstreaming prop)、src/hooks/use-consultation-run.tssrc/components/rectification-agentic-chat.tsx

  • 两个会话面的事件回调都不再直接 setState:事件写入累加器,requestAnimationFrame 每帧最多一次 flushrun.completed / run.failed / abort / 读流结束时 settle() 同步冲干净;finallydispose();校正 attempt.resetreset()
  • 释放节奏:max(2, ceil(积压 ÷ 12)) 字/帧,积压按「新文本到达时的未释放量」记,所以 3,000 字突发恰好 12 帧追平(测试锁死),稳态约落后网络 12 帧(≈200ms);页面隐藏走 250ms setTimeout 并一次放完。
  • 重活只做一次:parseAgentReply / applyThinkingSectionProgress 只在 flush 里对已释放文本跑。
  • Markdown:流式时在最后一个空行切「稳定前缀 / 尾块」,不切进代码围栏、同一列表的项之间、表格行之间;前缀交给 memoStableMarkdownPrefix,只有尾块每帧 parse;切分对象是 splitSpokenAnswerAndTechniqueAudit(text).spoken,技法审计表留在审计折叠里。结算后仍整篇一次 parse,DOM 结构与流式时一致(react-markdown 不加包裹元素)。
  • 没有另写"薄 hook"use-consultation-run 按前两轮拆页公证是"内部 0 个 React hook",缓冲器按每次 send 创建/销毁的工厂用法更贴合,两处调用都是这个形态。

渲染次数探针(tests/home-streaming-render-split.test.ts 新增用例,假 rAF 驱动):200 个单字 token、每帧到 4 个、共 50 帧 → streamingRowRenders = 50(改前的架构每 token 一次 = 200)。tests/stream-frame-buffer.test.ts 同一场景 flush 50 次,且每次释放 ≥2 字、结算后与网络全文一致。

Performance 面板录制(3k 字回答无 >50ms 长任务)未做:无登录态、无 Chrome,见 BLOCKED.md

任务 1 · 结算与时间线

改动:src/components/chat-transcript.tsx(重写)、src/lib/chat-message-view.tslatestAssistantView)、src/lib/home-streaming-render-probe.tslatestEntryMounts)、src/components/consultation-run-timeline.tsx(重写)、src/components/chat-message-row.tsxGSAP 0.16)、src/app/globals.cssscripts/home-streaming-render-benchmark.mts(改用 LatestAssistantEntry)。

  • ChatTranscript = SettledMessageList excludeLatestAssistant + 一个 LatestAssistantEntry key={renderKey}。同一个 MessageEntry 渲染历史行、流式行、刚结算行,动作条与追问只在 settled 出现。renderKey 在流式(message-N,N=历史条数)与结算(末条下标 N)下相同,纯函数 latestAssistantView 有测试锁。
  • .message 的 CSS message-enter 关键帧删除,只剩 GSAP 一份,时长 0.18 → 0.16DESIGN.md §6 = 160ms)。
  • 时间线:去掉 key={live…};外层 <section> + button[aria-expanded][aria-controls]open = userOpen ?? live;折叠是 grid-template-rows 0fr↔1fr 180ms,折叠后内容 inertsummary 文案换行 120ms 淡入;live 行与 summary 文案共用既有 agent-activity-status__text shimmerrows 为空且 live 时渲染 QUEUED_TIMELINE_ROW「正在处理…」。reduced-motion 下三处过渡为 0(全局 * 规则之外再显式写了一次)。
  • latestEntryMounts 探针只能在浏览器里手动读(无 DOM 测试库,renderToString 不跑 effect),见 BLOCKED.md

任务 2 · 双会话面统一

改动:src/lib/rectification-timeline-adapter.ts(新)、src/components/rectification-agentic-chat.tsxsrc/components/agent-activity-status.tsx(重写为兜底)、src/components/chat-message-row.tsxsrc/app/globals.csspackage.json / package-lock.jsonnpm uninstall thinking-orbs);删除 consultation-thinking-report.tsxthinking-step-tree.tsxcompleted-activity-receipt.tsx

  • 适配:tool trace → calculate 行(started=live / completed=done,标签沿用 RECTIFICATION_TOOL_*_LABELS);failedTool 的行标签加「未完成」且不挂 chips;回执 methodsPUBLIC_RECTIFICATION_METHOD_LABELS 去重 ≤8 作为最后一条完成 calculate 行的 sources;没有 live 行且 activity.label 以「正在」开头时补一条 live 行(answer-compositionwrite,其余 → calculate);think 类 trace 项映射为 think 行(现网不会出现,服务端丢弃)。
  • 校正结算后与普通会话一样收成「已完成 N 步」一行,方法 chips 在行内展开;第二个回执折叠块、消息上方红字失败行、重新生成时的假「正在组织回答…」全部删除;失败走既有 error 通知;重新生成显示 queued 行。
  • chat-message-row.tsx 只剩 timeline 一条主路径 + AgentActivityStatus 兜底(无 trace 分支、无 orb);live 标记全部 InlineSpinner.conversation.is-rectification .agent-thinking-step/.agent-thinking-marker 14px 覆写删除。
  • 校正的「已用时 N 秒」计时(ActivityElapsed)随 trace 面板一起退出——时间线本来就没有这个元素,两面一致优先。

任务 3 · 未做

git log origin/staging -- frontend/src/app/page.tsx 最新 551d6317(第二批),第三批未合入。按红线 7 登记 BLOCKED.md,未占 BUG-476/475。

任务 4 · DESIGN.md

已改:§3 思考正文 13px 写成显式例外;§5 Message 正文 16px → 17px(≤767px 时 16px,与 globals.css 一致)、Motion 改为"只有 GSAP 一份 160ms、结算不重放"、新增「Streaming states」七态表(含 failed 不用消息内红字、pacing 公式、reduced-motion 行为、校正只差不显示思考正文);§6 表新增 Thinking collapse 180ms / Timeline label swap 120ms / Text reveal;§9 等待表把时间线 live 步写成两面共用,注明 live 标记只有 InlineSpinner、orb 已移除。

随任务 3 延后:§4 阅读宽度(720 vs 900)、§5 composer 共用一句、§5 Jump to latest 条目、§7 --shadow-soft

被触碰的既有断言(红线 3,每处源码上方均有原值与错因注释)

文件 原断言 现在 理由
tests/home-streaming-render-split.test.ts split.streamingRowRenders === tokens.length ≤ tokens.length && ≥ 1,另加按帧驱动的新用例 原值是"每 token 一次提交"的现状快照(BUG-473
tests/chat-bundle-splitting-contract.test.ts duration: 0\.18 0\.16 DESIGN.md §6 160ms0.18 是 GSAP 副本漂移(BUG-474
tests/session-conversation-layout.test.ts duration:\s*0\.18 0\.16 同上
tests/class-name-definition-contract.test.ts knownUnstyledconsultation-run-timeline__summaryconsultation-step-tree 两条移除 前者现在有样式(按钮),后者类名已随组件删除;该测试本身要求列表无过期项
tests/chat-bundle-splitting-contract.test.ts "the thinking orb loads on demand…"OrbState 导入、dynamic(...)、20px 占位、prefetchOnIdle(importThinkingOrb) 改为"live 标记是共享 InlineSpinner,无 orb chunk" orb 是第二种 live 标记且被 14px 裁切(BUG-476
tests/chat-stream-layout.test.ts reportSource/treeSource 读取;<ThinkingOrb … size={20}ConsultationThinkingReporthasTraceactivityTrace={message.activityTrace}activityTrace ?? []function TraceStep.agent-thinking-step.is-think:has(...)、report/tree 内容锁、consultation-step-tree__* CSS 锁、.consultation-thinking-report .message-thinking 改为锁"只剩一条路径"(doesNotMatch 这些符号)+ InlineSpinner size={12} 锁的正是被删除的第二/第三条思考渲染路径(BUG-476)
tests/rectification-agentic-entry.test.ts 读取 completed-activity-receipt.tsx 并锁其 <details>/不 open/「本轮完成 · N 个步骤」;activityStatus /activityTrace/ 改为锁 chat /rectificationTimelineRows/chat /completedReceipt/activityStatus 不含 activityTrace 锁的是校正专属第二折叠块与 trace 面板(BUG-476
tests/rectification-activity-receipt.test.ts deltaBranchstate: raw.trim() ? "streaming" : "thinking"text: raw,message.state === "settled" && message.failed 状态派生改锁 frame flush 段(state: text.trim() ? …text,frames.setAnswer(raw));失败行改锁 && !message.failed 且无 rectification-activity-failure 前者锁的是每 chunk 一次 setMessagesBUG-473),后者锁的是校正专属红字失败行(BUG-476)。:这个文件在任务 0 的聚焦跑里漏跑了,任务 2 跑全量前才发现,改在 cc17d971

新增测试:tests/stream-frame-buffer.test.ts7)、tests/chat-markdown-split.test.ts4)、tests/chat-stream-settle-contract.test.ts4)、tests/rectification-timeline-adapter.test.ts6)、home-streaming-render-split +1。

命令输出

./node_modules/.bin/tsc --noEmit0(任务 0/1/2 各跑一次,均无诊断)。

./node_modules/.bin/eslint 触及文件 → 0 erroruse-consultation-run.ts 有 9 条既有 no-unused-vars warning(未使用的导入/startNewChat),本轮只加了一行 import,与 origin/staging 一致;stream-frame-buffer.ts 一条 dirty 未用已在本 commit 清掉。

./node_modules/.bin/tsx --test tests/*.test.ts

基线(开工时 fbb80fa3 现在
tests 2460 2482
pass 2426 2448
fail 24 24
skipped 10 10

24 条失败逐条比对完全同一集合comm 差集为空):全部是本机无 Docker / PostgreSQL / 部署夹具的数据库、Better Auth、staging workflow YAML、live sync 类(admin code functions、Owner recovery、local PostgreSQL、redeem security、personal report job/sections RLS、migration runner、v9 migration 等),与上一轮 PROGRESS 记录的环境缺口同类。10 条 skipped 同基线。无产品逻辑新增失败。

./node_modules/.bin/next build0✓ Compiled successfully in 21.7sFinished TypeScript in 58sGenerating static pages (62/62)/ 仍是 ○ (Static)。5 条既有 skill-package-registry.ts filesystem-tracing warning,未失败。

未做与原因

BLOCKED.md 顶部三条:任务 3 等第三批拆页;浏览器级手工验收(Performance 录制、结算录屏、两面并排截图、流式期间折叠)无登录态无 Chrome;latestEntryMounts 探针无 DOM 测试库不可驱动。