From 5ae5657ca4771793f51d92678bf5f843aa4d1de4 Mon Sep 17 00:00:00 2001 From: iris Date: Mon, 22 Jun 2026 22:15:23 +0200 Subject: [PATCH] fix(dashboard): extract themed-dialog styles into an importable modal.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The themed dialog component (modal.js: themedConfirm / themedPrompt / themedToast) is raised from common.js's data-async / data-confirm handler, which every page loads — but its .tc-* styles lived only in dashboard.css (the main tabbed dashboard's stylesheet). So on any standalone page (core/C0R3, settings, flow, logs) a dialog rendered completely unstyled: the build-queue rebuild-cancel confirm came up as raw text. Extract the .tc-* rules into a dedicated modal.css component (paired with modal.js) and @import it from common.css, so the styles load wherever a dialog can fire — not just the main dashboard. esbuild inlines the @import into the common.css bundle, so there's no extra request and the main dashboard is visually unchanged (it loads common.css too). The stale 'themedConfirm() in common.js' comment is gone with the move. Fixes #1910. --- frontend/packages/dashboard/src/common.css | 1 + frontend/packages/dashboard/src/dashboard.css | 107 ----------------- frontend/packages/dashboard/src/modal.css | 111 ++++++++++++++++++ 3 files changed, 112 insertions(+), 107 deletions(-) create mode 100644 frontend/packages/dashboard/src/modal.css diff --git a/frontend/packages/dashboard/src/common.css b/frontend/packages/dashboard/src/common.css index cfe2adc8..ea353cbe 100644 --- a/frontend/packages/dashboard/src/common.css +++ b/frontend/packages/dashboard/src/common.css @@ -4,6 +4,7 @@ @import "@hive/shared/terminal.css"; @import "@hive/shared/tabs.css"; @import "@hive/shared/chrome.css"; +@import "./modal.css"; /* ─── global typography ───────────────────────────────────────────── Element-level rules shared across all three pages (index, flow, diff --git a/frontend/packages/dashboard/src/dashboard.css b/frontend/packages/dashboard/src/dashboard.css index 0f80eee4..d53825a6 100644 --- a/frontend/packages/dashboard/src/dashboard.css +++ b/frontend/packages/dashboard/src/dashboard.css @@ -414,113 +414,6 @@ body.dashboard-shell { } .container-row .actions form.inline { display: inline-block; margin: 0; } -/* Themed confirm modal — `themedConfirm()` in common.js. In-theme replacement - for the native confirm() on destructive actions (stop / restart / destroy), - carrying the optional "stop gracefully" checkbox. */ -.tc-backdrop { - position: fixed; - inset: 0; - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - padding: 1em; - background: color-mix(in srgb, var(--crust) 60%, transparent); - -webkit-backdrop-filter: blur(2px); - backdrop-filter: blur(2px); -} -.tc-box { - background: var(--bg-elev); - border: 1px solid var(--purple-dim); - box-shadow: 0 8px 40px -8px var(--crust); - padding: 1.2em 1.4em; - max-width: min(32em, 92vw); - display: flex; - flex-direction: column; - gap: 0.9em; -} -.tc-title { - font-weight: bold; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--subtext0); -} -.tc-message { color: var(--fg); line-height: 1.45; } -.tc-checks { display: flex; flex-direction: column; gap: 0.4em; } -.tc-checkrow { - display: flex; - align-items: flex-start; - gap: 0.5em; - cursor: pointer; - color: var(--subtext0); - font-size: 0.92em; - line-height: 1.35; -} -.tc-checkrow .tc-check { margin-top: 0.2em; flex: 0 0 auto; } -.tc-actions { - display: flex; - justify-content: flex-end; - gap: 0.6em; - margin-top: 0.2em; -} -.tc-cancel { color: var(--subtext0); } -.tc-confirm { color: var(--green); } -.tc-confirm.tc-danger { color: var(--red); } - -/* themedPrompt input field */ -.tc-promptfield { display: flex; flex-direction: column; gap: 0.4em; } -.tc-promptlabel { color: var(--subtext0); font-size: 0.9em; } -.tc-input { - font-family: inherit; - font-size: 1em; - width: 100%; - box-sizing: border-box; - background: var(--crust); - color: var(--fg); - border: 1px solid var(--purple-dim); - padding: 0.4em 0.6em; -} -.tc-input:focus { outline: 1px solid var(--green); } -/* themedPrompt's field is always a resizable textarea (Enter submits, - Shift+Enter inserts a newline) — sensible min-height, wrapped output. */ -.tc-textarea { - resize: vertical; - min-height: 4.5em; - line-height: 1.4; - white-space: pre-wrap; -} - -/* themedToast — non-blocking transient notifications (errors/info/ok) */ -.tc-toasts { - position: fixed; - top: 1em; - right: 1em; - z-index: 1100; - display: flex; - flex-direction: column; - gap: 0.5em; - max-width: min(28em, 92vw); - pointer-events: none; -} -.tc-toast { - pointer-events: auto; - cursor: pointer; - background: var(--bg-elev); - border: 1px solid var(--purple-dim); - border-left-width: 3px; - box-shadow: 0 4px 20px -6px var(--crust); - padding: 0.6em 0.9em; - font-size: 0.9em; - color: var(--fg); - white-space: pre-wrap; - opacity: 1; - transition: opacity 0.2s ease, transform 0.2s ease; -} -.tc-toast-out { opacity: 0; transform: translateX(0.5em); } -.tc-toast-error { border-left-color: var(--red); } -.tc-toast-info { border-left-color: var(--purple-dim); } -.tc-toast-ok { border-left-color: var(--green); } - .agent-status { font-size: 0.82em; color: var(--subtext0); diff --git a/frontend/packages/dashboard/src/modal.css b/frontend/packages/dashboard/src/modal.css new file mode 100644 index 00000000..31318c18 --- /dev/null +++ b/frontend/packages/dashboard/src/modal.css @@ -0,0 +1,111 @@ +/* modal.css — themed dialog component styles. + Pairs with modal.js (themedConfirm / themedPrompt / themedToast). The + dialogs are raised from common.js's data-async / data-confirm handler, + which every page loads, so common.css @imports this to keep the styles + present wherever a dialog can fire — not just the main dashboard. + (Previously these rules lived in dashboard.css, so dialogs rendered + unstyled on standalone pages such as /core.) */ + +.tc-backdrop { + position: fixed; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 1em; + background: color-mix(in srgb, var(--crust) 60%, transparent); + -webkit-backdrop-filter: blur(2px); + backdrop-filter: blur(2px); +} +.tc-box { + background: var(--bg-elev); + border: 1px solid var(--purple-dim); + box-shadow: 0 8px 40px -8px var(--crust); + padding: 1.2em 1.4em; + max-width: min(32em, 92vw); + display: flex; + flex-direction: column; + gap: 0.9em; +} +.tc-title { + font-weight: bold; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--subtext0); +} +.tc-message { color: var(--fg); line-height: 1.45; } +.tc-checks { display: flex; flex-direction: column; gap: 0.4em; } +.tc-checkrow { + display: flex; + align-items: flex-start; + gap: 0.5em; + cursor: pointer; + color: var(--subtext0); + font-size: 0.92em; + line-height: 1.35; +} +.tc-checkrow .tc-check { margin-top: 0.2em; flex: 0 0 auto; } +.tc-actions { + display: flex; + justify-content: flex-end; + gap: 0.6em; + margin-top: 0.2em; +} +.tc-cancel { color: var(--subtext0); } +.tc-confirm { color: var(--green); } +.tc-confirm.tc-danger { color: var(--red); } + +/* themedPrompt input field */ +.tc-promptfield { display: flex; flex-direction: column; gap: 0.4em; } +.tc-promptlabel { color: var(--subtext0); font-size: 0.9em; } +.tc-input { + font-family: inherit; + font-size: 1em; + width: 100%; + box-sizing: border-box; + background: var(--crust); + color: var(--fg); + border: 1px solid var(--purple-dim); + padding: 0.4em 0.6em; +} +.tc-input:focus { outline: 1px solid var(--green); } +/* themedPrompt's field is always a resizable textarea (Enter submits, + Shift+Enter inserts a newline) — sensible min-height, wrapped output. */ +.tc-textarea { + resize: vertical; + min-height: 4.5em; + line-height: 1.4; + white-space: pre-wrap; +} + +/* themedToast — non-blocking transient notifications (errors/info/ok) */ +.tc-toasts { + position: fixed; + top: 1em; + right: 1em; + z-index: 1100; + display: flex; + flex-direction: column; + gap: 0.5em; + max-width: min(28em, 92vw); + pointer-events: none; +} +.tc-toast { + pointer-events: auto; + cursor: pointer; + background: var(--bg-elev); + border: 1px solid var(--purple-dim); + border-left-width: 3px; + box-shadow: 0 4px 20px -6px var(--crust); + padding: 0.6em 0.9em; + font-size: 0.9em; + color: var(--fg); + white-space: pre-wrap; + opacity: 1; + transition: opacity 0.2s ease, transform 0.2s ease; +} +.tc-toast-out { opacity: 0; transform: translateX(0.5em); } +.tc-toast-error { border-left-color: var(--red); } +.tc-toast-info { border-left-color: var(--purple-dim); } +.tc-toast-ok { border-left-color: var(--green); }