Skip to content

feat(ui): govern chat bubble/row shell onto @maka/ui chat primitives - #334

Merged
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr1
Jun 28, 2026
Merged

feat(ui): govern chat bubble/row shell onto @maka/ui chat primitives#334
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr1

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Summary

PR1 of #332 — migrate the conversation-flow row/bubble shell onto a new @maka/ui Message + Bubble primitive pair (cva + data-slot), retiring the bespoke .message.{role} / .maka-bubble-user / .maka-bubble-truncated shell CSS with zero visual change. The Markdown prose and the turn machinery (summary / lineage / footer / markers — PR2) are untouched.

Why

Part of #332. The chat conversation-flow display was the last bespoke island after the rest of packages/ui/src/primitives/* moved onto the shadcn Base UI + Tailwind substrate. This is governance — one substrate + a test net — not a redesign; the visual refresh is a separate follow-up.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx (new), exported from packages/ui/src/index.ts:
    • Message — per-turn row (role user/assistant/system). Keeps the authored .maka-message-row base (centered column + entrance fade/animation + the data-maka-visual-smoke disable); role layout moves to utilities. .maka-message-row lives in maka-tokens' @layer components, so the utilities (utilities layer) win — the assistant/system left-anchor (ml-0 mr-auto) overrides the row's centering exactly as the old .message.assistant did.
    • Bubblevariant="user" carries the tint as utilities on the neutral --chat-user-bg token path (never primary/accent); variant="assistant" re-emits .maka-bubble-assistant so the prose stays authored.
  • packages/ui/src/components.tsx — wire MessageBody, the committed user/system/assistant rows, the streaming row, and the streaming "已截断" pill onto the primitives (net −66 lines; no new responsibility added to the sink).
  • Retire shell CSS: .maka-bubble-user (both rules, incl. the effective min(100%,640px) width cap from the second rule), .message.user/.assistant/.system, the dead .message > span role labels, .maka-bubble-assistant-stack, .maka-bubble-truncated.
  • Re-anchor (cleanup from the deletions): the still-hand-written turn lineage-row / footer (PR2) and the system <pre> reset/pill ride [data-slot="message"][data-role=…] at identical specificity, so their layout is pixel-stable until PR2 migrates them.
  • apps/desktop/src/main/__tests__/chat-primitive-cascade-contract.test.ts (new).

Not included:

  • All .maka-bubble-assistant Markdown prose (task lists, code blocks, tables, blockquotes, links) — preserved verbatim.
  • Marker / turn summary / lineage / footer (PR2), tool activity (PR3), preview surfaces (PR4), MessageScroller (later).
  • No change to .messages scroll container, runtime, streaming, Markdown rendering, permission, turn model, tool output, or attachment IPC.

Verification

  • New chat-primitive-cascade-contract.test.ts locks: retired shell selectors gone, prose preserved, turn layout re-anchored onto the primitive, user bubble on --chat-user-bg with the 640px cap and never primary/accent.
  • @maka/desktop typecheck clean; 1582/1582 desktop tests pass (incl. the renderer cascade + dead-CSS pruning contracts); 4/4 @maka/ui tests; node scripts/check-dead-css.mjs --check → no dead classes.
  • Captured the turn-narrative scenario on this branch: user bubble tinted/rounded/right-anchored with the meta row beneath; assistant summary, tools, prose, and footer all left-anchored at the same measure column. The repo screenshot harness is a presence/dimension sanity gate, not a pixel diff (font drift makes byte-diff too noisy), so visual parity is held by the contract test + the capture rather than an automated before/after diff.

User-facing impact

None — zero visual change by design. No CHANGELOG.md, docs, breaking changes, or migrations.

Reviewer notes

  • Two non-obvious points to check:
    1. .maka-bubble-user had two rules; the second overrides max-width to min(100%,640px) (not the first rule's 78%). The primitive uses 640px, locked by the contract test.
    2. .message.assistant was the ancestor selector for the PR2 lineage/footer measure rule. Deleting it required re-anchoring those (and the system <pre> rules) onto [data-slot="message"][data-role] at identical specificity, so PR2 elements stay pixel-stable.
  • Next slice: PR2 — Marker (turn summary / lineage / footer / status markers), flat off main after this lands.

PR1 of #332. Move the conversation-flow row/bubble *shell* onto a new
`@maka/ui` `Message` + `Bubble` primitive pair (cva + data-slot), retiring
the bespoke `.message.{role}` / `.maka-bubble-user` / `.maka-bubble-truncated`
shell CSS with zero visual change.

- packages/ui/src/primitives/chat.tsx: `Message` (row, role variants) keeps the
  authored `.maka-message-row` base and adds the role layout as utilities;
  `Bubble` (user/assistant) carries the user tint as utilities on the neutral
  `--chat-user-bg` token path (never primary/accent) and re-emits
  `.maka-bubble-assistant` so the Markdown prose (OUT of scope) is untouched.
- Wire MessageBody, the committed user/system/assistant rows, the streaming
  row, and the streaming truncated pill onto the primitives.
- Retire shell CSS: `.maka-bubble-user` (both rules, incl. the 640px width
  cap), `.message.user/.assistant/.system`, the dead `.message > span` role
  labels, `.maka-bubble-assistant-stack`, `.maka-bubble-truncated`. Re-anchor
  the still-hand-written turn lineage/footer + system `pre` rules (PR2) onto
  the `[data-slot="message"][data-role]` hook at identical specificity. Keep
  all `.maka-bubble-assistant` Markdown prose.
- Add chat-primitive-cascade-contract.test.ts locking: shell selectors gone,
  prose preserved, turn layout re-anchored, neutral token path + 640px cap.
…le→variant

Address review on #334:
- User bubble shell uses literal arbitrary utilities (rounded-[10px],
  px-[14px] py-[10px]) instead of the semantic scale (rounded-lg, px-3.5).
  The retired .maka-bubble-user hardcoded these pixels, so the literal is the
  faithful translation, immune to scale/token re-tuning, and self-evidently
  equal to the old computed style — the source-string contract becomes a
  complete proof without a render harness. (rounded-lg already resolves to
  10px here via @theme inline --radius-lg, so this is not a regression fix —
  it removes the ambiguity the review flagged.)
- Rename the Message `role` prop to `variant` so it never shadows the native
  HTML/ARIA `role` (still emitted to the DOM as data-role for the re-anchored
  turn rules); symmetric with Bubble's `variant`.
- Strengthen the contract test to pin the literal radius/padding/leading/
  width/token values and reject the semantic scale.
…user-bubble contract

Address P3 review feedback on the #332 PR1 chat primitives (non-blocking
robustness + governance precision):

- Spread {...props} before the fixed data-slot/data-role/data-variant in
  Message/Bubble so the structural hooks the re-anchored selectors depend
  on always land last and can't be clobbered by a consumer.
- Drop the unreachable defaultVariants from messageVariants/bubbleVariants
  (variant is a required prop; every call site passes it explicitly).
- Tighten the user-bubble contract from contains-each-literal to an exact
  match on the whole variant class string, so a stray extra utility that
  would silently override the shell now fails the test.
- Add a hook-free render-prop assertion in @maka/ui proving the primitives
  keep their own data-* over conflicting props.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant