fix(web): keep composer enabled while generating and treat stop as neutral (BUG-551, BUG-552)
Enter now queues one follow-up instead of dropping it, and a rectification stop leaves the streamed reply with a grey notice instead of an error alert. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+8
-6
@@ -192,7 +192,8 @@ Radii have two visual steps. Controls use 8px (`--radius-md`; `--radius-xs` and
|
||||
### Input and composer
|
||||
|
||||
- **Structure:** warm canvas field, hairline, typed value, optional icon action.
|
||||
- **States:** default, hover, focus with deep-brown ring, disabled, invalid, loading.
|
||||
- **States:** default, hover, focus with deep-brown ring, disabled only for structural reasons (readonly, session loading, onboarding, the other surface open), invalid. Generating does not disable the textarea; the send control becomes stop.
|
||||
- **Queue:** one pending card (`.composer-queue`) sits above the field while an answer is in flight. Copy: “已排队,回答结束后发出”, plus the text and a 44px “撤回”. A second Enter appends to the same card with a newline. Successful settlement sends it; stop, failure, or recovery puts the text back in the field. The send button is disabled while a card is waiting. No spinner.
|
||||
- **Accessibility:** persistent label where practical; composer has an explicit accessible label.
|
||||
- **One composer:** the rectification surface renders the same `ChatComposer` as the main chat; there is no second composer. A surface that owns its own draft passes it as `value`; the main chat reads the draft store. Both count down from the same 500-character ceiling through `CharacterRemaining`.
|
||||
|
||||
@@ -223,18 +224,18 @@ The birth-time rectification session is the consultation transcript plus a house
|
||||
|
||||
| State | Trailing entry | Composer |
|
||||
|---|---|---|
|
||||
| `opening` | live row “正在读取你的出生资料,准备第一个问题…”, then tool labels | disabled, stop visible |
|
||||
| `opening` | live row “正在读取你的出生资料,准备第一个问题…”, then tool labels | enabled (typing queues), stop visible |
|
||||
| `empty` | “这段校正还没有开始。” and one primary action “开始提问” | enabled |
|
||||
| `question-live` | the asking message carries the embedded card or the spoken stem | enabled, placeholder points at the card |
|
||||
| `question-gap`, retries left | one timeline live row “正在准备下一个问题…”, refetching on a 2s timer up to two retries | enabled |
|
||||
| `question-gap`, retries spent | “没有拿到下一个问题。” and a 44px “重新加载” | enabled |
|
||||
| `verified_idle` | one closing line `postAdoptVerifyDone` after adopted reverse-verify is exhausted; no spinner, no reload | enabled |
|
||||
| `choice-pending` | the answered card (`data-selected` fill, a top row “正在记录…”) and the same live row from “正在记录本次选择…” through the follow-up turn | disabled, stop visible |
|
||||
| `choice-pending` | the answered card (`data-selected` fill, a top row “正在记录…”) and the same live row from “正在记录本次选择…” through the follow-up turn | enabled (typing queues), stop visible |
|
||||
| `candidates` | the offer cards under the message that offered them | enabled |
|
||||
| `adopting` | “正在采用 HH:MM…” through the follow-up turn | disabled during the chain |
|
||||
| `adopting` | “正在采用 HH:MM…” through the follow-up turn | enabled (typing queues), stop visible |
|
||||
| `confirmed` | “已确认校正时间:HH:MM” | enabled |
|
||||
| `readonly` | “该校正已结束,只能查看历史。” and “再次校正” | disabled |
|
||||
| stopped | the row settles with what streamed; notice “已停止,已生成的内容保留;本次不会扣点。” | enabled |
|
||||
| stopped | the row settles with what streamed; a grey caption “已停止,已生成的内容保留;本次不会扣点。” under the body, never `role="alert"` | enabled |
|
||||
|
||||
- **Rules:** a follow-up turn continues on the live row already in place; `busy` never drops in the middle of a chain. No copy may ask the reader to wait for the server; a gap is a live row with retries, then a reload button. A hydration that timed out is the same gap. A 402 shows “校正点数不足,正在前往兑换…” for 600ms before the page leaves.
|
||||
- **Board:** before any candidate exists the header clock shows the declared birth minute and the body reads “填报出生时间 HH:MM” / “回答几个问题后,这里会显示宫位随时间的变化。”; the column narrows to `minmax(16rem, 18rem)` (`is-board-empty`) and widens once a result arrives. No house table is invented for the declared time; the snapshot API does not provide one.
|
||||
@@ -329,7 +330,8 @@ Every assistant reply moves through the same states on both chat surfaces, and e
|
||||
| `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 |
|
||||
| `stopped` | the reader pressed stop | whatever streamed stays; a grey caption under the body says the run stopped and was not billed | none |
|
||||
| `failed` | `run.failed`, 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user