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:
Jesse_Chen
2026-09-01 23:14:42 +00:00
parent 6356d7b549
commit eeb82dfdc4
4 changed files with 128 additions and 9 deletions
+27 -4
View File
@@ -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; 1213px 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` 仍保留)。轨道环消失后不得再换一套动效继续等。
-5
View File
@@ -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() {