feat(chat): Claude parity visual uplift - #1161
Conversation
Two families now split the type system: Hanken Grotesk carries every control surface, Source Serif 4 carries the document half, meaning the chat greeting, destination page titles, and assistant prose in the transcript. Assistant prose is set at 19px on a 52rem measure shared by the composer, the message rows and the turn separators, so the conversation reads as one column. Machine output inside a reply (code, tables, buttons) stays in the grotesque or mono explicitly, because a blanket serif over the subtree would put it into code blocks too. Message toolbars reveal on hover via opacity rather than visibility so keyboard focus still reaches copy and regenerate; coarse pointers and prefers-reduced-motion get explicit handling. Source Serif 4 chosen for a real weight axis plus a true italic; synthetic oblique on a text serif would be seen constantly in emphasis. Three woff2 subsets with unicode-range so a Latin page fetches one file. The image build asserts the serif face is present in the shipped bundle alongside the existing brand typeface check.
The model selector moves out of the page header into the composer's control row, bound through Placeholder and Chat so a selection sticks to the conversation. On the chat home it was also the empty state's headline, which is why every session opened with a model slug instead of a greeting; the headline is now the person, set in the serif display register. The picker menu flips open upward when the trigger sits near the bottom of the window, anchored to a fixed lower edge so it never measures or jumps. Each row renders the alias's one sentence purpose line from the catalog: control-plane now carries display_name and summary as additive omitempty fields on GET /v1/models, edge-api re-serialises them verbatim, and strict OpenAI clients see no new keys when an alias has neither. Quick-start chips sit under the composer on the chat home. Static four, seed the composer and return focus rather than submitting, because a chip that spends credits on one click gives no chance to edit.
The theme control is System, Light, Dark, the whole set. oled-dark overwrote four gray ramp custom properties with raw hex inline styles, which silently discarded the warm charcoal the brand palette is defined in; her was an upstream easter egg. Stored legacy values fold onto dark or light so existing accounts still resolve. data-theme is now written alongside the dark class in all three places themes change (boot script, settings applyTheme, layout event), because the Hive token layer keys its dark register off data-theme while upstream utilities read the class. Without it a light OS user choosing Dark got upstream surfaces with Hive navigation on top. An unmatched URL now renders inside the product shell instead of SvelteKit's bare root error page. It has to be a catch-all page rather than a group error file: with no matching route there is no layout for an error page to render inside. Specific routes always win over the rest parameter, and auth, share and error routes outside (app) keep their own treatment.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reachedNext included review available in 7 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (23)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Text transcript of the visual proof run: standalone branded image against a stub OpenAI compatible backend, four surfaces captured, no credential-bearing URLs in any frame.
Visual proofVisual proof against hive-open-webui:v0.10.2-branded built from 4e0779d, running standalone on a throwaway network with a stub OpenAI compatible backend. (1) Light theme transcript: assistant prose in Source Serif 4 at the shared reading measure, chrome in Hanken Grotesk. (2) Model picker opened from the composer chip: each alias renders its catalog purpose line under its name. (3) Serif close-up: true italic emphasis plus a code span that stays mono. (4) Dark full window after a localStorage dark reload through the boot script that now writes data-theme alongside the class. Capture log: docs/proof/claude-parity-uplift-1161/capture-log.md |
Visual proofPost-deploy live confirmation on https://chat-hive.scubed.co (deployed fe7eccd): signed-in chat rendering the serif assistant register, reasoning disclosure line, and the composer model-identity control (Hive Auto) from the parity uplift. Captured headless against the running box after the CF colo maintenance window cleared. |
## Summary Adds the committed text capture log for PR #1161's post-deploy live screenshot, per the visual proof rule: the image itself lives as a permanent asset on the visual-proof-assets release, while the scanned text log must live under docs/proof/ so npm run lint:proof-tokens actually covers it. The log records the 2026-08-25 capture against https://chat-hive.scubed.co running the deployed sha fe7eccd, taken with headless chromium via Playwright, signed in through the admin one-time-token magiclink mint documented in docs/live-test-auth.md (no password touched; ssh tunnel to box GoTrue used for the admin mint step only). It lists what the capture shows (serif assistant register, reasoning disclosure, composer model identity Hive Auto, Hive nav sidebar, credits line), points to the uploaded release asset and the inline PR comment, and states the credential posture: no token-bearing URL in pixels or log, session state file deleted after capture. ## Test plan - [x] npm run lint:proof-tokens passes (169 files under docs/proof/ scanned) - [x] Log content matches the actual capture and artifact URLs - [x] Docs-only change, no code touched
…) (#1184) ## Summary Chat parity wave 2. Five of the six dispatched slices were already landed on main by #1161 earlier today; this PR carries the one remaining slice and records where each of the six stands. 1. **Fixed reading measure and hairline turn rules**: shipped in #1161 (slice 3). The transcript, composer column and turn separators share `--hv-measure`, with 1px rules between message rows via `.message-listitem + .message-listitem`. 2. **Four quick-start chips under the composer**: shipped in #1161 (slice 2) as `hive/QuickStartChips.svelte`. Chips seed the composer via `setText` and focus it; nothing auto-sends. 3. **Catalog purpose subtitles in the model picker**: shipped in #1161 (slice 2). `ModelItem.svelte` renders the catalog `description` (the catalog summary carried additively by GET /v1/models) as a subtitle line per alias. 4. **404 and loading states inside the app shell**: 404 shipped in #1161 (slice 1) as the `[...hivePath]` catch-all rendering branded chrome; in-shell slow-load state is the existing sidebar-plus-spinner block in `(app)/+layout.svelte`. The pre-hydration splash image remains an upstream asset, unchanged here. 5. **Theme control at exactly three options**: shipped in #1161 (slice 1). General settings offers System, Light and Dark only; the choice persists to localStorage and System honors `prefers-color-scheme`. 6. **Knowledge nav (#1109), new in this PR**: the row pointed at `/workspace/knowledge`, which sits behind the workspace layout's permission guard. That guard bounces a non-admin without the workspace.knowledge permission straight home, so the click navigated and the person landed back where they started: the row highlighted and did nothing. A new top-level `/knowledge` route outside that guard renders a read-only index of the caller's own bases from `GET /api/v1/knowledge/` (verified-user endpoint); admins and accounts holding the workspace.knowledge permission are forwarded to the full workspace surface. Nav data, tests and hive.css styles updated together. ## Verification - Full frontend unit suite: `npx vitest run`, 12 files, 119 tests passed. - Image build from this worktree: `docker build -f deploy/docker/Dockerfile.open-webui -t hive-open-webui:parity2 .` exited 0; final assertion line printed exactly: `hive: shell present, removed surfaces absent`. - Token audit: every `--hv-*` variable referenced by `hive.css` resolves to a definition in `packages/hive-tokens/tokens.css`; zero unresolved references. - Class anchors preserved: `.message-listitem`, `chat-assistant`, `data-hive-nav` untouched. ## Test plan - [x] Frontend unit tests pass - [x] Image build passes with fail-loud bundle assertions - [x] Token audit clean - [ ] Visual proof capture at merge time (orchestrator) ## Known risks - The read-only index shows only what `GET /api/v1/knowledge/` returns for that caller; a tenant user sees bases shared with them through groups. If product later wants inline document listing from this page, it is a follow-up. - #1056 (knowledge admin short-circuit across tenants) is being handled separately on the backend router; the frontend surface here consumes the same endpoint. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a Knowledge page accessible from the main navigation. * Administrators and permitted users can access full knowledge-base management. * Other signed-in users can view accessible knowledge bases, including descriptions and relative update times. * Added loading, error, and empty states for the knowledge-base list. * **Bug Fixes** * Updated navigation highlighting to correctly recognize Knowledge pages and subpages. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Four defects on the home surface, found by querying the deployed box's DOM rather than reading a screenshot, plus one rough edge in the run path. The greeting and the four quick-start chips shipped in #1161, deployed, and were invisible. Chat.svelte's landing branch read `$settings?.landingPageMode === 'chat' || <messages exist>`, so an account that had ever flipped an upstream personalisation toggle skipped Placeholder.svelte entirely and landed on upstream's ChatPlaceholder: model name over a placeholder string, no greeting, no chips. Nothing was broken and nothing logged; a stored setting was quietly deleting two features. Hive has one home, so the toggle no longer decides whether it exists, and the Interface row that set it goes with the branch it drove rather than staying on as a control that changes nothing. The model menu overflowed the window. It anchored its left edge to the trigger and let its own width run off the right, which is what the model chip in the composer's control row does at a normal desktop width; the panel's max-width clamps how wide it can be and cannot move it back inside. It now anchors right edge to right edge when the trigger is in the right half of the window, which is what the reference does and needs no measurement, so there is still no render-then-move jump. Measured on the box before: right edge 36px past the viewport. After: inside it. The Artifacts index had no title of its own, so its empty state was the first thing on the surface and sat flush against the top of the pane. It gets a page head in the same shape the Knowledge index uses, and the states below it now sit in a column flex, which is what lets their `m-auto` centre at all. A cowork run's row was titled "New Chat". Titles are generated by a follow-up completion the run path does not make, so the conversation list stopped being readable the moment there were two runs. The row takes the brief's first line. Tests: home-surface.test.ts pins the landing branch, the greeting and chip selectors a DOM check can find, and the absence of the removed control. Placeholder.svelte and Settings/Interface.svelte join the fixture list the frontend runner copies, since the pins read their sources.
…on (#944) (#1193) Closes #944. ## What this is D-045 rules that Cowork is a two segment control inside the chat composer, that a run IS a conversation, and that the agent surface gets no navigation row of its own. The shipped product still had the design the owner rejected: an Agents destination you navigate to, a separate form on a page the conversation cannot see, and a run that comes back only as a row in a list. This lands the frontend half of that ruling. ## The composer becomes mode aware A `Chat | Cowork` radiogroup sits immediately right of the plus button, in the same rail as the model chip. It is a `radiogroup` with two `radio` children rather than two buttons, so it announces as "Chat, selected, 1 of 2" and moves with the arrow keys, with a roving tabindex so Tab moves past the control rather than through it. Switching it changes what the next message does and navigates nowhere. Nothing in the change path touches the draft, so a half written brief survives a toggle in either direction. Selecting Cowork grows a second row welded to the bottom of the same composer container, separated by a hairline rather than a gap, and drops the voice mode button while keeping dictation. ## A run rendered as a conversation This is the load bearing part. The run reuses the ordinary chat machinery rather than a parallel one: the same `history`, the same `initChatHandler` that creates the chat and puts it in the sidebar list, the same `saveChatHandler` that persists it, and the same transcript component that renders a chat. The chat is created before the task is submitted, so a run takes a row in the conversation list from the moment it is sent rather than only once it answers. The assistant turn carries the run's state while it is queued or running and the run's own summary once it settles, and it stores the task id, so reopening the conversation picks a run back up. `loadChat` marks any assistant turn left mid flight as done, which is the right recovery for an interrupted completion and the wrong one for a run, because a run does not stop when the tab closes. ## Sidebar grammar that goes with it The Agents navigation row is gone and Artifacts takes its place in the destination set, which is the other half of D-045's sidebar grammar and had an index shipped by #1141 with nothing linking to it. The `/agents` route itself survives, unlinked, so runs submitted before the composer mode existed are still reachable by URL. The conversation list loses its date bucket headers and its heading becomes "Chats and tasks", which is exactly what #944's second acceptance criterion needs: a run and a chat in one list under one heading. New Chat becomes the sidebar's one filled primary row. Knowledge keeps its row for now, and the file says why rather than leaving a silent contradiction with D-045 ruling 2: Projects cannot hold RAG collections yet, so removing the row today would take the only route to them away and put nothing in its place, which is the failure mode #944 warns about for the Agents row. ## What is deliberately not here, and why Two controls the reference's second row carries are absent, for the reasons #944 sets out itself: - The autonomy control ships with the `waiting_for_confirmation` collapse in `engine.go` or not at all. With Auto selected there are no approval rows, so shipping it now would be a control with no observable effect. - The project or folder picker ships only if a run can be bound to a workspace or a collection. `POST /v1/agent/tasks` accepts `pack` and `instructions` and nothing else, so there is nothing for it to set, and a picker that sets nothing is worse than no picker. The Pack control is deleted rather than moved. `agent_kind` still carries the distinction on the wire; the Home composer in Cowork mode sends the knowledge work pack, and the coding pack belongs to a Code panel this shell does not have. ## Backend ceiling, named rather than worked around edge-api exposes a task's status and its final `result_summary_ref` and nothing in between. There is no per step or event feed and no read by id. Two consequences, both marked in the code with the upgrade path: 1. The run is followed by polling `GET /v1/agent/tasks` and filtering, because there is no `GET /v1/agent/tasks/{id}`. Fine at demo volume, wrong at scale. 2. The inline tool lines D-045 describes ("Used Claude in Chrome (2 actions)") and the right hand Progress / Working folder / Context panel cannot be populated by any frontend until that endpoint exists. They are not stubbed, faked or half drawn here. This PR is frontend only by instruction: `vendor/open-webui/src/` and nothing else. No `.py`, no `owui-patches/`, no Go, no `apps/web-console/`, no CI config. ## Verification - `scripts/test-owui-hive-frontend.sh`: 13 files, 139 tests passed, and all 13 Hive components compile against svelte@5.56.0, the version the image build resolves. - New tests: `coworkMode.test.ts`, 19 cases. Pure helpers for the mode, the pack derivation, the radiogroup's key handling and the run-to-turn projection, plus source pins asserting the toggle is mounted immediately after the plus button, that the second row appears only in Cowork, that voice mode drops and dictation does not, and that the submit path branches on the mode and goes through the ordinary chat machinery. The source pins are the ones that matter here: a correct module nobody mounted is the failure a unit test over pure helpers cannot see. - `nav.test.ts` updated: it now asserts the absence of an Agents row and the presence of Artifacts, rather than the reverse. ## Visual proof Posted as a comment on this PR via `scripts/post-pr-visual-proof.sh`. Capture log committed under `docs/proof/`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Cowork mode for launching sandboxed knowledge-work tasks with visible progress. * Added Chat/Cowork switching with keyboard accessibility. * Cowork results, errors, timeouts, and resumed active tasks now appear in conversations. * Added an Artifacts destination with an updated page header. * **UI Improvements** * Renamed “Recents” to “Chats and tasks” and simplified the conversation list. * Added responsive Cowork guidance and updated sidebar styling. * Improved model menu positioning and removed the landing-page mode setting. * **Bug Fixes** * Cowork submissions now reject blank instructions and attachments without losing entered content. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- ## Second commit: four home-surface defects found by querying the deployed DOM These came out of a live re-score of the box and are folded in here because they are the same surface and the same build. **The chat home was gone on some accounts.** The greeting and the four quick-start chips shipped in #1161, deployed, and were invisible. `Chat.svelte`'s landing branch read `$settings?.landingPageMode === 'chat' || <messages exist>`, so an account that had ever flipped an upstream personalisation toggle skipped `Placeholder.svelte` entirely and landed on upstream's `ChatPlaceholder`: model name over a placeholder string, no greeting, no chips. Nothing was broken and nothing logged; a stored setting was quietly deleting two features. Measured on the box before this branch: `[data-hive-quickstart]` null, `.hv-greeting` null. Both components were correct and both were mounted, so this is not dead code in the wrong component: it is a branch reached by nobody. Hive has one home, so `landingPageMode` no longer decides whether it exists, and the Interface row that set it goes with the branch it drove rather than staying on as a control that changes nothing. **The model menu overflowed the window.** It anchored its left edge to the trigger and let its own width run off the right, which is what the model chip in the composer's control row does at a normal desktop width; the panel's `max-width` clamps how wide it can be and cannot move it back inside. It now anchors right edge to right edge when the trigger is in the right half of the window, which is what the reference does and needs no measurement of the menu, so there is still no render-then-move jump. Before: right edge 36px past the viewport. After: inside it. **The Artifacts index had no title**, so its empty state was the first thing on the surface and sat flush at the top of the pane. It gets a page head in the same shape the Knowledge index uses, and the states below it now sit in a column flex, which is what lets their `m-auto` centre at all. Before: title null, first child at y=0. After: title present, first child at y=526. **A cowork run's row was titled "New Chat."** Titles are generated by a follow-up completion the run path does not make, so the conversation list stopped being readable the moment there were two runs. The row takes the brief's first line. ## The Knowledge contradiction, recorded rather than resolved D-045 ruling 2 eliminates Knowledge as a destination in favour of Projects and Artifacts. #1184 gave Knowledge a real destination in the shell, deliberately, to close a dead-nav bug (#1109). The fork therefore contradicts its own governing decision right now. This PR does not resolve that: it leaves the row alone and says so in `nav.ts`, because Projects cannot hold RAG collections yet and removing the row today would take the only route to them away and put nothing in its place, which is the failure mode #944 warns about for the Agents row. Flagged for the owner rather than settled here. ## Visual proof Two comments on this PR, both posted with `scripts/post-pr-visual-proof.sh` to the permanent `visual-proof-assets` release. The capture log, the substrate, the session mechanism and every DOM reading quoted above are committed under `docs/proof/cowork-composer-mode-2026-08-25/`. Substrate, stated because "ran it locally" is not one: the frontend under test is this branch's own build from `docker build -f deploy/docker/Dockerfile.open-webui --target frontend`, the exact stage the deploy image uses, served against the live demo box's backend. `before` frames come from the box's deployed bundle, `after` frames from this branch's, same backend.





Summary
Three committed slices on the Open WebUI fork moving the chat surface toward the Claude visual register:
data-themeonapp.html, General settings gains a theme selector, and an in shellhivePathcatch-all route replaces browser-default 404s.GET /v1/modelsgains additivedisplay_name/summaryfields on control-plane and edge-api catalog types;ModelItem.svelterenders the purpose line per alias; newQuickStartChips.svelte; Placeholder simplified accordingly.hive.css@font-face; assistant messages and project pages adopt a serif document register with reading-measure constraints; tokens extended inpackages/hive-tokens/tokens.css.The Go changes are additive optional fields on
GET /v1/models(omitempty), backward compatible for existing API consumers.Verification
./internal/catalog/...for control-plane and edge-api (toolchain image).--hv-*token referenced by new CSS exists intokens.css..message-listitem,.groupwrapper,chat-assistantrole class).docker build -f deploy/docker/Dockerfile.open-webui -t hive-open-webui:v0.10.2-branded .exited 0, with the final-stage read-back assertion printing "hive: shell present, removed surfaces absent" (SourceSerif4-Variable, HankenGrotesk-Variable and data-hive-nav all found in the built bundle; removed surfaces absent). One earlier attempt failed on a transient PyPI metadata fetch inside the frontend build; the retry passed clean from cached layers.Test plan
Known risks