fix(desktop): keep WorkHub chat bubbles on --radius-chat - #4919
Conversation
WorkHub's conversation surface passed `density="compact"` into Astryx's `ChatMessage` / `ChatMessageList`, and `ChatMessageBubble` maps that to `border-radius: var(--radius-container)` (12px). The WorkHub composer stayed on `--radius-chat` (28px), so a bubble and the dock on the same surface disagreed by more than 2x. The main transcript avoided this — apache#3452 dropped the compact density from its `ChatMessageList`; WorkHub reintroduced it in apache#3497. Drop `density="compact"` from WorkHub's chat primitives — both `ChatMessageList` calls and the user/assistant `ChatMessage` rows in `WorkHubMessageFrame` — so the bubble falls back to the primitive's `--radius-chat` and rounds together with the composer as one conversation surface, exactly as apache#3452 fixed the main transcript. `gap={4}` stays: it sets the row gap explicitly and never depended on density. The existing `SubmittedWorkKeepsTargetMetadataInside` story already renders the projected user bubble; its play now also pins the bubble's radius to a `--radius-chat` probe (measured, not read back — ink-ladder-contract forbids the latter), so an upstream token change moves both or fails here. Fixes apache#4914 Generated-by: Claude Code
FIDELITY.md keeps geometry contracts out of a Storybook `play`, and apache#3452 — the sibling chat-radius fix this PR mirrors — put its regression in the E2E harness. Drop the story-play probe added earlier and assert the pairing where it belongs: in workhub-reconstruction.spec.ts, comparing the projected user bubble's computed border radius against the real composer plate rather than a token literal, so an upstream `--radius-chat` change moves both or fails here. Generated-by: Claude Code
e2e-budget.json keeps layout geometry and pure CSS in a Storybook `play`, not the Electron tier, and apache#4877 already moved the sibling Side Chat / WorkHub geometry there while deleting the old E2E radius checks. Revert the E2E assertion added in the previous commit and put the contract in the existing SubmittedWorkKeepsTargetMetadataInside play, comparing the real user bubble's computed radius against the real composer plate — so the bubble and the dock on one surface must round together or this fails. Generated-by: Claude Code
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed current head 531249fd (OPEN, MERGEABLE, awaiting human review). Technical GO — no P0–P3. CI test fully green on this head (12m39s, including Electron e2e budget check, Test Storybook audit contracts, Build Storybook, and Storybook smoke).
What changed
Four WorkHub chat primitives (two ChatMessageLists, user/assistant ChatMessages) drop density="compact", so bubbles fall back from --radius-container (12px) to the primitive default --radius-chat (28px), matching the composer panel — the same fix previously applied to the main transcript. gap={4} is correctly kept (row gap never depended on density). No CSS override was added (chat-message.css:100 deliberately leaves radius to the primitive; an override would conflict).
Correctness points verified
- The density blast radius was checked at its widest point:
density=compactalso drives Markdown heading scale and list/quote rhythm, but WorkHub message rows render plain<p>text with no Markdown body — on this surface density only ever affected bubble radius, so removing it has no collateral density regression. - Before/after screenshots (light and dark) confirm the claim: 12px vs 28px mismatch before, identical bubbles after, with no other movement in metadata blocks, spacing, or text.
- The extended story asserts
border-top-left-radiusequality against the real composer plate element rather than a28pxliteral — both move together (or the test fails) if the upstream token changes, which is the right way to pin it. The e2e-budget annotation correctly attributes pure CSS geometry to the Storybook layer. - Six-layer pass: design (real issue, 12 vs 28 on one surface) → function (real WorkHub submission path) → complexity (removing a knob instead of adding an override, entropy down) → tests → naming/comments → style.
Verification boundary
- No real WorkHub submission was driven end to end; geometry is verified via screenshots plus the element-to-element regression, not a live session.
Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.
简体中文
评审结论来自自动化审查流程;发布者没有读这份 diff,核的是当前 head 有没有漂移、以及 exact-head 的门禁状态。当前 head 是 531249f,未关闭,检查全绿。技术上无阻断问题。合并由人类定。
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for returning the geometry to the Astryx primitives. No remaining P0–P3 findings at 531249fdd.
I checked the Astryx 0.5.2 ChatMessageList/ChatMessage/ChatMessageBubble contract and rendered this head's WorkHub story in light/dark at 1280px and 800px. Both user and assistant rows use the balanced primitives; the user bubble and actual composer plate compute to 28px on all four corners. User-bubble padding remains 12px 16px. The list's inner wrapper does change to 16px padding; I checked its interaction with Maka's existing outer gutters and inspected screenshots, alignment and overflow at both widths. No new visual defect or page error was observed. The fix adds no product radius override; explicit gap={4} remains a supported primitive prop. CI is green.
AI-assisted review and browser verification, approved with user authorization; not independently human-verified. The running check covered the submitted-work story; the coordination-status call site was checked in source. This is not a full Electron E2E run.
中文
感谢让几何样式回归 Astryx 原语。531249fdd 没有剩余 P0–P3 问题。
已核对 Astryx 0.5.2 的 ChatMessageList、ChatMessage、ChatMessageBubble 契约,并在明暗主题、1280px 和 800px 宽度运行精确 head 的 WorkHub story。用户和助手消息均使用 balanced 原语;用户气泡与实际输入框面板四角都是 28px,用户气泡 padding 保持 12px 16px。列表内层确实改为 16px padding;已检查其与 Maka 现有外层留白的组合,以及截图、对齐和溢出,两个宽度下没有观察到新增视觉缺陷或页面异常。修复没有新增产品圆角覆盖,gap={4} 仍是原语支持的参数。CI 已通过。
这是经用户授权的 AI 辅助评审与浏览器验收,未经独立人工验证。运行检查覆盖 submitted-work story;coordination-status 调用点通过源码核对。本次不是完整 Electron E2E。
Summary
WorkHub's conversation surface passed
density="compact"into Astryx'sChatMessage/ChatMessageList, andChatMessageBubblemaps that toborder-radius: var(--radius-container)(12px). The WorkHub composer stayed on--radius-chat(28px), so a bubble and the dock on the same surface disagreed by more than 2x. The main transcript already avoids this — #3452 dropped the compact density from itsChatMessageList; WorkHub reintroduced it in #3497.This drops
density="compact"from WorkHub's chat primitives — bothChatMessageListcalls and the user/assistantChatMessagerows inWorkHubMessageFrame— so the bubble falls back to the primitive's--radius-chatand rounds together with the composer as one conversation surface, exactly as #3452 fixed the main transcript.gap={4}stays: it sets the row gap explicitly and never depended on density. No product CSS override is added;chat-message.cssalready documents that the bubble radius is left to the primitive on purpose.Fixes #4914
Verification
Before / after on the real story render (
Product/WorkHub → SubmittedWorkKeepsTargetMetadataInside), light and dark:Computed
border-top-left-radius, measured in the running Storybook (Playwright):density="compact")Light and dark measured identical. After the fix the user bubble and the composer plate agree at 28px — matching the main transcript.
play): the existingSubmittedWorkKeepsTargetMetadataInsideplay now asserts the projected user bubble's computedborder-top-left-radiusequals the real composer plate's — compared element-to-element, so the bubble and dock on one surface must round together or it fails. This lives in the storyplaypere2e-budget.json(layout geometry / pure CSS is not an Electron-tier concern), the same tier test(desktop): finish moving non-Electron coverage out of the E2E tier #4877 moved the sibling Side Chat / WorkHub geometry into. Red without the fix (bubble 12px vs plate 28px); I ran the story both ways to confirm.biome checkon the changed files): pass, no fixes.tsc -p tsconfig.renderer.jsonandtsconfig.storybook.json): no errors inworkhub-surface.tsxorworkhub.stories.tsx. Remaining diagnostics in the run are pre-existing/unrelated, because this worktree had no installed deps — I ran against a lockfile-identical siblingnode_modules.workhub-surface.tsxare untouched, so norenderer-architecture.jsonregeneration is needed. No.tsx/.cssadded or renamed, so the surface-inventory gate is not triggered.AI use
Select exactly one:
Tool(s) and scope: Claude Code — diagnosed the issue, made the fix, added the story-
playregression assertion, and captured the before/after screenshots. Commits carry aGenerated-by: Claude Codetrailer.Checklist
Biome (lint/format) and typecheck of the changed files pass locally; the
SubmittedWorkKeepsTargetMetadataInsideplay was rendered both ways (fixed = green,density="compact"restored = red) via Playwright against the running Storybook.Does this PR entail a change in behavior?