diff --git a/apps/web-console/energy-monitor.css b/apps/web-console/energy-monitor.css index 5312b64..15219de 100644 --- a/apps/web-console/energy-monitor.css +++ b/apps/web-console/energy-monitor.css @@ -5,7 +5,7 @@ inset: 0; display: grid; place-items: center; - padding: 68px 20px 20px; + padding: 16px; background: color-mix(in oklch, var(--background) 58%, transparent); backdrop-filter: blur(6px) saturate(.9); -webkit-backdrop-filter: blur(6px) saturate(.9); @@ -17,8 +17,8 @@ } .console-v2 .energy-monitor-page { - width: min(1180px, 100%); - height: min(664px, 100%); + width: min(1480px, 100%); + height: min(900px, 100%); min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); diff --git a/apps/web-console/firmware-workbench-entry.css b/apps/web-console/firmware-workbench-entry.css index 7c766a2..71d67e6 100644 --- a/apps/web-console/firmware-workbench-entry.css +++ b/apps/web-console/firmware-workbench-entry.css @@ -4,7 +4,7 @@ inset: 0; display: grid; place-items: center; - padding: 68px 20px 20px; + padding: 16px; background: color-mix(in oklch, var(--background) 58%, transparent); backdrop-filter: blur(6px) saturate(.9); -webkit-backdrop-filter: blur(6px) saturate(.9); @@ -12,8 +12,8 @@ } .console-v2 .firmware-workbench-layer[hidden] { display: none !important; } .console-v2 .firmware-workbench-page { - width: min(1180px, 100%); - height: min(664px, 100%); + width: min(1480px, 100%); + height: min(900px, 100%); min-height: 0; display: grid; grid-template-rows: 76px minmax(0, 1fr); diff --git a/apps/web-console/firmware-workbench-entry.js b/apps/web-console/firmware-workbench-entry.js index cd652b2..25774bc 100644 --- a/apps/web-console/firmware-workbench-entry.js +++ b/apps/web-console/firmware-workbench-entry.js @@ -3,6 +3,17 @@ const state = { open: false, returnFocus: null, backgroundInert: [] }; + function currentWorkbenchTheme() { + return document.documentElement.dataset.glassTheme === "light" ? "light" : "dark"; + } + + function syncWorkbenchTheme(frame) { + frame?.contentWindow?.postMessage({ + type: "cognitive-agent:theme-change", + theme: currentWorkbenchTheme(), + }, window.location.origin); + } + function setBackgroundInert(inert) { const layer = document.querySelector("#firmwareWorkbenchLayer"); if (!layer) return; @@ -80,11 +91,19 @@ - + `; dock.append(trigger); document.body.append(layer); + const frame = document.querySelector(".firmware-workbench-frame", layer); + frame.src = `/firmware-workbench.html?embedded=1&theme=${currentWorkbenchTheme()}`; + frame.addEventListener("load", () => syncWorkbenchTheme(frame)); + window.addEventListener("cognitive-agent:theme-change", () => syncWorkbenchTheme(frame)); + new MutationObserver(() => syncWorkbenchTheme(frame)).observe(document.documentElement, { + attributes: true, + attributeFilter: ["data-glass-theme"], + }); trigger.addEventListener("click", () => setOpen(!state.open)); document.querySelector("#firmwareWorkbenchClose", layer).addEventListener("click", () => setOpen(false)); layer.addEventListener("click", (event) => { if (event.target === layer) setOpen(false); }); diff --git a/apps/web-console/firmware-workbench-theme.js b/apps/web-console/firmware-workbench-theme.js new file mode 100644 index 0000000..656b450 --- /dev/null +++ b/apps/web-console/firmware-workbench-theme.js @@ -0,0 +1,65 @@ +(() => { + "use strict"; + + const STORAGE_KEY = "cognitive-agent.glass-theme"; + + function normalizeTheme(value) { + return value === "light" ? "light" : value === "dark" ? "dark" : ""; + } + + function themeFromCookie() { + try { + return normalizeTheme(document.cookie + .split(";") + .map((part) => part.trim()) + .find((part) => part.startsWith(`${STORAGE_KEY}=`)) + ?.slice(STORAGE_KEY.length + 1)); + } catch (_error) { + return ""; + } + } + + function resolveInitialTheme() { + const requested = normalizeTheme(new URLSearchParams(window.location.search).get("theme")); + if (requested) return requested; + try { + const parentTheme = normalizeTheme(window.parent?.document?.documentElement?.dataset?.glassTheme); + if (parentTheme) return parentTheme; + } catch (_error) { + // Standalone and cross-origin rendering continue with local preferences. + } + try { + const stored = normalizeTheme(localStorage.getItem(STORAGE_KEY)); + if (stored) return stored; + } catch (_error) { + // Storage may be unavailable in hardened browser contexts. + } + return themeFromCookie() || "dark"; + } + + function applyTheme(theme) { + const normalized = normalizeTheme(theme) || "dark"; + document.documentElement.dataset.glassTheme = normalized; + document.documentElement.style.colorScheme = normalized; + document.querySelector("#firmwareWorkbenchThemeColor") + ?.setAttribute("content", normalized === "dark" ? "#0f1526" : "#f3f6fb"); + return normalized; + } + + applyTheme(resolveInitialTheme()); + + window.addEventListener("storage", (event) => { + if (event.key === STORAGE_KEY) applyTheme(event.newValue); + }); + window.addEventListener("message", (event) => { + if (event.origin !== window.location.origin) return; + if (window.parent !== window && event.source !== window.parent) return; + if (event.data?.type !== "cognitive-agent:theme-change") return; + applyTheme(event.data.theme); + }); + + window.CognitiveAgentFirmwareWorkbenchTheme = Object.freeze({ + apply: applyTheme, + current: () => document.documentElement.dataset.glassTheme, + }); +})(); diff --git a/apps/web-console/firmware-workbench.css b/apps/web-console/firmware-workbench.css index 3f0a587..b335af6 100644 --- a/apps/web-console/firmware-workbench.css +++ b/apps/web-console/firmware-workbench.css @@ -341,7 +341,7 @@ th:nth-child(4) { width: 28%; } th:nth-child(5) { width: 8%; } th:nth-child(6) { .modal-backdrop { position: fixed; inset: 0; z-index: 50; display: grid; place-items: center; padding: 24px; background: rgba(31, 43, 61, .34); backdrop-filter: blur(4px); } .modal-backdrop[hidden] { display: none; } -.modal { width: min(780px, 100%); max-height: min(680px, calc(100dvh - 48px)); display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; background: #fff; border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow); } +.modal { width: min(1040px, 100%); max-height: min(820px, calc(100dvh - 48px)); display: grid; grid-template-rows: auto minmax(0, 1fr) auto; overflow: hidden; background: #fff; border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow); } .modal header, .modal footer { padding: 14px 16px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--line); } .modal footer { justify-content: flex-end; border-top: 1px solid var(--line); border-bottom: 0; } .modal-body { overflow: auto; padding: 16px; } @@ -454,3 +454,155 @@ body.embedded .file-cell span { line-height: 1.15; } body.embedded .pipeline-card { overflow-x: auto; } body.embedded .pipeline-card .pipeline { width: 680px; } } + +/* The workbench is rendered in an iframe, so it owns a complete theme token + set instead of relying on custom properties from the parent document. */ +:root[data-glass-theme="dark"] { + color-scheme: dark; + --page: #090e1c; + --surface: #171e31; + --surface-soft: #11182a; + --line: #303a55; + --line-strong: #44516d; + --text: #edf2ff; + --muted: #9ba9c0; + --primary: #7aa3ff; + --primary-soft: #1b2b53; + --success: #55d4a1; + --success-soft: #12372d; + --warning: #f2bd68; + --danger: #ff8993; + --shadow: 0 26px 70px rgba(0, 0, 0, .42), 0 4px 18px rgba(0, 0, 0, .24); +} + +:root[data-glass-theme="dark"] body:not(.embedded) { + background: + radial-gradient(circle at 15% 8%, color-mix(in srgb, var(--primary) 18%, transparent), transparent 31%), + linear-gradient(145deg, #11182b 0%, var(--page) 100%); +} + +:root[data-glass-theme="dark"] body.embedded { background: transparent; } +:root[data-glass-theme="dark"] .shell, +:root[data-glass-theme="dark"] body.embedded .shell { + background: color-mix(in srgb, var(--surface-soft) 86%, var(--page)); + border-color: color-mix(in srgb, var(--line-strong) 78%, transparent); +} +:root[data-glass-theme="dark"] .topbar { + background: color-mix(in srgb, var(--surface) 94%, transparent); +} +:root[data-glass-theme="dark"] :is(.back-link, .icon-button, .button-secondary) { + color: var(--muted); + background: var(--surface); + border-color: var(--line-strong); +} +:root[data-glass-theme="dark"] :is(.back-link, .icon-button):hover { + color: var(--primary); + border-color: color-mix(in srgb, var(--primary) 50%, var(--line)); + background: var(--primary-soft); +} +:root[data-glass-theme="dark"] :is(.eyebrow, .section-kicker) { color: var(--muted); } +:root[data-glass-theme="dark"] :is(.environment-chip, .server-state) { + color: var(--muted); + background: var(--surface-soft); +} +:root[data-glass-theme="dark"] .server-state span { background: color-mix(in srgb, var(--muted) 72%, transparent); } +:root[data-glass-theme="dark"] .server-state.online, +:root[data-glass-theme="dark"] .status-badge[data-status="baseline"] { + border-color: color-mix(in srgb, var(--success) 44%, var(--line)); + background: var(--success-soft); +} +:root[data-glass-theme="dark"] .card { + box-shadow: 0 2px 10px rgba(0, 0, 0, .16); +} +:root[data-glass-theme="dark"] .command-card > label { color: var(--muted); } +:root[data-glass-theme="dark"] textarea:focus { background: var(--surface); } +:root[data-glass-theme="dark"] :is(select, option) { color: var(--text); background: var(--surface); } +:root[data-glass-theme="dark"] .prompt-templates button, +:root[data-glass-theme="dark"] .cap-tags span, +:root[data-glass-theme="dark"] .table-actions :is(button, a) { + color: var(--primary); + background: var(--primary-soft); + border-color: color-mix(in srgb, var(--primary) 34%, var(--line)); +} +:root[data-glass-theme="dark"] .capability-brief { + border-color: color-mix(in srgb, var(--primary) 25%, var(--line)); + background: color-mix(in srgb, var(--primary-soft) 44%, var(--surface-soft)); +} +:root[data-glass-theme="dark"] .capability-brief strong { color: var(--text); } +:root[data-glass-theme="dark"] .capability-brief > svg { color: var(--primary); } +:root[data-glass-theme="dark"] .pipeline-stage::after { background: var(--line); } +:root[data-glass-theme="dark"] .stage-node { + color: var(--muted); + border-color: var(--line-strong); + background: var(--surface); +} +:root[data-glass-theme="dark"] .pipeline-stage[data-status="success"] .stage-node { + border-color: color-mix(in srgb, var(--success) 62%, var(--line)); +} +:root[data-glass-theme="dark"] .pipeline-stage[data-status="failed"] .stage-node, +:root[data-glass-theme="dark"] .pipeline-stage[data-status="blocked"] .stage-node, +:root[data-glass-theme="dark"] .release-result[data-state="failed"], +:root[data-glass-theme="dark"] .status-badge[data-status="failed"], +:root[data-glass-theme="dark"] .status-badge[data-status="blocked"] { + color: var(--danger); + border-color: color-mix(in srgb, var(--danger) 44%, var(--line)); + background: color-mix(in srgb, var(--danger) 12%, var(--surface)); +} +:root[data-glass-theme="dark"] .pipeline-stage[data-status="canceled"] .stage-node { + color: var(--muted); + background: var(--surface-soft); +} +:root[data-glass-theme="dark"] .pipeline-stage[data-status="pending_confirmation"] .stage-node, +:root[data-glass-theme="dark"] .status-badge[data-status="ready"], +:root[data-glass-theme="dark"] .status-badge[data-status="pending_confirmation"] { + color: var(--warning); + border-color: color-mix(in srgb, var(--warning) 44%, var(--line)); + background: color-mix(in srgb, var(--warning) 12%, var(--surface)); +} +:root[data-glass-theme="dark"] .pipeline-stage[data-status="success"]::after { + background: color-mix(in srgb, var(--success) 64%, var(--line)); +} +:root[data-glass-theme="dark"] .release-result[data-state="success"], +:root[data-glass-theme="dark"] .status-badge[data-status="success"], +:root[data-glass-theme="dark"] .status-badge[data-status="published"] { + border-color: color-mix(in srgb, var(--success) 44%, var(--line)); + background: var(--success-soft); +} +:root[data-glass-theme="dark"] .result-icon { + color: var(--muted); + background: color-mix(in srgb, var(--surface-soft) 78%, var(--line)); +} +:root[data-glass-theme="dark"] .release-result[data-state="success"] .result-icon { + color: var(--success); + background: color-mix(in srgb, var(--success) 18%, var(--surface)); +} +:root[data-glass-theme="dark"] .metrics div { border-color: var(--line); } +:root[data-glass-theme="dark"] :is(.log-stream, .log-details) { background: var(--surface-soft); } +:root[data-glass-theme="dark"] .empty-state svg { color: var(--muted); } +:root[data-glass-theme="dark"] .empty-state strong { color: var(--text); } +:root[data-glass-theme="dark"] .log-entry, +:root[data-glass-theme="dark"] :is(.change-item, .test-item) { border-color: var(--line); } +:root[data-glass-theme="dark"] .log-time { color: var(--muted); } +:root[data-glass-theme="dark"] .log-dot { background: var(--muted); } +:root[data-glass-theme="dark"] .log-copy p { color: var(--muted); } +:root[data-glass-theme="dark"] .inspector-tabs { background: var(--surface); } +:root[data-glass-theme="dark"] .inspector-tabs button { color: var(--muted); } +:root[data-glass-theme="dark"] .inspector-tabs button.active { + color: var(--primary); + background: var(--surface-soft); +} +:root[data-glass-theme="dark"] :is(.build-output, .code-block) { color: var(--text); } +:root[data-glass-theme="dark"] .server-target strong { color: var(--text); } +:root[data-glass-theme="dark"] th { color: var(--muted); background: var(--surface); } +:root[data-glass-theme="dark"] td { color: var(--muted); border-color: var(--line); } +:root[data-glass-theme="dark"] :is(.software-name strong, .file-cell strong) { color: var(--text); } +:root[data-glass-theme="dark"] .table-actions .delete-action { + color: var(--danger); + background: color-mix(in srgb, var(--danger) 12%, var(--surface)); +} +:root[data-glass-theme="dark"] .modal-backdrop { background: rgba(2, 5, 14, .68); } +:root[data-glass-theme="dark"] .modal { background: var(--surface); } +:root[data-glass-theme="dark"] .diff-panel p { color: var(--muted); } +:root[data-glass-theme="dark"] .diff-panel.added { + border-color: color-mix(in srgb, var(--success) 42%, var(--line)); +} diff --git a/apps/web-console/firmware-workbench.html b/apps/web-console/firmware-workbench.html index 60ce069..2f851a9 100644 --- a/apps/web-console/firmware-workbench.html +++ b/apps/web-console/firmware-workbench.html @@ -3,11 +3,12 @@
- - + +