From fcce184af3b4df74422a98442a2d5501b08ac6e8 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:29:18 -0700 Subject: [PATCH 01/12] feat(example-layouts): scaffold Nx Angular library Co-Authored-By: Claude Sonnet 4.6 --- libs/example-layouts/ng-package.json | 7 +++++ libs/example-layouts/package.json | 10 ++++++ libs/example-layouts/project.json | 34 +++++++++++++++++++++ libs/example-layouts/src/public-api.ts | 2 ++ libs/example-layouts/src/test-setup.ts | 11 +++++++ libs/example-layouts/tsconfig.json | 24 +++++++++++++++ libs/example-layouts/tsconfig.lib.json | 13 ++++++++ libs/example-layouts/tsconfig.lib.prod.json | 9 ++++++ libs/example-layouts/vite.config.mts | 13 ++++++++ tsconfig.base.json | 3 +- 10 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 libs/example-layouts/ng-package.json create mode 100644 libs/example-layouts/package.json create mode 100644 libs/example-layouts/project.json create mode 100644 libs/example-layouts/src/public-api.ts create mode 100644 libs/example-layouts/src/test-setup.ts create mode 100644 libs/example-layouts/tsconfig.json create mode 100644 libs/example-layouts/tsconfig.lib.json create mode 100644 libs/example-layouts/tsconfig.lib.prod.json create mode 100644 libs/example-layouts/vite.config.mts diff --git a/libs/example-layouts/ng-package.json b/libs/example-layouts/ng-package.json new file mode 100644 index 000000000..e5d0c459b --- /dev/null +++ b/libs/example-layouts/ng-package.json @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/libs/example-layouts", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/libs/example-layouts/package.json b/libs/example-layouts/package.json new file mode 100644 index 000000000..a44559346 --- /dev/null +++ b/libs/example-layouts/package.json @@ -0,0 +1,10 @@ +{ + "name": "@cacheplane/example-layouts", + "version": "0.0.1", + "peerDependencies": { + "@angular/core": "^20.0.0 || ^21.0.0", + "@angular/common": "^20.0.0 || ^21.0.0" + }, + "license": "PolyForm-Noncommercial-1.0.0", + "sideEffects": false +} diff --git a/libs/example-layouts/project.json b/libs/example-layouts/project.json new file mode 100644 index 000000000..8c994fcf5 --- /dev/null +++ b/libs/example-layouts/project.json @@ -0,0 +1,34 @@ +{ + "name": "example-layouts", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "libs/example-layouts/src", + "prefix": "example", + "projectType": "library", + "tags": [], + "targets": { + "build": { + "executor": "@nx/angular:package", + "outputs": ["{workspaceRoot}/dist/{projectRoot}"], + "options": { + "project": "libs/example-layouts/ng-package.json", + "tsConfig": "libs/example-layouts/tsconfig.lib.json" + }, + "configurations": { + "production": { + "tsConfig": "libs/example-layouts/tsconfig.lib.prod.json" + }, + "development": {} + }, + "defaultConfiguration": "production" + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "executor": "@nx/vite:test", + "options": { + "configFile": "libs/example-layouts/vite.config.mts" + } + } + } +} diff --git a/libs/example-layouts/src/public-api.ts b/libs/example-layouts/src/public-api.ts new file mode 100644 index 000000000..9ab50d2cd --- /dev/null +++ b/libs/example-layouts/src/public-api.ts @@ -0,0 +1,2 @@ +export { ExampleChatLayoutComponent } from './lib/example-chat-layout.component'; +export { ExampleSplitLayoutComponent } from './lib/example-split-layout.component'; diff --git a/libs/example-layouts/src/test-setup.ts b/libs/example-layouts/src/test-setup.ts new file mode 100644 index 000000000..ca3d8a2b3 --- /dev/null +++ b/libs/example-layouts/src/test-setup.ts @@ -0,0 +1,11 @@ +import { getTestBed } from '@angular/core/testing'; +import { + BrowserTestingModule, + platformBrowserTesting, +} from '@angular/platform-browser/testing'; + +getTestBed().initTestEnvironment( + BrowserTestingModule, + platformBrowserTesting(), + { teardown: { destroyAfterEach: true } }, +); diff --git a/libs/example-layouts/tsconfig.json b/libs/example-layouts/tsconfig.json new file mode 100644 index 000000000..df5104e30 --- /dev/null +++ b/libs/example-layouts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "experimentalDecorators": true, + "noPropertyAccessFromIndexSignature": true, + "module": "preserve", + "emitDeclarationOnly": false, + "composite": false, + "baseUrl": "." + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/example-layouts/tsconfig.lib.json b/libs/example-layouts/tsconfig.lib.json new file mode 100644 index 000000000..afcadee07 --- /dev/null +++ b/libs/example-layouts/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "lib": ["es2022", "dom"], + "types": [] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/libs/example-layouts/tsconfig.lib.prod.json b/libs/example-layouts/tsconfig.lib.prod.json new file mode 100644 index 000000000..2a2faa884 --- /dev/null +++ b/libs/example-layouts/tsconfig.lib.prod.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.lib.json", + "compilerOptions": { + "declarationMap": false + }, + "angularCompilerOptions": { + "compilationMode": "partial" + } +} diff --git a/libs/example-layouts/vite.config.mts b/libs/example-layouts/vite.config.mts new file mode 100644 index 000000000..ce406638a --- /dev/null +++ b/libs/example-layouts/vite.config.mts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; + +export default defineConfig({ + plugins: [nxViteTsPaths()], + test: { + globals: true, + environment: 'jsdom', + include: ['src/**/*.spec.ts'], + setupFiles: ['src/test-setup.ts'], + passWithNoTests: true, + }, +}); diff --git a/tsconfig.base.json b/tsconfig.base.json index d74df592d..a27b1e954 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -27,7 +27,8 @@ "@cacheplane/render": ["libs/render/src/public-api.ts"], "@cacheplane/chat": ["libs/chat/src/public-api.ts"], "@cacheplane/partial-json": ["libs/partial-json/src/index.ts"], - "@cacheplane/a2ui": ["libs/a2ui/src/index.ts"] + "@cacheplane/a2ui": ["libs/a2ui/src/index.ts"], + "@cacheplane/example-layouts": ["libs/example-layouts/src/public-api.ts"] }, "skipLibCheck": true, "strict": true, From f9eda1c5e680d263d77f49916e3bfcdecd9d578f Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:35:53 -0700 Subject: [PATCH 02/12] feat(example-layouts): add ExampleChatLayoutComponent with responsive sidebar Co-Authored-By: Claude Sonnet 4.6 --- .../lib/example-chat-layout.component.spec.ts | 159 ++++++++++++++++++ .../src/lib/example-chat-layout.component.ts | 61 +++++++ 2 files changed, 220 insertions(+) create mode 100644 libs/example-layouts/src/lib/example-chat-layout.component.spec.ts create mode 100644 libs/example-layouts/src/lib/example-chat-layout.component.ts diff --git a/libs/example-layouts/src/lib/example-chat-layout.component.spec.ts b/libs/example-layouts/src/lib/example-chat-layout.component.spec.ts new file mode 100644 index 000000000..2c774b3d3 --- /dev/null +++ b/libs/example-layouts/src/lib/example-chat-layout.component.spec.ts @@ -0,0 +1,159 @@ +import { Component } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { ExampleChatLayoutComponent } from './example-chat-layout.component'; + +@Component({ + standalone: true, + imports: [ExampleChatLayoutComponent], + template: ` + +
Main Content
+
Sidebar Content
+
+ `, +}) +class TestHostComponent {} + +@Component({ + standalone: true, + imports: [ExampleChatLayoutComponent], + template: ` + +
Main Only
+
+ `, +}) +class NoSidebarHostComponent {} + +@Component({ + standalone: true, + imports: [ExampleChatLayoutComponent], + template: ` + +
Main
+
Sidebar
+
+ `, +}) +class LeftSidebarHostComponent {} + +@Component({ + standalone: true, + imports: [ExampleChatLayoutComponent], + template: ` + +
Main
+
Sidebar
+
+ `, +}) +class CustomWidthHostComponent {} + +describe('ExampleChatLayoutComponent', () => { + it('should render main and sidebar content', async () => { + await TestBed.configureTestingModule({ + imports: [TestHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + expect(el.querySelector('[data-testid="main-content"]')?.textContent).toBe('Main Content'); + expect(el.querySelector('[data-testid="sidebar-content"]')?.textContent).toBe('Sidebar Content'); + }); + + it('should use flex-col md:flex-row for responsive layout', async () => { + await TestBed.configureTestingModule({ + imports: [TestHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + const flexContainer = el.querySelector('.flex.flex-col'); + expect(flexContainer).toBeTruthy(); + expect(flexContainer?.classList.contains('md:flex-row')).toBe(true); + }); + + it('should hide aside when no sidebar content is projected', async () => { + await TestBed.configureTestingModule({ + imports: [NoSidebarHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(NoSidebarHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + expect(el.querySelector('[data-testid="main-only"]')?.textContent).toBe('Main Only'); + const aside = el.querySelector('aside'); + expect(aside).toBeTruthy(); + expect(aside?.children.length).toBe(0); + }); + + it('should apply md:flex-row-reverse for left sidebar position', async () => { + await TestBed.configureTestingModule({ + imports: [LeftSidebarHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(LeftSidebarHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + const flexContainer = el.querySelector('.flex.flex-col'); + expect(flexContainer?.classList.contains('md:flex-row-reverse')).toBe(true); + }); + + it('should use border-r instead of border-l for left sidebar', async () => { + await TestBed.configureTestingModule({ + imports: [LeftSidebarHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(LeftSidebarHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + const aside = el.querySelector('aside'); + expect(aside?.classList.contains('md:border-r')).toBe(true); + expect(aside?.classList.contains('md:border-l')).toBe(false); + }); + + it('should apply custom sidebar width class', async () => { + await TestBed.configureTestingModule({ + imports: [CustomWidthHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(CustomWidthHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + const aside = el.querySelector('aside'); + expect(aside?.classList.contains('md:w-80')).toBe(true); + }); + + it('should apply default w-72 sidebar width', async () => { + await TestBed.configureTestingModule({ + imports: [TestHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + + const el = fixture.nativeElement as HTMLElement; + const aside = el.querySelector('aside'); + expect(aside?.classList.contains('md:w-72')).toBe(true); + }); + + it('should set host to full viewport height', async () => { + await TestBed.configureTestingModule({ + imports: [TestHostComponent], + }).compileComponents(); + + const fixture = TestBed.createComponent(TestHostComponent); + fixture.detectChanges(); + + const hostEl = fixture.nativeElement.querySelector('example-chat-layout') as HTMLElement; + expect(hostEl).toBeTruthy(); + }); +}); diff --git a/libs/example-layouts/src/lib/example-chat-layout.component.ts b/libs/example-layouts/src/lib/example-chat-layout.component.ts new file mode 100644 index 000000000..92719aa6c --- /dev/null +++ b/libs/example-layouts/src/lib/example-chat-layout.component.ts @@ -0,0 +1,61 @@ +import { Component, computed, Input, signal } from '@angular/core'; + +@Component({ + selector: 'example-chat-layout', + standalone: true, + styles: ` + :host { + display: flex; + flex-direction: column; + height: 100vh; + height: 100dvh; + } + aside:empty { + display: none; + } + `, + template: ` +
+
+ +
+ +
+ `, +}) +export class ExampleChatLayoutComponent { + private readonly _sidebarPosition = signal<'left' | 'right'>('right'); + private readonly _sidebarWidth = signal('w-72'); + + @Input() + set sidebarPosition(value: 'left' | 'right') { + this._sidebarPosition.set(value); + } + get sidebarPosition(): 'left' | 'right' { + return this._sidebarPosition(); + } + + @Input() + set sidebarWidth(value: string) { + this._sidebarWidth.set(value); + } + get sidebarWidth(): string { + return this._sidebarWidth(); + } + + protected readonly containerClasses = computed(() => { + const base = 'flex flex-col md:flex-row flex-1 min-h-0'; + return this._sidebarPosition() === 'left' + ? `${base} md:flex-row-reverse` + : base; + }); + + protected readonly sidebarClasses = computed(() => { + const width = this._sidebarWidth(); + const position = this._sidebarPosition(); + const borderClass = position === 'left' ? 'md:border-r' : 'md:border-l'; + return `w-full md:${width} shrink-0 border-t md:border-t-0 ${borderClass} border-gray-800 overflow-y-auto`; + }); +} From 105cf40701164b8fcf4e028abd2ea66221d0ae06 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:38:52 -0700 Subject: [PATCH 03/12] fix(example-layouts): use @Input decorators for Vitest JIT compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signal-based input() doesn't register in ɵcmp.inputs metadata under Vitest JIT compilation, causing inputs to silently use defaults. Co-Authored-By: Claude Opus 4.6 --- .../src/lib/example-chat-layout.component.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libs/example-layouts/src/lib/example-chat-layout.component.ts b/libs/example-layouts/src/lib/example-chat-layout.component.ts index 92719aa6c..b9a094b21 100644 --- a/libs/example-layouts/src/lib/example-chat-layout.component.ts +++ b/libs/example-layouts/src/lib/example-chat-layout.component.ts @@ -1,5 +1,14 @@ import { Component, computed, Input, signal } from '@angular/core'; +/** + * Responsive chat-style layout with a main content area and optional sidebar. + * + * Content projection slots: + * - `[main]` — primary content area (required) + * - `[sidebar]` — optional sidebar that stacks below on mobile, beside on desktop + * + * When no `[sidebar]` content is projected, the ` - + + `, }) export class DurableExecutionComponent { diff --git a/cockpit/langgraph/memory/angular/src/app/memory.component.ts b/cockpit/langgraph/memory/angular/src/app/memory.component.ts index ae954171f..c4e2778c7 100644 --- a/cockpit/langgraph/memory/angular/src/app/memory.component.ts +++ b/cockpit/langgraph/memory/angular/src/app/memory.component.ts @@ -2,6 +2,7 @@ import { Component, computed } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -19,12 +20,12 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-memory', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class MemoryComponent { diff --git a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts index 67f2218b6..0d5766ef5 100644 --- a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts +++ b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts @@ -1,6 +1,7 @@ import { Component, signal } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; interface Thread { @@ -22,61 +23,50 @@ interface Thread { @Component({ selector: 'app-persistence', standalone: true, - imports: [ChatComponent], - styles: ` - :host { - display: flex; - height: 100vh; - } - `, + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` - + + - + `, }) export class PersistenceComponent { diff --git a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts index 9b98c234a..e7c7a0b74 100644 --- a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts +++ b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts @@ -2,6 +2,7 @@ import { Component, computed } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -19,12 +20,12 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-subgraphs', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class SubgraphsComponent { 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 492db6a64..384316621 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 @@ -2,6 +2,7 @@ import { Component, computed, signal } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; import type { ThreadState } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -17,15 +18,16 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-time-travel', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
+ - + -
- - + + `, }) export class TimeTravelComponent { From df9c709d8b147340a9335f35f93d312509bd85b5 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:45:34 -0700 Subject: [PATCH 07/12] refactor(langgraph): migrate 3 no-sidebar apps to ExampleChatLayoutComponent --- .../src/app/deployment-runtime.component.ts | 9 ++++++-- .../angular/src/app/interrupts.component.ts | 21 +++++++++++-------- .../angular/src/app/streaming.component.ts | 9 ++++++-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts b/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts index ab9429322..8035f1d97 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts +++ b/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -13,8 +14,12 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-deployment-runtime', standalone: true, - imports: [ChatComponent], - template: ``, + imports: [ChatComponent, ExampleChatLayoutComponent], + template: ` + + + + `, }) export class DeploymentRuntimeComponent { protected readonly stream = agent({ diff --git a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts index fade2da19..dfb210c94 100644 --- a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts +++ b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ChatComponent, ChatInterruptPanelComponent, views, type InterruptAction } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { signalStateStore } from '@cacheplane/render'; import { environment } from '../environments/environment'; import { ApprovalCardComponent } from './views/approval-card.component'; @@ -21,16 +22,18 @@ import { ApprovalCardComponent } from './views/approval-card.component'; @Component({ selector: 'app-interrupts', standalone: true, - imports: [ChatComponent, ChatInterruptPanelComponent], + imports: [ChatComponent, ChatInterruptPanelComponent, ExampleChatLayoutComponent], template: ` -
- - @if (stream.interrupt()) { -
- -
- } -
+ +
+ + @if (stream.interrupt()) { +
+ +
+ } +
+
`, }) export class InterruptsComponent { diff --git a/cockpit/langgraph/streaming/angular/src/app/streaming.component.ts b/cockpit/langgraph/streaming/angular/src/app/streaming.component.ts index cc445bfd3..63eed12a4 100644 --- a/cockpit/langgraph/streaming/angular/src/app/streaming.component.ts +++ b/cockpit/langgraph/streaming/angular/src/app/streaming.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -14,8 +15,12 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-streaming', standalone: true, - imports: [ChatComponent], - template: ``, + imports: [ChatComponent, ExampleChatLayoutComponent], + template: ` + + + + `, }) export class StreamingComponent { protected readonly stream = agent({ From ee19a9162c8da272dc9a8a8efc934da2407232c4 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:47:18 -0700 Subject: [PATCH 08/12] refactor(deep-agents): migrate 6 apps to ExampleChatLayoutComponent --- .../angular/src/app/filesystem.component.ts | 14 +++++++------- .../memory/angular/src/app/memory.component.ts | 14 +++++++------- .../planning/angular/src/app/planning.component.ts | 14 +++++++------- .../angular/src/app/sandboxes.component.ts | 14 +++++++------- .../skills/angular/src/app/skills.component.ts | 14 +++++++------- .../angular/src/app/subagents.component.ts | 14 +++++++------- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts index 872a1184a..c997cc1a2 100644 --- a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts +++ b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent, views } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { signalStateStore } from '@cacheplane/render'; import { environment } from '../environments/environment'; @@ -29,12 +30,11 @@ interface FileOperation { @Component({ selector: 'app-filesystem', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class FilesystemComponent { diff --git a/cockpit/deep-agents/memory/angular/src/app/memory.component.ts b/cockpit/deep-agents/memory/angular/src/app/memory.component.ts index 847917984..08699463a 100644 --- a/cockpit/deep-agents/memory/angular/src/app/memory.component.ts +++ b/cockpit/deep-agents/memory/angular/src/app/memory.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -18,12 +19,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-da-memory', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class MemoryComponent { diff --git a/cockpit/deep-agents/planning/angular/src/app/planning.component.ts b/cockpit/deep-agents/planning/angular/src/app/planning.component.ts index f20c2fb81..4004098c7 100644 --- a/cockpit/deep-agents/planning/angular/src/app/planning.component.ts +++ b/cockpit/deep-agents/planning/angular/src/app/planning.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent, views } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { signalStateStore } from '@cacheplane/render'; import { environment } from '../environments/environment'; @@ -30,12 +31,11 @@ interface PlanStep { @Component({ selector: 'app-planning', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class PlanningComponent { diff --git a/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts b/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts index a17949d02..0d81d87ff 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent, views } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { signalStateStore } from '@cacheplane/render'; import { environment } from '../environments/environment'; @@ -27,12 +28,11 @@ interface CodeExecution { @Component({ selector: 'app-sandboxes', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class SandboxesComponent { diff --git a/cockpit/deep-agents/skills/angular/src/app/skills.component.ts b/cockpit/deep-agents/skills/angular/src/app/skills.component.ts index c670f3cf8..089035cc8 100644 --- a/cockpit/deep-agents/skills/angular/src/app/skills.component.ts +++ b/cockpit/deep-agents/skills/angular/src/app/skills.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent, views } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { signalStateStore } from '@cacheplane/render'; import { environment } from '../environments/environment'; @@ -29,12 +30,11 @@ interface SkillInvocation { @Component({ selector: 'app-skills', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class SkillsComponent { diff --git a/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts b/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts index 412aa989a..f2fd7c23a 100644 --- a/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts +++ b/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts @@ -1,5 +1,6 @@ import { Component, computed } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -27,12 +28,11 @@ interface Delegation { @Component({ selector: 'app-subagents', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class SubagentsComponent { From f4e2033b7457af7ce275c2ffe3911aee5ed0a87f Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:49:12 -0700 Subject: [PATCH 09/12] refactor(chat): migrate 7 right-sidebar apps to ExampleChatLayoutComponent --- .../chat/input/angular/src/app/input.component.ts | 14 +++++++------- .../angular/src/app/interrupts.component.ts | 14 +++++++------- .../angular/src/app/messages.component.ts | 14 +++++++------- .../angular/src/app/subagents.component.ts | 14 +++++++------- .../theming/angular/src/app/theming.component.ts | 15 ++++++++------- .../angular/src/app/timeline.component.ts | 14 +++++++------- .../angular/src/app/tool-calls.component.ts | 14 +++++++------- 7 files changed, 50 insertions(+), 49 deletions(-) diff --git a/cockpit/chat/input/angular/src/app/input.component.ts b/cockpit/chat/input/angular/src/app/input.component.ts index b670f8b18..eeb4d43ad 100644 --- a/cockpit/chat/input/angular/src/app/input.component.ts +++ b/cockpit/chat/input/angular/src/app/input.component.ts @@ -2,6 +2,7 @@ import { Component, computed } from '@angular/core'; import { ChatInputComponent as ChatInputPrimitive } from '@cacheplane/chat'; import { ChatMessagesComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -13,10 +14,10 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-input', standalone: true, - imports: [ChatInputPrimitive, ChatMessagesComponent], + imports: [ChatInputPrimitive, ChatMessagesComponent, ExampleChatLayoutComponent], template: ` -
-
+ +

Chat Input Demo

@@ -27,8 +28,7 @@ import { environment } from '../environments/environment';
- -
+ + `, }) export class InputComponent { diff --git a/cockpit/chat/interrupts/angular/src/app/interrupts.component.ts b/cockpit/chat/interrupts/angular/src/app/interrupts.component.ts index 970bc2bfc..3d1147d8f 100644 --- a/cockpit/chat/interrupts/angular/src/app/interrupts.component.ts +++ b/cockpit/chat/interrupts/angular/src/app/interrupts.component.ts @@ -2,6 +2,7 @@ import { Component, computed } from '@angular/core'; import { JsonPipe } from '@angular/common'; import { ChatComponent, ChatInterruptPanelComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -14,12 +15,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-interrupts', standalone: true, - imports: [ChatComponent, ChatInterruptPanelComponent, JsonPipe], + imports: [ChatComponent, ChatInterruptPanelComponent, JsonPipe, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class InterruptsComponent { diff --git a/cockpit/chat/messages/angular/src/app/messages.component.ts b/cockpit/chat/messages/angular/src/app/messages.component.ts index 395bcb814..d81f16524 100644 --- a/cockpit/chat/messages/angular/src/app/messages.component.ts +++ b/cockpit/chat/messages/angular/src/app/messages.component.ts @@ -5,6 +5,7 @@ import { ChatInputComponent, ChatTypingIndicatorComponent, } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -18,10 +19,10 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-messages', standalone: true, - imports: [ChatMessagesComponent, ChatInputComponent, ChatTypingIndicatorComponent], + imports: [ChatMessagesComponent, ChatInputComponent, ChatTypingIndicatorComponent, ExampleChatLayoutComponent], template: ` -
-
+ +

Chat Messages Primitives

@@ -33,8 +34,7 @@ import { environment } from '../environments/environment';
- -
+ + `, }) export class MessagesComponent { diff --git a/cockpit/chat/subagents/angular/src/app/subagents.component.ts b/cockpit/chat/subagents/angular/src/app/subagents.component.ts index 1561f1b85..36f465368 100644 --- a/cockpit/chat/subagents/angular/src/app/subagents.component.ts +++ b/cockpit/chat/subagents/angular/src/app/subagents.component.ts @@ -5,6 +5,7 @@ import { ChatSubagentsComponent, ChatSubagentCardComponent, } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -16,12 +17,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-subagents', standalone: true, - imports: [ChatComponent, ChatSubagentsComponent, ChatSubagentCardComponent], + imports: [ChatComponent, ChatSubagentsComponent, ChatSubagentCardComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class SubagentsComponent { diff --git a/cockpit/chat/theming/angular/src/app/theming.component.ts b/cockpit/chat/theming/angular/src/app/theming.component.ts index a567fca35..571734262 100644 --- a/cockpit/chat/theming/angular/src/app/theming.component.ts +++ b/cockpit/chat/theming/angular/src/app/theming.component.ts @@ -1,6 +1,8 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component, signal } from '@angular/core'; +import { TitleCasePipe } from '@angular/common'; import { ChatComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -47,12 +49,11 @@ const THEMES: Record> = { @Component({ selector: 'app-theming', standalone: true, - imports: [ChatComponent], + imports: [ChatComponent, ExampleChatLayoutComponent, TitleCasePipe], template: ` -
- - -
+ + `, }) export class ThemingComponent { diff --git a/cockpit/chat/timeline/angular/src/app/timeline.component.ts b/cockpit/chat/timeline/angular/src/app/timeline.component.ts index 354c71ffd..ba6bb4adf 100644 --- a/cockpit/chat/timeline/angular/src/app/timeline.component.ts +++ b/cockpit/chat/timeline/angular/src/app/timeline.component.ts @@ -1,6 +1,7 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent, ChatTimelineSliderComponent } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -12,12 +13,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-timeline', standalone: true, - imports: [ChatComponent, ChatTimelineSliderComponent], + imports: [ChatComponent, ChatTimelineSliderComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class TimelineComponent { diff --git a/cockpit/chat/tool-calls/angular/src/app/tool-calls.component.ts b/cockpit/chat/tool-calls/angular/src/app/tool-calls.component.ts index a7e37e448..84530c5bb 100644 --- a/cockpit/chat/tool-calls/angular/src/app/tool-calls.component.ts +++ b/cockpit/chat/tool-calls/angular/src/app/tool-calls.component.ts @@ -5,6 +5,7 @@ import { ChatToolCallsComponent, ChatToolCallCardComponent, } from '@cacheplane/chat'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; @@ -15,12 +16,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-tool-calls', standalone: true, - imports: [ChatComponent, ChatToolCallsComponent, ChatToolCallCardComponent], + imports: [ChatComponent, ChatToolCallsComponent, ChatToolCallCardComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class ToolCallsComponent { From e591533e62f05cbbdac00208690bfa796adbe189 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:50:12 -0700 Subject: [PATCH 10/12] refactor(chat): migrate left-sidebar + no-sidebar apps to ExampleChatLayoutComponent Co-Authored-By: Claude Sonnet 4.6 --- .../chat/debug/angular/src/app/debug.component.ts | 9 +++++---- .../angular/src/app/generative-ui.component.ts | 9 +++++++-- .../threads/angular/src/app/threads.component.ts | 15 ++++++++------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/cockpit/chat/debug/angular/src/app/debug.component.ts b/cockpit/chat/debug/angular/src/app/debug.component.ts index 79a8da4b0..4c06846a1 100644 --- a/cockpit/chat/debug/angular/src/app/debug.component.ts +++ b/cockpit/chat/debug/angular/src/app/debug.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ChatDebugComponent } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -12,11 +13,11 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-debug', standalone: true, - imports: [ChatDebugComponent], + imports: [ChatDebugComponent, ExampleChatLayoutComponent], template: ` -
- -
+ + + `, }) export class DebugPageComponent { diff --git a/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts b/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts index ebfd92f18..df5121531 100644 --- a/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts +++ b/cockpit/chat/generative-ui/angular/src/app/generative-ui.component.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { ChatComponent, views } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; import { WeatherCardComponent } from './views/weather-card.component'; import { StatCardComponent } from './views/stat-card.component'; @@ -16,8 +17,12 @@ const myViews = views({ @Component({ selector: 'app-generative-ui', standalone: true, - imports: [ChatComponent], - template: ``, + imports: [ChatComponent, ExampleChatLayoutComponent], + template: ` + + + + `, }) export class GenerativeUiComponent { protected readonly agentRef = agent({ diff --git a/cockpit/chat/threads/angular/src/app/threads.component.ts b/cockpit/chat/threads/angular/src/app/threads.component.ts index a67e24298..cb9ff0f34 100644 --- a/cockpit/chat/threads/angular/src/app/threads.component.ts +++ b/cockpit/chat/threads/angular/src/app/threads.component.ts @@ -2,6 +2,7 @@ import { Component, signal } from '@angular/core'; import { ChatComponent, ChatThreadListComponent, type Thread } from '@cacheplane/chat'; import { agent } from '@cacheplane/angular'; +import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts'; import { environment } from '../environments/environment'; /** @@ -11,20 +12,20 @@ import { environment } from '../environments/environment'; @Component({ selector: 'app-threads', standalone: true, - imports: [ChatComponent, ChatThreadListComponent], + imports: [ChatComponent, ChatThreadListComponent, ExampleChatLayoutComponent], template: ` -
- - -
+ + `, }) export class ThreadsComponent { From 5858d5ca5bcec6b08f3a6f0fba5f860f1d44de6d Mon Sep 17 00:00:00 2001 From: Brian Love Date: Thu, 9 Apr 2026 22:52:31 -0700 Subject: [PATCH 11/12] refactor(render): migrate 6 apps to ExampleSplitLayoutComponent --- .../src/app/computed-functions.component.ts | 50 ++++---- .../src/app/element-rendering.component.ts | 80 +++++++------ .../angular/src/app/registry.component.ts | 50 ++++---- .../angular/src/app/repeat-loops.component.ts | 98 ++++++++-------- .../src/app/spec-rendering.component.ts | 50 ++++---- .../src/app/state-management.component.ts | 110 +++++++++--------- 6 files changed, 213 insertions(+), 225 deletions(-) diff --git a/cockpit/render/computed-functions/angular/src/app/computed-functions.component.ts b/cockpit/render/computed-functions/angular/src/app/computed-functions.component.ts index 02e687dc1..2e7c02a63 100644 --- a/cockpit/render/computed-functions/angular/src/app/computed-functions.component.ts +++ b/cockpit/render/computed-functions/angular/src/app/computed-functions.component.ts @@ -9,6 +9,7 @@ import { import type { Spec } from '@json-render/core'; import { StreamingSimulator } from '../../../../shared/streaming-simulator'; import { StreamingTimelineComponent } from '../../../../shared/streaming-timeline.component'; +import { ExampleSplitLayoutComponent } from '@cacheplane/example-layouts'; import { COMPUTED_FUNCTIONS_SPECS } from './specs'; // --- Inline view components --- @@ -111,11 +112,11 @@ class DemoCardComponent { @Component({ selector: 'app-computed-functions', standalone: true, - imports: [RenderSpecComponent, StreamingTimelineComponent], + imports: [RenderSpecComponent, StreamingTimelineComponent, ExampleSplitLayoutComponent], template: ` -
+ -
+
Spec: @for (spec of specs; track spec.label; let i = $index) { - @if (getItems().length) { -
- @for (item of getItems(); track $index) { -
- {{ item }} - -
- } -
- } -

- Modify /items array in the store. -

-
+ +
+
List Controls
+
+ + @if (getItems().length) { +
+ @for (item of getItems(); track $index) { +
+ {{ item }} + +
+ } +
+ } +

+ Modify /items array in the store. +

- -
+ + `, }) export class RepeatLoopsComponent implements OnDestroy { diff --git a/cockpit/render/spec-rendering/angular/src/app/spec-rendering.component.ts b/cockpit/render/spec-rendering/angular/src/app/spec-rendering.component.ts index 10ed745cd..536d76a37 100644 --- a/cockpit/render/spec-rendering/angular/src/app/spec-rendering.component.ts +++ b/cockpit/render/spec-rendering/angular/src/app/spec-rendering.component.ts @@ -9,6 +9,7 @@ import { import type { Spec } from '@json-render/core'; import { StreamingSimulator } from '../../../../shared/streaming-simulator'; import { StreamingTimelineComponent } from '../../../../shared/streaming-timeline.component'; +import { ExampleSplitLayoutComponent } from '@cacheplane/example-layouts'; import { SPEC_RENDERING_SPECS } from './specs'; // --- Inline view components registered in the demo registry --- @@ -124,11 +125,11 @@ class DemoCardComponent { @Component({ selector: 'app-spec-rendering', standalone: true, - imports: [RenderSpecComponent, StreamingTimelineComponent], + imports: [RenderSpecComponent, StreamingTimelineComponent, ExampleSplitLayoutComponent], template: ` -
+ -
+
Spec: @for (spec of specs; track spec.label; let i = $index) {