From d00272ec02f7403c96ee6ce40ca79eadc9bfd4dd Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 14 Mar 2026 20:52:14 +0100 Subject: [PATCH 1/3] test: capture failing shell frame expectations --- .../desktop-shell-frame-red-phase.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/installer/desktop-shell-frame-red-phase.test.ts diff --git a/tests/installer/desktop-shell-frame-red-phase.test.ts b/tests/installer/desktop-shell-frame-red-phase.test.ts new file mode 100644 index 0000000..db4e079 --- /dev/null +++ b/tests/installer/desktop-shell-frame-red-phase.test.ts @@ -0,0 +1,53 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; + +function readWorkspaceFile(relativePath: string): string { + return fs.readFileSync(path.resolve(process.cwd(), relativePath), "utf8"); +} + +test("RED: app shell stops using dashboard-main as the primary desktop wrapper", () => { + const app = readWorkspaceFile("src/installer-dashboard/web/src/App.tsx"); + + assert.doesNotMatch( + app, + /className="dashboard-main"/, + "Desktop shell work should remove the dashboard-main wrapper from the main window entry composition.", + ); + assert.match( + app, + /className="desktop-shell-app"/, + "Desktop shell work should introduce a desktop-shell-app wrapper for the main window.", + ); +}); + +test("RED: installer dashboard removes hero landing markup and exposes desktop shell frame regions", () => { + const ui = readWorkspaceFile("src/installer-dashboard/web/src/InstallerDashboard.tsx"); + + assert.doesNotMatch( + ui, + /className="hero"/, + "Desktop shell work should remove the hero landing section from the main window.", + ); + assert.doesNotMatch( + ui, + />\s*Skills & Hooks Dashboard\s* Date: Sat, 14 Mar 2026 20:52:40 +0100 Subject: [PATCH 2/3] feat: replace dashboard landing with desktop shell frame --- src/installer-dashboard/web/src/App.tsx | 4 +- .../web/src/InstallerDashboard.tsx | 102 +++++++++--------- src/installer-dashboard/web/src/styles.css | 89 +++++++++++++++ 3 files changed, 145 insertions(+), 50 deletions(-) diff --git a/src/installer-dashboard/web/src/App.tsx b/src/installer-dashboard/web/src/App.tsx index 4c79caf..6448c13 100644 --- a/src/installer-dashboard/web/src/App.tsx +++ b/src/installer-dashboard/web/src/App.tsx @@ -3,11 +3,11 @@ import { InstallerDashboard } from "./InstallerDashboard"; export function App(): JSX.Element { return ( -
+
Skip to Main Content -
+
diff --git a/src/installer-dashboard/web/src/InstallerDashboard.tsx b/src/installer-dashboard/web/src/InstallerDashboard.tsx index ea883da..d25d5d4 100644 --- a/src/installer-dashboard/web/src/InstallerDashboard.tsx +++ b/src/installer-dashboard/web/src/InstallerDashboard.tsx @@ -1634,22 +1634,26 @@ export function InstallerDashboard(): JSX.Element { }, [appUpdate, appUpdateBusy]); return ( -
-
-
-

ICA COMMAND CENTER

-

Multi-source

-
-

Skills & Hooks Dashboard

-

Manage repositories once, then install source-pinned skills and hooks across targets.

-
- {sources.length} sources - {installedSkillCount} skills installed - {installedHookCount} hooks installed +
+
+
+
+

ICA DESKTOP WORKSPACE

+

Installer Workspace

+

+ Keep source and installation operations in a persistent desktop shell instead of a dashboard landing page. +

+
+
+ {sources.length} sources + {installedSkillCount} skills installed + {installedHookCount} hooks installed +
-
+
+
@@ -1805,28 +1809,28 @@ export function InstallerDashboard(): JSX.Element { )}
-
- - {error && ( -
- Action needed: {error} -
- )} - {catalogLoading && ( -
-
- Loading skills catalog - {Math.round(catalogLoadingProgress)}% -
-
{catalogLoadingMessage || "Working…"}
-
- )} -
-
+
- {activeTab === "skills" && ( + {activeTab === "skills" && (
- )} + )} - {activeTab === "hooks" && ( + {activeTab === "hooks" && (
- )} + )} - {activeTab === "settings" && ( + {activeTab === "settings" && (

Repository Management

@@ -2571,9 +2575,9 @@ export function InstallerDashboard(): JSX.Element {
- )} + )} - {activeTab === "state" && ( + {activeTab === "state" && (

States & Reports

@@ -2826,6 +2830,8 @@ export function InstallerDashboard(): JSX.Element {
)} +
+ {skillPickerOpen && (
setSkillPickerOpen(false)}>
- setSkillPickerQuery(event.target.value)} + setSkillPickerQuery(event.target.value)} aria-label="Search local skill bundles" />
diff --git a/src/installer-dashboard/web/src/styles.css b/src/installer-dashboard/web/src/styles.css index 1a34f2d..572a2e2 100644 --- a/src/installer-dashboard/web/src/styles.css +++ b/src/installer-dashboard/web/src/styles.css @@ -374,6 +374,78 @@ input[type="radio"]:focus-visible { padding: var(--space-8) var(--space-6) var(--space-9); } +.desktop-shell-app { + min-height: 100vh; +} + +.desktop-shell-app-main { + display: block; +} + +.desktop-shell-frame { + display: grid; + gap: var(--space-5); +} + +.desktop-shell-header { + background: + linear-gradient(135deg, color-mix(in srgb, var(--accent) 7%, var(--surface)) 0%, var(--surface) 62%), + var(--surface); + border: 1px solid color-mix(in srgb, var(--line) 80%, transparent); + border-radius: 14px; + padding: var(--space-5) var(--space-6); + box-shadow: inset 0 1px 0 color-mix(in srgb, #ffffff 36%, transparent); +} + +.desktop-shell-header-bar { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: var(--space-4); +} + +.desktop-shell-header-copyblock { + display: grid; + gap: var(--space-3); + max-width: 46rem; +} + +.desktop-shell-header h1 { + margin: 0; + font-size: clamp(1.4rem, 2.5vw, 1.82rem); + line-height: 1.12; + font-weight: 300; + letter-spacing: -0.02em; +} + +.desktop-shell-header-copy { + margin: 0; + color: var(--text-soft); + font-size: 0.95rem; + line-height: 1.58; +} + +.desktop-shell-header-meta { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: var(--space-2); +} + +.desktop-shell-header-meta span { + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--line) 76%, transparent); + background: color-mix(in srgb, var(--surface) 72%, transparent); + color: var(--text-soft); + padding: 0.2rem 0.55rem; + font-size: 0.72rem; +} + +.desktop-shell-workspace { + display: grid; + gap: var(--space-5); +} + .hero { background: var(--surface); border: 1px solid var(--line); @@ -1877,6 +1949,19 @@ pre { padding: var(--space-5); } + .desktop-shell-header { + padding: var(--space-5); + } + + .desktop-shell-header-bar { + flex-direction: column; + align-items: stretch; + } + + .desktop-shell-header-meta { + justify-content: flex-start; + } + .panel-spacious { padding: var(--space-6); } @@ -1979,6 +2064,10 @@ pre { gap: var(--space-1); } + .desktop-shell-header { + padding: var(--space-5); + } + .tab-nav { width: 100%; gap: var(--space-2); From 9dc63538e4d0b64209d826134f027f04577ca733 Mon Sep 17 00:00:00 2001 From: Karsten Samaschke Date: Sat, 14 Mar 2026 20:53:12 +0100 Subject: [PATCH 3/3] refactor: simplify desktop shell layout primitives --- src/installer-dashboard/web/src/styles.css | 76 ---------------------- 1 file changed, 76 deletions(-) diff --git a/src/installer-dashboard/web/src/styles.css b/src/installer-dashboard/web/src/styles.css index 572a2e2..a0699b0 100644 --- a/src/installer-dashboard/web/src/styles.css +++ b/src/installer-dashboard/web/src/styles.css @@ -12,7 +12,6 @@ --text: #121826; --text-soft: #4b5b72; --text-muted: #65758a; - --hero-meta-bg: color-mix(in srgb, var(--accent) 10%, #f2f5fa); --status-error-border: color-mix(in srgb, var(--accent) 22%, #d5dbe5); --status-error-bg: color-mix(in srgb, var(--accent) 12%, #f6f8fb); --status-error-text: #4b5b72; @@ -75,7 +74,6 @@ body[data-mode="dark"] { --text: #e9eef4; --text-soft: #c4d0dc; --text-muted: #9eacbc; - --hero-meta-bg: color-mix(in srgb, var(--accent) 30%, #1e2329); --status-error-border: color-mix(in srgb, var(--accent) 42%, #3d4652); --status-error-bg: color-mix(in srgb, var(--accent) 24%, #1d2228); --status-error-text: #cfdbe7; @@ -446,22 +444,6 @@ input[type="radio"]:focus-visible { gap: var(--space-5); } -.hero { - background: var(--surface); - border: 1px solid var(--line); - border-radius: 12px; - padding: var(--space-5) var(--space-6); - box-shadow: none; - margin-bottom: var(--space-5); -} - -.hero-topline { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--space-3); -} - .eyebrow { margin: 0; font-size: 0.72rem; @@ -471,48 +453,6 @@ input[type="radio"]:focus-visible { font-weight: 400; } -.stamp { - margin: 0; - border-radius: 8px; - border: 1px solid color-mix(in srgb, var(--line) 76%, transparent); - color: var(--text-soft); - background: transparent; - padding: 0.14rem 0.46rem; - font-size: 0.7rem; -} - -.hero h1 { - margin: var(--space-3) 0 0; - font-size: clamp(1.38rem, 2.5vw, 1.78rem); - line-height: 1.2; - font-weight: 300; - letter-spacing: -0.01em; - text-wrap: balance; -} - -.hero > p { - margin: var(--space-3) 0 0; - color: var(--text-soft); - max-width: 68ch; - font-size: 0.94rem; -} - -.hero-meta { - margin-top: var(--space-4); - display: flex; - flex-wrap: wrap; - gap: var(--space-2); -} - -.hero-meta span { - border-radius: 8px; - background: transparent; - border: 1px solid color-mix(in srgb, var(--line) 75%, transparent); - color: var(--text-soft); - padding: 0.15rem 0.44rem; - font-size: 0.7rem; -} - .desktop-shell-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); @@ -1911,10 +1851,6 @@ pre { line-height: 1.55; } -.dashboard-main { - display: block; -} - .skip-link { position: absolute; left: 0.75rem; @@ -1945,10 +1881,6 @@ pre { padding: var(--space-7) var(--space-5) var(--space-8); } - .hero { - padding: var(--space-5); - } - .desktop-shell-header { padding: var(--space-5); } @@ -2056,14 +1988,6 @@ pre { grid-column: span 1; } - .hero { - padding: var(--space-5); - } - - .hero-meta { - gap: var(--space-1); - } - .desktop-shell-header { padding: var(--space-5); }