web-ui: port the Beautiful UI primitives into the chat surface - #992
Open
time-attack wants to merge 24 commits into
Open
web-ui: port the Beautiful UI primitives into the chat surface#992time-attack wants to merge 24 commits into
time-attack wants to merge 24 commits into
Conversation
Defines the Beautiful UI palette (page/canvas/surface/ink/line/field, blue/green/ orange/red with tints, tooltip colours), radii, easing and shared keyframes in shell.css and maps the existing shadcn tokens onto it so every surface recolours consistently in light and dark. Self-hosts the Inter and JetBrains Mono latin subsets. Splits net-new component CSS into src/styles/*.css, each imported from shell.css and covered by the css-vars guardrail. Imported custom themes now emit the same tokens so nothing bleeds through.
…s, compact transcript marker
…become the Beautiful UI recommendation card
…ad cards on inset
Markdown tables in chat and the shared transcript now render in the Beautiful UI Records Table shape: a rounded hairline container that scrolls in both axes with a sticky inset header, a CSS-counter row-number gutter in mono ink-3, 13px tabular-nums cells with a medium-weight first column, hairline row dividers and a hover wash. Column alignment comes from marked's align attribute, which DOMPurify keeps under both sanitizer configs; the new jsdom test renders a table through markdown() to pin that, and the shared-config sanitizer test now covers a table cell too. The Diff Table is left out: nothing in the web UI or core tool results carries row-level add/remove semantics, so there is no data to tint.
…, exec output card
…-card attachments
Unify the three thinking renderings on one Thinking primitive: a sparkle glyph, the label (shimmer while live, "Thought/Worked for Ns" settled) and a chevron, with the trace body opening via a grid-template-rows 0fr→1fr transition on ::details-content and every row sitting on a 1px guide line under the glyph. Per-chunk thinking now renders through thinkingRow, so the old .thinking rule and the horizontal .work-divider go away. Work folds that contain tool calls become task capsules: a status glyph (green check / red X / spinning ring with the call count while live), the duration label, a right-hand tool count, a Completed/Failed pill and the chevron. A fold is red when the turn failed or any tool in it failed; that decision lives in timeline.segmentStatus so it can be unit tested. The capsule marker rides a data-task attribute because the test suite pins the literal <details class="work-fold">. work-fold-source.test.ts: the .work-divider margin pin becomes a pin on the .work-rows guide line, since the divider element no longer exists.
…fix phone and reduced-motion overrides Fonts move under src/ so Vite hashes them into assets/ (base-aware, immutable cache) instead of the no-cache public/ path; the OFL notice ships beside them. Phone and reduced-motion overrides that lived in styles/*.css could never win against shell.css because @imports load first, so they move after the rules they override. The chat column no longer takes its width from the nowrap session title on phones. Drops the dead caret-blink keyframe, folds mc-spin into spin, reuses the thinking shimmer for the search palette's loading state, guards the search timestamp against a missing createdAt, and removes the leftover data-sheen attribute and cron-filter-empty class.
…yphs, keep fold meta in compact panes
…theme derivations, harden the shared sanitizer
…, resolve read-only from state
Tertiary text (--ink-3) moves to 55% / 66% lightness so 11-12.5px labels reach 4.5:1 on the surfaces they sit on. Status pills and diff markers get dedicated --green-ink / --orange-ink / --red-ink text tokens (also emitted for imported themes) instead of painting the fill colour as text on its own tint, and the check / cross glyphs use the surface colour rather than hard-coded white so the dark theme keeps 3:1. Also repairs the approval-pager test after the harness dedupe and flattens a nested ternary in the code gutter.
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
Ports the Beautiful UI component library (https://www.beautifului.dev/, "crafted primitives for AI-native interfaces") into the web UI, everything except the reference's sidebar. The sidebar keeps its structure and only picks up the shared tokens.
Foundation.
shell.cssnow defines the Beautiful UI palette (page / canvas / surface / inset / ink / line / field, blue / green / orange / red with tint and ink variants, tooltip colours), the 6 / 8 / 10 / 14 px radius scale, the strong ease, and the shared keyframes (pixel-on,shimmer-text,fade-up,fade-in,pop-in,spin). The existing shadcn tokens (--background,--card,--border,--muted-foreground,--sidebar, …) are mapped onto it, so every surface recolours consistently in light and dark. Inter and JetBrains Mono (latin subsets, OFL notice alongside) are hashed through Vite fromsrc/fonts. Net-new component CSS lives insrc/styles/*.css, each imported fromshell.cssand scanned by the samecss-varsguardrail (no undefined vars, no new shadows). Imported custom themes emit the same tokens so nothing bleeds through.Primitives mapped onto existing surfaces.
pixelLoader()(3×3 pixel grid) replaces the wave loader on the chat surface; shimmer label; live elapsed time on the working statusworkBlock/thinkingRowunified: sparkle + label + chevron, vertical guide line, grid-rows expansion.message-metabecomes the 24 px action row‹ 1/N ›pager and ghost / primary pills; the transcript marker is the compact version.resource-tabsare status chips with dots; every list page is a hairline table with tinted status pills<mark>, kbd chips, shimmer loading)</>glyph + language + Copy, line-number gutter,difffences tinted, exec output cardNot ported, and why. 11 Diff Table (no tool result carries row-level add/remove semantics), 14 Sidebar Nav (excluded by request), 16 Flowchart (no workflow-builder surface), 17 Insight Cards (no usage or KPI data reaches the web UI), 21 Agent Screen (no screen stream). Loader variants (Dots / Orbit / Surfer), the Pill composer variant, the Task Rows "List" variant, dictation, inline source chips and follow-ups have no counterpart or data in QM.
Review. Three independent lens reviews (control flow and integration; CSS, accessibility and responsive; security and simplification) plus the eight-angle
/code-reviewpipeline ran on the merged diff. Everything confirmed was fixed in the last six commits: no auto-submit from the selection toolbar (it also survives Escape and auto-scroll and resolves read-only from state), the approval pager resets per conversation, the elapsed counter isaria-hidden, expanded folds no longer clip on narrow widths, the chat column fits a phone, phone and reduced-motion overrides moved after the rules they override, fonts honourWEB_UI_BASE,--ink-3and the status pills meet AA,segmentStatusdistinguishes stopped from completed, the code gutter is bounded per frame and refreshes stale diff marks, the shared sanitizer forbidsbackground/bgcolor, and the new jsdom tests share one harness.Test expectations changed
keychain-flow.test.ts:.kc-loading .spinner→ asserts the pixel loader is rendered.work-duration-reconciliation.test.ts: import pin widened forelapsedLabel/workStartedAt.work-fold-source.test.ts:.work-divider→.work-rows { border-left … }(horizontal divider replaced by the vertical guide).pane-composer-source.test.ts: autosize floor pin updated; pane control size 34 → 28 px.paste-text.test.ts:pasteChipLabeltest removed with the function.css-vars.test.tsnow also scanssrc/styles/*.css.New tests:
pixel-loader,approval-pager,tool-chips,context-card-attachments,connector-widget,markdown-tables,session-topbar,search-palette-source,timeline(segment status),text-code(gutter / diff / streaming),settings-page(inspector cards),list-page(filter chips),selection-actions, sharedtest/dom-harness.ts.Verification
plugins/web-ui: typecheck, full suite (986 tests), build with the default and a customWEB_UI_BASE; rootlint,lint:ox,lint:knip,format:check.dev-instancewith a seeded conversation covering a tool call, a markdown table, TypeScript and diff fences, a pending approval, the ⌘K palette, list pages and settings, in light and dark and at 390 px.Screenshots
Before: the previous README hero (
docs/screenshots/web-ui-hero.pngonmain).After (rendered against the dev instance; full set on the
demo/beautiful-ui-screenshotsbranch):Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.