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
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# BLOCKED
|
||||
|
||||
## Agent 聊天流式体验与双会话面统一(2026-09-01,分支 `codex/streaming-ux-20260901`)
|
||||
|
||||
- **任务 3(校正面复用 `ChatComposer` + 统一滚动跟随 + `JumpToLatestButton` + 720→900 宽度)未做,等待 `TASK-home-split-batch3-20260901.md` 合入。** 任务书红线 7:任务 3 要动 `page.tsx` 两处(按 `activeStreamingText` 触发的滚动 effect、内联 Tailwind 的「跳到最新」按钮),与第三批拆页冲突。开工时 `git log origin/staging -- frontend/src/app/page.tsx` 最新为 `551d6317`(第二批),第三批尚未合入,故按红线登记。随之延后的还有任务 4 里与任务 3 对应的 DESIGN.md 条目(§4 阅读宽度、§5 composer 共用一句、§5 新增 Jump to latest、§7 `--shadow-soft`)与 BUG-476/475。任务 0/1/2 未碰 `page.tsx`。
|
||||
- **浏览器级手工验收未做:执行环境无登录态、无 Chrome。** 任务书里的 Performance 录制(3k 字回答无 >50ms 长任务)、结算不闪录屏、流式期间折叠时间线、校正/普通会话并排截图,全部留给有真实会话的人按 `docs/testing/staging-manual-walkthrough-20260901.md` 的方式补。本轮的替代证据是 `tests/home-streaming-render-split.test.ts` 的按帧驱动渲染计数与 `tests/stream-frame-buffer.test.ts` 的释放节奏断言。
|
||||
- **`LatestAssistantEntry` 的「一次会话只 mount 一次」探针无法在测试里驱动。** 仓库没有 jsdom / happy-dom,`renderToString` 不跑 effect,所以 `latestEntryMounts` 只是留给浏览器里手动读的探针;测试改用纯函数 `latestAssistantView` 的 key 一致性 + 源码锁(只有一处 `<LatestAssistantEntry`、无 `StreamingMessageEntry`/`SettledMessageEntry`)代替。
|
||||
|
||||
## 报告解读深度对齐(2026-09-01,分支 `codex/report-skill-parity-20260901`)
|
||||
|
||||
- **端到端真实生成未做:执行环境没有任何模型凭据。** `ANTHROPIC_*` / `OPENAI_*` / `DEEPSEEK_*` 等全部未设置,仓内只有 `.env.official.example` / `.env.cn.example` / `deploy/.env.staging.identity.example`,不含可用 key。按红线不得读取或猜测凭据。因此任务书交付物 5 的「真实 writer 输出抽查」与交付物 7 的「改前改后同盘同主题章节全文对照 + 真实 inputTokens / 墙钟对照」本地无法产出。离线替代物已给出:`PROGRESS-report-skill-parity-20260901.md` 里的每章 prompt 载荷改前改后逐章对照、claim card 全文对照、以及对服务器确定性 conclusion 的 5 处逐行回溯。真实生成待部署后由委托方补。
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# PROGRESS · Agent 聊天流式体验与双会话面统一(2026-09-01)
|
||||
|
||||
工作树:`.worktrees/streaming-ux-20260901`
|
||||
分支:`codex/streaming-ux-20260901`
|
||||
基线:`origin/staging` @ `fbb80fa3`(任务书写 `80e77361`,开工时远端已多一条任务书本身的 docs commit)。
|
||||
|
||||
未改 `.gitea/workflows/**`。未合并 staging,未碰 main。未手写 `useCallback` / `useMemo`(`rectification-agentic-chat.tsx` 既有的 `useCallback` 原样保留)。未动服务端事件语义;校正 `thinking.delta` 在 `stream-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.tsx`、`src/components/chat-message-row.tsx`(`streaming` prop)、`src/hooks/use-consultation-run.ts`、`src/components/rectification-agentic-chat.tsx`。
|
||||
|
||||
- 两个会话面的事件回调都不再直接 `setState`:事件写入累加器,`requestAnimationFrame` 每帧最多一次 flush;`run.completed` / `run.failed` / abort / 读流结束时 `settle()` 同步冲干净;`finally` 里 `dispose()`;校正 `attempt.reset` 走 `reset()`。
|
||||
- 释放节奏:`max(2, ceil(积压 ÷ 12))` 字/帧,积压按「新文本到达时的未释放量」记,所以 3,000 字突发恰好 12 帧追平(测试锁死),稳态约落后网络 12 帧(≈200ms);页面隐藏走 250ms `setTimeout` 并一次放完。
|
||||
- 重活只做一次:`parseAgentReply` / `applyThinkingSectionProgress` 只在 flush 里对已释放文本跑。
|
||||
- Markdown:流式时在最后一个空行切「稳定前缀 / 尾块」,不切进代码围栏、同一列表的项之间、表格行之间;前缀交给 `memo` 的 `StableMarkdownPrefix`,只有尾块每帧 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.ts`(`latestAssistantView`)、`src/lib/home-streaming-render-probe.ts`(`latestEntryMounts`)、`src/components/consultation-run-timeline.tsx`(重写)、`src/components/chat-message-row.tsx`(GSAP 0.16)、`src/app/globals.css`、`scripts/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.16(DESIGN.md §6 = 160ms)。
|
||||
- 时间线:去掉 `key={live…}`;外层 `<section>` + `button[aria-expanded][aria-controls]`;`open = userOpen ?? live`;折叠是 `grid-template-rows 0fr↔1fr` 180ms,折叠后内容 `inert`;summary 文案换行 120ms 淡入;live 行与 summary 文案共用既有 `agent-activity-status__text` shimmer;`rows` 为空且 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.tsx`、`src/components/agent-activity-status.tsx`(重写为兜底)、`src/components/chat-message-row.tsx`、`src/app/globals.css`、`package.json` / `package-lock.json`(`npm uninstall thinking-orbs`);删除 `consultation-thinking-report.tsx`、`thinking-step-tree.tsx`、`completed-activity-receipt.tsx`。
|
||||
|
||||
- 适配:tool trace → `calculate` 行(started=live / completed=done,标签沿用 `RECTIFICATION_TOOL_*_LABELS`);`failedTool` 的行标签加「未完成」且不挂 chips;回执 `methods` 经 `PUBLIC_RECTIFICATION_METHOD_LABELS` 去重 ≤8 作为最后一条完成 calculate 行的 `sources`;没有 live 行且 `activity.label` 以「正在」开头时补一条 live 行(`answer-composition` → `write`,其余 → `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 160ms,0.18 是 GSAP 副本漂移(BUG-474) |
|
||||
| `tests/session-conversation-layout.test.ts` | `duration:\s*0\.18` | `0\.16` | 同上 |
|
||||
| `tests/class-name-definition-contract.test.ts` | `knownUnstyled` 含 `consultation-run-timeline__summary`、`consultation-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}`;`ConsultationThinkingReport`、`hasTrace`、`activityTrace={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` | `deltaBranch` 内 `state: 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 一次 setMessages(BUG-473),后者锁的是校正专属红字失败行(BUG-476)。**注**:这个文件在任务 0 的聚焦跑里漏跑了,任务 2 跑全量前才发现,改在 `cc17d971` |
|
||||
|
||||
新增测试:`tests/stream-frame-buffer.test.ts`(7)、`tests/chat-markdown-split.test.ts`(4)、`tests/chat-stream-settle-contract.test.ts`(4)、`tests/rectification-timeline-adapter.test.ts`(6)、`home-streaming-render-split` +1。
|
||||
|
||||
## 命令输出
|
||||
|
||||
`./node_modules/.bin/tsc --noEmit` → **0**(任务 0/1/2 各跑一次,均无诊断)。
|
||||
|
||||
`./node_modules/.bin/eslint` 触及文件 → **0 error**。`use-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 build` → **0**。`✓ Compiled successfully in 21.7s`、`Finished TypeScript in 58s`、`Generating static pages (62/62)`,`/` 仍是 `○ (Static)`。5 条既有 `skill-package-registry.ts` filesystem-tracing warning,未失败。
|
||||
|
||||
## 未做与原因
|
||||
|
||||
见 `BLOCKED.md` 顶部三条:任务 3 等第三批拆页;浏览器级手工验收(Performance 录制、结算录屏、两面并排截图、流式期间折叠)无登录态无 Chrome;`latestEntryMounts` 探针无 DOM 测试库不可驱动。
|
||||
+27
-4
@@ -156,6 +156,8 @@ is read through an external store so a change in one tab reaches the others.
|
||||
|
||||
Display headings use the serif stack at weight 400. Body copy never drops below 14px; 12–13px is reserved for short labels and metadata. CJK text uses `text-wrap: pretty`; display text uses `text-wrap: balance`.
|
||||
|
||||
One documented exception: the thinking text inside a timeline step (`.consultation-run-timeline__thinking`) and the fallback thinking trace (`.message-thinking-body`) render at 13px. They are working notes shown on request inside a collapsed row, not reading copy; the answer itself never inherits that size.
|
||||
|
||||
## 4. Spacing & Layout
|
||||
|
||||
The base unit is 4px. Tokens are `--space-1: 4px`, `--space-2: 8px`, `--space-3: 12px`, `--space-4: 16px`, `--space-5: 20px`, `--space-6: 24px`, `--space-8: 32px`, `--space-10: 40px`, `--space-12: 48px`, `--space-16: 64px`, and `--space-24: 96px`.
|
||||
@@ -261,10 +263,26 @@ The base unit is 4px. Tokens are `--space-1: 4px`, `--space-2: 8px`, `--space-3:
|
||||
|
||||
- **Variants:** assistant editorial text on canvas; user text on warm card surface; streaming; error. Streaming uses a timeline of completed steps plus the current step; the thinking body expands while streaming, collapses when answer text appears, and is stored with the assistant message.
|
||||
- **Identity:** every assistant message carries the 32px Jyotisha logo avatar; user messages stay visually lighter and avatar-free.
|
||||
- **Typography:** assistant body 16px with serif subheadings; user body 14px.
|
||||
- **Typography:** assistant body 17px (16px below 768px) with serif subheadings; user body 14px.
|
||||
- **Tables:** three-column technique audit tables keep 状态 on one line. Below 768px they stack each row as title + status, then the note, instead of squeezing 已执行 into a vertical glyph column.
|
||||
- **Follow-up:** the latest settled consultation answer may offer two or three grounded next questions under that answer. Clicking one sends it in the current session. The composer never hosts suggestion chips. If the answer does not support a grounded continuation, nothing is shown.
|
||||
- **Motion:** new messages enter with a short opacity/translate transition only.
|
||||
- **Motion:** a new row enters once, through the GSAP tween in `chat-message-row.tsx` at the 160ms Message duration; there is no CSS entrance keyframe beside it. The trailing assistant reply is one component (`LatestAssistantEntry`) from its first streamed token through settlement, so settling never remounts it and never replays the entrance.
|
||||
|
||||
#### Streaming states
|
||||
|
||||
Every assistant reply moves through the same states on both chat surfaces, and each state has exactly one visual. The step timeline (`ConsultationRunTimeline`) is the only activity surface; the rectification session projects its tool trace onto the same rows and differs only in that it never shows thinking text, because the server drops rectification `thinking.delta` at the public boundary.
|
||||
|
||||
| State | When | Visible | Transition in |
|
||||
|---|---|---|---|
|
||||
| `queued` | request sent, no server event yet | timeline open with one live row “正在处理…” (spinner + shimmer label), summary “正在分析” | row enters with the message |
|
||||
| `loading-method` | `skill.started` | live method row | label swap, 120ms fade |
|
||||
| `calculating` | `tool.started` / chart activity | live calculate row; completed rows above it show the check marker | label swap, 120ms fade |
|
||||
| `thinking` | `thinking.section` / `thinking.delta` | live think row; thinking text inside the row, paced per frame | label swap, 120ms fade |
|
||||
| `composing` | first `answer.delta` | live write row; answer text below the timeline, released per frame | answer paragraphs appear as text, no per-token animation |
|
||||
| `settled` | `run.completed` | summary becomes “已完成 N 步”, timeline collapses in place unless the reader opened or closed it themselves; actions and follow-ups appear | 180ms height transition, 120ms label fade |
|
||||
| `failed` | `run.failed`, abort, network loss | whatever was received stays in place; the message carries no inline banner — the notice goes to the composer notice / error line | none |
|
||||
|
||||
Text release is paced, not animated: the frame buffer commits at most once per animation frame and reveals `max(2, ⌈backlog ÷ 12⌉)` characters per frame, so a burst catches up in about twelve frames and a slow model never reads as stalled. The reader's own toggle on the timeline always wins over the state default. Under `prefers-reduced-motion: reduce` the collapse and the label fade are instant; the pacing stays, because it is content arrival rather than decoration.
|
||||
|
||||
### Suggestion card
|
||||
|
||||
@@ -340,6 +358,9 @@ system.
|
||||
| Micro | 120ms | ease-out / `--ease-out` | Button, row, popover and tooltip feedback |
|
||||
| Standard | 180ms | `--ease-out` | Dialogs, onboarding cards, auth steps |
|
||||
| Message | 160ms | `--ease-out` | A message row entering the transcript |
|
||||
| Thinking collapse | 180ms | `--ease-out` | The step timeline opening or closing in place (`grid-template-rows` 0fr ↔ 1fr) |
|
||||
| Timeline label swap | 120ms | ease-out | “正在分析” → “已完成 N 步”, and a live row's label changing |
|
||||
| Text reveal | per-frame release, not a duration | none | Streamed answer and thinking text; see Streaming states |
|
||||
| Sheet | 280ms | `--ease-out` | Rectification sheet rising from the bottom |
|
||||
| Spatial | Instant | None | Sidebar state changes and mobile drawer |
|
||||
|
||||
@@ -372,8 +393,10 @@ or user IDs.
|
||||
| 类别 | 语义 | 组件 / 样式 | 时长 | `prefers-reduced-motion: reduce` |
|
||||
| --- | --- | --- | ---: | --- |
|
||||
| 整页 / 整块阻塞 | 载入账户、正在准备问题、生时评估浮层 | `AppLoadingIndicator`(轨道环 `app-loading-orbit`) | 1.4s linear | 全局 `*` 规则把循环收成一帧;不要改它的 DOM |
|
||||
| 行内 / 局部等待 | 出生地解析、咨询时间线 live 步、个人报告列表与详情 | `InlineSpinner`(`inline-spin`) | 0.8s linear | `animation: none`,收成静止圆点,不要半圈圆弧 |
|
||||
| 流式生成中 | 引导语打字、Agent 活动字 | `onboarding-caret` / `agent-activity-shimmer` | 700ms steps / 1.6s linear | 保持现有全局降级 |
|
||||
| 行内 / 局部等待 | 出生地解析、两个会话面时间线的 live 步、兜底活动面板的 live 行、个人报告列表与详情 | `InlineSpinner`(`inline-spin`) | 0.8s linear | `animation: none`,收成静止圆点,不要半圈圆弧 |
|
||||
| 流式生成中 | 引导语打字、时间线 summary 与 live 行的文案 | `onboarding-caret` / `agent-activity-shimmer` | 700ms steps / 1.6s linear | 保持现有全局降级 |
|
||||
|
||||
Agent 的 live 标记只有 `InlineSpinner` 一种。曾经并存的 canvas 小球(`thinking-orbs`)已移除,不得再引入第二种 live 标记。
|
||||
|
||||
今日星语首次拉取是行内等待,但不用 spinner、也不用透明度呼吸:卡片用静态占位文案(`aria-busy` 仍保留)。轨道环消失后不得再换一套动效继续等。
|
||||
|
||||
|
||||
@@ -104,7 +104,6 @@ export function createStreamFrameBuffer<Meta>(
|
||||
let answerBacklog = 0;
|
||||
let thinkingBacklog = 0;
|
||||
let meta = options.initialMeta;
|
||||
let dirty = false;
|
||||
let disposed = false;
|
||||
let frameHandle: number | null = null;
|
||||
let timeoutHandle: number | null = null;
|
||||
@@ -121,7 +120,6 @@ export function createStreamFrameBuffer<Meta>(
|
||||
};
|
||||
|
||||
const emit = (settled: boolean) => {
|
||||
dirty = false;
|
||||
options.flush({
|
||||
answer: releasedAnswer,
|
||||
thinking: releasedThinking,
|
||||
@@ -173,12 +171,10 @@ export function createStreamFrameBuffer<Meta>(
|
||||
setMeta(next) {
|
||||
if (disposed) return;
|
||||
meta = typeof next === "function" ? (next as (current: Meta) => Meta)(meta) : next;
|
||||
dirty = true;
|
||||
schedule();
|
||||
},
|
||||
touch() {
|
||||
if (disposed) return;
|
||||
dirty = true;
|
||||
schedule();
|
||||
},
|
||||
settle() {
|
||||
@@ -198,7 +194,6 @@ export function createStreamFrameBuffer<Meta>(
|
||||
releasedThinking = "";
|
||||
answerBacklog = 0;
|
||||
thinkingBacklog = 0;
|
||||
dirty = false;
|
||||
if (nextMeta !== undefined) meta = nextMeta;
|
||||
},
|
||||
dispose() {
|
||||
|
||||
Reference in New Issue
Block a user