From 07079407c7a6a2efb613c82416204df586ed6299 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:24:41 -0700 Subject: [PATCH 1/8] docs: langgraph examples redesign plan (7 affected, sky-blue accent) Co-Authored-By: Claude Opus 4.8 --- .../2026-07-07-langgraph-examples-redesign.md | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-07-langgraph-examples-redesign.md diff --git a/docs/superpowers/plans/2026-07-07-langgraph-examples-redesign.md b/docs/superpowers/plans/2026-07-07-langgraph-examples-redesign.md new file mode 100644 index 000000000..8157e14e6 --- /dev/null +++ b/docs/superpowers/plans/2026-07-07-langgraph-examples-redesign.md @@ -0,0 +1,136 @@ +# LangGraph Examples Redesign (7 affected) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax. + +**Goal:** Restyle the **bespoke** UI of the 7 affected langgraph examples off Tailwind utilities (which don't compile in the embedded example builds) onto encapsulated Angular component `styles:` on `--ds-*` design tokens, accent = sky-blue `--ds-accent`. Only the bespoke panels/cards change — the `@threadplane/chat` library UI and `example-chat-layout` are untouched. + +**Scope (audited live):** time-travel, durable-execution, persistence (heavy panels), subgraphs, memory (one small container each), interrupts, client-tools (conditional cards). NOT streaming/deployment-runtime (confirmed fine — pure library chat). + +**Architecture:** Each example uses `` (library: chat in `main`, bespoke panel projected into `sidebar`; durable-execution + client-tools also register inline chat *views*). Replace the Tailwind/`--tplane-chat-*` styling of the bespoke pieces with encapsulated `styles:` on `--ds-*`. Examples stay standalone — each component carries its own styles (no shared CSS file, per the standalone-examples convention). + +**Tech Stack:** Angular standalone + signals, `@threadplane/chat` + `@threadplane/render` view/tool registries, `@threadplane/design-tokens` (`--ds-*`), Nx production build gate (no lint target for example apps). + +**Reference exemplar (read first):** `cockpit/render/spec-rendering/angular/src/app/spec-rendering.component.ts` — the encapsulated-`styles:`-on-`--ds-*` approach (BEM-ish classes, `var(--ds-*, fallback)`). + +--- + +## Shared design kit (apply in every task) + +**Accent (interactive / primary / selected / active):** sky-blue. +- fill: `var(--ds-accent, #64C3FD)`, hover `var(--ds-accent-hover, #8dd4ff)`, text-on-accent `#08243a` (dark navy — legible on sky-blue). +- soft: bg `var(--ds-accent-surface, rgba(100,195,253,0.08))`, border `var(--ds-accent-border, rgba(100,195,253,0.25))`, text `var(--ds-accent, #64C3FD)`. + +**Neutrals:** `--ds-surface`, `--ds-surface-dim`, `--ds-surface-tinted`, `--ds-border`, `--ds-text-primary` / `-secondary` / `-muted`, `--ds-font-mono`, `--ds-radius-sm|md|lg`, `--ds-shadow-md`. + +**Semantic status (NO tokens exist — define as local constants in the component's `styles:`):** +```css +:host { + --st-done: #2ea567; /* complete / success — green */ + --st-active: #e0a850; /* running / in-progress — amber */ + --st-error: #e0645a; /* error — red */ + /* pending / idle → var(--ds-text-muted) */ +} +``` + +**Common class patterns (adapt per example — names illustrative):** +- Panel container: `padding: 1rem;` (sidebar panels). +- `.cap` panel title: `font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:0.12em; color:var(--ds-text-muted);` (copy from exemplar). +- `.empty` empty-state: `font-size:13px; font-style:italic; color:var(--ds-text-muted);`. +- `.row` list item: `display:flex; align-items:center; gap:0.5rem; padding:6px 10px; border:1px solid var(--ds-border); border-radius:var(--ds-radius-md); background:var(--ds-surface);` and `.row--active { background:var(--ds-accent-surface); border-color:var(--ds-accent-border); }`. +- `.badge` round: `width:24px; height:24px; border-radius:999px; display:flex; align-items:center; justify-content:center; font-size:11px; font-weight:700;` — active/selected: accent fill + `#08243a` text. +- `.btn` small action: `padding:4px 8px; font-size:11px; border-radius:var(--ds-radius-sm); border:1px solid var(--ds-border); background:var(--ds-surface-dim); color:var(--ds-text-secondary); cursor:pointer;` hover → `border-color:var(--ds-accent-border); color:var(--ds-accent);`. `.btn--primary { background:var(--ds-accent); color:#08243a; border:0; font-weight:600; }`. +- Mono id: `font-family:var(--ds-font-mono); font-size:11px; color:var(--ds-text-muted);`. +- status dot: `width:8px; height:8px; border-radius:999px;` colored via `--st-*`. + +**Rules (every task):** +- Remove ALL Tailwind utility classes from the bespoke templates. Only plain scoped class names remain. +- Reference `--ds-*` (theme-aware) for neutrals/accent; `--st-*` locals for status. Do NOT reference `--tplane-chat-primary`/`--tplane-chat-surface` (undefined) or Tailwind color names (`bg-green-600` etc.). +- Preserve every handler name, signal, input contract, and `@switch`/`@if`/`@for` logic — only styling changes. +- Do NOT touch ``, ``, ``, `` or any `@threadplane/*` library component's own chrome. +- Add `"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]` to each example's `tsconfig.app.json` if missing (defensive — matches render fix). +- Per-example gate: no-Tailwind grep (below) + `npx nx build --configuration=production` succeeds. + +**No-Tailwind grep template (per file):** +`grep -nE 'class="[^"]*(px-[0-9]|py-[0-9]|p-[0-9]|rounded-|bg-(green|amber|red|blue|indigo|slate|gray|zinc|emerald|purple|sky|teal|orange)-|gap-[0-9]|w-[0-9]|h-[0-9]|border-\[|animate-|space-[xy]-|text-\[|tracking-|font-(semibold|medium|bold))' ` → expect no output (exit 1). + +--- + +## Task 1: durable-execution (heavy — step tracker ×2) + +**Files:** `cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts`, `.../src/app/views/step-pipeline.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle both step trackers.** Read both files. The main component's `sidebar` has a vertical "Pipeline" step tracker; `step-pipeline.component.ts` is a horizontal inline chat-view of the same. For each step circle use `--st-*`: complete = `--st-done` fill + white check SVG; active = 2px `--st-active` border + `animate` spin (keep the CSS spin animation — define `@keyframes` in the component `styles:`) + small amber dot; pending = 2px `var(--ds-border)` + muted dot. Connecting lines: `--st-done` when prior complete else `var(--ds-border)`. Labels colored per state. Container (inline view): `border:1px solid var(--ds-border); border-radius:var(--ds-radius-xl); padding:1rem; background:var(--ds-surface);`. No interactive controls. Preserve `steps` computed + `StepPipelineComponent` selector `step-pipeline` and its `steps` input. +- [ ] **Step 2: Fix `sidebarWidth`.** In the main component template change `sidebarWidth="w-64"` → `sidebarWidth="16rem"` (Tailwind class was a no-op; input expects a CSS length). +- [ ] **Step 3: tsconfig spec-exclude** (if missing). +- [ ] **Step 4: grep gate** on both `.component.ts` files → no output. +- [ ] **Step 5: build** `npx nx build cockpit-langgraph-durable-execution-angular --configuration=production` → succeeds. +- [ ] **Step 6: commit** `feat(cockpit-langgraph): redesign durable-execution step trackers (encapsulated CSS, --ds-* + semantic status)` + +## Task 2: time-travel (heavy — checkpoint timeline) + +**Files:** `cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle the sidebar timeline.** Header `.cap` "Timeline" + `{{count}} checkpoints` subtitle. Empty state `.empty`. Checkpoint rows as `.row`/`.row--active` (selected). Numbered round `.badge` (selected = accent fill + `#08243a`, else `var(--ds-surface-tinted)` + muted). Label + mono id (truncate: `white-space:nowrap; overflow:hidden; text-overflow:ellipsis;`). Two per-row `.btn` "Replay"/"Fork". Preserve `selectedIndex`, `checkpoints`, `checkpointLabel()`, and the `replay(state,i)` / `fork(state,i)` handlers. +- [ ] **Step 2: tsconfig spec-exclude** (if missing). +- [ ] **Step 3: grep gate** → no output. +- [ ] **Step 4: build** `npx nx build cockpit-langgraph-time-travel-angular --configuration=production` → succeeds. +- [ ] **Step 5: commit** `feat(cockpit-langgraph): redesign time-travel checkpoint timeline (encapsulated CSS on --ds-*)` + +## Task 3: persistence (heavy — thread picker) + +**Files:** `cockpit/langgraph/persistence/angular/src/app/persistence.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle the sidebar thread picker.** Header `.cap` "Threads". Thread rows as full-width `.thread` buttons (`.thread--active` = `font-weight:600` + `background:var(--ds-accent-surface)`). Footer `.btn--primary` "+ New Thread" (full width). **Replace the inline `(mouseenter)/(mouseleave)` JS style hacks with real CSS `:hover`** (`.thread:hover { background: var(--ds-surface-tinted); }`, `.btn--primary:hover { ... }`) — remove the inline handlers from the template. Preserve `switchThread(id)`, `newThread()`, and the module-scope `threadsState`/`activeThreadIdState` signals + `provideAgent({ onThreadId })` wiring (do NOT touch providers). +- [ ] **Step 2: Fix `sidebarWidth`** `"w-56"` → `"14rem"`. +- [ ] **Step 3: tsconfig spec-exclude** (if missing). +- [ ] **Step 4: grep gate** → no output. Also verify no `mouseenter`/`mouseleave` remain: `grep -c 'mouseenter\|mouseleave' ` → 0. +- [ ] **Step 5: build** `npx nx build cockpit-langgraph-persistence-angular --configuration=production` → succeeds. +- [ ] **Step 6: commit** `feat(cockpit-langgraph): redesign persistence thread picker (encapsulated CSS, real :hover)` + +## Task 4: subgraphs (small — subagent status list) + +**Files:** `cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle the subagent list.** Container padding. `.cap` "Subagents". `.empty` state. Rows: status `.dot` (`--st-done` complete / `--st-error` error / `--st-active` running) + mono truncated id + right-aligned `.count` msg count (`margin-left:auto; font-size:11px; color:var(--ds-text-muted);`). No controls. Preserve `subagentEntries`. +- [ ] **Step 2: tsconfig spec-exclude** (if missing). **Step 3: grep gate** → no output. **Step 4: build** `npx nx build cockpit-langgraph-subgraphs-angular --configuration=production`. **Step 5: commit** `feat(cockpit-langgraph): redesign subgraphs subagent list (encapsulated CSS on --ds-*)` + +## Task 5: memory (small — learned facts list) + +**Files:** `cockpit/langgraph/memory/angular/src/app/memory.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle the facts list.** Container padding. `.cap` "Learned Facts". `.empty` state. Fact rows: `.fact__key` (`font-weight:600; color:var(--ds-text-primary);`) + `:` + `.fact__value` (`color:var(--ds-text-secondary);`), `font-size:13px`. Consider a subtle row separator (`border-bottom:1px solid var(--ds-border);`). No controls. Preserve `memoryEntries`. +- [ ] **Step 2: tsconfig spec-exclude** (if missing). **Step 3: grep gate** → no output. **Step 4: build** `npx nx build cockpit-langgraph-memory-angular --configuration=production`. **Step 5: commit** `feat(cockpit-langgraph): redesign memory learned-facts list (encapsulated CSS on --ds-*)` + +## Task 6: interrupts (conditional — approval-card body + edit form) + +**Files:** `cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Restyle the bespoke approval-card body.** This is the content projected into ``'s `#body` ng-template (the card chrome is a library component — DO NOT touch it). Convert the inline `style="..."` attributes to an encapsulated `styles:` block with classes: an Amount row (muted label + ``), Customer row (muted label + mono ``), optional italic Reason line, and the conditional Edit form (number `` + "Save" button). Style the input on `--ds-*` (`background:var(--ds-surface-dim); border:1px solid var(--ds-border); border-radius:var(--ds-radius-sm); color:var(--ds-text-primary); padding:5px 8px;` + `:focus` accent ring `box-shadow:0 0 0 3px var(--ds-accent-glow); border-color:var(--ds-accent);`) and the Save button as `.btn--primary` (accent fill). Preserve `editing`/`editAmount` signals, `onAction($event)`, `submitEdit(payload)`, `resetEdit()`, and all `agent.submit({ resume })` calls. **Do NOT change any `WELCOME_SUGGESTIONS` label/description text** (asserted by `interrupts.spec.ts` e2e). +- [ ] **Step 2: tsconfig spec-exclude** (if missing). **Step 3: grep gate** → no output (also no leftover `style="..."` on the bespoke body: prefer classes). **Step 4: build** `npx nx build cockpit-langgraph-interrupts-angular --configuration=production`. **Step 5: commit** `feat(cockpit-langgraph): redesign interrupts approval-card body + edit form (encapsulated CSS on --ds-*)` + +## Task 7: client-tools (conditional — weather + confirm-booking cards, token remap) + +**Files:** `cockpit/langgraph/client-tools/angular/src/app/weather-card.component.ts`, `.../confirm-booking.component.ts`, `.../tsconfig.app.json` + +- [ ] **Step 1: Remap the two cards' existing encapsulated styles from `--tplane-chat-*` to `--ds-*`.** These already use encapsulated `styles:` (NOT Tailwind) — this is a token rename, not a conversion. Map: `--tplane-chat-separator`→`var(--ds-border)`, `--tplane-chat-surface*`→`var(--ds-surface)`/`var(--ds-surface-dim)`, `--tplane-chat-text*`→`var(--ds-text-*)`, `--tplane-chat-accent`/`--tplane-chat-primary` (confirm-booking primary button)→`var(--ds-accent)` with `#08243a` text, `--tplane-chat-on-primary`→`#08243a`. Keep card structure, `.wc`/`.cb` classes, the loading badge, the 3-way confirm-booking states. Preserve the schema-anchored `input()`s (`ViewProps` / `confirmBookingSchema` — `strict:true`, do NOT rename) and `respond(true|false)` → `injectRenderHost().result(...)`. `client-tools.component.ts` itself has no bespoke UI — leave it. +- [ ] **Step 2: tsconfig spec-exclude** (if missing). **Step 3: grep gate** on both card files → no output; also confirm no `--tplane-chat-` remains: `grep -c 'tplane-chat' ` → 0. **Step 4: build** `npx nx build cockpit-langgraph-client-tools-angular --configuration=production`. **Step 5: commit** `feat(cockpit-langgraph): remap client-tool cards to --ds-* tokens (sky-blue accent)` + +--- + +## Task 8: Integration gate (orchestrator) + +- [ ] **Build all 7:** `npx nx run-many -t build --configuration=production -p cockpit-langgraph-durable-execution-angular cockpit-langgraph-time-travel-angular cockpit-langgraph-persistence-angular cockpit-langgraph-subgraphs-angular cockpit-langgraph-memory-angular cockpit-langgraph-interrupts-angular cockpit-langgraph-client-tools-angular` → all succeed. +- [ ] **Repo-wide grep** across the 7 examples' component files → no Tailwind utilities remain. + +## Task 9: Visual verification (orchestrator, Chrome MCP) + +- [ ] **Always-visible panels** — serve each and probe/screenshot (ports in `cockpit/ports.mjs`): durable-execution sidebar tracker, time-travel timeline, persistence thread picker, subgraphs dots, memory facts. Confirm styled (badges/rows/buttons have real radius/padding/accent), light + dark spot-check. +- [ ] **Conditional cards** — best-effort: on the served (or post-deploy production) example, drive the live agent to trigger the interrupt approval card (interrupts) and the weather/confirm-booking tool cards (client-tools) + durable-execution's inline step-pipeline view, and confirm they render styled. If the live backend isn't drivable locally, verify post-deploy on examples.threadplane.ai. + +--- + +## Self-review +- **Coverage:** all 7 audited-affected examples (Tasks 1–7); the 2 fine ones excluded. Semantic status via local `--st-*` (no tokens exist). sidebarWidth no-op fixed (Tasks 1, 3). persistence inline-hover anti-pattern → `:hover` (Task 3). interrupts inline styles → classes (Task 6). client-tools is a token remap not a conversion (Task 7). +- **Preservation:** every task lists the handlers/signals/input contracts to keep; library components explicitly out of bounds. +- **Accent:** sky-blue `--ds-accent` for interactive/primary/selected across all; semantic green/amber/red only for genuine status. +- **No placeholders:** token maps + class patterns concrete; per-example specifics from the live audit + source map. From 8bd0ae32b4c420d5d2264ebf9ef54ad6d9130c3c Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:27:22 -0700 Subject: [PATCH 2/8] feat(cockpit-langgraph): redesign durable-execution step trackers (encapsulated CSS, --ds-* + semantic status) --- .../src/app/durable-execution.component.ts | 140 +++++++++++++++--- .../src/app/views/step-pipeline.component.ts | 133 ++++++++++++++--- .../angular/tsconfig.app.json | 1 + 3 files changed, 228 insertions(+), 46 deletions(-) diff --git a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts index 0d64b8667..c124e4bcb 100644 --- a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts +++ b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts @@ -37,53 +37,149 @@ const STEP_LABELS: Record = { selector: 'app-durable-execution', standalone: true, imports: [ChatComponent, ExampleChatLayoutComponent], + styles: ` + :host { + --st-done: #2ea567; + --st-active: #e0a850; + --st-error: #e0645a; + } + .panel { + padding: 1rem; + background: var(--ds-surface, #1c1c1c); + color: var(--ds-text-primary, #f5f5f5); + height: 100%; + } + .cap { + margin-bottom: 1.5rem; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--ds-text-muted, #a0a0a0); + } + .steps { + display: flex; + flex-direction: column; + } + .step { + display: flex; + align-items: flex-start; + gap: 0.75rem; + } + .step__col { + display: flex; + flex-direction: column; + align-items: center; + } + .circle { + width: 28px; + height: 28px; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + .circle--complete { + background: var(--st-done); + } + .circle--complete svg { + width: 16px; + height: 16px; + color: #fff; + } + .circle--active { + border: 2px solid var(--st-active); + animation: dx-spin 1.2s linear infinite; + } + .circle--pending { + border: 2px solid var(--ds-border, #2d2d2d); + } + .dot { + width: 8px; + height: 8px; + border-radius: 999px; + } + .dot--active { + background: var(--st-active); + } + .dot--pending { + background: var(--ds-text-muted, #a0a0a0); + } + @keyframes dx-spin { + to { + transform: rotate(360deg); + } + } + .line { + width: 2px; + height: 2rem; + } + .line--done { + background: var(--st-done); + } + .line--pending { + background: var(--ds-border, #2d2d2d); + } + .label { + font-size: 13px; + padding-top: 4px; + } + .label--active { + font-weight: 600; + color: var(--st-active); + } + .label--complete { + color: var(--st-done); + } + .label--pending { + color: var(--ds-text-muted, #a0a0a0); + } + `, template: ` - + -
-

