feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348

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

feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3)#348
Astro-Han merged 3 commits into
mainfrom
claude/chat-primitives-pr3

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Moves the tool live-output stream panel (ToolOutputStream) onto the @maka/ui chat substrate, retiring the bespoke .maka-tool-output-stream-* shell CSS with zero visual change. This is PR3 of the #332 conversation-flow governance pass, following PR1 (#334, bubble/row shell) and PR2 (#337, turn markers).

The panel's static shell (container, header, counts row + its pills, scrolling body, chunk/tag spans) literalizes onto a new internal streamVariants cva exactly as PR2 did for markers. The pulsing "live" dot is the one part that can't ride those rails — an animation can't be a leaf-literal and getComputedStyle reads a phase-dependent value — so it moves onto a new governed LiveIndicator primitive backed by a single canonical @keyframes maka-pulse, pinned by a keyframe-values contract instead of the diff harness.

Why

Closes part of #332 (PR3). The conversation-flow display is being migrated onto one @maka/ui substrate + a test net, one island at a time, with zero visual change. PR3 covers the tool live-output stream. It also lays the groundwork for retiring the repo's duplicated pulse keyframes: the duplicate reasoning / composer / onboarding live dots can adopt LiveIndicator in a follow-up motion pass, retiring their own *-pulse keyframes onto maka-pulse.

Scope

Changed:

  • packages/ui/src/primitives/chat.tsx — add streamVariants (internal, off-barrel like markerVariants) for the panel shell, and an internal LiveIndicator primitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.
  • packages/ui/src/index.tsLiveIndicator and streamVariants both stay OFF the package barrel (single consumer today, applied by relative import). LiveIndicator is promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.
  • packages/ui/src/components.tsx — wire ToolOutputStream to the primitives. Live auto-scroll behavior is preserved verbatim.
  • apps/desktop/src/renderer/maka-tokens.css — add the canonical @keyframes maka-pulse (rest opacity 0.55, scale 1→1.1) in the shared motion home.
  • apps/desktop/src/renderer/styles/tool-stream.css — delete the .maka-tool-output-stream-* block + its per-feature @keyframes.
  • package.json — add npm run check:chat-visual, a discoverable entry point for the computed-style diff (the repo has no CI; this keeps the zero-visual proof reproducible by anyone).
  • Tests + the computed-style diff script (see Verification).

Not included:

  • Cross-feature pulse consolidation (reasoning-panel / composer / onboarding adopting LiveIndicator and deleting their duplicate keyframes) — a separate motion pass; that is where their visuals would change, so it stays out of this zero-visual PR. That pass is also when LiveIndicator would be promoted to a public export.
  • The live auto-scroll behavior is untouched; MessageScroller / scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.
  • Preview surfaces (terminal / diff / web-search) — PR4.

Verification

  • npm run -w @maka/ui test — 12/12 (adds a streamVariants literal/scale-drift guard + the LiveIndicator pulse-pin test).
  • npm run -w @maka/desktop test — 1604/1604 (adds chat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonical maka-pulse frames pinned, and the live-dot literals pinned — the one part the computed-style diff can't cover).
  • npm run typecheck — clean across all workspaces.
  • npm run check:chat-visual -- <pre-PR2 baseline.css> <head.css> (wraps scripts/check-chat-marker-computed-style.mjs, extended to cover the stream shell): against a pre-PR2 renderer CSS baseline (e033a8c4~1, which still carries both the bespoke marker and stream CSS), every row is 45/45 identical — TOTAL DIFFS: 0 across 36 element/state rows + 2 ::before middots (markers + all 14 stream rows incl. the data-live accent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.
  • Live dot: pinned by the @keyframes maka-pulse frame contract + the LiveIndicator literals (same values as the retired dot, so visually identical by construction); the animation escapes the computed-style proof by nature.

User-facing impact

None — zero visual change by design, locked by the contract + diff above.

Reviewer notes

  • streamVariants and LiveIndicator are deliberately off the barrel (like markerVariants): one consumer today, applied by relative import, so they stay internal, freely-removable styling details rather than public API. When a second consumer (reasoning / composer / onboarding) actually migrates, LiveIndicator gets promoted to public then.
  • The tests intentionally do NOT re-assert the stream shell's per-part class literals: that shell is proven by the computed-style diff, so re-stating the strings would only mirror the implementation. The source-string assertions are kept only for the live dot, which the diff cannot observe.
  • The maka-pulse keyframe lives in maka-tokens.css because @maka/ui ships no CSS of its own (its primitives style via app-provided tokens); a keyframe is a named global rule, not a leaf utility, so this is the same kind of token dependency as --accent. Reduced-motion suppression rides on the primitive's motion-reduce: utilities.
  • word-break:break-word stays an arbitrary literal, not Tailwind's break-words (which is the different overflow-wrap property).

…es (#332 PR3)
Move the tool live-output stream panel (`ToolOutputStream`) onto the
`@maka/ui` chat substrate, retiring the bespoke `.maka-tool-output-stream-*`
shell CSS with zero visual change.
- Add `streamVariants` (internal, off-barrel like `markerVariants`) to
packages/ui chat.tsx — the panel/header/counts/body/chunk/tag shell as
LITERAL arbitrary utilities that compile 1:1 to the retired declarations.
- Add the `LiveIndicator` primitive (barrel-public reuse surface) for the
pulsing live dot, backed by one canonical `@keyframes maka-pulse` in
maka-tokens.css. The dot's breath escapes the computed-style proof (a
keyframe is a named rule + getComputedStyle is phase-dependent), so it is
pinned by the keyframe frames + literals and verified by screenshots.
- Wire ToolOutputStream to the primitives; preserve the live auto-scroll
behavior untouched (MessageScroller stays a separate #332 concern).
- Delete the `.maka-tool-output-stream-*` block + its per-feature keyframe
from styles/tool-stream.css.
Tests: cascade source-string contract (apps/desktop) + streamVariants /
LiveIndicator unit tests (@maka/ui) + extend the computed-style diff script
to cover the stream shell. The 4 other duplicated pulse keyframes
(reasoning / composer / onboarding) adopt LiveIndicator in a follow-up
motion pass.
…ts (#332)
Review response for PR3. The migration kept the same rendered output but
trimmed the abstraction + test surface to the minimum the single consumer
actually needs:
- LiveIndicator off the package barrel (zero external consumers today). It
stays internal, applied by relative import like streamVariants; it gets
promoted to public only when the reasoning/composer/onboarding live dots
actually migrate onto it, not speculatively before a second consumer.
- drop the unused `StreamPart` type export (no importer anywhere).
- keep streamVariants as the internal cva table (mirrors the merged PR2
markerVariants pattern) — not inlined to local consts.
- thin the source-string tests: the stream SHELL is proven by the
computed-style diff harness, so the tests no longer re-assert those
literals (that just mirrored the impl). They keep only what the diff
can't cover — the live dot's animation/keyframe pins — plus the
retired-selector-absence + scale-drift guards.
- add `npm run check:chat-visual` wrapping the computed-style diff so the
zero-visual proof is reproducible by anyone (the repo has no CI).
Verified: @maka/ui 12/12, desktop 1604/1604, typecheck clean, and the
computed-style diff still reads TOTAL DIFFS: 0 across 36 rows + 2 ::before
(incl. all 14 stream rows + the data-live accent ring) vs the pre-PR2
baseline.
The no-arg guard already exits non-zero, but its usage line showed the raw
`npx electron …` form and `<main.css>`. Point it at the actual entry point
(`npm run check:chat-visual -- <baseline.css> <head.css>`) and spell out what
each bundle is, so running it without args is self-explanatory.
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