Repository navigation
feat(ui-web): bring the chat view to the Figma design - #710
Merged
Merged
Conversation
The transcript follows the Figma chat design system: a #333 bubble with sent files as tags inside it, a flat white reply card with 16px padding, 11px process lines under a quiet fold header with the turn's duration, the design's table and code-listing boxes, a centred date between two questions far apart, and delivered files as compact cards (type mark or first-page thumbnail, name, size, open / download / open-in-browser). A live thought starts folded, as the design's small thinking card. Listings that name a language are coloured with Shiki (sync core, JS regex engine, a fixed grammar set), in page tokens set to the design's roles; a build step escapes the grammars' literal "<!--" so the inlined script still parses. The desk grid no longer clips its panes' drop shadows. Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
gloryfromca
reviewed
Sep 23, 2026
gloryfromca
left a comment
Member
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
I reviewed the current github/main...HEAD diff and the surrounding transcript, workspace delivery, history/live-session, prose-rendering, build, and styling paths. I also checked the repository rules and Web UI vocabulary/layering constraints, backward compatibility of restored and live asks, and the test edits for weakened coverage. The Shiki payload increase is substantial, but it is measured and explicitly disclosed in the PR description rather than hidden.
Verification on this revision:
npm testinui-web: 208 files, 2,874 tests passed.npm run type-check && npm run lint && npm run gen:checkinui-web: passed; lint reported only the four existing warnings in untouched files.- Production build, Python assembly, boot snapshots,
check-page,check-css, andcheck-class-namespace: passed. - TUI generated-catalogue check, source-language gate, and large-file gate: passed.
- CI checks shown on the PR are green.
gloryfromca
approved these changes
Sep 23, 2026
7 of 12 tasks
LivXue
added a commit
that referenced
this pull request
Sep 23, 2026
…nk instead (#721) ## Summary At the narrowest width the web UI lays out, the bar under the composer field broke onto a second line and the send button dropped onto it, left-aligned under the "+". The bar was a flex row allowed to wrap, and a wrapping flex row picks its line breaks from its items' whole widths before any of them shrinks. So a second line was the only answer it had to a narrow column, and the send button, last in the row, was the item that landed there. Measured on main before this change, the range is wider than the 780px case reported. With the rail open, the bar wrapped at every width from the 780px floor to 820px (838px was the first width measured to fit), and up to 940px once the usage meter shows text (980px fit). It also wrapped at 780px in Chinese with the context ring shown, at 780px with the rail at its widest (which leaves the chat column its 430px floor), and at 1024px once a long folder name, a long model id, the context ring and a populated meter shared the row. The row no longer wraps, so no width can put the send button on a line of its own. The four items whose width is their text (the folder, the permission mode, the usage meter and the model) each take an equal share of what the fixed controls leave, never more than their own width, and each label ends in an ellipsis. Two decisions are worth reviewing: - Equal shares rather than proportional shrinking. Shrinking in proportion takes the most from the widest box, and a chip is mostly its icon and padding, so a short model name beside a long folder name went to nothing. Capped at its own width, a short name stays whole and only a long one gives: in Chinese at 780px with the context ring, both 48px labels stay whole and only the model name shrinks. - The chip shrinks with the anchor it hangs in, and only its name clips. The anchor itself is not clipped, because the popover it holds stands outside the anchor's box. Where the bar already fit, nothing moves: all 37 boxes in it measured identical before and after in the 8 layouts where the old bar fit (860px to 1440px, with and without the context ring, long names and a populated meter). The sub-agent composer shares the `.under` rule and loses its `flex-wrap` too. Its bar never wrapped, because its message line is already `flex: 1`, and all 12 of its layouts measured are identical before and after. Not fixed here: - The permission chip carries no hover title, so a shortened mode name is read in full only through its accessible name or its popover. The folder and model chips already carry one. - At the tightest combination (780px window, rail at its widest, context ring and usage meter shown) each label keeps about 5px, so those chips read as their icons. - Only Chrome was measured. The stylesheet already uses `:has()` in 28 places, so this adds no browser requirement. ## Type - [x] Fix - [ ] Feature - [ ] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification In `ui-web/`, with `NODE_OPTIONS=--localstorage-file=<file>` so that node 26 does not shadow happy-dom's `localStorage`. Everything below ran on the final base, `cfe082bb0` (#710), unless it says otherwise. - Red then green on the four new cases in `src/chrome/Dock.test.tsx`. With the stylesheet at the base, three fail: `never wraps`, `lets every box between a label and the bar give, and ends the label in an ellipsis`, and `shares what the controls leave equally, each at most its own width`. With the fix, the file passes 26 of 26. The fourth case, `keeps the controls that are not text at their own size`, is a guard and passes on both. - Two mutants, each caught by exactly one case: widening the anchor selector so it also takes the "+" fails `keeps the controls that are not text at their own size`, and going back to proportional shrinking fails `shares what the controls leave equally, each at most its own width`. - Every test file that reads `src/styles/page.css`, plus the CJK literal gate: 20 files, 474 tests passed (with `--testTimeout=30000`, for the load noted below). - `npx vitest run --no-file-parallelism` on an earlier base, `3a463d6cd`: 206 files, 2858 passed, 4 failed, 1 unhandled error. None of them is this change's: - `features/desk/palette.test.ts` and `features/desk/store.test.ts`, their "when storage refuses" cases: the known node 26 local artifact, green in CI, failing the same way on the base. - `state/session/registry.test.ts`, cases of "the live session switch": 5000ms timeouts on a box at load average 48 to 90 (two in the full run, three with the file alone, the same on a tree byte-equal to the base). The file passes 28 of 28 with `--testTimeout=30000`, and CI's `ui rpc contract` job is green on that base. - an unhandled `ReferenceError: document is not defined` from a timer in `state/detail.ts` that fired after its test environment was torn down, attributed to `features/extAgents/ExtAgentsPage.test.tsx`. It does not reproduce with that file alone, on either tree, and neither file reads anything this change touches. - `npm run type-check`: clean. `npm run lint`: 0 errors, 4 warnings, all `react-hooks/exhaustive-deps` in files this branch does not touch. `npm run gen:check`: matches the contract. - `npm run build` then `python3 build.py`: both boot goldens match (290 and 289 nodes). `check-page.mjs`, `check-css.mjs` and `check-class-namespace.mjs`: OK. Gates: `commitlint --from origin/main --to HEAD`, `scripts/check_commit_messages.py origin/main..HEAD`, `scripts/check_large_files.py origin/main...HEAD`, `scripts/check_source_language.py origin/main...HEAD` and `pre-commit run --from-ref origin/main --to-ref HEAD` all exit 0, and no hook modified a file. The suite has no layout engine, so the bar was also measured in headless Chrome against the fixture transport (`?stub=1`), built with and without the fix from the final base. Per layout the probe reads whether every item in the bar shares one row, the send button's offset from the bar's right edge, overlap between neighbours, and any icon spilling out of its chip: | layouts measured | base | this change | |---|---|---| | English, 21, from 780px to 1440px | 15 wrap | 0 wrap | | Chinese, 10, from 780px to 1440px | 3 wrap | 0 wrap | | usage meter shown, 900 / 940 / 980px | 2 wrap | 0 wrap | | where the old bar fit, 8 | - | 0 of 37 boxes move | | sub-agent composer bar, 12 | - | identical | | 3 popovers over shortened chips | - | anchored, in view | The same measurements on the first base, `3a463d6cd`, gave the same results. - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [x] User-facing docs or screenshots are updated when needed No user-facing doc describes the bar, so none changed. ## Risk At widths where the bar used to wrap, it now stays on one row and its text labels shorten with an ellipsis instead; the send button stays at the right end. Where the bar already fit, nothing moves. The `.under` rule is shared with the sub-agent composer, which loses `flex-wrap: wrap` too. Its layouts measured identical, since its message line never let it wrap. No security surface: stylesheet rules and one test file. Rollback: revert the commit. - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A Co-authored-by: Claude (claude-opus-5-5[1m]) <noreply@anthropic.com>
0xKT
added a commit
that referenced
this pull request
Sep 23, 2026
The redesigned transcript (#710) landed on main with its own icon map, which still named the retired tool beside web_fetch. The entry goes; an archived call renders through the drawn-glyph fallback like any other name the map does not know. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
0xKT
added a commit
that referenced
this pull request
Sep 23, 2026
The redesigned transcript (#710) landed on main with its own icon map, which still named the retired tool beside web_fetch. The entry goes; an archived call renders through the drawn-glyph fallback like any other name the map does not know. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
6 of 12 tasks
0xKT
pushed a commit
that referenced
this pull request
Sep 23, 2026
) ## Summary The fold that stands in for a finished turn -- "done", and every line inside it -- was set at 11px while the answer it sits with is 14px. Someone who opens that fold is reading the trail, not glancing past it, so type three sizes below the copy around it made them lean in for the one thing they had asked to see. The head, the call rows, the stretch summary and the thought line all move to 14px on a 22px line. The parts that ride on a row move with them -- the index, the clock, the diff counts, the glyph -- so a row stays one piece rather than a large label with small furniture beside it. The badge pill goes to 12px instead: a pill reads as a notch below its row, not level with it. Two decisions worth a reviewer's eye: - The four line kinds (`.think`, `.tfh`, `.wrow.sum`, and `.wrow`) stay declared together. #710 put them in one rule on purpose, after they had drifted to 13 and 13.5px in two different greys and read as three kinds of thing. Raising only the fold head would have recreated exactly that drift, so the whole kind moves at once. - The two comments that named the old numbers are rewritten. A comment still saying 11px would misdescribe the rule directly under it, which is how the previous drift went unnoticed. This reverses the sizing part of #710 for these four line kinds only. The greys, the spacing, the glyph treatment and everything else that PR brought to the Figma design are untouched. ## Type - [x] Fix - [ ] Feature - [ ] Docs - [ ] CI / tooling - [ ] Refactor - [ ] Other ## Verification One file changed, `ui-web/src/styles/page.css`, +22 -18. Run on the branch, based on `5c4ae70b6`: ``` npm test --prefix ui-web 208 files, 2890 tests passed npm run lint --prefix ui-web 0 errors, 4 pre-existing warnings npm run type-check --prefix ui-web passed npm run gen:check --prefix ui-web generated.ts matches the contract (202 methods) make check-large-files passed scripts/check_source_language.py github/main..HEAD no findings npx commitlint --from github/main --to HEAD passed ``` Measured in a real browser rather than read off the stylesheet: the page was built, served on a live gateway, and `getComputedStyle` read back on an actual finished turn with its fold open and its rows expanded. | | before | after | answer body | |---|---|---|---| | `.tfh` (done) | 11px / 15px | 14px / 22px | 14px | | `.wrow` (call row) | 11px / 15px | 14px / 22px | 14px | | `.wrow.sum` (summary) | 11px / 15px | 14px / 22px | 14px | | `.think` (thought) | 11px / 15px | 14px / 22px | 14px | - [x] Relevant tests pass locally - [x] Relevant lint / type checks pass locally - [ ] User-facing docs or screenshots are updated when needed The last box is unchecked because no checked-in screenshot shows this surface. The before and after were captured locally to compare the two, and are not committed -- this repo does not take image assets. Note for anyone re-running the gates: `make check-source-language` hardcodes `origin/main..HEAD`, and in a checkout whose `origin` is the GitLab mirror that range covers a few hundred upstream commits and fails on their fixtures. Run the script with `github/main..HEAD` to lint this branch alone. ## Risk Presentation only: no markup, no state, no behaviour. A finished turn's trail occupies more vertical space than it did, which is the point of the change and the one thing to look at. Rollback is the single file: revert this commit. - [x] Security impact considered - [x] Backward compatibility considered - [x] Rollback path is clear for risky changes ## Related Issues N/A Co-authored-by: arelchan <204152633+arelchan@users.noreply.github.com> Co-authored-by: Claude (claude-opus-5) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the web chat view to the Figma chat design system (Raven file: UserMessage, AssistantMessage, the Thinking compositions, dates between long-gap messages, and the flattened layout of message types).
Messages
< CSS >.Process and thinking
Dates
AskData.at) for this.Delivered files
Syntax highlighting
--code-*, light and dark) set to the design's roles. Unknown or unnamed languages render as before.<!--, which breaks an inlined script. A build-only Vite step rewrites it to\x3C!--after minification (it occurs only in string, template and regex literals, where it is the same character).Other
overflow: hidden, which clipped its panes' drop shadows.gui.transcript.date_today,gui.transcript.date_yesterday,gui.arts.browser,gui.arts.download;gui.think.livegains an ellipsis. ui-tui's generated catalogue is regenerated.lone-delivery-cssis replaced bydelivery-picture-css, which pins the same invariant for the new card (the picture is sized from its height and never cropped). The class-namespace pins for the transcript went down and are lowered.Type
Verification
npx tsc --noEmit -p tsconfig.json(ui-web): passnpx eslint .(ui-web): 0 errors (4 existing warnings in files this PR does not touch)npx vitest run(ui-web): 2873 passed, 1 failed. The failure issettings-drawer-css("sizes classes the markup actually carries"), which fails on main too on Windows: the gate builds the pathD:\D:\...and cannot scan it.node scripts/check-css.mjs,node scripts/check-class-namespace.mjs: OKnpm run buildthenuv run python ui-web/build.pythennode ui-web/scripts/check-page.mjs: OK (boot snapshots match their goldens)node ui-tui/scripts/gen-i18n.mjs --check: up to datescripts/check_source_language.py origin/main..HEAD,scripts/check_large_files.py origin/main..HEAD: exit 0Checked in the browser on
?stub=1at 1440px against the Figma frames: one-line bubble 40px, card padding 16px, table rows 40px, file card 340x66, process lines 23px apart, thinking card 40px; light and dark themes; a desk pane's shadow drawing past the grid. The built dist page also boots offline with no console errors.Relevant tests pass locally
Relevant lint / type checks pass locally
User-facing docs or screenshots are updated when needed
Risk
User-visible: the whole chat transcript restyles; a live thought is folded by default; delivered markdown / code files show a type mark instead of a miniature. Highlighter output is built from escaped token text in spans whose colours come only from the theme, so no source markup reaches the page. The main operational cost is the page size above. Rollback is reverting this one squash commit; the page has no stored state that depends on it.
Related Issues
N/A