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..5033c21f25 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,39 @@ 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 inlined on the copy button's `className`. We slice the + // `ToolErrorBanner` block and require the utilities to appear on an actual + // `className="maka-button …"` so the assertion proves BOTH that the state + // utilities exist AND that the banner's copy button wears them — a whole-file + // scan would false-pass if the string drifted to another component. These are + // arbitrary-value utilities (source == computed), so this source contract is the + // proof; the computed-style harness only re-diffs the non-trivial container box. + const componentsPath = resolve(process.cwd(), '..', '..', 'packages', 'ui', 'src', 'components.tsx'); + const src = await readFile(componentsPath, 'utf8'); + const block = src.match(/function ToolErrorBanner[\s\S]*?export function OverlayHost/)?.[0] ?? ''; assert.match( - src, - /\.maka-tool-error-copy\[data-pending="true"\]\s*\{[\s\S]*cursor:\s*progress;/, + block, + /className="maka-button \[align-self:start\] data-\[pending=true\]:cursor-progress/, + 'The tool-error copy button must wear the leaf state utilities inline on its className.', + ); + assert.match( + block, + /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.', + block, + /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.', + block, + /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..ddb42fa5ac 100644 --- a/packages/ui/src/components.tsx +++ b/packages/ui/src/components.tsx @@ -6325,6 +6325,9 @@ function ToolOutputStream(props: { ); } +// Preserve the retired `.maka-tool-error*` leaf utilities onto Alert (#332 PR3c) — +// Alert owns the shell; these are the few declarations it doesn't set, kept arbitrary +// so they map 1:1 to the old CSS (`[align-self:start]`, not Tailwind's `flex-start`). 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 +6351,11 @@ function ToolErrorBanner(props: { result: ToolActivityItem['result'] }) { } return ( - +