From 04bc9de2d13d28b3245bbba1dd794ae9acb4225c Mon Sep 17 00:00:00 2001 From: AstroHan Date: Mon, 29 Jun 2026 00:13:49 +0800 Subject: [PATCH 1/3] feat(ui): retire ToolErrorBanner bespoke CSS onto the @maka/ui Alert primitive (#332 PR3c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 a1e78b47, 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 e033a8c4~1). --- .../renderer-style-pruning-contract.test.ts | 3 -- .../visible-copy-hygiene-contract.test.ts | 20 +++++--- apps/desktop/src/renderer/maka-tokens.css | 49 ------------------- .../src/renderer/styles/tool-output.css | 14 ------ packages/ui/src/components.tsx | 35 +++++++++++-- scripts/check-chat-marker-computed-style.mjs | 48 +++++++++++++++++- 6 files changed, 93 insertions(+), 76 deletions(-) diff --git a/apps/desktop/src/main/__tests__/renderer-style-pruning-contract.test.ts b/apps/desktop/src/main/__tests__/renderer-style-pruning-contract.test.ts index 651a1ad2a5..c0790ef46e 100644 --- a/apps/desktop/src/main/__tests__/renderer-style-pruning-contract.test.ts +++ b/apps/desktop/src/main/__tests__/renderer-style-pruning-contract.test.ts @@ -63,9 +63,6 @@ const DYNAMIC_STYLE_HOOKS = new Set([ 'maka-sidebar-row', 'maka-sidebar-section', 'maka-titlebar', - 'maka-tool-error-body', - 'maka-tool-error-icon', - 'maka-tool-error-title', 'scrollbar-hide', 'scrollbar-hover', 'shadow-medium', diff --git a/apps/desktop/src/main/__tests__/visible-copy-hygiene-contract.test.ts b/apps/desktop/src/main/__tests__/visible-copy-hygiene-contract.test.ts index dee4779c62..7d322aeb8a 100644 --- a/apps/desktop/src/main/__tests__/visible-copy-hygiene-contract.test.ts +++ b/apps/desktop/src/main/__tests__/visible-copy-hygiene-contract.test.ts @@ -553,22 +553,30 @@ describe('tool error copy feedback contract', () => { }); it('styles tool-error copy pending and failure states', async () => { - const src = await readRendererContractCss(); + // `.maka-tool-error-copy[…]` retired onto the `@maka/ui` Alert primitive + // (issue #332 PR3c): the pending / copy-feedback chrome — which lived UNLAYERED + // in tool-output.css so it out-ranked the ghost button — now lives as literal + // arbitrary utilities on the `UiButton` className, exactly like the turn-footer + // copy above. Asserting them on the component source (which compiles 1:1) keeps + // the same "pending/failure is visibly styled" guarantee; the resting render is + // diffed by scripts/check-chat-marker-computed-style.mjs (err-copy-* rows). + const componentsPath = resolve(process.cwd(), '..', '..', 'packages', 'ui', 'src', 'components.tsx'); + const src = await readFile(componentsPath, 'utf8'); assert.match( src, - /\.maka-tool-error-copy\[data-pending="true"\]\s*\{[\s\S]*cursor:\s*progress;/, + /data-\[pending=true\]:cursor-progress/, 'Tool-error pending copy should visibly indicate in-progress work.', ); assert.match( src, - /\.maka-tool-error-copy\[data-copy-feedback="copied"\]/, - 'Tool-error copied state should have a stable CSS selector.', + /data-\[copy-feedback=copied\]:text-\[color:var\(--accent\)\] data-\[copy-feedback=copied\]:border-\[oklch\(from_var\(--accent\)_l_c_h_\/_0\.35\)\]/, + 'Tool-error copied state should have a stable color + border styling hook.', ); assert.match( src, - /\.maka-tool-error-copy\[data-copy-feedback="failed"\]/, - 'Tool-error failed copy state should have a stable CSS selector.', + /data-\[copy-feedback=failed\]:text-\[color:var\(--destructive\)\] data-\[copy-feedback=failed\]:border-\[oklch\(from_var\(--destructive\)_l_c_h_\/_0\.35\)\]/, + 'Tool-error failed copy state should have a stable color + border styling hook.', ); }); }); diff --git a/apps/desktop/src/renderer/maka-tokens.css b/apps/desktop/src/renderer/maka-tokens.css index 769076ef6b..3f3abbfa46 100644 --- a/apps/desktop/src/renderer/maka-tokens.css +++ b/apps/desktop/src/renderer/maka-tokens.css @@ -1496,55 +1496,6 @@ 50% { opacity: 1; transform: scale(1.1); } } - /* Inline failure banner shown at the top of an errored tool body so the - * user sees what failed without expanding the result blob. The wrapper - * already paints a red border via [data-status="errored"]; this banner - * gives the failure a heading + copy-button affordance. */ - .maka-tool-error { - display: grid; - grid-template-columns: 18px minmax(0, 1fr) auto; - align-items: start; - gap: 10px; - padding: 10px 12px; - margin: 0 0 10px; - border: 1px solid oklch(from var(--destructive) l c h / 0.30); - border-radius: 10px; - background: oklch(from var(--destructive) l c h / 0.08); - } - .maka-tool-error-icon { - display: grid; - place-items: center; - color: var(--destructive); - margin-top: 1px; - } - .maka-tool-error-body { - display: grid; - gap: 4px; - min-width: 0; - } - .maka-tool-error-title { - color: var(--destructive); - font-size: 12px; - font-weight: 700; - letter-spacing: 0.02em; - } - .maka-tool-error-text { - margin: 0; - color: var(--foreground-80); - font-family: var(--font-mono); - font-size: 12px; - line-height: 1.5; - white-space: pre-wrap; - word-break: break-word; - } - .maka-tool-error-copy { - align-self: start; - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 11px; - } - /* ---- Composer --------------------------------------------------- */ .maka-composer { diff --git a/apps/desktop/src/renderer/styles/tool-output.css b/apps/desktop/src/renderer/styles/tool-output.css index bcbf448967..ec464a7f1b 100644 --- a/apps/desktop/src/renderer/styles/tool-output.css +++ b/apps/desktop/src/renderer/styles/tool-output.css @@ -334,20 +334,6 @@ border-color: oklch(from var(--destructive) l c h / 0.35); } -.maka-tool-error-copy[data-pending="true"] { - cursor: progress; -} - -.maka-tool-error-copy[data-copy-feedback="copied"] { - color: var(--accent); - border-color: oklch(from var(--accent) l c h / 0.35); -} - -.maka-tool-error-copy[data-copy-feedback="failed"] { - color: var(--destructive); - border-color: oklch(from var(--destructive) l c h / 0.35); -} - .maka-office-document-preview { display: grid; gap: 8px; diff --git a/packages/ui/src/components.tsx b/packages/ui/src/components.tsx index cdcadde8a6..fbda172518 100644 --- a/packages/ui/src/components.tsx +++ b/packages/ui/src/components.tsx @@ -6325,6 +6325,35 @@ function ToolOutputStream(props: { ); } +// `.maka-tool-error*` retired onto the `@maka/ui` `Alert` primitive (issue #332, +// PR3c). Unlike the marker / stream / tool-card shells, there was NO bespoke +// shell left to literalize: `ToolErrorBanner` has rendered on `Alert` since +// a1e78b47, and `Alert`'s slot utilities live in `@layer utilities` while +// `.maka-tool-error*` lived in `@layer components` — so the layer order (utilities +// beats components, irrespective of specificity) had already made the container's +// bespoke grid / padding / radius / border / background INERT. The only +// declarations that ever rendered were the ones `Alert` doesn't set, so the +// retirement is mostly deletion plus these few leaf literals (each proven equal to +// the retired CSS by the chat computed-style diff; the copy-feedback state hooks +// are also pinned as source strings by visible-copy-hygiene-contract). No +// `errorBannerVariants` cva table: there is no bespoke shell to carry. +// - container: `mb-[10px]` — the lone surviving `.maka-tool-error` declaration +// (`Alert` sets no margin); everything else now comes from `variant="error"`. +// - description: mono / 12px / 1.5 / pre-wrap / break-word — `AlertDescription` +// sets none of these. Its `color` stays `text-muted-foreground`: the bespoke +// `--foreground-80` was shadowed and never rendered, so re-adding it would be a +// visual CHANGE, not a preservation. +// - copy button: `align-self:start` (kept arbitrary so it byte-matches the old +// `align-self:start`, not Tailwind's `flex-start`) plus the `[data-copy-feedback]` +// / `[data-pending]` state chrome that lived UNLAYERED in tool-output.css (so it +// out-ranked the ghost button), now leaf utilities mirroring the turn-footer copy. +const TOOL_ERROR_TEXT = + '[font-family:var(--font-mono)] text-[12px] leading-[1.5] whitespace-pre-wrap [word-break:break-word]'; +const TOOL_ERROR_COPY = + 'maka-button [align-self:start] data-[pending=true]:cursor-progress' + + ' data-[copy-feedback=copied]:text-[color:var(--accent)] data-[copy-feedback=copied]:border-[oklch(from_var(--accent)_l_c_h_/_0.35)]' + + ' data-[copy-feedback=failed]:text-[color:var(--destructive)] data-[copy-feedback=failed]:border-[oklch(from_var(--destructive)_l_c_h_/_0.35)]'; + function ToolErrorBanner(props: { result: ToolActivityItem['result'] }) { // Tool stderr / raw provider errors occasionally slip credential paths, // bearer tokens, or API keys through main-side redaction. Apply a @@ -6348,11 +6377,11 @@ function ToolErrorBanner(props: { result: ToolActivityItem['result'] }) { } return ( - +