feat(ui): migrate tool live-output stream onto @maka/ui chat primitives (#332 PR3) - #348
Merged
Conversation
…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.
This was referenced Jun 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Moves the tool live-output stream panel (
ToolOutputStream) onto the@maka/uichat 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
streamVariantscva 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 andgetComputedStylereads a phase-dependent value — so it moves onto a new governedLiveIndicatorprimitive 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/uisubstrate + 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 adoptLiveIndicatorin a follow-up motion pass, retiring their own*-pulsekeyframes ontomaka-pulse.Scope
Changed:
packages/ui/src/primitives/chat.tsx— addstreamVariants(internal, off-barrel likemarkerVariants) for the panel shell, and an internalLiveIndicatorprimitive for the live dot. Every shell value is a LITERAL arbitrary utility that compiles 1:1 to the retired declaration.packages/ui/src/index.ts—LiveIndicatorandstreamVariantsboth stay OFF the package barrel (single consumer today, applied by relative import).LiveIndicatoris promoted to a public export only when a second real consumer migrates onto it, not speculatively before then.packages/ui/src/components.tsx— wireToolOutputStreamto 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— addnpm 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).Not included:
LiveIndicatorand 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 whenLiveIndicatorwould be promoted to a public export.MessageScroller/ scroll-area governance remains the separately-evaluated feat(ui): govern chat conversation-flow display onto @maka/ui Base UI primitives #332 concern.Verification
npm run -w @maka/ui test— 12/12 (adds astreamVariantsliteral/scale-drift guard + theLiveIndicatorpulse-pin test).npm run -w @maka/desktop test— 1604/1604 (addschat-stream-cascade-contract.test.ts: retired stream selectors + per-feature pulse keyframe absent, canonicalmaka-pulseframes 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>(wrapsscripts/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::beforemiddots (markers + all 14 stream rows incl. thedata-liveaccent ring). Non-vacuous: the baseline and head bundles genuinely differ in source, yet the migrated chrome reads byte-identical computed style on both sides.@keyframes maka-pulseframe contract + theLiveIndicatorliterals (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
streamVariantsandLiveIndicatorare deliberately off the barrel (likemarkerVariants): 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,LiveIndicatorgets promoted to public then.maka-pulsekeyframe lives inmaka-tokens.cssbecause@maka/uiships 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'smotion-reduce:utilities.word-break:break-wordstays an arbitrary literal, not Tailwind'sbreak-words(which is the differentoverflow-wrapproperty).