merge origin/staging into the report translation fix (issue #53)
Independent Staging Quality Gate / validate (push) Successful in 15m24s
Independent Staging Quality Gate / publish (push) Successful in 3m48s

Docs-only conflict in BUG_HISTORY: BUG-1311 (#52) and BUG-1312..1315 (#53)
both appended after BUG-1310; kept both in number order. Product owner
chose option A on #53: Claude merges a stale branch when only docs conflict.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LutgzyJqNhPpZy1nFsJtN3
This commit is contained in:
Claude
2026-10-13 18:44:38 +08:00
6 changed files with 50 additions and 2 deletions
+1
View File
@@ -344,6 +344,7 @@ Radii have two visual steps. Controls use 8px (`--radius-md`; `--radius-xs` and
- **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`, which the composer renders inline and only once `characterRemainingVisible()` is true — it no longer has a permanent container. Controls that belong to the composer go through the `toolbar` slot; nothing is stacked below it.
- **Inner field corners:** the textarea inside `.composer` has `border-radius: 0`. Rounding stays on the outer `.composer` only. The field has no horizontal padding, so a radius on the textarea itself clips the first glyph's left edge (BUG-1311).
### Answer start anchor
+1 -1
View File
@@ -1857,7 +1857,7 @@ section[popover]:has(> [data-sonner-toaster]), section[popover][aria-label^="页
.composer-tools { min-width: 0; display: flex; align-items: center; gap: var(--space-2); }
.composer-row-end { display: flex; flex: 1; align-items: center; justify-content: flex-end; gap: var(--space-3); }
.composer:focus-within { border-color: var(--color-action); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-action) 15%, transparent); }
.composer textarea { width: 100%; min-width: 0; min-height: 28px; max-height: 148px; resize: none; padding: var(--space-1) 0 0; border: 0; outline: 0; background: transparent; color: var(--color-ink); line-height: 1.5; font-size: var(--type-body-md); }
.composer textarea { width: 100%; min-width: 0; min-height: 28px; max-height: 148px; resize: none; padding: var(--space-1) 0 0; border: 0; outline: 0; background: transparent; color: var(--color-ink); line-height: 1.5; font-size: var(--type-body-md); border-radius: 0; }
.composer .composer-row-end > button { width: 36px; height: 36px; display: grid; flex: 0 0 auto; place-items: center; border: 0; color: var(--color-on-action-fill); cursor: pointer; transition: background-color 120ms ease-out, transform 120ms ease-out; border-radius: 999px; background: var(--color-action-fill); }
.composer .composer-row-end > button:disabled { opacity: 1; background: var(--color-action-fill-disabled); color: var(--color-on-action-fill-disabled); }
.composer .composer-stop { background: var(--color-action-fill); }
@@ -62,6 +62,10 @@ test("otp and composer keep their existing safe sizes", () => {
assert.match(ruleFor(".composer textarea {"), /font-size:\s*var\(--type-body-md\)/);
});
test("composer textarea radius is 0 because a 0 left padding clips the first glyph on rounded corners", () => {
assert.match(ruleFor(".composer textarea {"), /border-radius:\s*0/);
});
test("personal-report tables are not retargeted by the chat wide-table rule", () => {
const mobile = css.slice(css.indexOf("@media (max-width: 767px)"));
const wide = mobile.match(