') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); feat(ui): retire ToolErrorBanner bespoke CSS onto the @maka/ui Alert primitive (#332 PR3c) by Astro-Han · Pull Request #355 · apache/maka · GitHub
Skip to content

feat(ui): retire ToolErrorBanner bespoke CSS onto the @maka/ui Alert primitive (#332 PR3c) - #355

Merged
Astro-Han merged 3 commits into
mainfrom
claude/pr3c-tool-error-banner
Jun 29, 2026
Merged

feat(ui): retire ToolErrorBanner bespoke CSS onto the @maka/ui Alert primitive (#332 PR3c)#355
Astro-Han merged 3 commits into
mainfrom
claude/pr3c-tool-error-banner

Conversation

@Astro-Han

@Astro-HanAstro-Han commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Retire the .maka-tool-error* CSS family onto the @maka/uiAlert primitive — the last tool-card-adjacent bespoke CSS in the #332 chat governance pass (follows PR3b #350). Zero visual change.

Why

ToolErrorBanner has rendered on Alert since a1e78b4, and Alert's slot utilities sit in @layer utilities while .maka-tool-error* lived in @layer components. Cascade layers mean utilities beat components irrespective of specificity, so the container's bespoke grid (18px) / radius (10px) / padding / border / background never rendered — they were already shadowed by variant="error"'s own grid (16px) / rounded-xl. The bespoke container was a zombie the earlier Alert migration silently killed. The governed end-state is to let the primitive own the shell and keep only the declarations it does not set — not to literalize dead CSS into a new abstraction.

Part of #332 (PR3c). Does not close the umbrella issue.

Scope

Changed:

  • packages/ui/src/components.tsxToolErrorBanner drops the .maka-tool-error* classes; the genuinely-rendering residue becomes leaf literals:
    • container → mb-[10px] (the lone surviving .maka-tool-error declaration; Alert sets no margin)
    • description → mono / 12px / 1.5 / pre-wrap / break-word; color stays text-muted-foreground (the bespoke --foreground-80 was shadowed and never rendered, so re-adding it would be a change)
    • copy button → align-self:start + the [data-copy-feedback] / [data-pending] state chrome (formerly UNLAYERED in tool-output.css), mirroring the turn-footer copy
  • apps/desktop/src/renderer/maka-tokens.css — delete the .maka-tool-error* block (−49).
  • apps/desktop/src/renderer/styles/tool-output.css — delete the unlayered copy-feedback state rules (−14).
  • apps/desktop/src/main/__tests__/renderer-style-pruning-contract.test.ts — drop the dead .maka-tool-error-{icon,body,title} allowlist entries (no JSX consumer; Alert subsumed icon/title/body).
  • apps/desktop/src/main/__tests__/visible-copy-hygiene-contract.test.ts — rewrite the tool-error assertions from raw .maka-tool-error-copy[…] CSS selectors to the literal-utility source-string form (same shape PR2 used for the turn-footer copy).
  • scripts/check-chat-marker-computed-style.mjs — add the error-banner subtree (container + description + copy button resting/pending/copied/failed) to the zero-visual harness.

Not included:

Verification

No CI in this repo — all local:

  • @maka/ui typecheck + desktop main typecheck clean; renderer typecheck adds zero new errors (only the pre-existing main.tsx:3184 from refactor(ui): unify chat model pickers onto the grouped settings-select form #353/feat(core): enrich model catalog metadata #349, reproduced on clean main).
  • @maka/ui test 14/14; pruning + hygiene contracts 32/32.
  • Zero-visual proof: scripts/check-chat-marker-computed-style.mjs diffed vs the pre-governance baseline e033a8c4~167 rows, TOTAL DIFFS: 0. The 6 new err-* rows are 49/49 computed properties identical — the empirical proof the retired container CSS was inert (Alert + .maka-tool-error computes the same as Alert + mb-[10px]).

Net: −65 CSS lines, 6 files.

User-facing impact

None. Computed-style identical; the error banner renders byte-for-byte as before. No docs / CHANGELOG / migration.

Reviewer notes

  • The headline claim is "the bespoke container CSS was already inert." The harness err-banner row (49/49 identical between Alert + .maka-tool-error and Alert + mb-[10px]) is the proof — that is the row to scrutinize.
  • align-self:start is kept as the arbitrary [align-self:start], not self-start, so its computed value byte-matches the retired CSS (start) rather than Tailwind's flex-start.
  • Baseline is e033a8c4~1 (pre-PR2); its .maka-tool-error* block is byte-identical to main, so it also greens every pre-existing marker / stream / tool-card harness row.

…primitive (#332 PR3c)
Retire the `.maka-tool-error*` family, the last tool-card-adjacent bespoke CSS in
the #332 chat governance pass.
`ToolErrorBanner` has rendered on the `@maka/ui` `Alert` primitive since a1e78b4,
and `Alert`'s slot utilities (`@layer utilities`) already shadowed `.maka-tool-error*`
(`@layer components`) — utilities beat components irrespective of specificity — so the
container's bespoke grid / padding / radius / border / background were INERT. The
retirement is therefore mostly deletion plus a few leaf literals for the declarations
`Alert` does not set:
- container: `mb-[10px]` (the lone surviving `.maka-tool-error` declaration)
- description: mono / 12px / 1.5 / pre-wrap / break-word on `AlertDescription`
- copy button: `align-self:start` + the `[data-copy-feedback]` / `[data-pending]` state
chrome (formerly UNLAYERED in tool-output.css), mirroring the turn-footer copy
No `errorBannerVariants` cva table: there is no bespoke shell left to carry. Delete the
dead `.maka-tool-error-{icon,body,title}` classes (no JSX consumer) and their
pruning-contract allowlist entries; rewrite the hygiene contract's tool-error
assertions to the literal-utility form.
Zero visual change proven by scripts/check-chat-marker-computed-style.mjs (new err-*
rows, 49/49 computed properties identical vs the pre-governance baseline e033a8c~1).
Follow-up to #355 review (#332 PR3c). No behavioral or visual change.
- P2b: source the computed-style fixture's Alert container string from the
real `alertVariants({variant:'error'})` cva instead of a hand-copied literal,
so the proof's subject can't drift from production. Export `alertVariants`
from the primitive module; convert the barrel's `export *` to a named
re-export so the table stays off the public barrel (same governance pattern
as the chat styling tables). Verified the cva output is byte-identical to the
retired literal, so the 0-diff result is unchanged.
- P2a: scope the tool-error copy-style assertion to the `TOOL_ERROR_TEXT`..
`OverlayHost` block and add a `className={TOOL_ERROR_COPY}` wiring assertion,
so it fails if a state class is removed or the button is decoupled, and
can't false-pass if the string drifts to another component.
- P3a: trim the migration comment in components.tsx from the @layer essay to
the two reader-useful gotchas (arbitrary `align-self:start`, don't re-add the
shadowed description color); the cascade rationale lives in the PR body.
Addresses three P3 notes; net less production code, no behavior/visual change.
- Revert the round-1 `alertVariants` export + `index.ts` named re-export. The
computed-style harness now reads Alert's slot classes by CALLING the real
primitive components (`Alert({variant:'error'}).props.className`), so it tracks
production with zero production-API change and a single source of truth.
- Drop the harness's hand-copied `ALERT_*` / `ERR_*` literals and the err-text /
err-copy-* leaf rows. Those leaf utilities are arbitrary-value (source ==
computed) and are pinned by visible-copy-hygiene-contract; the harness keeps
only the err-banner container row, the one thing it uniquely proves (the
retired `.maka-tool-error` container was inert).
- Inline `TOOL_ERROR_TEXT` / `TOOL_ERROR_COPY` into the JSX and trim the migration
comment to one sentence; the visible-copy test now slices the `ToolErrorBanner`
block and asserts the literals on the actual button `className`.
@Astro-Han
Astro-Han merged commit 8163e1a into mainJun 29, 2026
@Astro-Han
Astro-Han deleted the claude/pr3c-tool-error-banner branch June 29, 2026 09:54
Astro-Han added a commit that referenced this pull request Jun 29, 2026
PR3c (8163e1a) retired the ToolErrorBanner bespoke CSS onto the Alert
primitive after this branch forked, overlapping PR4's tool-output.css /
maka-tokens.css retirements. Resolution:
- maka-tokens.css: keep PR4's @Keyframes maka-tool-card-enter; accept PR3c's
deletion of the .maka-tool-error* block.
- tool-output.css: keep PR4's preview-family deletion; accept PR3c's deletion
of the .maka-tool-error-copy residue (drop the stale 'residue stays' comment).
- components.tsx / visible-copy-hygiene-contract.test.ts: auto-merged
(PR3c's Alert error banner + PR4's previewVariants live in disjoint regions).
- chat-tool-card-cascade-contract (PR3b) + chat-preview-cascade-contract (PR4):
drop .maka-tool-error from the out-of-scope 'kept' lists — PR3c legitimately
retired it, so asserting it stays is now wrong. (This contract was latently
red on main since PR3c merged; CI never caught it because the desktop
pretest check-console gate fails first and the node:test suite never runs.)
Verified: @maka/ui 15/15, desktop 1618/1618, renderer bundle has 0
.maka-tool-error rules + 0 dead rules + the maka-tool-card-enter keyframe.
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