From d5ac58c6ff902807b6f8e8bfa09892e87ce47cd4 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 15 May 2026 13:00:41 -0700 Subject: [PATCH 1/8] docs: examples theme sync Stage 2 design Completes the rollout of cockpit-example theme sync to the remaining 31 Angular example apps. Five-PR sequence: PR-0 (library auto-install + pilot cleanup), PR-1 (chat wave, 10 apps incl. a2ui migration to example-chat-layout), PR-2 (langgraph, 8 apps), PR-3 (deep-agents, 6 apps), PR-4 (render + ag-ui, 7 apps). Key decisions: - installEmbeddedTheme moves to module-level side effect in @ngaf/example-layouts; main.ts files stay clean - Templates use --ngaf-chat-* (chat lib namespace), not --ds-* - A2UI wrapped in for structural consistency - Manual chrome MCP smoke per app per wave Co-Authored-By: Claude Opus 4.7 --- ...5-15-examples-theme-sync-stage-2-design.md | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-15-examples-theme-sync-stage-2-design.md diff --git a/docs/superpowers/specs/2026-05-15-examples-theme-sync-stage-2-design.md b/docs/superpowers/specs/2026-05-15-examples-theme-sync-stage-2-design.md new file mode 100644 index 000000000..fda72c4f9 --- /dev/null +++ b/docs/superpowers/specs/2026-05-15-examples-theme-sync-stage-2-design.md @@ -0,0 +1,200 @@ +# Cockpit Examples Theme Sync — Stage 2 Design + +**Date:** 2026-05-15 +**Status:** Spec — pending implementation plan +**Predecessor:** `docs/superpowers/specs/2026-05-13-cockpit-examples-theme-sync-design.md` (Stage 1 — library + chat-timeline pilot) + +## Goal + +Complete the rollout of cockpit-example theme sync to the remaining 31 Angular example apps under `cockpit///angular/`. Embedded chat surfaces in cockpit follow the host theme (light + dark) seamlessly across every capability, not just the pilot. Reference code stays clean for external developers. + +Out of scope: +- Backend examples (the `python/` agent servers under each capability) — no UI, no theming +- Stage 1 work — already shipped (#301, #302) +- Light or dark palette changes — already shipped (#321 light, #333 dark) +- Chat lib palette unification — independent (decision C from PR 2) + +## Decisions + +| # | Decision | Choice | +|---|---|---| +| 1 | Auto-install mechanism | Module-level side effect in `@ngaf/example-layouts/src/public-api.ts` — runs once on first import of any export from the library | +| 2 | Template var convention | `--ngaf-chat-*` (chat lib namespace) for all surface/text/border references in example component templates; no `--ds-*` in template source | +| 3 | A2UI consistency | Wrap a2ui's `` in `` for structural consistency with the other 30 chat-family apps | +| 4 | PR cadence | 5-PR sequence: PR-0 (library + pilot cleanup), PR-1 (chat wave), PR-2 (langgraph), PR-3 (deep-agents), PR-4 (render + ag-ui) | +| 5 | Smoke verification | Full manual chrome MCP smoke per wave — every app's capability route in cockpit, light + dark, eyeball each before merging that wave | + +## Architecture + +**Reference code surface (per example app, after migration):** + +`src/main.ts`: +```ts +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { TimelineComponent } from './app/timeline.component'; + +bootstrapApplication(TimelineComponent, appConfig).catch((err) => console.error(err)); +``` + +No cockpit-specific imports. No `installEmbeddedTheme()` call. External developers can copy this verbatim into their own app and it just works (no theme sync needed when not iframed). + +`src/styles.css`: +```css +@import "@ngaf/example-layouts/theme.css"; + +html, body { + height: 100%; + margin: 0; + background: var(--ngaf-chat-bg); + color: var(--ngaf-chat-text); + font-family: var(--ngaf-chat-font-family); +} +``` + +References `--ngaf-chat-*` (auto-injected when any `@ngaf/chat` component imports). + +`src/index.html`: +```html + + + + + ... + + + + + + + +``` + +No Tailwind v3 CDN script. No hardcoded body classes. Tailwind v4 comes from the styles.css import. + +**How theme sync works under the hood:** + +1. App boots; imports `ExampleChatLayoutComponent` (or `ExampleSplitLayoutComponent`) from `@ngaf/example-layouts` — this is sufficient to trigger the module-level side effect +2. `@ngaf/example-layouts/src/public-api.ts` side effect: calls `installEmbeddedTheme('dark')` if `typeof document !== 'undefined'` +3. `installEmbeddedTheme()`: + - Sets `data-theme="dark"` on `` + - Applies `cssVars('dark')` to `document.documentElement.style` + - Posts `{ type: 'ngaf:theme-request' }` to `window.parent` (no-op if not iframed) + - Listens for `{ type: 'ngaf:theme', theme }` messages +4. When iframed by cockpit's ``: cockpit's broadcast or handshake reply updates the iframe's theme; chat lib's auto-injected `--ngaf-chat-*` tokens follow via the bridge in `@ngaf/example-layouts/theme.css` +5. When NOT iframed (standalone, `examples.cacheplane.ai`): the default `'dark'` theme stays applied. Chat lib's auto-injection populates `--ngaf-chat-*` independently. + +**External developer experience:** +- Sees clean Angular reference code (no postMessage, no cockpit-specific calls in the template/main) +- The library imports they'd already do (`@ngaf/chat`, `@ngaf/example-layouts`, `@ngaf/langgraph`) handle theme setup transparently +- Optional: external dev wires up their own theme system via `data-theme="light"` or `"dark"` on root — chat lib's three-layer cascade respects that + +## Pilot cleanup + +`cockpit/chat/timeline/angular/` (the Stage 1 pilot) currently has the explicit `installEmbeddedTheme()` call. Migrate it to the same clean form as Stage 2 apps: + +- **`src/main.ts`** — remove the `import { installEmbeddedTheme } from '@ngaf/example-layouts';` line and the `installEmbeddedTheme();` call +- **`src/app/timeline.component.ts`** — replace `style="background: var(--ds-canvas); color: var(--ds-text-primary);"` with `style="background: var(--ngaf-chat-bg); color: var(--ngaf-chat-text);"` and `style="color: var(--ds-text-muted);"` with `style="color: var(--ngaf-chat-text-muted);"` + +After this cleanup, the pilot is structurally identical to every other Stage 2 app — same `main.ts` shape, same template var convention. + +This cleanup happens in **PR-0** (library + pilot cleanup). + +## A2UI migration shape + +Current `cockpit/chat/a2ui/angular/src/app/a2ui.component.ts`: +```ts +template: ``, +``` + +After migration: +```ts +imports: [ChatComponent, ExampleChatLayoutComponent], +template: ` + + + +`, +``` + +The chat goes in the `main` slot of ``. No sidebar content is projected — a2ui doesn't need one. The layout's default behavior with an empty sidebar slot handles this gracefully (other chat examples like `chat-messages` similarly have no sidebar content). + +This migration happens in **PR-1** (chat wave) alongside the other chat apps. + +## Per-app migration pattern (every Stage 2 app) + +The pattern follows the Stage 1 pilot (with the Q2=B adjustment to use `--ngaf-chat-*`): + +1. **`src/index.html`** — drop the `