Pipeline

+
+

Pipeline

-
+
@for (step of steps(); track step.id; let last = $last) { -
+
-
+
@switch (step.status) { @case ('complete') { -
- +
+
} @case ('active') { -
-
+
+
} @default { -
-
+
+
} } @if (!last) { -
-
+
}
- + {{ step.label }}
diff --git a/cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts b/cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts index 235a1efbf..f97e1eef2 100644 --- a/cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts +++ b/cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts @@ -8,54 +8,139 @@ interface PipelineStep { @Component({ selector: 'step-pipeline', standalone: true, + styles: ` + :host { + --st-done: #2ea567; + --st-active: #e0a850; + --st-error: #e0645a; + } + .pipeline { + border: 1px solid var(--ds-border, #2d2d2d); + border-radius: var(--ds-radius-xl, 18px); + padding: 1rem; + margin: 0.5rem 0; + background: var(--ds-surface, #1c1c1c); + overflow-x: auto; + } + .row { + display: flex; + align-items: center; + gap: 0; + min-width: max-content; + } + .node { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + } + .circle { + width: 32px; + height: 32px; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + } + .circle--complete { + background: var(--st-done); + } + .circle--complete svg { + width: 16px; + height: 16px; + color: #fff; + } + .circle--active { + border: 2px solid var(--st-active); + animation: sp-spin 1.2s linear infinite; + } + .circle--pending { + border: 2px solid var(--ds-border, #2d2d2d); + } + .dot { + width: 8px; + height: 8px; + border-radius: 999px; + } + .dot--active { + background: var(--st-active); + } + .dot--pending { + background: var(--ds-text-muted, #a0a0a0); + } + @keyframes sp-spin { + to { + transform: rotate(360deg); + } + } + .label { + font-size: 12px; + white-space: nowrap; + } + .label--active { + font-weight: 600; + color: var(--st-active); + } + .label--complete { + color: var(--st-done); + } + .label--pending { + color: var(--ds-text-muted, #a0a0a0); + } + .line { + width: 2.5rem; + height: 2px; + margin: 0 4px; + margin-top: -20px; + } + .line--done { + background: var(--st-done); + } + .line--pending { + background: var(--ds-border, #2d2d2d); + } + `, template: ` -
-
+
+
@for (step of steps(); track step.label; let i = $index; let last = $last) { -
+
@switch (step.status) { @case ('complete') { -
- +
+
} @case ('active') { -
-
+
+
} @default { -
-
+
+
} } - + {{ step.label }}
@if (!last) { -
-
+
} }
diff --git a/cockpit/langgraph/durable-execution/angular/tsconfig.app.json b/cockpit/langgraph/durable-execution/angular/tsconfig.app.json index ef34262b1..785b27786 100644 --- a/cockpit/langgraph/durable-execution/angular/tsconfig.app.json +++ b/cockpit/langgraph/durable-execution/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry" From 61088f0a13c3ae98c726c74d03c6196646167a8f Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:29:15 -0700 Subject: [PATCH 3/8] feat(cockpit-langgraph): redesign time-travel checkpoint timeline (encapsulated CSS on --ds-*) --- .../angular/src/app/time-travel.component.ts | 173 +++++++++++++----- .../time-travel/angular/tsconfig.app.json | 1 + 2 files changed, 133 insertions(+), 41 deletions(-) diff --git a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts index c5793c291..6ecaa5524 100644 --- a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts +++ b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts @@ -18,76 +18,167 @@ import { ExampleChatLayoutComponent } from '@threadplane/example-layouts'; selector: 'app-time-travel', standalone: true, imports: [ChatComponent, ExampleChatLayoutComponent], + styles: ` + .panel { + display: flex; + flex-direction: column; + overflow: hidden; + height: 100%; + background: var(--ds-surface, #1c1c1c); + color: var(--ds-text-primary, #f5f5f5); + } + .head { + padding: 0.75rem 1rem; + border-bottom: 1px solid var(--ds-border, #2d2d2d); + } + .cap { + margin: 0; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--ds-text-muted, #a0a0a0); + } + .sub { + margin: 4px 0 0; + font-size: 12px; + color: var(--ds-text-muted, #a0a0a0); + } + .list { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 0.5rem 0.75rem; + display: flex; + flex-direction: column; + gap: 6px; + } + .empty { + padding: 1.5rem 0; + font-size: 13px; + font-style: italic; + text-align: center; + color: var(--ds-text-muted, #a0a0a0); + } + .row { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 6px 10px; + border: 1px solid var(--ds-border, #2d2d2d); + border-radius: var(--ds-radius-md, 10px); + background: var(--ds-surface, #1c1c1c); + transition: background 0.15s ease, border-color 0.15s ease; + } + .row:hover { + background: var(--ds-surface-tinted, rgba(255, 255, 255, 0.04)); + } + .row--active { + background: var(--ds-accent-surface, rgba(100, 195, 253, 0.08)); + border-color: var(--ds-accent-border, rgba(100, 195, 253, 0.25)); + } + .badge { + flex-shrink: 0; + width: 24px; + height: 24px; + border-radius: 999px; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + font-weight: 700; + background: var(--ds-surface-tinted, rgba(255, 255, 255, 0.06)); + color: var(--ds-text-muted, #a0a0a0); + } + .badge--active { + background: var(--ds-accent, #64c3fd); + color: #08243a; + } + .info { + flex: 1; + min-width: 0; + } + .label { + margin: 0; + font-size: 12px; + font-weight: 500; + color: var(--ds-text-primary, #f5f5f5); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .id { + margin: 2px 0 0; + font-family: var(--ds-font-mono, ui-monospace, monospace); + font-size: 11px; + color: var(--ds-text-muted, #a0a0a0); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + .actions { + flex-shrink: 0; + display: flex; + gap: 4px; + } + .btn { + padding: 4px 8px; + font-size: 11px; + border-radius: var(--ds-radius-sm, 6px); + border: 1px solid var(--ds-border, #2d2d2d); + background: var(--ds-surface-dim, #0a0a0a); + color: var(--ds-text-secondary, #c8c8c8); + cursor: pointer; + transition: border-color 0.15s ease, color 0.15s ease; + } + .btn:hover { + border-color: var(--ds-accent-border, rgba(100, 195, 253, 0.25)); + color: var(--ds-accent, #64c3fd); + } + `, template: ` -
-
-

- Timeline -

-

- {{ checkpoints().length }} checkpoint(s) -

+
+
+

Timeline

+

{{ checkpoints().length }} checkpoint(s)

-
+
@if (checkpoints().length === 0) { -

- No checkpoints yet. Send a message to begin. -

+

No checkpoints yet. Send a message to begin.

} @for (state of checkpoints(); track $index; let i = $index) { -
+
- + {{ i + 1 }} -
-

- {{ checkpointLabel(state, i) }} -

+
+

{{ checkpointLabel(state, i) }}

@if (state.checkpoint?.checkpoint_id) { -

- {{ state.checkpoint.checkpoint_id }} -

+

{{ state.checkpoint.checkpoint_id }}

}
-
+
-
- +
diff --git a/cockpit/langgraph/persistence/angular/tsconfig.app.json b/cockpit/langgraph/persistence/angular/tsconfig.app.json index 925942d5d..5039e9318 100644 --- a/cockpit/langgraph/persistence/angular/tsconfig.app.json +++ b/cockpit/langgraph/persistence/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry" From 0e11f10f6cfc80c09a3a58d867ed422383abf8e5 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:32:05 -0700 Subject: [PATCH 5/8] feat(cockpit-langgraph): redesign subgraphs subagent list (encapsulated CSS on --ds-*) --- .../angular/src/app/subgraphs.component.ts | 75 ++++++++++++++++--- .../subgraphs/angular/tsconfig.app.json | 1 + 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts index 3aea00f02..d91dfc9fc 100644 --- a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts +++ b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts @@ -20,23 +20,76 @@ import { ExampleChatLayoutComponent } from '@threadplane/example-layouts'; selector: 'app-subgraphs', standalone: true, imports: [ChatComponent, ExampleChatLayoutComponent], + styles: ` + :host { + --st-done: #2ea567; + --st-active: #e0a850; + --st-error: #e0645a; + } + .panel { + padding: 1rem; + } + .cap { + margin-bottom: 1rem; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--ds-text-muted); + } + .empty { + font-size: 13px; + font-style: italic; + color: var(--ds-text-muted); + } + .row { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 6px 0; + } + .dot { + width: 8px; + height: 8px; + border-radius: 999px; + flex-shrink: 0; + background: var(--ds-text-muted); + } + .dot--complete { background: var(--st-done); } + .dot--error { background: var(--st-error); } + .dot--running { background: var(--st-active); } + .id { + font-family: var(--ds-font-mono); + font-size: 11px; + color: var(--ds-text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + } + .count { + margin-left: auto; + font-size: 11px; + color: var(--ds-text-muted); + flex-shrink: 0; + } + `, template: ` -
-

Subagents

+
+

Subagents

@if (subagentEntries().length === 0) { -

No subagents active

+

No subagents active

} @for (entry of subagentEntries(); track entry.id) { -
- - - {{ entry.id }} - {{ entry.msgCount }} msgs +
+ + {{ entry.id }} + {{ entry.msgCount }} msgs
}
diff --git a/cockpit/langgraph/subgraphs/angular/tsconfig.app.json b/cockpit/langgraph/subgraphs/angular/tsconfig.app.json index 925942d5d..5039e9318 100644 --- a/cockpit/langgraph/subgraphs/angular/tsconfig.app.json +++ b/cockpit/langgraph/subgraphs/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry" From b506af33b0a544e7b02ebfb1a341abad2f26f7cf Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:33:15 -0700 Subject: [PATCH 6/8] feat(cockpit-langgraph): redesign memory learned-facts list (encapsulated CSS on --ds-*) --- .../angular/src/app/memory.component.ts | 34 ++++++++++++++----- .../memory/angular/tsconfig.app.json | 1 + 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/cockpit/langgraph/memory/angular/src/app/memory.component.ts b/cockpit/langgraph/memory/angular/src/app/memory.component.ts index 11d911b62..8bdf69032 100644 --- a/cockpit/langgraph/memory/angular/src/app/memory.component.ts +++ b/cockpit/langgraph/memory/angular/src/app/memory.component.ts @@ -20,20 +20,38 @@ import { ExampleChatLayoutComponent } from '@threadplane/example-layouts'; selector: 'app-memory', standalone: true, imports: [ChatComponent, ExampleChatLayoutComponent], + styles: ` + .panel { padding: 1rem; } + .cap { + margin-bottom: 1rem; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--ds-text-muted, #a0a0a0); + } + .empty { font-size: 13px; font-style: italic; color: var(--ds-text-muted, #a0a0a0); } + .fact { + padding: 6px 0; + font-size: 13px; + border-bottom: 1px solid var(--ds-border, #2d2d2d); + } + .fact:last-child { border-bottom: none; } + .fact__key { font-weight: 600; color: var(--ds-text-primary, #f5f5f5); } + .fact__value { color: var(--ds-text-secondary, #c8c8c8); } + `, template: ` -
-

Learned Facts

+
+

Learned Facts

@if (memoryEntries().length === 0) { -

No facts learned yet

+

No facts learned yet

} @for (entry of memoryEntries(); track entry[0]) { -
- {{ entry[0] }}: - {{ entry[1] }} +
+ {{ entry[0] }}: + {{ entry[1] }}
}
diff --git a/cockpit/langgraph/memory/angular/tsconfig.app.json b/cockpit/langgraph/memory/angular/tsconfig.app.json index 925942d5d..5039e9318 100644 --- a/cockpit/langgraph/memory/angular/tsconfig.app.json +++ b/cockpit/langgraph/memory/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry" From 36f6d7ae282a2a6af4645b870f64a1e0a31487be Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:34:48 -0700 Subject: [PATCH 7/8] feat(cockpit-langgraph): redesign interrupts approval-card body + edit form (encapsulated CSS on --ds-*) --- .../angular/src/app/interrupts.component.ts | 49 ++++++++++++++++--- .../interrupts/angular/tsconfig.app.json | 1 + 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts index cc432ee33..e5c02be7e 100644 --- a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts +++ b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts @@ -45,6 +45,39 @@ const WELCOME_SUGGESTIONS = [ CurrencyPipe, ], changeDetection: ChangeDetectionStrategy.OnPush, + styles: ` + .body { display: flex; flex-direction: column; gap: 6px; } + .field { font-size: 13px; } + .field__label { color: var(--ds-text-muted); margin-right: 6px; } + .field__value { color: var(--ds-text-primary); } + .reason { font-style: italic; color: var(--ds-text-muted); margin-top: 4px; font-size: 13px; } + .edit-form { margin-top: 10px; display: flex; gap: 6px; align-items: center; } + .edit-form__label { color: var(--ds-text-muted); font-size: 12px; } + .control-input { + background: var(--ds-surface-dim); + border: 1px solid var(--ds-border); + border-radius: var(--ds-radius-sm); + color: var(--ds-text-primary); + padding: 5px 8px; + width: 120px; + font-size: 13px; + } + .control-input:focus { + outline: none; + border-color: var(--ds-accent); + box-shadow: 0 0 0 3px var(--ds-accent-glow); + } + .btn--primary { + background: var(--ds-accent); + color: #08243a; + border: 0; + border-radius: var(--ds-radius-sm); + padding: 6px 12px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + } + `, template: `
@@ -69,17 +102,17 @@ const WELCOME_SUGGESTIONS = [ (action)="onAction($event)" > -
-
Amount{{ payload.amount | currency }}
-
Customer{{ payload.customer_id }}
+
+
Amount{{ payload.amount | currency }}
+
Customer{{ payload.customer_id }}
@if (payload.reason) { -
{{ payload.reason }}
+
{{ payload.reason }}
} @if (editing()) { -
- - - +
+ + +
}
diff --git a/cockpit/langgraph/interrupts/angular/tsconfig.app.json b/cockpit/langgraph/interrupts/angular/tsconfig.app.json index 4d1fa84d2..30c38f15c 100644 --- a/cockpit/langgraph/interrupts/angular/tsconfig.app.json +++ b/cockpit/langgraph/interrupts/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry" From e5758bbf2feaa42ee88fc1719e04c965e0fea6cf Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Jul 2026 16:36:21 -0700 Subject: [PATCH 8/8] feat(cockpit-langgraph): remap client-tool cards to --ds-* tokens (sky-blue accent) --- .../angular/src/app/confirm-booking.component.ts | 8 ++++---- .../angular/src/app/weather-card.component.ts | 8 ++++---- cockpit/langgraph/client-tools/angular/tsconfig.app.json | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/cockpit/langgraph/client-tools/angular/src/app/confirm-booking.component.ts b/cockpit/langgraph/client-tools/angular/src/app/confirm-booking.component.ts index 40e0c3397..8067b3238 100644 --- a/cockpit/langgraph/client-tools/angular/src/app/confirm-booking.component.ts +++ b/cockpit/langgraph/client-tools/angular/src/app/confirm-booking.component.ts @@ -47,12 +47,12 @@ type ConfirmBookingProps = ViewProps; } `, styles: [` - .cb { border: 1px solid var(--tplane-chat-separator, #e5e7eb); border-radius: 12px; padding: 16px; max-width: 360px; } + .cb { border: 1px solid var(--ds-border, #e5e7eb); border-radius: 12px; padding: 16px; max-width: 360px; background: var(--ds-surface); color: var(--ds-text-primary); } .cb__summary { margin: 0 0 12px; } - .cb--resolved .cb__summary { margin: 0; opacity: 0.85; } + .cb--resolved .cb__summary { margin: 0; color: var(--ds-text-secondary); } .cb__actions { display: flex; gap: 8px; } - .cb__btn { padding: 6px 14px; border-radius: 8px; border: 1px solid var(--tplane-chat-separator, #e5e7eb); background: transparent; color: inherit; cursor: pointer; } - .cb__btn--primary { background: var(--tplane-chat-accent, #2563eb); color: #fff; border-color: transparent; } + .cb__btn { padding: 6px 14px; border-radius: 8px; border: 1px solid var(--ds-border, #e5e7eb); background: var(--ds-surface-dim); color: var(--ds-text-secondary); cursor: pointer; } + .cb__btn--primary { background: var(--ds-accent, #64C3FD); color: #08243a; border-color: transparent; font-weight: 600; } `], }) export class ConfirmBookingComponent { diff --git a/cockpit/langgraph/client-tools/angular/src/app/weather-card.component.ts b/cockpit/langgraph/client-tools/angular/src/app/weather-card.component.ts index f12ca7947..fee109be7 100644 --- a/cockpit/langgraph/client-tools/angular/src/app/weather-card.component.ts +++ b/cockpit/langgraph/client-tools/angular/src/app/weather-card.component.ts @@ -38,14 +38,14 @@ type WeatherCardProps = ViewProps;
`, styles: [` - .wc { border: 1px solid var(--tplane-chat-separator, #e5e7eb); border-radius: 12px; padding: 16px; max-width: 320px; } + .wc { border: 1px solid var(--ds-border, #e5e7eb); border-radius: 12px; padding: 16px; max-width: 320px; background: var(--ds-surface); color: var(--ds-text-primary); } .wc__head { display: flex; align-items: center; justify-content: space-between; } .wc__loc { font-weight: 600; } - .wc__badge { font-size: 12px; opacity: 0.7; } + .wc__badge { font-size: 12px; color: var(--ds-text-muted); } .wc__temp { font-size: 32px; font-weight: 700; margin-top: 8px; } - .wc__cond { opacity: 0.8; } + .wc__cond { color: var(--ds-text-secondary); } .wc__meta { display: flex; gap: 24px; margin: 12px 0 0; } - .wc__meta dt { font-size: 11px; text-transform: uppercase; opacity: 0.6; } + .wc__meta dt { font-size: 11px; text-transform: uppercase; color: var(--ds-text-muted); } .wc__meta dd { margin: 0; font-weight: 600; } `], }) diff --git a/cockpit/langgraph/client-tools/angular/tsconfig.app.json b/cockpit/langgraph/client-tools/angular/tsconfig.app.json index 925942d5d..5039e9318 100644 --- a/cockpit/langgraph/client-tools/angular/tsconfig.app.json +++ b/cockpit/langgraph/client-tools/angular/tsconfig.app.json @@ -8,6 +8,7 @@ }, "files": ["src/main.ts"], "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"], "references": [ { "path": "../../../../libs/cockpit-telemetry"