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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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-HanAstro-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/uiMessage + 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 freeto 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

@Astro-Han