From d61614608ed198563b7b863e9c4476af7ea43af1 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 07:16:23 -0700 Subject: [PATCH 01/17] feat(cockpit): complete cockpit application with 14 capability examples (#14) Cockpit is an integrated developer reference surface for @cacheplane/stream-resource. It embeds running Angular examples, shows syntax-highlighted source code, renders tutorial documentation, and displays auto-generated API references for each capability. Shared libraries: - @cacheplane/design-tokens: colors, glass, gradient, glow, typography tokens - @cacheplane/ui-react: GlassPanel, GlassButton, Callout, Steps, Tabs, Card, CodeGroup, NavLink - @cacheplane/chat: Angular chat component library (cp-chat, cp-chat-message, cp-chat-input) 14 capability examples (Angular + Python + docs + e2e each): - LangGraph: streaming, persistence, interrupts, memory, durable-execution, subgraphs, time-travel, deployment-runtime - Deep Agents: planning, filesystem, subagents, memory, skills, sandboxes Unified harness: - Capability registry as single source of truth - Serve orchestrator with --capability and --all modes - Per-capability Nx serve targets - CI build job for all Angular apps - LangGraph deployment matrix for all 14 backends - Smoke e2e test suite (28 tests: 14 UI render + 14 send/receive) Light glassmorphism theme matching the website design with frosted glass panels, warm-to-cool gradient backgrounds, and EB Garamond/Inter/JetBrains Mono typography. From 7c86a83e7249059e4d0754955d33d39de1e4ba2a Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Apr 2026 10:10:10 -0700 Subject: [PATCH 02/17] fix(website): add remark-gfm dependency for markdown table rendering (#55) The import was added in #48 but the dependency was missing from package.json, causing CI/production builds to fail on table-heavy docs. --- package-lock.json | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/package-lock.json b/package-lock.json index b079e407f..e2b6c4bba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "react-dom": "^19.0.0", "rehype-pretty-code": "^0.14.3", "rehype-slug": "^6.0.0", + "remark-gfm": "^4.0.1", "rxjs": "~7.8.0", "shiki": "^4.0.2" }, diff --git a/package.json b/package.json index 712d6ae5f..81897b19d 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "react-dom": "^19.0.0", "rehype-pretty-code": "^0.14.3", "rehype-slug": "^6.0.0", + "remark-gfm": "^4.0.1", "rxjs": "~7.8.0", "shiki": "^4.0.2" }, From 5d5afacef53f509b71fe49937a0cc4c07aaef948 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Tue, 7 Apr 2026 11:45:59 -0700 Subject: [PATCH 03/17] feat: migrate all domains from stream-resource.dev to cacheplane.ai (#56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: animated arch diagram, copy buttons, tab labels, deploy docs (#8) * feat(website): add animated architecture flow diagram to introduction * feat(website): redesign arch diagram, fix tabs/table/deploy/copy button - Redesign ArchFlowDiagram as chat+console simulation showing full AI turn - Fix Tab labels using label prop instead of items array - Replace Key Concepts table with CardGroup (better MDX compat) - Expand Deploy section with 4 steps + env config + Angular build - Add copy button to all code blocks via Pre component - Use gpt-5-mini in code examples * feat: gradient feature chips, copy icon, TOC fix (#9) - Add FeatureChips component for Key Concepts (gradient chips, horizontal scroll) - Redesign copy button with SVG icons (copy/check), better visibility - Fix code block border (darker, more visible on light backgrounds) - Fix TOC sticky positioning with alignSelf and right padding - Replace CardGroup in Key Concepts with FeatureChips * docs: improve all 18 docs pages to intro quality (#10) * docs: add plan for improving all docs pages to intro quality * docs(website): expand streaming guide with modes, errors, throttle * docs(website): expand time-travel guide with history UI and debugging * docs(website): expand API reference stubs with intros and examples * docs(website): expand subgraphs guide with orchestrator and error handling * docs(website): add What's Next navigation to all docs pages --------- * docs: expand LangGraph Basics with 4 agent patterns + fix data flow (#11) * docs(website): massively expand LangGraph Basics with agent patterns From 66 to 377 lines. Covers: - Core concepts: nodes, edges, state with detailed code - 4 agent patterns: ReAct, human-in-the-loop, multi-agent, persistence - Each pattern with Python graph code + Angular streamResource connection - Signal mapping table showing every LangGraph concept → Signal - Graph API vs Functional API comparison - Expanded What's Next with 6 cards * fix(website): replace ASCII data flow diagram with Steps component * docs: comprehensive overhaul — 8 pages rewritten, 3300+ lines added (#12) * docs: add comprehensive docs overhaul master plan (15 tasks, 3 phases) * fix(website): resolve import paths, broken links, code fence, .tsx extensions * fix(website): convert all Tabs to label prop syntax * docs(website): rewrite Angular Signals concept with streaming lifecycle and Python code * docs(website): rewrite State Management with Python reducers and TypeScript mapping * docs(website): rewrite Memory guide with Python Store API and Angular patterns * docs(website): rewrite Agent Architecture with full Python patterns and Angular mapping * docs(website): rewrite Deployment guide with full LangGraph Cloud + Angular deployment * docs(website): rewrite Persistence guide with Python checkpointers and thread UI * docs(website): rewrite Testing guide with comprehensive mock patterns * docs(website): rewrite Interrupts guide with Python interrupt code and approval component * fix(website): fix Callout type='warn' → type='warning' + strip code fence titles * docs: Phase 2+3 — polish guides, fix quickstart/install, expand API pages (#13) * docs(website): polish quickstart and installation with fixes * docs(website): expand API reference pages with navigation and context * docs(website): polish streaming, time-travel, subgraphs with Python code * fix(website): fix Callout type warn in installation * feat(cockpit): complete cockpit application with 14 capability examples (#2) Cockpit is an integrated developer reference surface for @cacheplane/stream-resource. It embeds running Angular examples, shows syntax-highlighted source code, renders tutorial documentation, and displays auto-generated API references for each capability. Shared libraries: - @cacheplane/design-tokens: colors, glass, gradient, glow, typography tokens - @cacheplane/ui-react: GlassPanel, GlassButton, Callout, Steps, Tabs, Card, CodeGroup, NavLink - @cacheplane/chat: Angular chat component library (cp-chat, cp-chat-message, cp-chat-input) 14 capability examples (Angular + Python + docs + e2e each): - LangGraph: streaming, persistence, interrupts, memory, durable-execution, subgraphs, time-travel, deployment-runtime - Deep Agents: planning, filesystem, subagents, memory, skills, sandboxes Unified harness: - Capability registry as single source of truth - Serve orchestrator with --capability and --all modes - Per-capability Nx serve targets - CI build job for all Angular apps - LangGraph deployment matrix for all 14 backends - Smoke e2e test suite (28 tests: 14 UI render + 14 send/receive) Light glassmorphism theme matching the website design with frosted glass panels, warm-to-cool gradient backgrounds, and EB Garamond/Inter/JetBrains Mono typography. * feat(cockpit): complete cockpit application with 14 capability examples (#14) Cockpit is an integrated developer reference surface for @cacheplane/stream-resource. It embeds running Angular examples, shows syntax-highlighted source code, renders tutorial documentation, and displays auto-generated API references for each capability. Shared libraries: - @cacheplane/design-tokens: colors, glass, gradient, glow, typography tokens - @cacheplane/ui-react: GlassPanel, GlassButton, Callout, Steps, Tabs, Card, CodeGroup, NavLink - @cacheplane/chat: Angular chat component library (cp-chat, cp-chat-message, cp-chat-input) 14 capability examples (Angular + Python + docs + e2e each): - LangGraph: streaming, persistence, interrupts, memory, durable-execution, subgraphs, time-travel, deployment-runtime - Deep Agents: planning, filesystem, subagents, memory, skills, sandboxes Unified harness: - Capability registry as single source of truth - Serve orchestrator with --capability and --all modes - Per-capability Nx serve targets - CI build job for all Angular apps - LangGraph deployment matrix for all 14 backends - Smoke e2e test suite (28 tests: 14 UI render + 14 send/receive) Light glassmorphism theme matching the website design with frosted glass panels, warm-to-cool gradient backgrounds, and EB Garamond/Inter/JetBrains Mono typography. * feat: add @cacheplane/render and @cacheplane/chat libraries * docs: chat component library & angular renderer design spec Spec for three deliverables: @cacheplane/render (Angular renderer for @json-render/core), @cacheplane/chat (headless primitives + Tailwind compositions for LangGraph/Deep Agent UIs), and cockpit integration. Covers rendering pipeline, debug tool architecture, testing strategy, and dependency chain. * docs: implementation plans for render, chat, and cockpit integration Three plans covering the full chat component library: 1. @cacheplane/render — 11 tasks, Angular renderer for @json-render/core 2. @cacheplane/chat — 14 tasks, headless primitives + Tailwind compositions 3. Cockpit integration — 9 tasks, capability examples consuming chat lib * feat: scaffold @cacheplane/render Nx library Generates the publishable Angular library libs/render with Vitest test executor, ng-packagr build, flat ESLint config, and SPDX license headers matching the stream-resource reference patterns. Installs @json-render/core as a devDependency and registers the @cacheplane/render path alias. * feat(render): add types and defineAngularRegistry * feat(render): add signalStateStore backed by Angular signals * feat(render): add DI tokens for RenderContext and RepeatScope * feat(render): add prop resolution context builder * feat(render): add provideRender DI provider * feat(render): add RenderElementComponent — recursive element renderer Implements the core rendering pipeline component that looks up elements from the spec, resolves component classes from the registry, evaluates visibility conditions, resolves dynamic prop expressions and bindings, and renders via NgComponentOutlet. Includes repeat element support with child Injector-scoped RepeatScope. * feat(render): add RenderSpecComponent — top-level spec entry point Entry point component that accepts spec, registry, store, functions, handlers, and loading as inputs. Provides RENDER_CONTEXT to child RenderElementComponents via viewProviders. Falls back to RENDER_CONFIG from provideRender() for registry/store defaults, and creates an internal signalStateStore from spec.state when no store is provided. * feat(render): add children rendering tests for recursive element tree Adds tests verifying the recursive rendering pattern: parent components receive childKeys and spec as inputs, each child element resolves independently from the same spec, and deeply nested trees are traversable. * feat(render): export rendering pipeline from public API Adds RenderElementComponent, RenderSpecComponent, RENDER_CONTEXT, RenderContext, REPEAT_SCOPE, and RepeatScope to the public API. * feat(render): finalize public API and verify build - Fix @json-render/core peer dep version to ^0.16.0 - Fix componentClass() to return null (not undefined) for ngComponentOutlet compatibility - Fix test component selectors to use render- prefix per eslint config - Remove unused imports from spec files - Suppress no-empty-function lint errors in test helpers * chore: scaffold @cacheplane/chat library * feat(chat): add shared types and mock test utilities - Add ChatConfig and MessageTemplateType to chat.types.ts - Add createMockStreamResourceRef() with writable signals, matching the full StreamResourceRef interface including all signals and action methods * feat(chat): add ChatMessages primitive with messageTemplate directive - MessageTemplateDirective: ng-template[messageTemplate] with input.required() - ChatMessagesComponent: collects templates via contentChildren, computes messages from ref.messages(), renders via ngTemplateOutlet with findTemplate() - Extract getMessageType() as standalone function (human/ai/tool/system/function, fallback 'ai') to enable logic-level unit tests without DOM rendering - 15 tests across 2 spec files; all passing * feat(chat): add ChatInput primitive Adds ChatInputComponent with submitMessage() pure function, isDisabled computed from ref.isLoading, and onSubmit/onKeydown methods. Exports submitMessage for logic-level testing. 7 new tests passing (22 total). * feat(chat): add ChatTypingIndicator and ChatError primitives Adds ChatTypingIndicatorComponent (visible computed from ref.isLoading) and ChatErrorComponent (errorMessage computed with Error/string/unknown handling). Exports isTyping() and extractErrorMessage() pure functions for logic-level testing. 14 new tests passing (36 total). * feat(chat): add ChatInterrupt primitive Adds ChatInterruptComponent with interrupt computed from ref.interrupt(), contentChild(TemplateRef) for consumer-provided templates, and ngTemplateOutlet rendering with interrupt as implicit context. Exports getInterrupt() pure function for logic-level testing. 5 new tests passing (41 total). * feat(chat): add ChatToolCalls and ChatSubagents primitives * feat(chat): add ChatThreadList primitive * feat(chat): add ChatTimeline and ChatGenerativeUi primitives * feat(chat): export all new primitives from public-api * feat(chat): add provideChat DI provider * feat(chat): add prebuilt composition * feat(chat): add InterruptPanel, ToolCallCard, SubagentCard compositions * feat(chat): add ChatTimelineSlider composition * feat(chat): export compositions and provideChat from public-api * feat(chat): add ChatDebug composition with timeline, state inspector, and diff * fix(chat): fix test failures and verify build - Fix provide-chat test failures (tests were already passing, addressed pre-existing lint and build errors instead) - Add missing peerDependencies: @angular/forms, @json-render/core, @langchain/langgraph-sdk - Rename debug-* component selectors to chat-debug-* prefix to satisfy @angular-eslint/component-selector rule; update all template usages - Rename messageTemplate directive attribute to chatMessageTemplate to satisfy @angular-eslint/directive-selector rule - Fix tsconfig.json: remove baseUrl override so inherited paths from tsconfig.base.json resolve correctly in ng-packagr builds - Fix TS2307: move ToolCallWithResult and ToolProgress imports from @cacheplane/stream-resource to @langchain/langgraph-sdk (not exported) - Fix TS2551: update checkpoint_id access to state.checkpoint.checkpoint_id to match actual ThreadState type shape; update spec accordingly - Fix TS6133: remove unused 'computed' and 'ThreadState' imports - Fix TS2345: cast keydown event with $any() in chat-input template - Add eslint-disable comments for intentionally empty mock no-op methods * fix(render): address code review issues (store recreation, array handling, type accuracy) - I-1: Replace internalStore computed with lazy _internalStore field to prevent store recreation on every spec change - I-2: Update AngularComponentInputs to reflect spread props pattern with index signature instead of props bag - I-3: Fix setByPath to preserve array type when setting by numeric index; add test - I-4: Extract repeatScopes computed to eliminate duplicate RepeatScope construction in repeatInjectors/repeatInputs * fix(chat): address code review issues (ng-content, dedup, types, API correctness) - C1: Remove ng-content from ChatTypingIndicator and ChatError; both are now self-contained status components with default markup (no content projection) - I1+I2: Extract messageContent() to libs/chat/src/lib/compositions/shared/message-utils.ts; both Chat and ChatDebug expose it as a class property with co-location comment - I3: Export MockStreamResourceRef interface with writable signals so tests avoid unsafe casts; createMockStreamResourceRef() now returns MockStreamResourceRef - I4: ChatToolCalls uses AIMessage instanceof check + ref().getToolCalls() instead of (msg as any).tool_calls - I5: ChatTimelineSlider emits replayRequested/forkRequested outputs instead of calling setBranch() with a checkpoint ID (setBranch takes a branch name) - I6: ChatComponent accepts optional threads/activeThreadId inputs and renders a ChatThreadList sidebar when threads are provided * feat(cockpit): add Angular capability examples consuming @cacheplane/chat Creates 14 Angular CockpitCapabilityModule descriptors (8 LangGraph + 6 deep-agents), each with package.json, project.json, tsconfig.json, src/index.ts, src/app.component.ts, and prompts/{topic}.md. LangGraph topics: streaming, persistence, interrupts, memory, time-travel, subgraphs, durable-execution, deployment-runtime. Deep-agents topics: planning, filesystem, subagents, memory, skills, sandboxes. Each Angular component demonstrates the relevant @cacheplane/chat primitives: , , , , , , , , and . * feat(cockpit): make Angular examples standalone bootstrappable apps Add main.ts, app.config.ts, and index.html to all 14 cockpit Angular examples (8 LangGraph + 6 Deep Agents) so each can be independently bootstrapped. Deep Agents examples include provideRender({}) from @cacheplane/render to support generative UI via . --------- Co-authored-by: Brian Love * feat(cockpit): add design tokens CSS, Tailwind v4 to Angular apps, fix sidebar and code overflow (#16) - Add tokens.css to @cacheplane/design-tokens with all 29 --ds-* CSS custom properties - Replace hardcoded dark theme in all 14 Angular example apps with design-token-based light theme - Add Tailwind v4 to all Angular apps via @import "tailwindcss" + @theme block - Remove overview entries from cockpit sidebar navigation - Fix code block horizontal overflow in docs mode with overflow-x: auto * feat(chat): Apple-clean UI redesign + streaming example integration * feat(cockpit): wire streaming example to @cacheplane/chat - Rewrite StreamingComponent to use (correct API) - Remove duplicate app.component.ts and app.config.ts entry points - Add @angular-devkit/build-angular for app build/serve targets - Add provideChat({}) to app config alongside provideStreamResource - Configure dev proxy (/api → localhost:8123) and file replacements - Update tsconfig.app.json with DOM lib and emitDeclarationOnly override The streaming example is now ~10 lines of component code — all UI complexity lives in the @cacheplane/chat library. * docs: chat UI redesign spec — Apple-clean aesthetic with theming * docs: chat UI redesign implementation plan * feat(chat): add CSS custom property theme system with dark/light modes * feat(chat): redesign composition — Apple-clean aesthetic with theme vars * feat(chat): restyle ChatInput as pill-shaped with theme vars * feat(chat): restyle ChatTypingIndicator with dot animation and theme vars * feat(chat): restyle ChatError with theme vars * fix(chat): inline theme CSS in styles[] to avoid JIT styleUrls resolution in tests * feat(chat): restyle ChatInterruptPanel with neutral theme vars * feat(chat): restyle ToolCallCard and SubagentCard with theme vars * feat(chat): restyle ChatDebug with theme vars * fix(cockpit): update streaming example selector chat-ui → chat * test(chat): update statusColor spec assertions for CSS var return values --------- * feat(cockpit): wire all 13 remaining Angular examples to @cacheplane/chat * docs: cockpit Angular examples chat integration spec (Phase 1) * docs: cockpit examples chat integration plan * docs: add narrative redesign and white paper pipeline specs Spec 1 (2026-04-05-narrative-redesign): Four new landing page sections — ProblemSection (gap animation + 3 stats), FullStackSection (3-layer stack with SVG particle connectors), ChatFeaturesSection (interactive 4-tab chat scenarios), FairComparisonSection (honest LangChain comparison table). Includes animation detail, correct fix for border-radius fill artifact, and FeatureStrip copy update. Spec 2 (2026-04-05-whitepaper-pipeline): One-time Anthropic SDK generation script producing a static PDF from six assessment-framework chapters. Free download with optional lead-gen form. No gate. * feat(cockpit): wire 7 LangGraph Angular examples to @cacheplane/chat * feat(cockpit): wire 6 Deep Agents Angular examples to @cacheplane/chat-debug - Replace placeholder component implementations with ChatDebugComponent pattern - Delete src/app.component.ts and src/app.config.ts root duplicates from all 6 - Rewrite capability components to use - Rewrite app.config.ts to include provideRender({}) alongside provideChat({}) - Rewrite main.ts to bootstrap from src/app/ (not root src/) - Switch project.json from @nx/js:tsc library to @angular-devkit/build-angular:application - Fix tsconfig.json extends path (6 levels → 4 levels to workspace root) - Update tsconfig.app.json to add lib and emitDeclarationOnly: false - Add to all index.html files; fix memory selector app-da-memory - Set environment.development.ts langGraphApiUrl to '/api' (proxy-relative) - Fix proxy.conf.json targets to correct ports (8140–8145) - All 6 production builds pass with no errors --------- * feat(cockpit): production deployment - LangGraph Cloud, Angular hosting, CI (#19) * feat(cockpit): add design tokens CSS, Tailwind v4 to Angular apps, fix sidebar and code overflow - Add tokens.css to @cacheplane/design-tokens with all 29 --ds-* CSS custom properties - Replace hardcoded dark theme in all 14 Angular example apps with design-token-based light theme - Add Tailwind v4 to all Angular apps via @import "tailwindcss" + @theme block - Add explicit tsconfig paths for @cacheplane/chat and @cacheplane/stream-resource (CI fix) - Remove overview entries from cockpit sidebar navigation - Fix code block horizontal overflow in docs mode with overflow-x: auto - Exclude all-examples-smoke test from default e2e (requires running servers) * feat(cockpit): production deployment - LangGraph Cloud, Angular hosting, CI pipeline - Switch all 14 Angular apps to @angular/build:application for production builds - Deploy all 14 Python backends to LangGraph Cloud (all healthy) - Wire Angular production environments to LangGraph Cloud URLs - Add Vercel static hosting for Angular examples (cockpit-examples.vercel.app) - Add deployment verification script and assemble script - Add CI jobs for examples deploy and production smoke tests - Fix langgraph.json configs (dependencies, python_version) - Update deploy-langgraph.yml workflow for correct CLI usage * feat(cockpit): Tier 1 example customization — thread management + capability sidebars * docs: cockpit examples Tier 1 customization spec * feat(cockpit): customize persistence example with thread management Rewrites PersistenceComponent to track threads via onThreadId callback, maintain Thread[] and activeThreadId signals, and handle threadSelected to call stream.switchThread(id). Deployment-runtime component verified correct and left unchanged. Both examples build successfully. * feat(cockpit): add capability-specific sidebars to 6 deep-agents examples Each example now renders a live sidebar alongside using computed() signals derived from stream.value() or stream.messages(). Sidebar styling uses var(--chat-*) CSS custom properties throughout. --------- * feat(cockpit): Tier 2 — memory sidebar, subagent tracking, interrupt panel * feat(cockpit): Tier 2 — memory sidebar, subagent tracking, interrupt panel Rewrites the three LangGraph cockpit examples to use alongside capability-specific sidebars/panels, replacing LegacyChatComponent. - memory: derives memoryEntries from stream.value().memory (confirmed key from Python MemoryState) - subgraphs: derives subagentEntries from stream.subagents() Map with status + message count - interrupts: wires ChatInterruptPanelComponent (action) output to stream.submit(null) resume * feat: add ProblemSection with animated gap progress bar --------- * feat(cockpit): Tier 3 — time-travel checkpoint nav + durable-execution step pipeline Replace LegacyChatComponent with ChatComponent + ChatTimelineSliderComponent in time-travel, and ChatComponent with a status bar pipeline in durable-execution. * fix(cockpit): production review pass — 22 fixes across 14 examples * fix(cockpit): restore streaming and deployment-runtime to use ChatComponent (reverted by PR #19) * fix(cockpit): update e2e tests for current UI (#24) * feat(cockpit): production deployment with serverless API proxy - Switch all 14 Angular apps to @angular/build:application - Deploy all 14 Python backends to LangGraph Cloud - Add Vercel serverless proxy that injects x-api-key server-side - Revert Angular environments to relative /api URLs (proxy handles routing) - Build Output API for proper multi-segment catch-all routing - Angular examples hosted at examples.stream-resource.dev - Deployment verification script and production smoke tests - CI jobs for examples deploy and production smoke * fix(cockpit): update e2e tests for current UI, exclude production-smoke from default run - Update cockpit.spec.ts to match current shell (stripped sidebar prefixes, removed obsolete heading assertions) - Add production-smoke.spec.ts to testIgnore in playwright config (only runs in dedicated CI job, not during cockpit-e2e) * fix(cockpit): restore fileReplacements for dev environment in all 13 Angular examples * fix(cockpit): use full URLs in dev environments (SDK requires absolute URLs) * feat(website): add Examples link to header and footer (#25) * feat(cockpit): production deployment with serverless API proxy - Switch all 14 Angular apps to @angular/build:application - Deploy all 14 Python backends to LangGraph Cloud - Add Vercel serverless proxy that injects x-api-key server-side - Revert Angular environments to relative /api URLs (proxy handles routing) - Build Output API for proper multi-segment catch-all routing - Angular examples hosted at examples.stream-resource.dev - Deployment verification script and production smoke tests - CI jobs for examples deploy and production smoke * fix(cockpit): update e2e tests for current UI, exclude production-smoke from default run - Update cockpit.spec.ts to match current shell (stripped sidebar prefixes, removed obsolete heading assertions) - Add production-smoke.spec.ts to testIgnore in playwright config (only runs in dedicated CI job, not during cockpit-e2e) * feat(website): add Examples link to header and footer pointing to cockpit * fix(chat): fix broken style interpolation in ChatInput border-color * fix(ci): isolate examples deploy to prevent overwriting website (#26) * feat(cockpit): production deployment with serverless API proxy - Switch all 14 Angular apps to @angular/build:application - Deploy all 14 Python backends to LangGraph Cloud - Add Vercel serverless proxy that injects x-api-key server-side - Revert Angular environments to relative /api URLs (proxy handles routing) - Build Output API for proper multi-segment catch-all routing - Angular examples hosted at examples.stream-resource.dev - Deployment verification script and production smoke tests - CI jobs for examples deploy and production smoke * fix(cockpit): update e2e tests for current UI, exclude production-smoke from default run - Update cockpit.spec.ts to match current shell (stripped sidebar prefixes, removed obsolete heading assertions) - Add production-smoke.spec.ts to testIgnore in playwright config (only runs in dedicated CI job, not during cockpit-e2e) * feat(website): add Examples link to header and footer pointing to cockpit * fix(ci): isolate examples deploy to prevent overwriting website project * fix(chat): send simple {role, content} dict instead of HumanMessage object The LangGraph SDK serializes HumanMessage as its LangChain constructor format ({lc, type, id, kwargs}) which the Python backend rejects with: ValueError: Message dict must contain 'role' and 'content' keys Sending {role: 'human', content: text} is the correct wire format. * docs: add continuation context for chat library + cockpit integration * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(chat): fix runtime errors + ViewEncapsulation breaking theme (#30) * fix(stream-resource): fix 3 runtime errors in streaming chat flow 1. Guard Object.keys(v) against null/undefined in values$ subscriber (stream-resource.fn.ts:94) — crashed when values event had no data 2. Handle plain JSON messages from SSE (not hydrated BaseMessage instances) in getMessageType() — _getType() is a class method not available on plain objects; fall back to reading the `type` property 3. Fix event data extraction in processEvent — normalizeSdkEvent spreads data into the event object, so event['values'] was always undefined; use extractEventData() to read from event['data'] instead. Also sync messages$ from values events and merge messages/partial updates by id to preserve the full message history including human messages. * fix(chat): fix ViewEncapsulation breaking all CSS theme variables ViewEncapsulation.None caused :host selectors in CHAT_THEME_STYLES to not match anything (no shadow DOM = :host doesn't apply). All 40+ CSS custom properties were empty, breaking the entire design. Fix: remove ViewEncapsulation.None from ChatComponent and ChatDebugComponent (default Emulated encapsulation processes :host correctly). Prefix markdown styles with ::ng-deep for innerHTML content penetration. * fix(chat): replace [innerHTML] icon bindings with inline SVG (sanitizer fix) * fix(chat): center input text, remove Assistant label, ChatGPT pattern - Input: items-end → items-center for vertical centering of single-line text - Input: inline SVG for send button (replaces [innerHTML] which Angular sanitizes) - AI messages: remove "Assistant" label, use avatar inline with content (ChatGPT pattern) - Typing indicator: match new AI message layout (avatar + dots, no label) * fix(stream-resource): handle both SDK and mock event formats in extractEventData * feat(cockpit): complete sidebar implementations for all 14 capability examples (#31) * fix(stream-resource): fix 3 runtime errors in streaming chat flow 1. Guard Object.keys(v) against null/undefined in values$ subscriber (stream-resource.fn.ts:94) — crashed when values event had no data 2. Handle plain JSON messages from SSE (not hydrated BaseMessage instances) in getMessageType() — _getType() is a class method not available on plain objects; fall back to reading the `type` property 3. Fix event data extraction in processEvent — normalizeSdkEvent spreads data into the event object, so event['values'] was always undefined; use extractEventData() to read from event['data'] instead. Also sync messages$ from values events and merge messages/partial updates by id to preserve the full message history including human messages. * fix(stream-resource): handle both SDK and mock event formats in extractEventData * docs(cockpit): add cockpit examples validation implementation plan * feat(cockpit): add thread picker sidebar to persistence example * feat(cockpit): add checkpoint timeline sidebar to time-travel example * feat(cockpit): add step progress sidebar to durable-execution example * feat(cockpit): add plan checklist sidebar to planning example * feat(cockpit): add skill invocations sidebar to skills example * feat(cockpit): add delegation tracker sidebar to subagents example Derive delegations from stream.messages() by finding tool_calls in AI messages and matching them with tool result messages. Each delegation shows a status dot (green=complete, amber=running, red=error), agent name, and status text. * feat(cockpit): add file operations sidebar to filesystem example * feat(cockpit): add execution output sidebar to sandboxes example * feat(cockpit): add learned facts sidebar to memory example Derive memory entries from stream.value() by checking for agent_memory or memory dict fields in graph state. Sidebar displays each fact as a bold key with muted value text, plus a count in the header. * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * fix(stream-resource): persist thread ID across messages + smooth scroll (#32) Two critical fixes: 1. Thread persistence: The bridge's onThreadId callback was not connected to currentThreadId tracking. Each submit() created a brand new thread, losing all conversation history. Fix: wrap the transport's onThreadId to update currentThreadId before calling the consumer's callback. 2. Smooth scroll: Replace setTimeout + scrollTop assignment with requestAnimationFrame + scrollTo({behavior:'smooth'}). Only auto-scrolls when user is near bottom (<150px), so reading earlier messages isn't interrupted by new content. * fix(website): replace unsourced stats with verified Gartner citations (#33) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) --------- * fix(chat): refocus input after submit + force scroll on new messages (#34) * fix(cockpit): align all proxy configs to port 8123 (#35) * docs: add cockpit E2E verification plan * fix(cockpit): align all proxy configs to port 8123 (serve script hardcodes this port) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add lead notification email template * feat: add whitepaper download email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: add OpenGraph and Twitter Card meta tags * feat(website): lead gen with Resend + mobile fixes + design improvements (#36) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags --------- * fix(deploy): rewrite base href per example so assets resolve to correct subpath (#37) Production Angular examples at examples.stream-resource.dev returned 404 for main.js, styles.css, and chunks. Root cause: all index.html files had <base href="/"> so asset URLs resolved to the domain root instead of /{product}/{topic}/. Fix: assemble script now rewrites base href to the correct subpath for each example. * fix(website): fix flaky e2e test blocking production deploys (#38) * fix(deploy): rewrite base href per example so assets resolve to correct subpath Production Angular examples at examples.stream-resource.dev returned 404 for main.js, styles.css, and chunks. Root cause: all index.html files had <base href="/"> so asset URLs resolved to the domain root instead of /{product}/{topic}/. Fix: assemble script now rewrites base href to the correct subpath for each example. * fix(website): use .first() on form locator to handle multiple forms on pricing page * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * feat: rebrand to Angular Agent Framework — agent() + @cacheplane/angular (#39) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name --------- * fix(website): correct api reference e2e test path after rebrand (#40) * chore: trigger examples redeploy with base-href fix (#41) * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: complete rebrand audit — nav bug, hero copy, templates (#42) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) --------- * fix: remove $20k from pilot program, include with app license, annual term * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * fix: API docs, pricing updates, rebrand audit fixes (#44) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent --------- * fix(angular): normalize relative apiUrl to absolute for LangGraph SDK (#43) Production environments use relative paths like '/api' that are proxied by Vercel middleware. The LangGraph SDK Client constructor requires an absolute URL. Fix: prepend window.location.origin for relative paths. * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * refactor: rename stream-resource lib to agent in Nx project structure (#45) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged --------- * fix(ci): trigger examples redeploy when libs change (#46) The examples deploy change detection only checked cockpit/**/angular/ and scripts/assemble-examples.ts. Library changes (libs/angular/, libs/chat/, libs/render/) were not detected, so the apiUrl fix from PR #43 was never deployed. Now checks all three lib directories. * feat: add airplane emoji favicon and logo to header/footer * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * fix: docs tables, pricing, favicon, animation, announcement toast (#47) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users --------- * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email * feat: add lead capture form to whitepaper announcement toast (#49) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email --------- * feat: replace social proof badges with animated logo scroll strip - 12 LangChain ecosystem company names in an infinite scrolling strip - Glassmorphism container with fade edges - Pauses on hover, 30s full scroll cycle - Eyebrow: "Built for teams shipping with LangChain" * feat: replace social proof badges with animated logo scroll strip - 12 LangChain ecosystem company names in an infinite scrolling strip - Glassmorphism container with fade edges - Pauses on hover, 30s full scroll cycle - Eyebrow: "Built for teams shipping with LangChain" * feat: animated company logo scroll strip for social proof (#50) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email * feat: replace social proof badges with animated logo scroll strip - 12 LangChain ecosystem company names in an infinite scrolling strip - Glassmorphism container with fade edges - Pauses on hover, 30s full scroll cycle - Eyebrow: "Built for teams shipping with LangChain" --------- * feat: add Loops.so integration for drip email campaigns - Shared lib/loops.ts with upsertContact and sendEvent helpers - Graceful degradation when LOOPS_API_KEY is missing - Three events: lead_submitted, whitepaper_downloaded, newsletter_subscribed - Contacts created with source tracking (lead-form, whitepaper, newsletter) - Wired into all 3 API routes alongside existing Resend calls * feat: add Loops.so integration for drip email campaigns - Shared lib/loops.ts with upsertContact and sendEvent helpers - Graceful degradation when LOOPS_API_KEY is missing - Three events: lead_submitted, whitepaper_downloaded, newsletter_subscribed - Contacts created with source tracking (lead-form, whitepaper, newsletter) - Wired into all 3 API routes alongside existing Resend calls * feat: add Loops.so integration for drip email campaigns (#51) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email * feat: replace social proof badges with animated logo scroll strip - 12 LangChain ecosystem company names in an infinite scrolling strip - Glassmorphism container with fade edges - Pauses on hover, 30s full scroll cycle - Eyebrow: "Built for teams shipping with LangChain" * feat: add Loops.so integration for drip email campaigns - Shared lib/loops.ts with upsertContact and sendEvent helpers - Graceful degradation when LOOPS_API_KEY is missing - Three events: lead_submitted, whitepaper_downloaded, newsletter_subscribed - Contacts created with source tracking (lead-form, whitepaper, newsletter) - Wired into all 3 API routes alongside existing Resend calls --------- * feat(website): multi-library docs for agent, render, and chat (#48) * docs: add multi-library docs architecture spec * docs: add multi-library docs implementation plan * refactor: move docs-v2 to docs/agent, rename docs-new and component suffixes * feat: multi-library docs architecture with dropdown selector - Add DocsLibrary type with agent, render, chat configs - Restructure routing to /docs/[library]/[section]/[slug] - Add docs landing page at /docs - Add library dropdown selector to sidebar - Update mobile nav, breadcrumb, prev/next, search for multi-lib - Fix all internal doc links to new URL structure * docs: author render library documentation * docs: author chat library documentation * fix: correct Callout type="warn" to type="warning" in chat docs * fix: update e2e tests for new multi-library doc URLs --------- * feat: replace Loops with self-hosted drip via Resend scheduled_at - Remove Loops.so integration (lib/loops.ts deleted, all route imports removed) - Add 4-email whitepaper drip sequence (Day 2, 5, 10, 20) - Drip emails scheduled at signup via Resend's scheduledAt parameter - Add /api/unsubscribe route with NDJSON persistence - Unsubscribe link in every drip email footer - sendEmail() now supports optional scheduledAt parameter * feat: replace Loops with self-hosted drip via Resend scheduled_at - Remove Loops.so integration (lib/loops.ts deleted, all route imports removed) - Add 4-email whitepaper drip sequence (Day 2, 5, 10, 20) - Drip emails scheduled at signup via Resend's scheduledAt parameter - Add /api/unsubscribe route with NDJSON persistence - Unsubscribe link in every drip email footer - sendEmail() now supports optional scheduledAt parameter * feat: replace Loops with self-hosted drip via Resend scheduled_at (#52) * feat(chat): ship-readiness polish — Tailwind, auto-scroll, markdown, a11y (#27) * docs(chat): add ship-readiness polish implementation plan Addresses all 19 audit issues: theme consolidation, Tailwind conversion, auto-scroll, textarea auto-expand, markdown rendering, empty state, responsive sidebar, SVG icons, ARIA, and API cleanup. * feat(chat): consolidate theme into shared TS module, add icons + markdown utils * feat(chat): convert ChatComponent to Tailwind, add auto-scroll + empty state + responsive sidebar - Replace 80+ lines of inlined CSS vars with CHAT_THEME_STYLES import - Add CHAT_MARKDOWN_STYLES + renderMarkdown for AI message rendering - Convert all inline style="" attributes to Tailwind utility classes - Add auto-scroll via viewChild + effect tracking message count - Add empty state when no messages and not loading - Make thread sidebar responsive with hidden md:flex + mobile toggle - Add ARIA attributes: role=log, aria-live=polite, role=navigation - Use ViewEncapsulation.None for markdown styles * feat(chat): convert primitives to Tailwind, add textarea auto-expand + focused signal * feat(chat): convert ChatDebug + sub-components to theme-aware Tailwind * feat(chat): convert remaining compositions to Tailwind with SVG icons + theme vars * feat(chat): clean up public API, add marked peer dep, verify build Remove legacy cp-chat/cp-chat-input/cp-chat-message components and migrate all cockpit examples to the new ChatComponent composition. Export shared styles, icons, and markdown utilities from public API. Update ChatConfig with renderRegistry, avatarLabel, assistantName. Add marked as optional peer dep and fix dynamic import for library build. Add @source directive to cockpit styles for Tailwind scanning. --------- * Rebrand to Angular Stream Resource (#28) * docs: add rebrand design spec for Angular Stream Resource * docs: add implementation plan for Angular Stream Resource rebrand * docs: rebrand StreamResource → Angular Stream Resource in README and COMMERCIAL Update product name in prose/marketing contexts: README alt text and license section, COMMERCIAL.md license header. * docs: rebrand StreamResource → Angular Stream Resource in website shared components Update product name in layout metadata title, Nav logo link text, Footer brand name, and Footer copyright line. * docs: rebrand StreamResource → Angular Stream Resource in landing and llms.txt Update product name in ProblemSection badge text, GenerativeUIFrame aria-label, and llms.txt route title and description line. * docs: rebrand StreamResource → Angular Stream Resource in documentation files Update product name in headings and prose across limitations.md and four design specs (titles, overview sentences, section headings). * docs: rebrand stream-resource → Angular Stream Resource in LLM context files Update product name in the H1 title of AGENTS.md and CLAUDE.md. * docs: rebrand remaining spec and plan references to Angular Stream Resource * docs: fix remaining brand references missed in initial pass * feat(website): add narrative sections, pilot-to-prod page, and rebrand integration (#29) * fix(website): add track shake animation to ProblemSection stall phase * fix(website): ProblemSection quality fixes — timer cleanup, unique SVG ID, aria-hidden, correct import - Store setTimeout IDs and clear them on unmount (prevents state updates on unmounted component) - Use useId() to generate unique hatchId per instance (prevents SVG pattern id collision) - Add role=progressbar + aria-valuenow to track container for screen readers - Add aria-hidden=true to decorative animated elements (pins, labels, badge, counter) - Fix import: use local lib/design-tokens instead of unresolved @cacheplane/design-tokens - Add invariant comment for done-timeout vs counter-duration coupling * feat: add FullStackSection with animated stack diagram and roadmap strip * feat: add ChatFeaturesSection with 4 interactive chat scenarios * feat: add FairComparisonSection comparison table * feat: wire ProblemSection, FullStackSection, ChatFeaturesSection, FairComparisonSection into landing page - Insert ProblemSection + FullStackSection + ChatFeaturesSection after StatsStrip - Insert FairComparisonSection after DeepAgentsShowcase - Add two ambient gradient blobs for extended page height - Task 5 (FeatureStrip copy): no-op — the problematic 'no established pattern' copy was not present in this branch * chore: add puppeteer devDependency and generate-whitepaper script * feat: add whitepaper signup API route with NDJSON persistence * feat: add whitepaper generation script * feat: add WhitePaperSection with free download and optional lead-gen form * feat: add WhitePaperSection to landing page; remove useStream parity copy from FeatureStrip * fix(whitepaper): add JetBrains Mono to Google Fonts URL and regenerate preview Fixes missing code font in whitepaper output. Regenerates whitepaper-preview.html with correct 'EB Garamond' and 'JetBrains Mono' font references throughout. * feat: add PilotHero component and /pilot-to-prod page skeleton * fix: PilotHero responsive padding, eyebrow style conflict, page metadata * feat: add WhatIsIncluded 3-column component for pilot-to-prod page * feat: add HowItWorks 3-phase timeline for pilot-to-prod page * feat: add PricingSignal pricing callout for pilot-to-prod page * feat: add WhitePaperGate 5-field lead gen form for pilot-to-prod page * fix: change role=alert to role=status to match aria-live=polite in WhitePaperGate * feat: add PilotFooterCTA and wire complete pilot-to-prod page * fix: use tokens.colors.accent in PilotFooterCTA, add aria-hidden to page blobs * feat: add Pilot to Prod nav link and restructure homepage (remove FeatureStrip/CockpitCTA/CodeBlock, add PilotProgram CTA) * fix: correct design-tokens import path in pilot-to-prod page (3 levels up) * fix: apply full review findings — messaging, mobile, UX, and RiskRemoval section - Remove useStream() parity messaging from HeroTwoCol, WhatIsIncluded, StatsStrip - Fix PricingSignal: remove ambiguous '/year', clarify as fixed fee + pilot included - Add PricingSignal mobile padding reduction via media query - Fix ProblemSection stat grid to collapse on mobile (auto-fit minmax) - Add RiskRemoval section to pilot-to-prod page (between PricingSignal and WhitePaperGate) - Fix Nav Examples link: external=true, target=_blank, rel=noopener noreferrer - Fix WhitePaperGate: role field sent in message body, not merged into company string - Fix PilotFooterCTA: replace broken whileHover borderColor with CSS class hover - Fix PilotHero: remove opacity from initial animations (prevents blank hero flash) - Increase PilotHero CTA padding to meet 44px touch target * fix: remove remaining useStream parity messaging from layout, Footer, and ValueProps * fix: second review pass — docs messaging, title, broken link, a11y labels - introduction.mdx: remove parity/useStream opening line, use Signal-native positioning - AGENTS.md.template + CLAUDE.md.template: update tagline to Signal-native - layout.tsx: update <title> from LangChain to LangGraph - Footer.tsx: fix /api-reference → /docs/api/stream-resource (was 404) - PilotHero.tsx: add aria-hidden to decorative gradient blobs - WhitePaperSection.tsx: add sr-only labels + aria-label to name/email inputs - LeadForm.tsx (pricing): add sr-only labels + aria-label to all four form inputs * feat: add whitepaper.pdf to public directory Generated from whitepaper-preview.html via Puppeteer. All 6 chapters present (Streaming State Management, Thread Persistence, Tool-Call Rendering, Human Approval Flows, Generative UI, Deterministic Testing). Fixes dead 'Download the Guide' CTAs on pilot-to-prod and homepage. * feat: citation badges on stats, pricing reframe to app deployment license Citation badges: - New CitationBadge component — click-to-open popover with source, stat, note, and link - 66% stat → Stack Overflow Developer Survey 2025 - 31% stat → ISG AI Adoption Reports - 75% stat → Stack Overflow Developer Survey 2025 - Keyboard (Escape) and outside-click dismissal, ARIA dialog role Pricing reframe (app deployment license): - Remove ALL refund/money-back/guarantee language site-wide - PilotHero: trust line → "App deployment license · $20,000 · 3-month co-pilot engagement" - PilotHero: subheadline removes "guaranteed outcome" - WhatIsIncluded: card 3 renamed from "Production Guarantee" → "App Deployment License" - HowItWorks: phase 3 removes "full refund" language, deliverable → "Production deployment" - PricingSignal: subtitle + features list updated to license/co-pilot framing - RiskRemoval: section reframed from guarantee → "What's included in the license" Replaces money-back card with "We work alongside your team" card - PilotFooterCTA: fine print updated - pilot-to-prod/page.tsx: meta description updated * feat: subtler citation badge + citations on all 77% claims CitationBadge: - Reduced to 13px, transparent background, faint border (rgba 0.2) - Text color rgba(0,64,144,0.35) at rest — nearly invisible until hovered - No fill on idle state, border-only approach New citation placements: - PilotHero subheadline: 77% → McKinsey State of AI 2024 - PilotFooterCTA body copy: 77% → McKinsey State of AI 2024 - HomePilotCTA (new component): extracts inline pilot CTA from page.tsx so it can be a client component with CitationBadge on the 77% claim - page.tsx: replaces inline section with <HomePilotCTA /> * docs: add FullStackSection redesign spec (EM/CTO layer narrative + Gen UI bug fix) * feat(website): redesign FullStackSection for EM/CTO audience * docs: apply Angular Stream Resource rebrand to narrative components * chore: sync package-lock.json after merge * fix(website): update e2e test for new landing page structure --------- * fix(website): replace unsourced stats with verified Gartner and Stack Overflow citations - Replace fabricated 77% McKinsey stat with Gartner "50% of GenAI projects abandoned after POC" (Jan 2026) across ProblemSection, PilotHero, HomePilotCTA, and PilotFooterCTA - Replace stale ISG 31% stat with same Gartner citation in ProblemSection - Update progress bar animation to stall at 50% instead of 77% - Update FairComparisonSection to reference @langchain/langgraph-sdk explicitly in column header and subtitle - All three stat cards now cite verified sources: Stack Overflow 2025 Developer Survey (66%, 75%) and Gartner GenAI Project Failure 2026 (50%) * docs: add website audit and lead generation specs - Spec 1: Mobile responsive fixes (ChatFeaturesSection overflow, FairComparisonSection mobile layout, touch targets, min font sizes) plus design improvements (social proof, footer newsletter, white paper soft gate, OG meta tags) - Spec 2: Resend integration for lead gen (email notifications on lead forms, white paper PDF delivery via email, newsletter signup with Resend audience, React Email templates) * docs: add implementation plans for lead gen and website audit - Plan 1: Lead generation with Resend (9 tasks) — install deps, shared module, 3 email templates, 3 API route upgrades, e2e verify - Plan 2: Website audit mobile + design (9 tasks) — ChatFeatures responsive, FairComparison mobile cards, touch targets, min font sizes, social proof, footer newsletter, white paper soft gate, OG tags * chore: install resend and react-email dependencies * feat: add shared resend module with audience helper * feat: add lead notification email template * feat: add whitepaper download email template * feat: add newsletter welcome email template * feat: wire /api/leads to Resend email + audience * feat: wire /api/whitepaper-signup to Resend email delivery * feat: add /api/newsletter route with Resend welcome email * fix: convert email templates to plain HTML to avoid React dual-instance crash Replace @react-email/components JSX templates with plain TypeScript string functions and switch Resend calls from react: to html:, eliminating the duplicate React instance that caused useInsertionEffect to crash in API routes. * fix: lazy-init Resend client to gracefully handle missing API key - Resend client is now created lazily on first use, not at module load - When RESEND_API_KEY is missing, all email operations no-op with a log - Forms still return { ok: true } and write NDJSON backup without keys - Extracted sendEmail() helper that encapsulates the null-client check * fix: make ChatFeaturesSection responsive on mobile * fix: stack FairComparisonSection rows vertically on mobile * fix: increase touch targets to meet WCAG 44px minimum * fix: enforce 12px minimum font size on progress bar labels * feat: add social proof badge strip below stats * feat: add newsletter signup form to footer * feat: restructure white paper section with soft gate * feat: add OpenGraph and Twitter Card meta tags * feat: rebrand from streamResource to agent() — @cacheplane/angular - Rename streamResource() → agent(), StreamResource → Agent - Rename @cacheplane/stream-resource → @cacheplane/angular - Rebrand 'Angular Stream Resource' → 'Angular Agent Framework' - Rename libs/stream-resource → libs/angular, e2e/stream-resource-e2e → e2e/angular-e2e - Update all imports, types, providers, docs, and configs - Preserve stream-resource.dev domain and GitHub repo URLs - Preserve STREAM_RESOURCE_CONFIG injection token name * fix: complete rebrand audit — nav bug, hero copy, templates, footer - Fix hamburger menu showing on desktop (inline display:flex was overriding Tailwind md:hidden — moved to className) - Fix hero headline: "Enterprise Streaming Resource" → "Enterprise Agent Framework" - Fix footer tagline and layout.tsx meta description - Fix hero.svg text - Update CLAUDE.md.template and AGENTS.md.template with new API names (agent(), provideAgent(), @cacheplane/angular) * fix: remove $20k from pilot program, include with app license, annual term * fix: resolve API docs slug mismatch after rename - docs-config.ts slugs now match MDX filenames (agent.mdx, provide-agent.mdx) - API_NAME_MAP updated with correct slug→name mappings - Nav API link updated to /docs/api/agent * refactor: rename stream-resource lib to agent in Nx project structure - Rename libs/angular/ → libs/agent/ - Rename e2e/angular-e2e/ → e2e/agent-e2e/ - Update Nx project names, configs, tsconfig paths, CI workflows - Package name (@cacheplane/angular) and source code unchanged * feat: add airplane emoji favicon and logo to header/footer * fix: prevent FullStackSection connector animations from overlapping cards * fix: add remark-gfm to enable markdown table rendering in docs * fix: update developer seat feature text to "12-month license" * feat: add dismissible whitepaper announcement toast - Shows after 30s on site, bottom-right with glassmorphism - Dismissible via X, 'Not now', or downloading the PDF - Persisted in localStorage keyed by announcement date - Bump ANNOUNCEMENT_DATE constant to re-show for all users * feat: add lead capture form to announcement toast - Clicking "Get the Guide" now shows an email form instead of downloading directly - Form submits to /api/whitepaper-signup (Resend integration) - Success state shows confirmation, auto-dismisses after 4s - "or download directly" skip link below the form for users who don't want to provide email * feat: replace social proof badges with animated logo scroll strip - 12 LangChain ecosystem company names in an infinite scrolling strip - Glassmorphism container with fade edges - Pauses on hover, 30s full scroll cycle - Eyebrow: "Built for teams shipping with LangChain" * feat: add Loops.so integration for drip email campaigns - Shared lib/loops.ts with upsertContact and sendEvent helpers - Graceful degradation when LOOPS_API_KEY is missing - Three events: lead_submitted, whitepaper_downloaded, newsletter_subscribed - Contacts created with source tracking (lead-form, whitepaper, newsletter) - Wired into all 3 API routes alongside existing Resend calls * feat: replace Loops with self-hosted drip via Resend scheduled_at - Remove Loops.so integration (lib/loops.ts deleted, all route imports removed) - Add 4-email whitepaper drip sequence (Day 2, 5, 10, 20) - Drip emails scheduled at signup via Resend's scheduledAt parameter - Add /api/unsubscribe route with NDJSON persistence - Unsubscribe link in every drip email footer - sendEmail() now supports optional scheduledAt parameter --------- * feat: unified gradient-header email template design - New shared email-wrapper.ts with gradient header, logo, footer - All 7 templates (4 drip + 3 transactional) updated to use wrapper - Consistent design: gradient band, white body, accent CTA buttons - Lead notification uses red 'New Lead' eyebrow - Drip emails include unsubscribe link, transactional emails don't - Day 10 drip has styled timeline card for pilot program phases * feat: unified gradient-header email template design - New shared email-wrapper.ts with gradient header, logo, footer - All 7 templates (4 drip + 3 transactional) updated to use wrapper - Consistent design: gradient band, white body, accent CTA buttons - Lead notification uses red 'New Lead' eyebrow - Drip emails include unsubscribe link, transactional emails don't - Day 10 drip has styled timeline card for pilot program phases * feat: migrate all domains from stream-resource.dev to cacheplane.ai - stream-resource.dev → cacheplane.ai - cockpit.stream-resource.dev → cockpit.cacheplane.ai - examples.stream-resource.dev → examples.cacheplane.ai - 83 references updated across source, CI, emails, docs, templates --------- Co-authored-by: Brian Love <brian@brianlove.com> --- .github/workflows/ci.yml | 10 +++---- COMMERCIAL.md | 2 +- README.md | 20 ++++++------- apps/cockpit/e2e/production-smoke.spec.ts | 8 +++--- apps/cockpit/scripts/deploy-smoke.spec.ts | 12 ++++---- apps/cockpit/src/lib/content-bundle.spec.ts | 6 ++-- .../emails/drip-whitepaper-followup.ts | 6 ++-- apps/website/emails/email-wrapper.ts | 2 +- apps/website/emails/newsletter-welcome.ts | 2 +- apps/website/emails/whitepaper-download.ts | 2 +- apps/website/public/AGENTS.md | 2 +- apps/website/public/CLAUDE.md | 2 +- apps/website/src/app/llms.txt/route.ts | 2 +- apps/website/src/components/shared/Footer.tsx | 2 +- apps/website/src/components/shared/Nav.tsx | 2 +- .../plans/2026-03-17-angular-library.md | 6 ++-- docs/superpowers/plans/2026-03-17-readme.md | 2 +- .../plans/2026-03-18-agentic-additions.md | 6 ++-- .../plans/2026-03-19-langsmith-deployment.md | 2 +- .../plans/2026-03-19-next-steps.md | 2 +- docs/superpowers/plans/2026-03-19-roadmap.md | 6 ++-- .../2026-03-19-vercel-deployment-notes.md | 2 +- .../2026-04-03-cockpit-example-harness.md | 2 +- .../plans/2026-04-03-content-bundle.md | 6 ++-- .../plans/2026-04-05-production-deployment.md | 28 +++++++++---------- .../2026-04-06-lead-generation-resend.md | 4 +-- ...6-04-06-rebrand-angular-stream-resource.md | 2 +- .../specs/2026-03-18-agentic-docs-design.md | 2 +- .../specs/2026-03-19-licensing-model.md | 2 +- .../specs/2026-03-20-cockpit-app-design.md | 2 +- ...0-cockpit-testing-and-deployment-design.md | 2 +- ...2026-04-05-production-deployment-design.md | 22 +++++++-------- .../2026-04-06-lead-generation-resend.md | 2 +- ...6-04-06-rebrand-angular-stream-resource.md | 4 +-- 34 files changed, 92 insertions(+), 92 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 528a7fb40..aa1113a2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,7 +102,7 @@ jobs: node-version: 22 cache: npm - run: npm ci - - run: npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.stream-resource.dev --dry-run + - run: npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.cacheplane.ai --dry-run mcp: name: MCP — build / smoke @@ -239,7 +239,7 @@ jobs: if: steps.affected.outputs.website == 'true' run: npx nx e2e website --skip-nx-cache env: - BASE_URL: https://stream-resource.dev + BASE_URL: https://cacheplane.ai - name: Prepare cockpit Vercel project if: steps.affected.outputs.cockpit == 'true' run: | @@ -259,7 +259,7 @@ jobs: - name: Verify deployed cockpit if: steps.affected.outputs.cockpit == 'true' run: | - npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.stream-resource.dev --retries 20 --retry-delay-ms 5000 + npx tsx apps/cockpit/scripts/deploy-smoke.ts --url https://cockpit.cacheplane.ai --retries 20 --retry-delay-ms 5000 # ── Angular examples deploy ────────────────────────────────────────── - name: Check if examples changed @@ -314,6 +314,6 @@ jobs: - name: Run production smoke tests run: npx playwright test apps/cockpit/e2e/production-smoke.spec.ts --reporter=list env: - BASE_URL: https://cockpit.stream-resource.dev - EXAMPLES_URL: https://examples.stream-resource.dev + BASE_URL: https://cockpit.cacheplane.ai + EXAMPLES_URL: https://examples.cacheplane.ai OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/COMMERCIAL.md b/COMMERCIAL.md index 7fe244ca5..8ab6900dd 100644 --- a/COMMERCIAL.md +++ b/COMMERCIAL.md @@ -23,7 +23,7 @@ Any use in a for-profit product, service, or organization requires a paid commer ## Purchase or inquire -- Website: https://stream-resource.dev/pricing +- Website: https://cacheplane.ai/pricing - Email: hello@cacheplane.ai See [`LICENSE-COMMERCIAL`](./LICENSE-COMMERCIAL) for the full commercial license terms. diff --git a/README.md b/README.md index 1ef81f2df..0452fc388 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ <p align="center"> <img - src="https://stream-resource.dev/assets/hero.svg" + src="https://cacheplane.ai/assets/hero.svg" alt="Angular Agent Framework — The Enterprise Streaming Resource for LangChain and Angular" width="100%" /> @@ -17,7 +17,7 @@ <a href="./LICENSE"> <img alt="License: PolyForm Noncommercial + Commercial" src="https://img.shields.io/badge/license-PolyForm%20Noncommercial%20%2B%20Commercial-6C8EFF?labelColor=080B14&style=flat-square" /> </a> - <a href="https://stream-resource.dev"> + <a href="https://cacheplane.ai"> <img alt="Angular 20+" src="https://img.shields.io/badge/Angular-20%2B-6C8EFF?labelColor=080B14&style=flat-square" /> </a> <a href="https://langchain-ai.github.io/langgraph/"> @@ -110,7 +110,7 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp <p align="center"> <img - src="https://stream-resource.dev/assets/arch-diagram.svg" + src="https://cacheplane.ai/assets/arch-diagram.svg" alt="Angular Agent Framework architecture: Angular Component → agent() → StreamManager Bridge → LangGraph Platform, with signals returned reactively" width="100%" /> @@ -129,17 +129,17 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp | **Application Deployment** | $2,000 / app | One-time per application — covers dev, staging, and prod | | **Enterprise** | Custom | Volume licensing, priority support, custom contract | -[Full pricing details and license terms →](https://stream-resource.dev/pricing) +[Full pricing details and license terms →](https://cacheplane.ai/pricing) --- ## Documentation -- [Getting Started](https://stream-resource.dev/docs/getting-started) -- [API Reference](https://stream-resource.dev/api-reference) -- [Testing with MockAgentTransport](https://stream-resource.dev/docs/testing) -- [Human-in-the-Loop / Interrupts](https://stream-resource.dev/docs/interrupts) -- [Subagent Streaming](https://stream-resource.dev/docs/subagents) +- [Getting Started](https://cacheplane.ai/docs/getting-started) +- [API Reference](https://cacheplane.ai/api-reference) +- [Testing with MockAgentTransport](https://cacheplane.ai/docs/testing) +- [Human-in-the-Loop / Interrupts](https://cacheplane.ai/docs/interrupts) +- [Subagent Streaming](https://cacheplane.ai/docs/subagents) --- @@ -150,4 +150,4 @@ That's it. `chat.messages()` is an Angular Signal. Bind it directly in your temp - **PolyForm Noncommercial 1.0.0** — free for noncommercial use (personal projects, academic, research, non-profit internal tooling). See [`LICENSE`](./LICENSE). - **Angular Agent Framework Commercial License** — required for any for-profit or revenue-generating use. See [`LICENSE-COMMERCIAL`](./LICENSE-COMMERCIAL) and [`COMMERCIAL.md`](./COMMERCIAL.md). -This is **not** an open-source license. Commercial use — including use in a for-profit product, service, or organization — requires a paid commercial license. See [pricing](https://stream-resource.dev/pricing). +This is **not** an open-source license. Commercial use — including use in a for-profit product, service, or organization — requires a paid commercial license. See [pricing](https://cacheplane.ai/pricing). diff --git a/apps/cockpit/e2e/production-smoke.spec.ts b/apps/cockpit/e2e/production-smoke.spec.ts index 7f00dbad9..fd78774e3 100644 --- a/apps/cockpit/e2e/production-smoke.spec.ts +++ b/apps/cockpit/e2e/production-smoke.spec.ts @@ -4,16 +4,16 @@ import { expect, test } from '@playwright/test'; * Production smoke test — verifies the deployed stack works end-to-end. * * Requires: - * EXAMPLES_URL - e.g., https://examples.stream-resource.dev + * EXAMPLES_URL - e.g., https://examples.cacheplane.ai * OPENAI_API_KEY - for send/receive tests (optional) * * Run: - * BASE_URL=https://cockpit.stream-resource.dev \ - * EXAMPLES_URL=https://examples.stream-resource.dev \ + * BASE_URL=https://cockpit.cacheplane.ai \ + * EXAMPLES_URL=https://examples.cacheplane.ai \ * npx playwright test apps/cockpit/e2e/production-smoke.spec.ts */ -const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.stream-resource.dev'; +const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.cacheplane.ai'; const CAPABILITIES = [ 'langgraph/streaming', diff --git a/apps/cockpit/scripts/deploy-smoke.spec.ts b/apps/cockpit/scripts/deploy-smoke.spec.ts index b00c951a8..9c1720157 100644 --- a/apps/cockpit/scripts/deploy-smoke.spec.ts +++ b/apps/cockpit/scripts/deploy-smoke.spec.ts @@ -6,7 +6,7 @@ describe('deploy smoke helper', () => { expect( parseDeploySmokeArgs([ '--url', - 'https://cockpit.stream-resource.dev', + 'https://cockpit.cacheplane.ai', '--dry-run', '--retries', '5', @@ -14,7 +14,7 @@ describe('deploy smoke helper', () => { '1000', ]) ).toEqual({ - url: 'https://cockpit.stream-resource.dev', + url: 'https://cockpit.cacheplane.ai', expectedTitle: 'Cockpit', dryRun: true, retries: 5, @@ -25,11 +25,11 @@ describe('deploy smoke helper', () => { it('formats dry-run output without performing a network request', async () => { await expect( runDeploySmoke({ - url: 'https://cockpit.stream-resource.dev', + url: 'https://cockpit.cacheplane.ai', expectedTitle: 'Cockpit', dryRun: true, }) - ).resolves.toBe('dry-run:https://cockpit.stream-resource.dev:Cockpit'); + ).resolves.toBe('dry-run:https://cockpit.cacheplane.ai:Cockpit'); }); it('retries until the deployment responds with the expected title', async () => { @@ -46,13 +46,13 @@ describe('deploy smoke helper', () => { await expect( runDeploySmoke({ - url: 'https://cockpit.stream-resource.dev', + url: 'https://cockpit.cacheplane.ai', retries: 1, retryDelayMs: 1, fetchImpl, sleep, }) - ).resolves.toBe('pass:https://cockpit.stream-resource.dev:Cockpit'); + ).resolves.toBe('pass:https://cockpit.cacheplane.ai:Cockpit'); expect(fetchImpl).toHaveBeenCalledTimes(2); expect(sleep).toHaveBeenCalledTimes(1); diff --git a/apps/cockpit/src/lib/content-bundle.spec.ts b/apps/cockpit/src/lib/content-bundle.spec.ts index 6de2f442f..1e61d58a9 100644 --- a/apps/cockpit/src/lib/content-bundle.spec.ts +++ b/apps/cockpit/src/lib/content-bundle.spec.ts @@ -28,10 +28,10 @@ describe('resolveRuntimeUrl', () => { }); it('uses NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL when set', () => { - vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev'); + vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai'); expect( resolveRuntimeUrl({ runtimeUrl: 'langgraph/streaming', devPort: 4300 }) - ).toBe('https://examples.stream-resource.dev/langgraph/streaming'); + ).toBe('https://examples.cacheplane.ai/langgraph/streaming'); }); it('falls back to localhost with devPort when no env var is set', () => { @@ -49,7 +49,7 @@ describe('resolveRuntimeUrl', () => { }); it('returns null when runtimeUrl is undefined even with env var set', () => { - vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev'); + vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai'); expect( resolveRuntimeUrl({ runtimeUrl: undefined, devPort: undefined }) ).toBeNull(); diff --git a/apps/website/emails/drip-whitepaper-followup.ts b/apps/website/emails/drip-whitepaper-followup.ts index dc45d4e91..da4b05e57 100644 --- a/apps/website/emails/drip-whitepaper-followup.ts +++ b/apps/website/emails/drip-whitepaper-followup.ts @@ -9,7 +9,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html <p style="font-size:11px;font-family:monospace;text-transform:uppercase;letter-spacing:0.08em;color:#004090;font-weight:700;margin:0 0 8px">Whitepaper Follow-up</p> <p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 14px;line-height:1.3">Did you get a chance to read Chapter 3?</p> <p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">Chapter 3 covers <strong>tool-call rendering</strong> — how to surface agent actions as real UI instead of raw JSON. It's the chapter most teams bookmark first.</p> - <a href="https://stream-resource.dev/whitepaper.pdf" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Read the Guide →</a> + <a href="https://cacheplane.ai/whitepaper.pdf" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Read the Guide →</a> `, showUnsubscribe: true, }), @@ -24,7 +24,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html <p style="font-size:11px;font-family:monospace;text-transform:uppercase;letter-spacing:0.08em;color:#004090;font-weight:700;margin:0 0 8px">Production Readiness</p> <p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 14px;line-height:1.3">The gap between demo and production</p> <p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">Half of GenAI projects die after proof of concept. The gap isn't the model — it's the frontend production path: streaming state, thread persistence, human approval flows, and deterministic testing.</p> - <a href="https://stream-resource.dev/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">See How It Works →</a> + <a href="https://cacheplane.ai/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">See How It Works →</a> `, showUnsubscribe: true, }), @@ -44,7 +44,7 @@ export function dripWhitepaperFollowupHtml(day: number): { subject: string; html <p style="font-size:13px;color:#555770;margin:0 0 4px;line-height:1.6"><strong style="color:#004090">Month 1</strong> · First agent in staging</p> <p style="font-size:13px;color:#555770;margin:0;line-height:1.6"><strong style="color:#004090">Month 3</strong> · Production deployment</p> </div> - <a href="https://stream-resource.dev/pilot-to-prod" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Learn About the Pilot →</a> + <a href="https://cacheplane.ai/pilot-to-prod" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Learn About the Pilot →</a> `, showUnsubscribe: true, }), diff --git a/apps/website/emails/email-wrapper.ts b/apps/website/emails/email-wrapper.ts index 77bb9d1eb..b12324734 100644 --- a/apps/website/emails/email-wrapper.ts +++ b/apps/website/emails/email-wrapper.ts @@ -16,7 +16,7 @@ export function wrapEmail(opts: { ${opts.body} <div style="border-top:1px solid #e4e4e7;margin-top:28px;padding-top:16px"> <p style="font-size:11px;color:#a1a1aa;line-height:1.5;margin:0">Angular Agent Framework — Signal-native streaming for LangGraph.</p> - ${opts.showUnsubscribe ? '<p style="font-size:10px;color:#d4d4d8;margin:6px 0 0"><a href="https://stream-resource.dev/api/unsubscribe?email=RECIPIENT" style="color:#d4d4d8;text-decoration:underline">Unsubscribe</a></p>' : ''} + ${opts.showUnsubscribe ? '<p style="font-size:10px;color:#d4d4d8;margin:6px 0 0"><a href="https://cacheplane.ai/api/unsubscribe?email=RECIPIENT" style="color:#d4d4d8;text-decoration:underline">Unsubscribe</a></p>' : ''} </div> </div> </div> diff --git a/apps/website/emails/newsletter-welcome.ts b/apps/website/emails/newsletter-welcome.ts index f8701ba05..08c7624ac 100644 --- a/apps/website/emails/newsletter-welcome.ts +++ b/apps/website/emails/newsletter-welcome.ts @@ -5,7 +5,7 @@ export function newsletterWelcomeHtml(): string { body: ` <p style="font-size:20px;font-weight:700;color:#1a1a2e;margin:0 0 8px;line-height:1.3">Welcome to Angular Agent Framework updates</p> <p style="font-size:14px;color:#555770;line-height:1.7;margin:0 0 24px">You'll receive updates on new capabilities, production patterns, and Angular agent best practices. We keep it focused and infrequent — no spam.</p> - <a href="https://stream-resource.dev/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Explore the Docs</a> + <a href="https://cacheplane.ai/docs" style="display:inline-block;background-color:#004090;color:#fff;padding:12px 28px;border-radius:8px;font-size:14px;font-weight:700;text-decoration:none">Explore the Docs</a> `, }); } diff --git a/apps/website/emails/whitepaper-download.ts b/apps/website/emails/whitepaper-download.ts index 4c0141b40..5a1978fe9 100644 --- a/apps/website/emails/whitepaper-download.ts +++ b/apps/website/emails/whitepaper-download.ts @@ -1,6 +1,6 @@ import { wrapEmail, esc } from './email-wrapper'; -const DOWNLOAD_URL = 'https://stream-resource.dev/whitepaper.pdf'; +const DOWNLOAD_URL = 'https://cacheplane.ai/whitepaper.pdf'; export function whitepaperDownloadHtml(name?: string): string { return wrapEmail({ diff --git a/apps/website/public/AGENTS.md b/apps/website/public/AGENTS.md index 7e9ff976a..1ed806f13 100644 --- a/apps/website/public/AGENTS.md +++ b/apps/website/public/AGENTS.md @@ -41,4 +41,4 @@ Add to ~/.claude/settings.json: {"mcpServers":{"angular":{"command":"npx","args":["@angular/mcp"]}}} ## Version check -If this file is stale, fetch the latest: https://stream-resource.dev/llms-full.txt +If this file is stale, fetch the latest: https://cacheplane.ai/llms-full.txt diff --git a/apps/website/public/CLAUDE.md b/apps/website/public/CLAUDE.md index 7e9ff976a..1ed806f13 100644 --- a/apps/website/public/CLAUDE.md +++ b/apps/website/public/CLAUDE.md @@ -41,4 +41,4 @@ Add to ~/.claude/settings.json: {"mcpServers":{"angular":{"command":"npx","args":["@angular/mcp"]}}} ## Version check -If this file is stale, fetch the latest: https://stream-resource.dev/llms-full.txt +If this file is stale, fetch the latest: https://cacheplane.ai/llms-full.txt diff --git a/apps/website/src/app/llms.txt/route.ts b/apps/website/src/app/llms.txt/route.ts index de348aac3..6068ea8ef 100644 --- a/apps/website/src/app/llms.txt/route.ts +++ b/apps/website/src/app/llms.txt/route.ts @@ -31,7 +31,7 @@ function buildLlmsTxt(): string { 'npx @cacheplane/angular-mcp', '', '## Full reference', - 'https://stream-resource.dev/llms-full.txt', + 'https://cacheplane.ai/llms-full.txt', ].join('\n'); } diff --git a/apps/website/src/components/shared/Footer.tsx b/apps/website/src/components/shared/Footer.tsx index 8e051eace..d26c3646a 100644 --- a/apps/website/src/components/shared/Footer.tsx +++ b/apps/website/src/components/shared/Footer.tsx @@ -146,7 +146,7 @@ export function Footer() { onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> API Reference </Link> - <a href="https://cockpit.stream-resource.dev" className="transition-colors" style={{ color: tokens.colors.textSecondary }} + <a href="https://cockpit.cacheplane.ai" className="transition-colors" style={{ color: tokens.colors.textSecondary }} onMouseEnter={(e) => (e.currentTarget.style.color = tokens.colors.accent)} onMouseLeave={(e) => (e.currentTarget.style.color = tokens.colors.textSecondary)}> Examples diff --git a/apps/website/src/components/shared/Nav.tsx b/apps/website/src/components/shared/Nav.tsx index fe44fbe19..813e8cd1e 100644 --- a/apps/website/src/components/shared/Nav.tsx +++ b/apps/website/src/components/shared/Nav.tsx @@ -7,7 +7,7 @@ const links = [ { label: 'Pilot to Prod', href: '/pilot-to-prod', external: false }, { label: 'Docs', href: '/docs', external: false }, { label: 'API', href: '/docs/agent/api/agent', external: false }, - { label: 'Examples', href: 'https://cockpit.stream-resource.dev', external: true }, + { label: 'Examples', href: 'https://cockpit.cacheplane.ai', external: true }, { label: 'Pricing', href: '/pricing', external: false }, ]; diff --git a/docs/superpowers/plans/2026-03-17-angular-library.md b/docs/superpowers/plans/2026-03-17-angular-library.md index 183c3f458..746a05c0f 100644 --- a/docs/superpowers/plans/2026-03-17-angular-library.md +++ b/docs/superpowers/plans/2026-03-17-angular-library.md @@ -1627,7 +1627,7 @@ NON-COMMERCIAL USE is defined as: - Internal tooling at non-profit organizations COMMERCIAL USE requires a separate commercial license. See LICENSE-COMMERCIAL -or visit https://stream-resource.dev/pricing for details. +or visit https://cacheplane.ai/pricing for details. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. @@ -1664,9 +1664,9 @@ ENTERPRISE LICENSE: Custom pricing - Volume seat discounts (10+ developers) - Unlimited deployment licenses - Dedicated support and SLA agreements -- Contact: enterprise@stream-resource.dev +- Contact: enterprise@cacheplane.ai -To purchase a license, visit: https://stream-resource.dev/pricing +To purchase a license, visit: https://cacheplane.ai/pricing Unauthorized commercial use is a violation of this license. ``` diff --git a/docs/superpowers/plans/2026-03-17-readme.md b/docs/superpowers/plans/2026-03-17-readme.md index 76fbf7ceb..4022e94be 100644 --- a/docs/superpowers/plans/2026-03-17-readme.md +++ b/docs/superpowers/plans/2026-03-17-readme.md @@ -260,7 +260,7 @@ apps/website/public/assets/ <a href="./LICENSE"> <img alt="License: MIT (non-commercial)" src="https://img.shields.io/badge/license-MIT%20%2B%20Commercial-d4aa6a?labelColor=080705&style=flat-square" /> </a> - <a href="https://stream-resource.dev"> + <a href="https://cacheplane.ai"> <img alt="Angular 20+" src="https://img.shields.io/badge/Angular-20%2B-d4aa6a?labelColor=080705&style=flat-square" /> </a> <a href="https://langchain-ai.github.io/langgraph/"> diff --git a/docs/superpowers/plans/2026-03-18-agentic-additions.md b/docs/superpowers/plans/2026-03-18-agentic-additions.md index 5e300bb8e..8fb6d5351 100644 --- a/docs/superpowers/plans/2026-03-18-agentic-additions.md +++ b/docs/superpowers/plans/2026-03-18-agentic-additions.md @@ -74,7 +74,7 @@ Download Angular logo SVG into the project: ```bash mkdir -p apps/website/public/logos curl -o apps/website/public/logos/angular.svg \ - "https://stream-resource.dev/assets/icons/logo.svg" 2>/dev/null || \ + "https://cacheplane.ai/assets/icons/logo.svg" 2>/dev/null || \ echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 250"><path fill="#DD0031" d="M125 30L31.9 63.2l14.2 123.1L125 230l78.9-43.7 14.2-123.1z"/><path fill="#C3002F" d="M125 30v22.2l-61.7 162.4L125 230z"/><path fill="#fff" d="M125 52.1L66.8 182.6h21.7l11.7-29.2h49.4l11.7 29.2H183L125 52.1zm17 83.3h-34l17-40.9z"/></svg>', ``` @@ -755,7 +755,7 @@ const chat = agent({ assistantId: 'chat_agent', apiUrl: 'http://localhost:2024' npx @angular/mcp ## Full reference -https://stream-resource.dev/llms-full.txt +https://cacheplane.ai/llms-full.txt `.trim(); } @@ -887,7 +887,7 @@ Add to ~/.claude/settings.json: {"mcpServers":{"angular":{"command":"npx","args":["@angular/mcp"]}}} ## Version check -If this file is stale, fetch the latest: https://stream-resource.dev/llms-full.txt +If this file is stale, fetch the latest: https://cacheplane.ai/llms-full.txt ``` Create `apps/website/content/AGENTS.md.template` with identical content (same file, different name). diff --git a/docs/superpowers/plans/2026-03-19-langsmith-deployment.md b/docs/superpowers/plans/2026-03-19-langsmith-deployment.md index ac41beb66..74ddf2d74 100644 --- a/docs/superpowers/plans/2026-03-19-langsmith-deployment.md +++ b/docs/superpowers/plans/2026-03-19-langsmith-deployment.md @@ -165,7 +165,7 @@ Either push a commit or manually trigger a redeploy from the Vercel dashboard. ### Step 3: Verify live demo works -Visit https://stream-resource.dev and confirm the `<stream-chat-demo>` web component connects and streams responses. +Visit https://cacheplane.ai and confirm the `<stream-chat-demo>` web component connects and streams responses. --- diff --git a/docs/superpowers/plans/2026-03-19-next-steps.md b/docs/superpowers/plans/2026-03-19-next-steps.md index 7fd94a833..768c8afb6 100644 --- a/docs/superpowers/plans/2026-03-19-next-steps.md +++ b/docs/superpowers/plans/2026-03-19-next-steps.md @@ -69,7 +69,7 @@ 2. Add `NEXT_PUBLIC_LANGGRAPH_URL` = `https://<your-langgraph-cloud-url>` 3. Set for: Production + Preview + Development 4. Trigger a redeploy -5. Verify live demo at https://stream-resource.dev streams real responses +5. Verify live demo at https://cacheplane.ai streams real responses --- diff --git a/docs/superpowers/plans/2026-03-19-roadmap.md b/docs/superpowers/plans/2026-03-19-roadmap.md index 5dadcd24d..0446df984 100644 --- a/docs/superpowers/plans/2026-03-19-roadmap.md +++ b/docs/superpowers/plans/2026-03-19-roadmap.md @@ -69,7 +69,7 @@ After 1.3 completes: - Vercel Dashboard → cacheplane / angular → Settings → Environment Variables - Add `NEXT_PUBLIC_LANGGRAPH_URL` for all environments - Trigger redeploy -- Verify live demo at stream-resource.dev +- Verify live demo at cacheplane.ai --- @@ -78,7 +78,7 @@ After 1.3 completes: **Status:** Complete **Effort:** Trivial -Completed in-session by updating the README asset and docs links from `angular.vercel.app` to `stream-resource.dev`. +Completed in-session by updating the README asset and docs links from `angular.vercel.app` to `cacheplane.ai`. --- @@ -171,7 +171,7 @@ These are logical continuations of the project's direction. They're not explicit ### 2.6 SEO and Analytics -**Why:** The site is live at stream-resource.dev but has no analytics, no structured metadata beyond the basic `<title>` and `<description>`, and no sitemap. +**Why:** The site is live at cacheplane.ai but has no analytics, no structured metadata beyond the basic `<title>` and `<description>`, and no sitemap. **What to add:** - **Vercel Analytics** — one-line integration, free tier, no cookies diff --git a/docs/superpowers/plans/2026-03-19-vercel-deployment-notes.md b/docs/superpowers/plans/2026-03-19-vercel-deployment-notes.md index cbc573600..4fa1c1e15 100644 --- a/docs/superpowers/plans/2026-03-19-vercel-deployment-notes.md +++ b/docs/superpowers/plans/2026-03-19-vercel-deployment-notes.md @@ -115,5 +115,5 @@ In production, `NEXT_PUBLIC_LANGGRAPH_URL` must be set to the LangGraph Cloud de ## 6. Domain -**Production URL:** https://stream-resource.dev +**Production URL:** https://cacheplane.ai **Vercel project:** https://vercel.com/cacheplane/angular diff --git a/docs/superpowers/plans/2026-04-03-cockpit-example-harness.md b/docs/superpowers/plans/2026-04-03-cockpit-example-harness.md index 46f783211..2b0d6b711 100644 --- a/docs/superpowers/plans/2026-04-03-cockpit-example-harness.md +++ b/docs/superpowers/plans/2026-04-03-cockpit-example-harness.md @@ -1242,7 +1242,7 @@ The `runtimeUrl` field on the capability module is used with `NEXT_PUBLIC_COCKPI runtimeUrl: 'langgraph/streaming', // → ${NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL}/langgraph/streaming ``` -This resolves to e.g., `https://examples.stream-resource.dev/langgraph/streaming` in production. +This resolves to e.g., `https://examples.cacheplane.ai/langgraph/streaming` in production. - [ ] **Step 4: Add deployment instructions to the streaming prompt (as a code comment in graph.py)** diff --git a/docs/superpowers/plans/2026-04-03-content-bundle.md b/docs/superpowers/plans/2026-04-03-content-bundle.md index acc86b2f7..1379855f3 100644 --- a/docs/superpowers/plans/2026-04-03-content-bundle.md +++ b/docs/superpowers/plans/2026-04-03-content-bundle.md @@ -48,10 +48,10 @@ describe('resolveRuntimeUrl', () => { }); it('uses NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL when set', () => { - vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev'); + vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai'); expect( resolveRuntimeUrl({ runtimeUrl: 'langgraph/streaming', devPort: 4300 }) - ).toBe('https://examples.stream-resource.dev/langgraph/streaming'); + ).toBe('https://examples.cacheplane.ai/langgraph/streaming'); }); it('falls back to localhost with devPort when no env var is set', () => { @@ -69,7 +69,7 @@ describe('resolveRuntimeUrl', () => { }); it('returns null when runtimeUrl is undefined even with env var set', () => { - vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.stream-resource.dev'); + vi.stubEnv('NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL', 'https://examples.cacheplane.ai'); expect( resolveRuntimeUrl({ runtimeUrl: undefined, devPort: undefined }) ).toBeNull(); diff --git a/docs/superpowers/plans/2026-04-05-production-deployment.md b/docs/superpowers/plans/2026-04-05-production-deployment.md index 33fd71576..3158fafa5 100644 --- a/docs/superpowers/plans/2026-04-05-production-deployment.md +++ b/docs/superpowers/plans/2026-04-05-production-deployment.md @@ -4,7 +4,7 @@ **Goal:** Deploy all 14 capability backends to LangGraph Cloud, host all 14 Angular example apps on Vercel, wire the cockpit to production URLs, and verify end-to-end with smoke tests. -**Architecture:** Three-phase deployment: (1) deploy Python backends to LangGraph Cloud via existing `deploy-langgraph.yml`, capture URLs into a registry file; (2) update Angular production environments with LangGraph Cloud URLs, build and deploy as static sites to a new Vercel project at `examples.stream-resource.dev`; (3) set `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL` in cockpit Vercel env, add a production smoke test to CI that verifies the full stack. +**Architecture:** Three-phase deployment: (1) deploy Python backends to LangGraph Cloud via existing `deploy-langgraph.yml`, capture URLs into a registry file; (2) update Angular production environments with LangGraph Cloud URLs, build and deploy as static sites to a new Vercel project at `examples.cacheplane.ai`; (3) set `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL` in cockpit Vercel env, add a production smoke test to CI that verifies the full stack. **Tech Stack:** LangGraph Cloud (LangSmith), Vercel, Angular static builds, Playwright, GitHub Actions @@ -667,16 +667,16 @@ import { expect, test } from '@playwright/test'; * cockpit → iframe → Angular app → LangGraph Cloud backend → AI response. * * Requires environment variables: - * EXAMPLES_URL - e.g., https://examples.stream-resource.dev + * EXAMPLES_URL - e.g., https://examples.cacheplane.ai * OPENAI_API_KEY - for send/receive tests (optional, skips if not set) * * Run against production: - * BASE_URL=https://cockpit.stream-resource.dev \ - * EXAMPLES_URL=https://examples.stream-resource.dev \ + * BASE_URL=https://cockpit.cacheplane.ai \ + * EXAMPLES_URL=https://examples.cacheplane.ai \ * npx playwright test apps/cockpit/e2e/production-smoke.spec.ts */ -const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.stream-resource.dev'; +const EXAMPLES_URL = process.env['EXAMPLES_URL'] ?? 'https://examples.cacheplane.ai'; const CAPABILITIES = [ 'langgraph/streaming', @@ -772,8 +772,8 @@ Add a new job after the `deploy` job in `.github/workflows/ci.yml`: - name: Run production smoke tests run: npx playwright test apps/cockpit/e2e/production-smoke.spec.ts --reporter=list env: - BASE_URL: https://cockpit.stream-resource.dev - EXAMPLES_URL: https://examples.stream-resource.dev + BASE_URL: https://cockpit.cacheplane.ai + EXAMPLES_URL: https://examples.cacheplane.ai OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ``` @@ -810,20 +810,20 @@ In GitHub repo Settings → Secrets: - [ ] **Step 3: Set cockpit env var in Vercel** In the cockpit Vercel project settings → Environment Variables: -1. Add `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL` = `https://examples.stream-resource.dev` +1. Add `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL` = `https://examples.cacheplane.ai` 2. Apply to Production environment - [ ] **Step 4: Set examples domain in Vercel** In the examples Vercel project settings → Domains: -1. Add `examples.stream-resource.dev` +1. Add `examples.cacheplane.ai` 2. Configure DNS (CNAME to `cname.vercel-dns.com`) - [ ] **Step 5: Trigger initial deploy** Push all committed changes to `main`. The CI pipeline will: 1. Build and deploy the cockpit (picks up `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL`) -2. Build and deploy the Angular examples to `examples.stream-resource.dev` +2. Build and deploy the Angular examples to `examples.cacheplane.ai` 3. Run the production smoke test - [ ] **Step 6: Verify end-to-end** @@ -834,15 +834,15 @@ npx tsx scripts/verify-langgraph-deployments.ts --smoke # Verify examples load for cap in langgraph/streaming langgraph/persistence deep-agents/planning; do - curl -s -o /dev/null -w "%{http_code} $cap\n" "https://examples.stream-resource.dev/$cap/" + curl -s -o /dev/null -w "%{http_code} $cap\n" "https://examples.cacheplane.ai/$cap/" done # Verify cockpit -curl -s -o /dev/null -w "%{http_code} cockpit\n" "https://cockpit.stream-resource.dev" +curl -s -o /dev/null -w "%{http_code} cockpit\n" "https://cockpit.cacheplane.ai" # Run full production smoke -BASE_URL=https://cockpit.stream-resource.dev \ -EXAMPLES_URL=https://examples.stream-resource.dev \ +BASE_URL=https://cockpit.cacheplane.ai \ +EXAMPLES_URL=https://examples.cacheplane.ai \ npx playwright test apps/cockpit/e2e/production-smoke.spec.ts --reporter=list ``` diff --git a/docs/superpowers/plans/2026-04-06-lead-generation-resend.md b/docs/superpowers/plans/2026-04-06-lead-generation-resend.md index 25bf0e760..192bab687 100644 --- a/docs/superpowers/plans/2026-04-06-lead-generation-resend.md +++ b/docs/superpowers/plans/2026-04-06-lead-generation-resend.md @@ -172,7 +172,7 @@ interface WhitepaperDownloadProps { name?: string; } -const DOWNLOAD_URL = 'https://stream-resource.dev/whitepaper.pdf'; +const DOWNLOAD_URL = 'https://cacheplane.ai/whitepaper.pdf'; export default function WhitepaperDownload({ name }: WhitepaperDownloadProps) { return ( @@ -248,7 +248,7 @@ export default function NewsletterWelcome() { You'll receive updates on new capabilities, production patterns, and Angular agent best practices. We keep it focused and infrequent — no spam. </Text> - <Button href="https://stream-resource.dev/docs" style={{ + <Button href="https://cacheplane.ai/docs" style={{ backgroundColor: '#004090', color: '#fff', padding: '12px 28px', borderRadius: 10, fontSize: 14, fontWeight: 700, textDecoration: 'none', }}> diff --git a/docs/superpowers/plans/2026-04-06-rebrand-angular-stream-resource.md b/docs/superpowers/plans/2026-04-06-rebrand-angular-stream-resource.md index 19f11b96d..8b387afb7 100644 --- a/docs/superpowers/plans/2026-04-06-rebrand-angular-stream-resource.md +++ b/docs/superpowers/plans/2026-04-06-rebrand-angular-stream-resource.md @@ -429,7 +429,7 @@ to: # Angular Agent Framework v0.0.1 ``` -Note: All other references in these files (`agent()`, `provideAgent`, `import ... from 'angular'`, `@angular/mcp`, `stream-resource.dev`) are code/infrastructure references and should NOT be changed. +Note: All other references in these files (`agent()`, `provideAgent`, `import ... from 'angular'`, `@angular/mcp`, `cacheplane.ai`) are code/infrastructure references and should NOT be changed. - [ ] **Step 3: Commit** diff --git a/docs/superpowers/specs/2026-03-18-agentic-docs-design.md b/docs/superpowers/specs/2026-03-18-agentic-docs-design.md index ba360c001..1f084589f 100644 --- a/docs/superpowers/specs/2026-03-18-agentic-docs-design.md +++ b/docs/superpowers/specs/2026-03-18-agentic-docs-design.md @@ -193,7 +193,7 @@ npm install angular npx @angular/mcp (add to Claude Code settings for tool access) ## Version -@VERSION@ — regenerate if stale: https://stream-resource.dev/llms-full.txt +@VERSION@ — regenerate if stale: https://cacheplane.ai/llms-full.txt ``` ### `AGENTS.md` diff --git a/docs/superpowers/specs/2026-03-19-licensing-model.md b/docs/superpowers/specs/2026-03-19-licensing-model.md index 86ab35813..549269723 100644 --- a/docs/superpowers/specs/2026-03-19-licensing-model.md +++ b/docs/superpowers/specs/2026-03-19-licensing-model.md @@ -60,7 +60,7 @@ Angular Agent Framework uses a **source-available dual-license model**. It is ** | Enterprise | Custom | Volume licensing, priority support, custom contract | **Contact:** hello@cacheplane.ai -**Pricing page:** https://stream-resource.dev/pricing +**Pricing page:** https://cacheplane.ai/pricing **Governing law:** State of Oregon, Deschutes County, United States. **Copyright:** Brian Love d/b/a cacheplane diff --git a/docs/superpowers/specs/2026-03-20-cockpit-app-design.md b/docs/superpowers/specs/2026-03-20-cockpit-app-design.md index e70d60268..427e19132 100644 --- a/docs/superpowers/specs/2026-03-20-cockpit-app-design.md +++ b/docs/superpowers/specs/2026-03-20-cockpit-app-design.md @@ -11,7 +11,7 @@ Define a single integrated cockpit application that serves as: - the live reference surface for all supported capabilities -- a marketing asset at `cockpit.stream-resource.dev` +- a marketing asset at `cockpit.cacheplane.ai` - the canonical integration surface for examples and testing --- diff --git a/docs/superpowers/specs/2026-03-20-cockpit-testing-and-deployment-design.md b/docs/superpowers/specs/2026-03-20-cockpit-testing-and-deployment-design.md index b5f424175..5b93970e0 100644 --- a/docs/superpowers/specs/2026-03-20-cockpit-testing-and-deployment-design.md +++ b/docs/superpowers/specs/2026-03-20-cockpit-testing-and-deployment-design.md @@ -94,7 +94,7 @@ These tests validate the shell contract, not capability business logic. The cockpit is deployed as a product surface at: -- `cockpit.stream-resource.dev` +- `cockpit.cacheplane.ai` Deployment verification should confirm: diff --git a/docs/superpowers/specs/2026-04-05-production-deployment-design.md b/docs/superpowers/specs/2026-04-05-production-deployment-design.md index 28324d326..4b49aa97a 100644 --- a/docs/superpowers/specs/2026-04-05-production-deployment-design.md +++ b/docs/superpowers/specs/2026-04-05-production-deployment-design.md @@ -2,7 +2,7 @@ ## Problem -The cockpit has 14 capability examples (8 LangGraph + 6 Deep Agents), each with an Angular frontend and a Python LangGraph backend. None are deployed to production. The Angular `environment.ts` files still point to `localhost`, the LangGraph Cloud deployments have never been triggered, and there is no hosting for the Angular apps. The cockpit at `https://cockpit.stream-resource.dev` deploys via Vercel but its Run mode iframes show nothing because the Angular apps aren't hosted anywhere. +The cockpit has 14 capability examples (8 LangGraph + 6 Deep Agents), each with an Angular frontend and a Python LangGraph backend. None are deployed to production. The Angular `environment.ts` files still point to `localhost`, the LangGraph Cloud deployments have never been triggered, and there is no hosting for the Angular apps. The cockpit at `https://cockpit.cacheplane.ai` deploys via Vercel but its Run mode iframes show nothing because the Angular apps aren't hosted anywhere. ## Goal @@ -11,13 +11,13 @@ Deploy the full stack to production in three phases: 2. Build and deploy all 14 Angular apps to Vercel as static sites 3. Wire the cockpit to the production Angular apps and verify end-to-end -After completion, a user visiting `https://cockpit.stream-resource.dev`, navigating to any capability, and switching to Run mode sees a working chat interface that sends messages to a production LangGraph Cloud backend and receives AI responses. +After completion, a user visiting `https://cockpit.cacheplane.ai`, navigating to any capability, and switching to Run mode sees a working chat interface that sends messages to a production LangGraph Cloud backend and receives AI responses. ## Architecture Overview ``` -cockpit.stream-resource.dev (Vercel / Next.js) - └─ iframe src: examples.stream-resource.dev/{product}/{topic}/ +cockpit.cacheplane.ai (Vercel / Next.js) + └─ iframe src: examples.cacheplane.ai/{product}/{topic}/ └─ Angular static app (Vercel) └─ API proxy → https://{org}-{name}.us.langgraph.app └─ LangGraph Cloud (LangSmith-hosted Python backend) @@ -78,7 +78,7 @@ For each of the 14 backends: ### What needs to happen -1. **Create a Vercel project** for `examples.stream-resource.dev`. This is a static hosting project — no framework, just serves static files. +1. **Create a Vercel project** for `examples.cacheplane.ai`. This is a static hosting project — no framework, just serves static files. 2. **Update Angular `environment.ts` production files** — replace `http://localhost:4XXX/api` with the LangGraph Cloud URL for each capability (from `deployment-urls.json`). The development files stay pointing to localhost for local dev. @@ -100,7 +100,7 @@ For each of the 14 backends: 6. **Add CI deploy job** — a new job in `ci.yml` (or a separate workflow) that builds, assembles, and deploys all Angular apps on push to main. -7. **Set `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL`** — in the cockpit's Vercel project environment variables, set this to `https://examples.stream-resource.dev`. The cockpit's `content-bundle.ts` uses this to construct iframe URLs like `https://examples.stream-resource.dev/langgraph/streaming/`. +7. **Set `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL`** — in the cockpit's Vercel project environment variables, set this to `https://examples.cacheplane.ai`. The cockpit's `content-bundle.ts` uses this to construct iframe URLs like `https://examples.cacheplane.ai/langgraph/streaming/`. ### Angular Proxy in Production @@ -118,7 +118,7 @@ export const environment = { ### CORS -LangGraph Cloud backends need to allow requests from `examples.stream-resource.dev`. LangGraph Cloud typically allows CORS from any origin for the streaming API, but this should be verified. If CORS is restricted, the LangGraph deployment config may need a `cors_allowed_origins` setting. +LangGraph Cloud backends need to allow requests from `examples.cacheplane.ai`. LangGraph Cloud typically allows CORS from any origin for the streaming API, but this should be verified. If CORS is restricted, the LangGraph deployment config may need a `cors_allowed_origins` setting. ### Verification @@ -131,7 +131,7 @@ For each of the 14 Angular apps: ### What exists -- Cockpit deploys to `https://cockpit.stream-resource.dev` via Vercel +- Cockpit deploys to `https://cockpit.cacheplane.ai` via Vercel - CI deploy job already exists and runs on push to main - Post-deploy smoke script already exists (`cockpit-deploy-smoke`) @@ -140,7 +140,7 @@ For each of the 14 Angular apps: 1. **Set `NEXT_PUBLIC_COCKPIT_RUNTIME_BASE_URL`** in Vercel (done in Phase 2) 2. **Trigger cockpit redeploy** — the env var change requires a redeploy for Next.js to pick it up 3. **Verify the cockpit renders** — navigate to cockpit, confirm sidebar loads with all capabilities (no "overview" entries) -4. **Verify Run mode** — for each capability, switch to Run mode, confirm the iframe loads the Angular app from `examples.stream-resource.dev` +4. **Verify Run mode** — for each capability, switch to Run mode, confirm the iframe loads the Angular app from `examples.cacheplane.ai` 5. **End-to-end smoke** — for a representative subset (at minimum streaming, persistence, planning), send a message in Run mode and verify AI response ### CI Verification Pipeline @@ -160,8 +160,8 @@ production-smoke: - run: npx playwright install --with-deps chromium - run: npx playwright test apps/cockpit/e2e/production-smoke.spec.ts env: - BASE_URL: https://cockpit.stream-resource.dev - EXAMPLES_URL: https://examples.stream-resource.dev + BASE_URL: https://cockpit.cacheplane.ai + EXAMPLES_URL: https://examples.cacheplane.ai OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ``` diff --git a/docs/superpowers/specs/2026-04-06-lead-generation-resend.md b/docs/superpowers/specs/2026-04-06-lead-generation-resend.md index a1de785eb..c5bb793df 100644 --- a/docs/superpowers/specs/2026-04-06-lead-generation-resend.md +++ b/docs/superpowers/specs/2026-04-06-lead-generation-resend.md @@ -95,7 +95,7 @@ Sent to the user after white paper form submission. - **To:** User's email - **Subject:** `Your Angular Agent Readiness Guide` -- **Body:** Brief intro, prominent "Download the Guide" button linking to `https://stream-resource.dev/whitepaper.pdf`, chapter summary list. +- **Body:** Brief intro, prominent "Download the Guide" button linking to `https://cacheplane.ai/whitepaper.pdf`, chapter summary list. ### `newsletter-welcome.tsx` diff --git a/docs/superpowers/specs/2026-04-06-rebrand-angular-stream-resource.md b/docs/superpowers/specs/2026-04-06-rebrand-angular-stream-resource.md index 69293e5b2..cdb2da04b 100644 --- a/docs/superpowers/specs/2026-04-06-rebrand-angular-stream-resource.md +++ b/docs/superpowers/specs/2026-04-06-rebrand-angular-stream-resource.md @@ -15,7 +15,7 @@ Angular 20 introduced `rxResource` with a `stream` property, which the official | Full product name | Angular Agent Framework | | Parent brand | Cacheplane | | npm package | `@cacheplane/angular` (unchanged) | -| Domain | `stream-resource.dev` (unchanged) | +| Domain | `cacheplane.ai` (unchanged) | | Tagline | "The Enterprise Streaming Resource for LangChain and Angular" (unchanged) | ## Scope @@ -65,7 +65,7 @@ Brand-layer text only. All changes are string replacements in documentation and | File/directory names | `angular.fn.ts`, `/libs/angular/` | File system identity | | Nx project names | `angular`, `angular-e2e` | Build system identity | | TypeScript path aliases | `@cacheplane/angular` in tsconfig | Build system identity | -| Domain URLs | `stream-resource.dev`, `examples.stream-resource.dev` | Infrastructure identity | +| Domain URLs | `cacheplane.ai`, `examples.cacheplane.ai` | Infrastructure identity | | CI/CD workflows | GitHub Actions references | Infrastructure identity | | package.json `name` fields | `"name": "angular"` | Package identity | From a49e8bf9305f8ae98ef61fb2f4c609ff7fbffd2f Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:29:58 -0700 Subject: [PATCH 04/17] docs: add generative UI views system design spec --- .../2026-04-07-generative-ui-views-design.md | 428 ++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-07-generative-ui-views-design.md diff --git a/docs/superpowers/specs/2026-04-07-generative-ui-views-design.md b/docs/superpowers/specs/2026-04-07-generative-ui-views-design.md new file mode 100644 index 000000000..6138702e6 --- /dev/null +++ b/docs/superpowers/specs/2026-04-07-generative-ui-views-design.md @@ -0,0 +1,428 @@ +# Generative UI: Views System Design + +**Date:** 2026-04-07 +**Status:** Draft +**Scope:** `@cacheplane/render` views API + compositional rendering + `@cacheplane/chat` inline integration + +--- + +## Overview + +A system for exposing Angular components to the agent as **views** — renderable UI that appears inline in the chat. The agent produces JSON specs (in tool results, state, or message metadata) that the chat library renders using the existing `@cacheplane/render` pipeline. Developers register views with a functional API, compose them via object spread, and the chat handles detection and rendering automatically. + +### Design Principles + +1. **Views are what the user sees** — not implementation details (components), not small widgets. A view is a visual representation of data, inspired by Apple's SwiftUI View protocol. +2. **Backend controls the UI** — the Python graph produces the full nested spec. The frontend is a pure renderer. +3. **Data down, actions up** — props flow from spec → view. User interactions flow up through `signalStateStore()`. +4. **Composition via spread** — no special merge functions. JavaScript object spread handles composition, override, and extension. + +--- + +## Deliverable 1: `views()` Functional API + +### Public API + +```typescript +// @cacheplane/render + +// Create a view registry +function views(map: Record<string, Type<unknown>>): ViewRegistry; + +// Add views without overwriting existing entries +function withViews(base: ViewRegistry, additions: Record<string, Type<unknown>>): ViewRegistry; + +// Remove views by name +function withoutViews(base: ViewRegistry, ...names: string[]): ViewRegistry; + +// Provide views globally via Angular DI +function provideViews(registry: ViewRegistry): EnvironmentProviders; +``` + +### ViewRegistry Type + +```typescript +// Opaque type wrapping a frozen Record<string, Type<unknown>> +// Supports iteration, lookup, and spread +interface ViewRegistry { + readonly [name: string]: Type<unknown>; +} +``` + +`ViewRegistry` is a plain frozen object. `views()` creates one, `withViews()` and `withoutViews()` return new ones. All are immutable. + +### Implementation + +```typescript +export function views(map: Record<string, Type<unknown>>): ViewRegistry { + return Object.freeze({ ...map }) as ViewRegistry; +} + +export function withViews( + base: ViewRegistry, + additions: Record<string, Type<unknown>>, +): ViewRegistry { + // Additions go first, base overwrites → existing keys preserved + return Object.freeze({ ...additions, ...base }) as ViewRegistry; +} + +export function withoutViews( + base: ViewRegistry, + ...names: string[] +): ViewRegistry { + const result = { ...base }; + for (const name of names) delete result[name]; + return Object.freeze(result) as ViewRegistry; +} + +const VIEW_REGISTRY = new InjectionToken<ViewRegistry>('VIEW_REGISTRY'); + +export function provideViews(registry: ViewRegistry): EnvironmentProviders { + return makeEnvironmentProviders([ + { provide: VIEW_REGISTRY, useValue: registry }, + ]); +} +``` + +### Relationship to `defineAngularRegistry()` + +`views()` is the high-level API. Under the hood, when the chat library needs to render a spec, it converts the `ViewRegistry` to an `AngularRegistry` (the format `RenderSpecComponent` expects). `defineAngularRegistry()` remains the low-level API for direct `@cacheplane/render` usage. + +### Usage Examples + +```typescript +// Define +const agentViews = views({ + 'plan-checklist': PlanChecklistComponent, + 'file-preview': FilePreviewComponent, + 'code-output': CodeOutputComponent, + 'choice-card': ChoiceCardComponent, +}); + +// Compose via spread (last key wins = override) +const all = views({ ...agentViews, ...thirdPartyViews }); + +// Add without overwriting +const extended = withViews(agentViews, { 'chart': ChartComponent }); + +// Remove +const restricted = withoutViews(agentViews, 'file-preview'); + +// Provide globally +providers: [provideViews(agentViews)] + +// Provide per-instance +<chat [ref]="stream" [views]="agentViews" /> + +// Route-level scoping +{ path: 'planning', providers: [provideViews(planningViews)] } +``` + +--- + +## Deliverable 2: Compositional Rendering + +### Spec Format + +The agent produces specs conforming to `@json-render/core`'s `Spec` type — a flat map of elements with a root key. Each element has a `type` (matched against the view registry), `props`, and optional `children`. + +```json +{ + "root": "plan", + "elements": { + "plan": { + "type": "plan-checklist", + "props": { "title": "Birthday Party Plan" }, + "children": ["step-0", "step-1", "step-2"] + }, + "step-0": { + "type": "checkbox-row", + "props": { + "label": "Choose a venue", + "checked": { "$state": "/steps/0/done" } + } + }, + "step-1": { + "type": "checkbox-row", + "props": { + "label": "Send invitations", + "checked": { "$state": "/steps/1/done" } + } + }, + "step-2": { + "type": "checkbox-row", + "props": { + "label": "Order cake", + "checked": { "$state": "/steps/2/done" } + } + } + } +} +``` + +### Rendering Pipeline + +1. Agent returns spec in tool result, state, or message metadata +2. Chat detects spec (see Deliverable 3) +3. Chat passes spec to `<render-spec>` with the view registry + state store +4. `RenderSpecComponent` resolves root element → looks up `plan-checklist` in registry +5. `RenderElementComponent` renders the component via `NgComponentOutlet` +6. Recurses into `children` → renders each `checkbox-row` +7. Props with `$state` expressions bind to `signalStateStore()` for reactivity + +### View Component Contract + +Each view component receives props as Angular `input()` signals. The component doesn't know or care that it's being rendered by json-render — it's a normal Angular standalone component. + +```typescript +@Component({ + selector: 'plan-checklist', + standalone: true, + template: ` + <div class="border rounded-xl p-4"> + <h3 class="font-semibold">{{ title() }}</h3> + <ng-content /> <!-- children rendered here by json-render --> + </div> + `, +}) +export class PlanChecklistComponent { + readonly title = input<string>(''); +} +``` + +Children are projected via `<ng-content>` — json-render's `RenderElementComponent` handles the recursion and content projection. + +### Nested Composition + +Views compose by nesting in the spec. The registry is flat (name → component), but the rendered output is a tree: + +``` +plan-checklist +├── checkbox-row ("Choose a venue") +├── checkbox-row ("Send invitations") +└── checkbox-row ("Order cake") +``` + +A `file-preview` view could contain `code-block` and `metadata-row` children: + +``` +file-preview +├── metadata-row ("path: /config.json") +├── metadata-row ("size: 2.4 KB") +└── code-block (file contents) +``` + +The backend defines the tree structure. The frontend renders it faithfully. + +--- + +## Deliverable 3: Chat Integration + +### How the Chat Detects Specs + +The `<chat>` composition checks three locations for UI specs: + +1. **Tool call results** — if a tool result message contains a `ui` field with a valid spec +2. **Graph state** — if `stream.value()` contains a `ui` field +3. **Message metadata** — if an AI message contains a `ui` field + +Detection priority: tool result > message metadata > state. The chat renders the first spec found per message. + +### Chat Component Changes + +```typescript +// chat.component.ts — new inputs +readonly views = input<ViewRegistry | undefined>(undefined); +readonly store = input<StateStore | undefined>(undefined); + +// Inject DI-provided registry as fallback +private readonly diViews = inject(VIEW_REGISTRY, { optional: true }); + +// Resolved registry: input takes precedence over DI +private readonly resolvedViews = computed(() => + this.views() ?? this.diViews ?? undefined +); +``` + +### Template Integration + +In the `ChatMessagesComponent` template, after rendering the message text, check for a UI spec: + +```html +@for (message of messages(); track $index) { + <!-- Render message text (human/ai/tool as before) --> + @let template = findTemplate(getMessageType(message)); + @if (template) { + <ng-container [ngTemplateOutlet]="template.templateRef" ... /> + } + + <!-- Render inline generative UI if spec present --> + @if (resolvedViews() && getUiSpec(message); as spec) { + <div class="ml-10 mt-2"> + <render-spec + [spec]="spec" + [registry]="toRenderRegistry(resolvedViews()!)" + [store]="store()" + /> + </div> + } +} +``` + +### Spec Extraction + +```typescript +function getUiSpec(message: BaseMessage): Spec | null { + const msg = message as Record<string, unknown>; + + // Check tool result ui field + if (msg['ui'] && isValidSpec(msg['ui'])) { + return msg['ui'] as Spec; + } + + // Check message additional_kwargs + const kwargs = msg['additional_kwargs'] as Record<string, unknown> | undefined; + if (kwargs?.['ui'] && isValidSpec(kwargs['ui'])) { + return kwargs['ui'] as Spec; + } + + return null; +} + +function isValidSpec(value: unknown): boolean { + return typeof value === 'object' + && value !== null + && 'root' in value + && 'elements' in value; +} +``` + +--- + +## Deliverable 4: State Store Integration + +### Two-Way Interactivity + +When views need interactivity (checkboxes, buttons, form inputs), they read/write through `signalStateStore()`. + +**Flow:** + +1. Spec includes state-bound props: `{ "$state": "/steps/0/done" }` +2. json-render resolves the binding → creates a signal from the store path +3. Component receives the value as an input (reactive) +4. User interacts → component calls `store.set('/steps/0/done', true)` +5. Store updates → signal fires → all bound views re-render +6. Consumer observes changes via the store + +### Consumer Owns the Store + +The consumer creates and owns the `signalStateStore()`, passing it to `<chat>`: + +```typescript +@Component({ + template: `<chat [ref]="stream" [views]="ui" [store]="uiStore" />`, +}) +export class PlanningComponent { + stream = agent({ apiUrl: '/api', assistantId: 'planning' }); + ui = views({ 'plan-checklist': PlanChecklist, 'checkbox-row': CheckboxRow }); + uiStore = signalStateStore({}); + + constructor() { + // Observe user interactions with views + effect(() => { + const state = this.uiStore.getSnapshot(); + // React to checkbox toggles, button clicks, form submissions + }); + } +} +``` + +### Event Handlers + +For actions beyond state updates (e.g., "submit form", "approve action"), views can use json-render's event system: + +```json +{ + "type": "approval-form", + "props": { "email": { "$state": "/draft" } }, + "on": { + "approve": { "action": "submitApproval" }, + "cancel": { "action": "cancelDraft" } + } +} +``` + +The chat component exposes an `(action)` output for these events: + +```typescript +<chat + [ref]="stream" + [views]="ui" + [store]="uiStore" + (action)="onViewAction($event)" +/> +``` + +Where `$event` is `{ name: string; params: Record<string, unknown> }`. + +--- + +## Per-Example Application + +### How each cockpit example benefits: + +| Example | Current | With Generative UI | +|---------|---------|-------------------| +| **Streaming** | Plain text chat | No change needed (no tool calls) | +| **Persistence** | Custom thread sidebar | Thread list could be a view in the sidebar slot | +| **Interrupts** | Static interrupt panel | `approval-form` view with editable fields + approve/cancel | +| **Memory** | Custom facts sidebar | `fact-list` view rendered from state, reactive updates | +| **Durable Execution** | Custom pipeline sidebar | `step-pipeline` view with animated progress | +| **Subgraphs** | "No subagents active" text | `subagent-card` views showing nested message streams | +| **Time Travel** | Custom timeline | `checkpoint-timeline` view with replay/fork actions | +| **Planning** | Plain text plan | `plan-checklist` with interactive checkboxes | +| **Filesystem** | Custom file ops log | `file-preview` view with syntax-highlighted content | +| **Skills** | Custom skill log | `tool-result-card` view per skill invocation | +| **Sandboxes** | Custom execution log | `code-execution` view with stdout/stderr panels | +| **DA-Subagents** | Custom delegation tracker | `delegation-card` views with live status | +| **DA-Memory** | Custom facts sidebar | Same as LG memory — `fact-list` view | + +### Priority for implementation: + +1. **Planning** — interactive checklist (highest demo value) +2. **Skills** — tool result cards (validates the tool call → view pipeline) +3. **Interrupts** — approval form (shows interactivity + state store) +4. **Filesystem** — file preview cards (validates nested composition) + +--- + +## Files Changed + +### New files +- `libs/render/src/lib/views.ts` — `views()`, `withViews()`, `withoutViews()`, `provideViews()`, `VIEW_REGISTRY` token +- `libs/render/src/lib/views.spec.ts` — tests for view registry operations + +### Modified files +- `libs/render/src/public-api.ts` — export new views API +- `libs/chat/src/lib/compositions/chat/chat.component.ts` — add `[views]`, `[store]`, `(action)` inputs/outputs, detect specs, render inline +- `libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts` — detect UI specs in messages, render via `<render-spec>` +- `libs/chat/src/lib/provide-chat.ts` — add `views` to `ChatConfig` +- `libs/chat/src/public-api.ts` — re-export views API from render + +### Unchanged +- `@json-render/core` — no changes needed, existing spec format supports everything +- `libs/render/src/lib/` — existing `RenderSpecComponent`, `RenderElementComponent`, `signalStateStore()` used as-is + +--- + +## Key Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| API naming | `views()` not `components()` | "View" = what the user sees (SwiftUI precedent). "Component" collides with Angular's `@Component`. | +| Composition | Object spread | No special merge functions. JavaScript developers already know spread. Last key wins for override. | +| Spec source | Backend produces full spec | Agent controls the UI. Frontend is a pure renderer. | +| State management | Consumer owns `signalStateStore()` | Most flexible. Consumer observes changes and decides actions. | +| Registry level | High-level `views()` wraps low-level `defineAngularRegistry()` | Clean abstraction. Power users can still use the low-level API. | +| Detection | `ui` field in tool results / state / message metadata | Convention-based. No new protocol. Backend just includes `ui` in its output. | +| Strip "Angular" from API | Yes | The whole library is Angular. Saying it is redundant. | From 313250aa015a7e0ec2ea6dd29b2c3575c66f0b28 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:32:53 -0700 Subject: [PATCH 05/17] docs: add generative UI views implementation plan --- .../plans/2026-04-07-generative-ui-views.md | 622 ++++++++++++++++++ 1 file changed, 622 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-07-generative-ui-views.md diff --git a/docs/superpowers/plans/2026-04-07-generative-ui-views.md b/docs/superpowers/plans/2026-04-07-generative-ui-views.md new file mode 100644 index 000000000..deb3698b5 --- /dev/null +++ b/docs/superpowers/plans/2026-04-07-generative-ui-views.md @@ -0,0 +1,622 @@ +# Generative UI Views System — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a `views()` functional API to `@cacheplane/render` and wire it into `@cacheplane/chat` so agents can produce JSON specs that render as interactive Angular components inline in the chat. + +**Architecture:** `views()` creates an immutable registry mapping names to Angular components. The chat component detects UI specs in messages (tool results, state, metadata), converts the view registry to an `AngularRegistry`, and passes specs to `<render-spec>` for recursive rendering. `signalStateStore()` handles two-way interactivity. + +**Tech Stack:** Angular 20+, `@json-render/core`, `@cacheplane/render`, `@cacheplane/chat`, Vitest + +--- + +## File Structure + +### New files +- `libs/render/src/lib/views.ts` — `views()`, `withViews()`, `withoutViews()`, `ViewRegistry` type +- `libs/render/src/lib/views.spec.ts` — unit tests for view registry operations +- `libs/render/src/lib/provide-views.ts` — `provideViews()`, `VIEW_REGISTRY` token + +### Modified files +- `libs/render/src/public-api.ts` — export new views API +- `libs/chat/src/lib/provide-chat.ts` — add `views` to `ChatConfig` +- `libs/chat/src/lib/compositions/chat/chat.component.ts` — add `[views]`, `[store]`, `(action)` inputs/outputs, render inline specs +- `libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts` — add `[views]`, `[store]` inputs, detect and render UI specs per message +- `libs/chat/src/public-api.ts` — re-export views API + +--- + +## Task 1: views() Functional API + +**Files:** +- Create: `libs/render/src/lib/views.ts` +- Create: `libs/render/src/lib/views.spec.ts` + +- [ ] **Step 1: Write failing tests for views(), withViews(), withoutViews()** + +```typescript +// libs/render/src/lib/views.spec.ts +import { describe, it, expect } from 'vitest'; +import { Component } from '@angular/core'; +import { views, withViews, withoutViews } from './views'; + +@Component({ selector: 'test-a', standalone: true, template: 'A' }) +class CompA {} + +@Component({ selector: 'test-b', standalone: true, template: 'B' }) +class CompB {} + +@Component({ selector: 'test-c', standalone: true, template: 'C' }) +class CompC {} + +describe('views()', () => { + it('creates a frozen registry from a map', () => { + const reg = views({ 'a': CompA, 'b': CompB }); + expect(reg['a']).toBe(CompA); + expect(reg['b']).toBe(CompB); + expect(Object.isFrozen(reg)).toBe(true); + }); + + it('composes via spread (last key wins)', () => { + const base = views({ 'a': CompA }); + const override = views({ ...base, 'a': CompB }); + expect(override['a']).toBe(CompB); + }); +}); + +describe('withViews()', () => { + it('adds new entries without overwriting existing', () => { + const base = views({ 'a': CompA }); + const extended = withViews(base, { 'b': CompB, 'a': CompC }); + expect(extended['a']).toBe(CompA); // preserved + expect(extended['b']).toBe(CompB); // added + }); + + it('returns a frozen registry', () => { + const result = withViews(views({}), { 'a': CompA }); + expect(Object.isFrozen(result)).toBe(true); + }); +}); + +describe('withoutViews()', () => { + it('removes named entries', () => { + const base = views({ 'a': CompA, 'b': CompB, 'c': CompC }); + const result = withoutViews(base, 'b', 'c'); + expect(result['a']).toBe(CompA); + expect(result['b']).toBeUndefined(); + expect(result['c']).toBeUndefined(); + }); + + it('returns a frozen registry', () => { + const result = withoutViews(views({ 'a': CompA }), 'a'); + expect(Object.isFrozen(result)).toBe(true); + }); +}); +``` + +- [ ] **Step 2: Run tests to verify they fail** + +```bash +npx vitest run --config libs/render/vite.config.mts +``` + +Expected: FAIL — `views`, `withViews`, `withoutViews` not found. + +- [ ] **Step 3: Implement views(), withViews(), withoutViews()** + +```typescript +// libs/render/src/lib/views.ts +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Type } from '@angular/core'; +import type { AngularRegistry } from './render.types'; +import { defineAngularRegistry } from './define-angular-registry'; + +/** + * A registry of view components available for generative UI rendering. + * Plain frozen object mapping view names to Angular component types. + * Compose via object spread: `views({ ...base, ...more })`. + */ +export type ViewRegistry = Readonly<Record<string, Type<unknown>>>; + +/** + * Creates a view registry from a name → component map. + * + * @example + * ```typescript + * const ui = views({ + * 'plan-checklist': PlanChecklistComponent, + * 'file-preview': FilePreviewComponent, + * }); + * ``` + */ +export function views(map: Record<string, Type<unknown>>): ViewRegistry { + return Object.freeze({ ...map }); +} + +/** + * Adds views to a registry without overwriting existing entries. + * New keys are added; keys that already exist in `base` are preserved. + * + * @example + * ```typescript + * const extended = withViews(base, { 'chart': ChartComponent }); + * ``` + */ +export function withViews( + base: ViewRegistry, + additions: Record<string, Type<unknown>>, +): ViewRegistry { + return Object.freeze({ ...additions, ...base }); +} + +/** + * Removes views from a registry by name. + * + * @example + * ```typescript + * const restricted = withoutViews(base, 'file-preview', 'code-output'); + * ``` + */ +export function withoutViews( + base: ViewRegistry, + ...names: string[] +): ViewRegistry { + const result = { ...base }; + for (const name of names) delete result[name]; + return Object.freeze(result); +} + +/** + * Converts a ViewRegistry to an AngularRegistry for use with RenderSpecComponent. + * This is the bridge between the high-level views API and the low-level render API. + */ +export function toRenderRegistry(registry: ViewRegistry): AngularRegistry { + return defineAngularRegistry(registry); +} +``` + +- [ ] **Step 4: Run tests to verify they pass** + +```bash +npx vitest run --config libs/render/vite.config.mts +``` + +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +git add libs/render/src/lib/views.ts libs/render/src/lib/views.spec.ts +git commit -m "feat(render): add views() functional API for generative UI registry" +``` + +--- + +## Task 2: provideViews() DI Provider + +**Files:** +- Create: `libs/render/src/lib/provide-views.ts` +- Modify: `libs/render/src/public-api.ts` + +- [ ] **Step 1: Create provideViews() and VIEW_REGISTRY token** + +```typescript +// libs/render/src/lib/provide-views.ts +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { InjectionToken, makeEnvironmentProviders } from '@angular/core'; +import type { ViewRegistry } from './views'; + +/** + * Injection token for providing a ViewRegistry globally or at route level. + */ +export const VIEW_REGISTRY = new InjectionToken<ViewRegistry>('VIEW_REGISTRY'); + +/** + * Provides a ViewRegistry via Angular dependency injection. + * Use at app level for global views, or at route level for scoped views. + * + * @example + * ```typescript + * // app.config.ts + * providers: [provideViews(agentViews)] + * + * // Route-level + * { path: 'planning', providers: [provideViews(planningViews)] } + * ``` + */ +export function provideViews(registry: ViewRegistry) { + return makeEnvironmentProviders([ + { provide: VIEW_REGISTRY, useValue: registry }, + ]); +} +``` + +- [ ] **Step 2: Export from public-api.ts** + +Add these lines to `libs/render/src/public-api.ts`: + +```typescript +// Views +export { views, withViews, withoutViews, toRenderRegistry } from './lib/views'; +export type { ViewRegistry } from './lib/views'; +export { provideViews, VIEW_REGISTRY } from './lib/provide-views'; +``` + +- [ ] **Step 3: Run render library tests** + +```bash +npx nx test render +``` + +Expected: All tests pass. + +- [ ] **Step 4: Commit** + +```bash +git add libs/render/src/lib/provide-views.ts libs/render/src/public-api.ts +git commit -m "feat(render): add provideViews() DI provider and VIEW_REGISTRY token" +``` + +--- + +## Task 3: Chat Integration — Detect and Render UI Specs + +**Files:** +- Modify: `libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts` +- Modify: `libs/chat/src/lib/compositions/chat/chat.component.ts` +- Modify: `libs/chat/src/lib/provide-chat.ts` + +- [ ] **Step 1: Add spec detection utility** + +Add to the bottom of `libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts`: + +```typescript +/** + * Extracts a UI spec from a message if present. + * Checks the message's `ui` field and `additional_kwargs.ui` field. + */ +export function getUiSpec(message: BaseMessage): unknown | null { + const msg = message as unknown as Record<string, unknown>; + + // Check direct ui field + if (msg['ui'] && isValidSpec(msg['ui'])) { + return msg['ui']; + } + + // Check additional_kwargs.ui + const kwargs = msg['additional_kwargs'] as Record<string, unknown> | undefined; + if (kwargs?.['ui'] && isValidSpec(kwargs['ui'])) { + return kwargs['ui']; + } + + return null; +} + +function isValidSpec(value: unknown): boolean { + return typeof value === 'object' + && value !== null + && 'root' in (value as Record<string, unknown>) + && 'elements' in (value as Record<string, unknown>); +} +``` + +- [ ] **Step 2: Add views and store inputs to ChatMessagesComponent** + +Update `ChatMessagesComponent` to accept `views` and `store` inputs and render specs inline: + +```typescript +// Add imports +import type { ViewRegistry } from '@cacheplane/render'; +import type { StateStore } from '@json-render/core'; +import { RenderSpecComponent } from '@cacheplane/render'; +import { toRenderRegistry } from '@cacheplane/render'; + +// Update @Component +@Component({ + selector: 'chat-messages', + standalone: true, + imports: [NgTemplateOutlet, MessageTemplateDirective, RenderSpecComponent], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + @for (message of messages(); track $index) { + @let template = findTemplate(getMessageType(message)); + @if (template) { + <ng-container + [ngTemplateOutlet]="template.templateRef" + [ngTemplateOutletContext]="{ $implicit: message, index: $index }" + /> + } + + @if (renderRegistry() && getUiSpec(message); as spec) { + <div class="ml-10 mt-2"> + <render-spec + [spec]="$any(spec)" + [registry]="renderRegistry()!" + [store]="store()" + /> + </div> + } + } + `, +}) +export class ChatMessagesComponent { + readonly ref = input.required<AgentRef<any, any>>(); + readonly views = input<ViewRegistry | undefined>(undefined); + readonly store = input<StateStore | undefined>(undefined); + + readonly messageTemplates = contentChildren(MessageTemplateDirective); + readonly messages = computed(() => this.ref().messages()); + readonly getMessageType = getMessageType; + readonly getUiSpec = getUiSpec; + + /** Convert ViewRegistry to AngularRegistry for render-spec */ + readonly renderRegistry = computed(() => { + const v = this.views(); + return v ? toRenderRegistry(v) : undefined; + }); + + findTemplate(type: MessageTemplateType): MessageTemplateDirective | undefined { + return this.messageTemplates().find(t => t.chatMessageTemplate() === type); + } +} +``` + +- [ ] **Step 3: Wire views and store through ChatComponent** + +In `libs/chat/src/lib/compositions/chat/chat.component.ts`, add inputs and pass them to `<chat-messages>`: + +```typescript +// Add imports +import type { ViewRegistry } from '@cacheplane/render'; +import type { StateStore } from '@json-render/core'; +import { VIEW_REGISTRY } from '@cacheplane/render'; + +// Add inputs to class +readonly views = input<ViewRegistry | undefined>(undefined); +readonly store = input<StateStore | undefined>(undefined); +readonly action = output<{ name: string; params: Record<string, unknown> }>(); + +// Inject DI-provided registry as fallback +private readonly diViews = inject(VIEW_REGISTRY, { optional: true }); + +// Resolved registry: input takes precedence over DI +private readonly resolvedViews = computed(() => + this.views() ?? this.diViews ?? undefined +); +``` + +Update the template's `<chat-messages>` tag: + +```html +<chat-messages + [ref]="ref()" + [views]="resolvedViews()" + [store]="store()" +> +``` + +- [ ] **Step 4: Update ChatConfig to accept views** + +In `libs/chat/src/lib/provide-chat.ts`, update the interface: + +```typescript +import type { ViewRegistry } from '@cacheplane/render'; + +export interface ChatConfig { + /** View registry for generative UI rendering. */ + views?: ViewRegistry; + /** Override the default AI avatar label (default: "A"). */ + avatarLabel?: string; + /** Override the default assistant display name (default: "Assistant"). */ + assistantName?: string; +} +``` + +- [ ] **Step 5: Run tests** + +```bash +npx nx test chat && npx nx test render +``` + +Expected: All tests pass. + +- [ ] **Step 6: Build to verify compilation** + +```bash +npx nx build chat +``` + +Expected: Build succeeds. + +- [ ] **Step 7: Commit** + +```bash +git add libs/chat/src/lib/ libs/render/src/ +git commit -m "feat(chat): integrate views registry for inline generative UI rendering" +``` + +--- + +## Task 4: Update Public API Exports + +**Files:** +- Modify: `libs/chat/src/public-api.ts` + +- [ ] **Step 1: Re-export views API from chat library** + +Add to `libs/chat/src/public-api.ts`: + +```typescript +// Views (re-exported from @cacheplane/render for convenience) +export { views, withViews, withoutViews, toRenderRegistry } from '@cacheplane/render'; +export type { ViewRegistry } from '@cacheplane/render'; +export { provideViews, VIEW_REGISTRY } from '@cacheplane/render'; +``` + +- [ ] **Step 2: Export getUiSpec utility** + +```typescript +export { getUiSpec } from './lib/primitives/chat-messages/chat-messages.component'; +``` + +- [ ] **Step 3: Build to verify** + +```bash +npx nx build chat +``` + +Expected: Build succeeds. + +- [ ] **Step 4: Commit** + +```bash +git add libs/chat/src/public-api.ts +git commit -m "feat(chat): export views API and getUiSpec from public API" +``` + +--- + +## Task 5: Planning Example — Interactive Checklist View + +**Files:** +- Create: `cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts` +- Create: `cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts` +- Modify: `cockpit/deep-agents/planning/angular/src/app/planning.component.ts` + +This task validates the full pipeline: views() → agent spec → inline rendering. + +- [ ] **Step 1: Create PlanChecklistComponent view** + +```typescript +// cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'plan-checklist', + standalone: true, + template: ` + <div class="border rounded-xl p-4 my-2" style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <h4 class="text-sm font-semibold mb-2" style="color: var(--chat-text);">{{ title() }}</h4> + <ng-content /> + </div> + `, +}) +export class PlanChecklistComponent { + readonly title = input<string>('Plan'); +} +``` + +- [ ] **Step 2: Create CheckboxRowComponent view** + +```typescript +// cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts +import { Component, input, inject } from '@angular/core'; +import { RENDER_CONTEXT } from '@cacheplane/render'; + +@Component({ + selector: 'checkbox-row', + standalone: true, + template: ` + <label class="flex items-center gap-2 py-1 cursor-pointer text-sm" style="color: var(--chat-text);"> + <input + type="checkbox" + [checked]="checked()" + (change)="toggle()" + class="w-4 h-4" + /> + <span [class.line-through]="checked()" [style.opacity]="checked() ? '0.5' : '1'"> + {{ label() }} + </span> + </label> + `, +}) +export class CheckboxRowComponent { + readonly label = input<string>(''); + readonly checked = input<boolean>(false); + readonly emit = input<(event: string) => void>(() => {}); + + toggle(): void { + this.emit()('toggle'); + } +} +``` + +- [ ] **Step 3: Update PlanningComponent to use views** + +```typescript +// cockpit/deep-agents/planning/angular/src/app/planning.component.ts +import { Component } from '@angular/core'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; +import { agent } from '@cacheplane/angular'; +import { environment } from '../environments/environment'; +import { PlanChecklistComponent } from './views/plan-checklist.component'; +import { CheckboxRowComponent } from './views/checkbox-row.component'; + +@Component({ + selector: 'app-planning', + standalone: true, + imports: [ChatComponent], + template: `<chat [ref]="stream" [views]="ui" [store]="uiStore" class="block h-screen" />`, +}) +export class PlanningComponent { + protected readonly stream = agent({ + apiUrl: environment.langGraphApiUrl, + assistantId: environment.planningAssistantId, + }); + + readonly ui = views({ + 'plan-checklist': PlanChecklistComponent, + 'checkbox-row': CheckboxRowComponent, + }); + + readonly uiStore = signalStateStore({}); +} +``` + +- [ ] **Step 4: Build to verify** + +```bash +npx nx build cockpit-deep-agents-planning-angular +``` + +Expected: Build succeeds. + +- [ ] **Step 5: Commit** + +```bash +git add cockpit/deep-agents/planning/angular/src/app/ +git commit -m "feat(cockpit): add planning example with generative UI views" +``` + +--- + +## Task 6: Build All + Final Verification + +- [ ] **Step 1: Run all library tests** + +```bash +npx nx test render && npx nx test chat +``` + +Expected: All tests pass. + +- [ ] **Step 2: Build all libraries** + +```bash +npx nx build render && npx nx build chat +``` + +Expected: Both build successfully. + +- [ ] **Step 3: Build all cockpit examples** + +```bash +npx nx run-many -t build --projects='cockpit-*-angular' +``` + +Expected: All 14 examples build. + +- [ ] **Step 4: Commit any fixes** + +If any builds fail, fix and commit. From 0decf78ad8ef39677f803d408530a468294db346 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:34:53 -0700 Subject: [PATCH 06/17] feat(render): add views() functional API for generative UI registry --- libs/render/src/lib/views.spec.ts | 79 +++++++++++++++++++++++++++++++ libs/render/src/lib/views.ts | 48 +++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 libs/render/src/lib/views.spec.ts create mode 100644 libs/render/src/lib/views.ts diff --git a/libs/render/src/lib/views.spec.ts b/libs/render/src/lib/views.spec.ts new file mode 100644 index 000000000..695d5fe51 --- /dev/null +++ b/libs/render/src/lib/views.spec.ts @@ -0,0 +1,79 @@ +import { describe, it, expect } from 'vitest'; +import { Component } from '@angular/core'; +import { views, withViews, withoutViews, toRenderRegistry } from './views'; + +@Component({ selector: 'test-a', standalone: true, template: 'A' }) +class CompA {} + +@Component({ selector: 'test-b', standalone: true, template: 'B' }) +class CompB {} + +@Component({ selector: 'test-c', standalone: true, template: 'C' }) +class CompC {} + +describe('views()', () => { + it('creates a frozen registry from a map', () => { + const reg = views({ 'a': CompA, 'b': CompB }); + expect(reg['a']).toBe(CompA); + expect(reg['b']).toBe(CompB); + expect(Object.isFrozen(reg)).toBe(true); + }); + + it('returns empty frozen object for empty input', () => { + const reg = views({}); + expect(Object.keys(reg)).toHaveLength(0); + expect(Object.isFrozen(reg)).toBe(true); + }); + + it('composes via spread (last key wins)', () => { + const base = views({ 'a': CompA }); + const override = views({ ...base, 'a': CompB }); + expect(override['a']).toBe(CompB); + }); +}); + +describe('withViews()', () => { + it('adds new entries without overwriting existing', () => { + const base = views({ 'a': CompA }); + const extended = withViews(base, { 'b': CompB, 'a': CompC }); + expect(extended['a']).toBe(CompA); + expect(extended['b']).toBe(CompB); + }); + + it('returns a frozen registry', () => { + const result = withViews(views({}), { 'a': CompA }); + expect(Object.isFrozen(result)).toBe(true); + }); +}); + +describe('withoutViews()', () => { + it('removes named entries', () => { + const base = views({ 'a': CompA, 'b': CompB, 'c': CompC }); + const result = withoutViews(base, 'b', 'c'); + expect(result['a']).toBe(CompA); + expect(result['b']).toBeUndefined(); + expect(result['c']).toBeUndefined(); + }); + + it('returns a frozen registry', () => { + const result = withoutViews(views({ 'a': CompA }), 'a'); + expect(Object.isFrozen(result)).toBe(true); + }); + + it('handles removing non-existent keys gracefully', () => { + const base = views({ 'a': CompA }); + const result = withoutViews(base, 'nonexistent'); + expect(result['a']).toBe(CompA); + }); +}); + +describe('toRenderRegistry()', () => { + it('converts ViewRegistry to AngularRegistry', () => { + const reg = views({ 'a': CompA, 'b': CompB }); + const renderReg = toRenderRegistry(reg); + expect(renderReg.get('a')).toBe(CompA); + expect(renderReg.get('b')).toBe(CompB); + expect(renderReg.names()).toContain('a'); + expect(renderReg.names()).toContain('b'); + }); +}); diff --git a/libs/render/src/lib/views.ts b/libs/render/src/lib/views.ts new file mode 100644 index 000000000..1d1d877a7 --- /dev/null +++ b/libs/render/src/lib/views.ts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Type } from '@angular/core'; +import type { AngularRegistry } from './render.types'; +import { defineAngularRegistry } from './define-angular-registry'; + +/** + * A registry of view components available for generative UI rendering. + * Plain frozen object mapping view names to Angular component types. + * Compose via object spread: `views({ ...base, ...more })`. + */ +export type ViewRegistry = Readonly<Record<string, Type<unknown>>>; + +/** + * Creates a view registry from a name → component map. + */ +export function views(map: Record<string, Type<unknown>>): ViewRegistry { + return Object.freeze({ ...map }); +} + +/** + * Adds views to a registry without overwriting existing entries. + * New keys are added; keys that already exist in `base` are preserved. + */ +export function withViews( + base: ViewRegistry, + additions: Record<string, Type<unknown>>, +): ViewRegistry { + return Object.freeze({ ...additions, ...base }); +} + +/** + * Removes views from a registry by name. + */ +export function withoutViews( + base: ViewRegistry, + ...names: string[] +): ViewRegistry { + const result = { ...base }; + for (const name of names) delete result[name]; + return Object.freeze(result); +} + +/** + * Converts a ViewRegistry to an AngularRegistry for use with RenderSpecComponent. + */ +export function toRenderRegistry(registry: ViewRegistry): AngularRegistry { + return defineAngularRegistry(registry); +} From 8fe2e9609ccf16984d45e0d7848d4c56185ca71e Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:36:01 -0700 Subject: [PATCH 07/17] feat(render): add provideViews() DI provider and export views API --- libs/render/src/lib/provide-views.ts | 11 +++++++++++ libs/render/src/public-api.ts | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 libs/render/src/lib/provide-views.ts diff --git a/libs/render/src/lib/provide-views.ts b/libs/render/src/lib/provide-views.ts new file mode 100644 index 000000000..e5b49f588 --- /dev/null +++ b/libs/render/src/lib/provide-views.ts @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { InjectionToken, makeEnvironmentProviders } from '@angular/core'; +import type { ViewRegistry } from './views'; + +export const VIEW_REGISTRY = new InjectionToken<ViewRegistry>('VIEW_REGISTRY'); + +export function provideViews(registry: ViewRegistry) { + return makeEnvironmentProviders([ + { provide: VIEW_REGISTRY, useValue: registry }, + ]); +} diff --git a/libs/render/src/public-api.ts b/libs/render/src/public-api.ts index 690e3454a..3a9b438de 100644 --- a/libs/render/src/public-api.ts +++ b/libs/render/src/public-api.ts @@ -26,3 +26,8 @@ export { provideRender, RENDER_CONFIG } from './lib/provide-render'; // Components export { RenderElementComponent } from './lib/render-element.component'; export { RenderSpecComponent } from './lib/render-spec.component'; + +// Views +export { views, withViews, withoutViews, toRenderRegistry } from './lib/views'; +export type { ViewRegistry } from './lib/views'; +export { provideViews, VIEW_REGISTRY } from './lib/provide-views'; From ca9e56305bb4d89ebf4d866ec68993c4f7fe81b6 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:38:40 -0700 Subject: [PATCH 08/17] feat(chat): integrate views registry for inline generative UI rendering --- .../lib/compositions/chat/chat.component.ts | 16 ++++++- .../chat-messages/chat-messages.component.ts | 43 ++++++++++++++++++- libs/chat/src/lib/provide-chat.ts | 6 +-- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/libs/chat/src/lib/compositions/chat/chat.component.ts b/libs/chat/src/lib/compositions/chat/chat.component.ts index b895bd1e3..4bee63c4f 100644 --- a/libs/chat/src/lib/compositions/chat/chat.component.ts +++ b/libs/chat/src/lib/compositions/chat/chat.component.ts @@ -14,6 +14,9 @@ import { } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import type { AgentRef } from '@cacheplane/angular'; +import type { ViewRegistry } from '@cacheplane/render'; +import type { StateStore } from '@json-render/core'; +import { VIEW_REGISTRY } from '@cacheplane/render'; import { ChatMessagesComponent } from '../../primitives/chat-messages/chat-messages.component'; import { MessageTemplateDirective } from '../../primitives/chat-messages/message-template.directive'; import { ChatInputComponent } from '../../primitives/chat-input/chat-input.component'; @@ -96,7 +99,7 @@ import { CHAT_MARKDOWN_STYLES, renderMarkdown } from '../../styles/chat-markdown </div> } - <chat-messages [ref]="ref()"> + <chat-messages [ref]="ref()" [views]="resolvedViews()" [store]="store()"> <!-- Human messages: right-aligned bubble --> <ng-template chatMessageTemplate="human" let-message> <div class="flex justify-end"> @@ -176,11 +179,22 @@ export class ChatComponent { private readonly sanitizer = inject(DomSanitizer); readonly ref = input.required<AgentRef<any, any>>(); + readonly views = input<ViewRegistry | undefined>(undefined); + readonly store = input<StateStore | undefined>(undefined); readonly threads = input<Thread[]>([]); readonly activeThreadId = input<string>(''); readonly threadSelected = output<string>(); + readonly action = output<{ name: string; params: Record<string, unknown> }>(); readonly sidebarOpen = signal(false); + // Inject DI-provided registry as fallback + private readonly diViews = inject(VIEW_REGISTRY, { optional: true }); + + // Resolved registry: input takes precedence over DI + protected readonly resolvedViews = computed(() => + this.views() ?? this.diViews ?? undefined + ); + readonly messageContent = messageContent; private readonly scrollContainer = viewChild<ElementRef<HTMLElement>>('scrollContainer'); diff --git a/libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts b/libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts index 2ab8c405e..b2ba08f55 100644 --- a/libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts +++ b/libs/chat/src/lib/primitives/chat-messages/chat-messages.component.ts @@ -9,6 +9,9 @@ import { import { NgTemplateOutlet } from '@angular/common'; import type { BaseMessage } from '@langchain/core/messages'; import type { AgentRef } from '@cacheplane/angular'; +import type { ViewRegistry } from '@cacheplane/render'; +import type { StateStore } from '@json-render/core'; +import { RenderSpecComponent, toRenderRegistry } from '@cacheplane/render'; import { MessageTemplateDirective } from './message-template.directive'; import type { MessageTemplateType } from '../../chat.types'; @@ -42,7 +45,7 @@ export function getMessageType(message: BaseMessage): MessageTemplateType { @Component({ selector: 'chat-messages', standalone: true, - imports: [NgTemplateOutlet, MessageTemplateDirective], + imports: [NgTemplateOutlet, MessageTemplateDirective, RenderSpecComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: ` @for (message of messages(); track $index) { @@ -53,19 +56,57 @@ export function getMessageType(message: BaseMessage): MessageTemplateType { [ngTemplateOutletContext]="{ $implicit: message, index: $index }" /> } + + @if (renderRegistry() && getUiSpec(message); as spec) { + <div class="ml-10 mt-2"> + <render-spec + [spec]="$any(spec)" + [registry]="renderRegistry()!" + [store]="store()" + /> + </div> + } } `, }) export class ChatMessagesComponent { readonly ref = input.required<AgentRef<any, any>>(); + readonly views = input<ViewRegistry | undefined>(undefined); + readonly store = input<StateStore | undefined>(undefined); readonly messageTemplates = contentChildren(MessageTemplateDirective); readonly messages = computed(() => this.ref().messages()); readonly getMessageType = getMessageType; + readonly getUiSpec = getUiSpec; + + /** Convert ViewRegistry to AngularRegistry for render-spec */ + readonly renderRegistry = computed(() => { + const v = this.views(); + return v ? toRenderRegistry(v) : undefined; + }); findTemplate(type: MessageTemplateType): MessageTemplateDirective | undefined { return this.messageTemplates().find(t => t.chatMessageTemplate() === type); } } + +/** + * Extracts a UI spec from a message if present. + * Checks the message's `ui` field and `additional_kwargs.ui` field. + */ +export function getUiSpec(message: BaseMessage): unknown | null { + const msg = message as unknown as Record<string, unknown>; + if (msg['ui'] && isValidSpec(msg['ui'])) return msg['ui']; + const kwargs = msg['additional_kwargs'] as Record<string, unknown> | undefined; + if (kwargs?.['ui'] && isValidSpec(kwargs['ui'])) return kwargs['ui']; + return null; +} + +function isValidSpec(value: unknown): boolean { + return typeof value === 'object' + && value !== null + && 'root' in (value as Record<string, unknown>) + && 'elements' in (value as Record<string, unknown>); +} diff --git a/libs/chat/src/lib/provide-chat.ts b/libs/chat/src/lib/provide-chat.ts index 9ce65b02c..a0d05b591 100644 --- a/libs/chat/src/lib/provide-chat.ts +++ b/libs/chat/src/lib/provide-chat.ts @@ -1,10 +1,10 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { InjectionToken, makeEnvironmentProviders } from '@angular/core'; -import type { AngularRegistry } from '@cacheplane/render'; +import type { ViewRegistry } from '@cacheplane/render'; export interface ChatConfig { - /** Default render registry for generative UI components. */ - renderRegistry?: AngularRegistry; + /** View registry for generative UI rendering. */ + views?: ViewRegistry; /** Override the default AI avatar label (default: "A"). */ avatarLabel?: string; /** Override the default assistant display name (default: "Assistant"). */ From c0661bcffea1837c4dae9a905b3da49b09754e48 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:40:43 -0700 Subject: [PATCH 09/17] feat(chat): export views API and getUiSpec from public API --- libs/chat/src/public-api.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/chat/src/public-api.ts b/libs/chat/src/public-api.ts index 591bf6ac0..8faf6a48a 100644 --- a/libs/chat/src/public-api.ts +++ b/libs/chat/src/public-api.ts @@ -51,5 +51,13 @@ export { ICON_WARNING, ICON_AGENT, ICON_CHECK, ICON_SEND, } from './lib/styles/chat-icons'; +// Views (re-exported from @cacheplane/render for convenience) +export { views, withViews, withoutViews, toRenderRegistry } from '@cacheplane/render'; +export type { ViewRegistry } from '@cacheplane/render'; +export { provideViews, VIEW_REGISTRY } from '@cacheplane/render'; + +// Spec detection +export { getUiSpec } from './lib/primitives/chat-messages/chat-messages.component'; + // Test utilities export { createMockAgentRef } from './lib/testing/mock-agent-ref'; From fa43671900a29b2afc8b205417355fc5bb1a10a5 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 15:40:47 -0700 Subject: [PATCH 10/17] feat(cockpit): add planning example with generative UI views --- .../angular/src/app/planning.component.ts | 86 +++---------------- .../src/app/views/checkbox-row.component.ts | 28 ++++++ .../src/app/views/plan-checklist.component.ts | 15 ++++ .../environments/environment.development.ts | 1 + .../angular/src/environments/environment.ts | 1 + 5 files changed, 57 insertions(+), 74 deletions(-) create mode 100644 cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts create mode 100644 cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts 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 4678db670..669d7d2cf 100644 --- a/cockpit/deep-agents/planning/angular/src/app/planning.component.ts +++ b/cockpit/deep-agents/planning/angular/src/app/planning.component.ts @@ -1,89 +1,27 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +import { Component } from '@angular/core'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { PlanChecklistComponent } from './views/plan-checklist.component'; +import { CheckboxRowComponent } from './views/checkbox-row.component'; -/** - * Represents a single step in an agent-generated plan. - */ -interface PlanStep { - title: string; - status: 'pending' | 'in_progress' | 'complete'; -} - -/** - * PlanningComponent demonstrates agent task decomposition. - * - * The agent receives a complex task, breaks it into ordered steps, - * and executes them sequentially. The sidebar displays a live checklist - * of plan steps derived from the `plan` array in the graph state. - * - * Key integration points: - * - `stream.value()` exposes the full graph state, including the `plan` array - * - `planSteps` is derived from `stream.value()` for reactive sidebar rendering - * - Step status icons update in real time as the agent progresses - */ @Component({ selector: 'app-planning', standalone: true, imports: [ChatComponent], - template: ` - <div class="flex h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> - <aside class="w-72 shrink-0 border-l overflow-y-auto p-4 space-y-2" - style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> - <h3 class="text-xs font-semibold uppercase tracking-wide" - style="color: var(--chat-text-muted, #777);">Plan</h3> - @if (planSteps().length === 0) { - <p class="text-sm italic" style="color: var(--chat-text-muted, #777);">No plan yet</p> - } - @for (step of planSteps(); track step.title) { - <div class="flex items-start gap-2 rounded-md px-2 py-1.5 text-sm" - style="background: var(--chat-bg-hover, #222);"> - <span class="mt-0.5 shrink-0 text-base leading-none"> - @if (step.status === 'complete') { - <span style="color: #22c55e;">✓</span> - } @else if (step.status === 'in_progress') { - <span class="inline-block animate-spin" style="color: var(--chat-text-muted, #777);">◠</span> - } @else { - <span style="color: var(--chat-border-light, #444);">○</span> - } - </span> - <span style="color: var(--chat-text, #e0e0e0);">{{ step.title }}</span> - </div> - } - </aside> - </div> - `, + template: `<chat [ref]="stream" [views]="ui" [store]="uiStore" class="block h-screen" />`, }) export class PlanningComponent { - /** - * The streaming resource connected to the planning graph. - * - * The graph returns a `plan` array alongside messages in its state. - * Each plan entry has a `title` and `status` that drive the sidebar checklist. - */ protected readonly stream = agent({ apiUrl: environment.langGraphApiUrl, - assistantId: environment.streamingAssistantId, + assistantId: environment.planningAssistantId, }); - /** - * Reactive list of plan steps derived from the graph state. - * - * The Python graph stores the plan as `state.plan` — an array of objects - * with `title` and `status` fields. This signal re-computes whenever - * the stream state changes. - */ - protected readonly planSteps = computed<PlanStep[]>(() => { - const val = this.stream.value() as Record<string, unknown>; - const plan = val?.['plan']; - if (!Array.isArray(plan)) return []; - return plan.map((step: Record<string, unknown>) => ({ - title: String(step['title'] ?? ''), - status: (['pending', 'in_progress', 'complete'].includes(step['status'] as string) - ? step['status'] - : 'pending') as PlanStep['status'], - })); + readonly ui = views({ + 'plan-checklist': PlanChecklistComponent, + 'checkbox-row': CheckboxRowComponent, }); + + readonly uiStore = signalStateStore({}); } diff --git a/cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts b/cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts new file mode 100644 index 000000000..941efbe87 --- /dev/null +++ b/cockpit/deep-agents/planning/angular/src/app/views/checkbox-row.component.ts @@ -0,0 +1,28 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'checkbox-row', + standalone: true, + template: ` + <label class="flex items-center gap-2 py-1 cursor-pointer text-sm" style="color: var(--chat-text);"> + <input + type="checkbox" + [checked]="checked()" + (change)="toggle()" + class="w-4 h-4" + /> + <span [class.line-through]="checked()" [style.opacity]="checked() ? '0.5' : '1'"> + {{ label() }} + </span> + </label> + `, +}) +export class CheckboxRowComponent { + readonly label = input<string>(''); + readonly checked = input<boolean>(false); + readonly emit = input<(event: string) => void>(() => {}); + + toggle(): void { + this.emit()('toggle'); + } +} diff --git a/cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts b/cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts new file mode 100644 index 000000000..84a06a608 --- /dev/null +++ b/cockpit/deep-agents/planning/angular/src/app/views/plan-checklist.component.ts @@ -0,0 +1,15 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'plan-checklist', + standalone: true, + template: ` + <div class="border rounded-xl p-4 my-2" style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <h4 class="text-sm font-semibold mb-2" style="color: var(--chat-text);">{{ title() }}</h4> + <ng-content /> + </div> + `, +}) +export class PlanChecklistComponent { + readonly title = input<string>('Plan'); +} diff --git a/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts b/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts index b0133086b..b013bb6d3 100644 --- a/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts @@ -8,4 +8,5 @@ export const environment = { production: false, langGraphApiUrl: 'http://localhost:4310/api', streamingAssistantId: 'planning', + planningAssistantId: 'planning', }; diff --git a/cockpit/deep-agents/planning/angular/src/environments/environment.ts b/cockpit/deep-agents/planning/angular/src/environments/environment.ts index d0950d618..0fbf05c5b 100644 --- a/cockpit/deep-agents/planning/angular/src/environments/environment.ts +++ b/cockpit/deep-agents/planning/angular/src/environments/environment.ts @@ -8,4 +8,5 @@ export const environment = { production: true, langGraphApiUrl: '/api', streamingAssistantId: 'planning', + planningAssistantId: 'planning', }; From 0502e0e3fcd470f8f5af30b446715a643356fb0d Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:01:13 -0700 Subject: [PATCH 11/17] docs: add views examples and docs implementation plan --- .../plans/2026-04-07-views-examples-docs.md | 486 ++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-07-views-examples-docs.md diff --git a/docs/superpowers/plans/2026-04-07-views-examples-docs.md b/docs/superpowers/plans/2026-04-07-views-examples-docs.md new file mode 100644 index 000000000..f43ddf093 --- /dev/null +++ b/docs/superpowers/plans/2026-04-07-views-examples-docs.md @@ -0,0 +1,486 @@ +# Views Examples & Docs — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Update cockpit examples to demonstrate the `views()` API with inline generative UI, and add docs for the views system to the website. + +**Architecture:** Each example that has tool calls or structured state gets view components registered via `views()`. The custom sidebars remain as secondary UI — the primary display moves inline. Docs go in the existing multi-library docs structure at `apps/website/content/docs-v2/`. + +**Tech Stack:** Angular 20+, `@cacheplane/chat`, `@cacheplane/render`, Tailwind CSS v4 + +**Parallelism:** Tasks 1-5 (examples) are independent. Task 6 (docs) depends on examples being done for accurate code samples. + +--- + +## File Structure + +### Modified files (5 examples getting view components) + +| Task | Example | View Components | +|------|---------|----------------| +| 1 | skills | CalculatorResultComponent, WordCountResultComponent | +| 2 | filesystem | FilePreviewComponent | +| 3 | sandboxes | CodeExecutionComponent | +| 4 | interrupts | ApprovalCardComponent | +| 5 | durable-execution | StepPipelineComponent | + +### New docs files +| Task | File | +|------|------| +| 6 | `apps/website/content/docs-v2/render/views.mdx` | + +--- + +## Task 1: Skills — Tool Result Views + +**Files:** +- Create: `cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts` +- Create: `cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts` +- Modify: `cockpit/deep-agents/skills/angular/src/app/skills.component.ts` + +- [ ] **Step 1: Create CalculatorResultComponent** + +```typescript +// cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'calculator-result', + standalone: true, + template: ` + <div class="border rounded-lg p-3 my-2 inline-flex items-center gap-3" + style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <span class="text-xs font-mono px-2 py-0.5 rounded" + style="background: var(--chat-success); color: white;">calculator</span> + <span class="font-mono text-sm" style="color: var(--chat-text-muted);">{{ expression() }}</span> + <span class="text-sm font-semibold" style="color: var(--chat-text);">= {{ result() }}</span> + </div> + `, +}) +export class CalculatorResultComponent { + readonly expression = input<string>(''); + readonly result = input<string>(''); +} +``` + +- [ ] **Step 2: Create WordCountResultComponent** + +```typescript +// cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'word-count-result', + standalone: true, + template: ` + <div class="border rounded-lg p-3 my-2 inline-flex items-center gap-3" + style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <span class="text-xs font-mono px-2 py-0.5 rounded" + style="background: var(--chat-warning-text); color: white;">word_count</span> + <span class="text-sm" style="color: var(--chat-text);">{{ count() }} words</span> + </div> + `, +}) +export class WordCountResultComponent { + readonly count = input<string>(''); + readonly text = input<string>(''); +} +``` + +- [ ] **Step 3: Update SkillsComponent to register views** + +Read the current file first, then update to import `views` from `@cacheplane/chat` and `signalStateStore` from `@cacheplane/render`, register the view components, and pass `[views]` and `[store]` to `<chat>`. Keep the existing sidebar. + +```typescript +import { views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; +import { CalculatorResultComponent } from './views/calculator-result.component'; +import { WordCountResultComponent } from './views/word-count-result.component'; + +// In class: +readonly ui = views({ + 'calculator-result': CalculatorResultComponent, + 'word-count-result': WordCountResultComponent, +}); +readonly uiStore = signalStateStore({}); + +// In template, update <chat> tag: +// <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> +``` + +- [ ] **Step 4: Build to verify** + +```bash +npx nx build cockpit-deep-agents-skills-angular +``` + +- [ ] **Step 5: Commit** + +```bash +git add cockpit/deep-agents/skills/angular/src/app/ +git commit -m "feat(cockpit): add tool result views to skills example" +``` + +--- + +## Task 2: Filesystem — File Preview View + +**Files:** +- Create: `cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts` +- Modify: `cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts` + +- [ ] **Step 1: Create FilePreviewComponent** + +```typescript +// cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'file-preview', + standalone: true, + template: ` + <div class="border rounded-lg overflow-hidden my-2" + style="border-color: var(--chat-border);"> + <div class="flex items-center justify-between px-3 py-1.5 text-xs" + style="background: var(--chat-bg-alt); border-bottom: 1px solid var(--chat-border);"> + <span class="font-mono" style="color: var(--chat-text);">{{ path() }}</span> + <span style="color: var(--chat-text-muted);">{{ size() }}</span> + </div> + <pre class="px-3 py-2 m-0 text-xs font-mono overflow-x-auto" + style="color: var(--chat-text); background: var(--chat-bg);">{{ content() }}</pre> + </div> + `, +}) +export class FilePreviewComponent { + readonly path = input<string>(''); + readonly content = input<string>(''); + readonly size = input<string>(''); +} +``` + +- [ ] **Step 2: Update FilesystemComponent** + +Read the current file, then add views registration alongside existing sidebar: + +```typescript +import { views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; +import { FilePreviewComponent } from './views/file-preview.component'; + +readonly ui = views({ 'file-preview': FilePreviewComponent }); +readonly uiStore = signalStateStore({}); + +// Update <chat> tag: <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> +``` + +- [ ] **Step 3: Build and commit** + +```bash +npx nx build cockpit-deep-agents-filesystem-angular +git add cockpit/deep-agents/filesystem/angular/src/app/ +git commit -m "feat(cockpit): add file preview view to filesystem example" +``` + +--- + +## Task 3: Sandboxes — Code Execution View + +**Files:** +- Create: `cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts` +- Modify: `cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts` + +- [ ] **Step 1: Create CodeExecutionComponent** + +```typescript +// cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'code-execution', + standalone: true, + template: ` + <div class="border rounded-lg overflow-hidden my-2" + style="border-color: var(--chat-border);"> + <div class="px-3 py-1.5 text-[10px] font-semibold uppercase" + style="background: var(--chat-bg-alt); color: var(--chat-text-muted); border-bottom: 1px solid var(--chat-border);"> + Code Execution + </div> + <pre class="px-3 py-2 m-0 text-xs font-mono overflow-x-auto" + style="color: var(--chat-text); background: var(--chat-bg);">{{ code() }}</pre> + @if (stdout()) { + <div style="border-top: 1px solid var(--chat-border);"> + <div class="px-3 py-1 text-[10px] font-semibold uppercase" style="color: var(--chat-success);">stdout</div> + <pre class="px-3 py-1 m-0 text-xs font-mono" style="color: var(--chat-success);">{{ stdout() }}</pre> + </div> + } + @if (stderr()) { + <div style="border-top: 1px solid var(--chat-border);"> + <div class="px-3 py-1 text-[10px] font-semibold uppercase" style="color: var(--chat-error-text);">stderr</div> + <pre class="px-3 py-1 m-0 text-xs font-mono" style="color: var(--chat-error-text);">{{ stderr() }}</pre> + </div> + } + </div> + `, +}) +export class CodeExecutionComponent { + readonly code = input<string>(''); + readonly stdout = input<string>(''); + readonly stderr = input<string>(''); +} +``` + +- [ ] **Step 2: Update SandboxesComponent with views registration, build, commit** + +Same pattern as Tasks 1-2. Register `'code-execution': CodeExecutionComponent` via `views()`. + +```bash +npx nx build cockpit-deep-agents-sandboxes-angular +git add cockpit/deep-agents/sandboxes/angular/src/app/ +git commit -m "feat(cockpit): add code execution view to sandboxes example" +``` + +--- + +## Task 4: Interrupts — Approval Card View + +**Files:** +- Create: `cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts` +- Modify: `cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts` + +- [ ] **Step 1: Create ApprovalCardComponent** + +```typescript +// cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'approval-card', + standalone: true, + template: ` + <div class="border rounded-lg p-4 my-2" + style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <div class="text-xs font-semibold uppercase mb-2" style="color: var(--chat-warning-text);"> + Requires Approval + </div> + <p class="text-sm mb-3" style="color: var(--chat-text);">{{ description() }}</p> + <div class="flex gap-2"> + <button class="px-3 py-1.5 text-xs font-medium rounded border-0 cursor-pointer" + style="background: var(--chat-success); color: white;" + (click)="emit()('approve')">Approve</button> + <button class="px-3 py-1.5 text-xs font-medium rounded border cursor-pointer" + style="background: var(--chat-bg); color: var(--chat-text); border-color: var(--chat-border);" + (click)="emit()('edit')">Edit</button> + <button class="px-3 py-1.5 text-xs rounded border-0 cursor-pointer" + style="background: transparent; color: var(--chat-text-muted);" + (click)="emit()('cancel')">Cancel</button> + </div> + </div> + `, +}) +export class ApprovalCardComponent { + readonly description = input<string>(''); + readonly emit = input<(event: string) => void>(() => {}); +} +``` + +- [ ] **Step 2: Update InterruptsComponent with views registration, build, commit** + +```bash +npx nx build cockpit-langgraph-interrupts-angular +git add cockpit/langgraph/interrupts/angular/src/app/ +git commit -m "feat(cockpit): add approval card view to interrupts example" +``` + +--- + +## Task 5: Durable Execution — Step Pipeline View + +**Files:** +- Create: `cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts` +- Modify: `cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts` + +- [ ] **Step 1: Create StepPipelineComponent** + +```typescript +// cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'step-pipeline', + standalone: true, + template: ` + <div class="border rounded-lg p-3 my-2" + style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <div class="text-xs font-semibold uppercase mb-2" style="color: var(--chat-text-muted);">Pipeline Progress</div> + <div class="flex items-center gap-2"> + @for (step of steps(); track $index) { + <div class="flex items-center gap-1.5"> + @if (step.status === 'complete') { + <div class="w-5 h-5 rounded-full flex items-center justify-center" + style="background: var(--chat-success);"> + <svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="white" stroke-width="2.5"><path d="M2.5 6L5 8.5L9.5 3.5"/></svg> + </div> + } @else if (step.status === 'active') { + <div class="w-5 h-5 rounded-full border-2 border-t-transparent animate-spin" + style="border-color: var(--chat-warning-text); border-top-color: transparent;"></div> + } @else { + <div class="w-5 h-5 rounded-full" style="background: var(--chat-bg-hover);"></div> + } + <span class="text-xs" [style.color]="step.status === 'pending' ? 'var(--chat-text-muted)' : 'var(--chat-text)'" + [style.fontWeight]="step.status === 'active' ? '500' : '400'">{{ step.label }}</span> + </div> + @if ($index < steps().length - 1) { + <div class="w-4 h-0.5" [style.background]="step.status === 'complete' ? 'var(--chat-success)' : 'var(--chat-border)'"></div> + } + } + </div> + </div> + `, +}) +export class StepPipelineComponent { + readonly steps = input<Array<{ label: string; status: 'pending' | 'active' | 'complete' }>>([]); +} +``` + +- [ ] **Step 2: Update DurableExecutionComponent with views registration, build, commit** + +```bash +npx nx build cockpit-langgraph-durable-execution-angular +git add cockpit/langgraph/durable-execution/angular/src/app/ +git commit -m "feat(cockpit): add step pipeline view to durable-execution example" +``` + +--- + +## Task 6: Views Documentation + +**Files:** +- Create: `apps/website/content/docs-v2/render/views.mdx` + +- [ ] **Step 1: Create views documentation page** + +```mdx +--- +title: Views +description: Expose Angular components to the agent for generative UI rendering +--- + +# Views + +Views are Angular components that the agent can render inline in the chat. Register views with the `views()` function, and the agent produces JSON specs that render as interactive UI. + +## Quick Start + +```typescript +import { views } from '@cacheplane/render'; +import { ChatComponent } from '@cacheplane/chat'; +import { agent } from '@cacheplane/angular'; + +const ui = views({ + 'plan-checklist': PlanChecklistComponent, + 'file-preview': FilePreviewComponent, +}); + +@Component({ + template: `<chat [ref]="stream" [views]="ui" />`, + imports: [ChatComponent], +}) +export class MyComponent { + stream = agent({ apiUrl: '/api', assistantId: 'my-agent' }); + ui = ui; +} +``` + +## API + +### `views(map)` + +Creates an immutable view registry from a name → component map. + +```typescript +const ui = views({ + 'calculator': CalculatorComponent, + 'chart': ChartComponent, +}); +``` + +### `withViews(base, additions)` + +Adds views without overwriting existing entries. + +```typescript +const extended = withViews(base, { + 'new-widget': NewWidgetComponent, +}); +``` + +### `withoutViews(base, ...names)` + +Removes views by name. + +```typescript +const restricted = withoutViews(base, 'dangerous-widget'); +``` + +### `provideViews(registry)` + +Provides views globally via Angular DI. + +```typescript +// app.config.ts +providers: [provideViews(ui)] +``` + +## Composition + +Compose registries via object spread: + +```typescript +import { thirdPartyViews } from '@acme/agent-ui'; + +const all = views({ + ...thirdPartyViews, // third-party views + ...myViews, // your views + 'chart': MyChart, // override specific entries +}); +``` + +## How It Works + +1. Register views with `views()` +2. Pass to `<chat [views]="ui" />` +3. Agent produces a JSON spec with `ui` field in tool results or state +4. Chat detects the spec and renders via `<render-spec>` +5. Views receive props as Angular `input()` signals +``` + +- [ ] **Step 2: Build website to verify** + +```bash +npx nx build website +``` + +- [ ] **Step 3: Commit** + +```bash +git add apps/website/content/docs-v2/render/views.mdx +git commit -m "docs(website): add views API documentation" +``` + +--- + +## Task 7: Build All + Final Verification + +- [ ] **Step 1: Run all tests** + +```bash +npx nx test render && npx nx test chat +``` + +- [ ] **Step 2: Build all cockpit examples** + +```bash +npx nx run-many -t build --projects='cockpit-*-angular' +``` + +- [ ] **Step 3: Commit any fixes** From f1449e12c3f029f25a007157bfd1cf7aad77bb72 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:03:30 -0700 Subject: [PATCH 12/17] feat(cockpit): add view components for skills example Register calculator-result and word-count-result view components with the chat widget to enable generative UI rendering of skill invocations. --- .../angular/src/app/skills.component.ts | 14 +++++++++-- .../app/views/calculator-result.component.ts | 25 +++++++++++++++++++ .../app/views/word-count-result.component.ts | 25 +++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts create mode 100644 cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts 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 71c3cb577..7b436e164 100644 --- a/cockpit/deep-agents/skills/angular/src/app/skills.component.ts +++ b/cockpit/deep-agents/skills/angular/src/app/skills.component.ts @@ -1,7 +1,10 @@ import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { CalculatorResultComponent } from './views/calculator-result.component'; +import { WordCountResultComponent } from './views/word-count-result.component'; /** * Represents a matched skill invocation: tool call paired with its result. @@ -29,7 +32,7 @@ interface SkillInvocation { imports: [ChatComponent], template: ` <div class="flex h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> + <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> <aside class="w-72 shrink-0 border-l overflow-y-auto p-4 space-y-3" style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> <h3 class="text-xs font-semibold uppercase tracking-wide" @@ -70,6 +73,13 @@ export class SkillsComponent { assistantId: environment.streamingAssistantId, }); + readonly ui = views({ + 'calculator-result': CalculatorResultComponent, + 'word-count-result': WordCountResultComponent, + }); + + readonly uiStore = signalStateStore({}); + private readonly SKILL_NAMES = new Set(['calculator', 'word_count', 'summarize']); /** diff --git a/cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts b/cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts new file mode 100644 index 000000000..f9a884e6a --- /dev/null +++ b/cockpit/deep-agents/skills/angular/src/app/views/calculator-result.component.ts @@ -0,0 +1,25 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'calculator-result', + standalone: true, + template: ` + <div class="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 my-1" + style="background: var(--chat-bg-alt); border: 1px solid var(--chat-border);"> + <span class="rounded-full px-2 py-0.5 text-xs font-semibold" + style="background: rgba(74, 222, 128, 0.15); color: var(--chat-success, #4ade80);"> + calculator + </span> + <span class="text-sm font-mono" style="color: var(--chat-text);"> + {{ expression() }} + </span> + <span class="text-sm font-semibold" style="color: var(--chat-text);"> + = {{ result() }} + </span> + </div> + `, +}) +export class CalculatorResultComponent { + readonly expression = input<string>(''); + readonly result = input<string>(''); +} diff --git a/cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts b/cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts new file mode 100644 index 000000000..7ca97bd31 --- /dev/null +++ b/cockpit/deep-agents/skills/angular/src/app/views/word-count-result.component.ts @@ -0,0 +1,25 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'word-count-result', + standalone: true, + template: ` + <div class="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 my-1" + style="background: var(--chat-bg-alt); border: 1px solid var(--chat-border);"> + <span class="rounded-full px-2 py-0.5 text-xs font-semibold" + style="background: rgba(251, 191, 36, 0.15); color: var(--chat-warning-text, #fbbf24);"> + word_count + </span> + <span class="text-sm font-semibold font-mono" style="color: var(--chat-text);"> + {{ count() }} + </span> + <span class="text-sm" style="color: var(--chat-text-muted);"> + words + </span> + </div> + `, +}) +export class WordCountResultComponent { + readonly count = input<string>(''); + readonly text = input<string>(''); +} From 61bbb62c9cdc3c9eb5fa9aae20ee09f7b1c41569 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:03:48 -0700 Subject: [PATCH 13/17] docs(website): add views() API documentation --- .../website/content/docs/render/api/views.mdx | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 apps/website/content/docs/render/api/views.mdx diff --git a/apps/website/content/docs/render/api/views.mdx b/apps/website/content/docs/render/api/views.mdx new file mode 100644 index 000000000..9e88f82fc --- /dev/null +++ b/apps/website/content/docs/render/api/views.mdx @@ -0,0 +1,135 @@ +--- +title: views() +description: Create and compose view registries for generative UI rendering +--- + +# views() + +Creates an immutable view registry mapping names to Angular components. Views are rendered inline in the chat when the agent produces a JSON spec. + +## Usage + +```typescript +import { views } from '@cacheplane/render'; + +const ui = views({ + 'plan-checklist': PlanChecklistComponent, + 'file-preview': FilePreviewComponent, + 'code-output': CodeOutputComponent, +}); +``` + +Pass to the chat component: + +```html +<chat [ref]="stream" [views]="ui" /> +``` + +Or provide globally via DI: + +```typescript +import { provideViews } from '@cacheplane/render'; + +// app.config.ts +providers: [provideViews(ui)] +``` + +## Composition + +Compose registries via object spread. Last key wins for overrides: + +```typescript +const all = views({ + ...thirdPartyViews, + ...myViews, + 'chart': MyCustomChart, // overrides thirdPartyViews['chart'] +}); +``` + +## API + +### `views(map)` + +Creates a frozen `ViewRegistry` from a `Record<string, Type<unknown>>`. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `map` | `Record<string, Type<unknown>>` | Name → Angular component mapping | +| **Returns** | `ViewRegistry` | Frozen immutable registry | + +### `withViews(base, additions)` + +Adds new views without overwriting existing entries. Existing keys in `base` are preserved. + +```typescript +const extended = withViews(base, { + 'new-widget': NewWidget, // added + 'existing': Other, // IGNORED — base already has 'existing' +}); +``` + +### `withoutViews(base, ...names)` + +Removes views by name: + +```typescript +const restricted = withoutViews(base, 'dangerous-widget', 'internal-tool'); +``` + +### `provideViews(registry)` + +Angular DI provider. Works at app level or route level: + +```typescript +// Global +providers: [provideViews(ui)] + +// Route-scoped +{ path: 'planning', providers: [provideViews(planningViews)] } +``` + +### `toRenderRegistry(registry)` + +Converts a `ViewRegistry` to the low-level `AngularRegistry` type used by `<render-spec>`. Called internally by the chat component — most developers won't need this. + +## View Components + +View components are standard Angular standalone components. They receive props as `input()` signals: + +```typescript +@Component({ + selector: 'plan-checklist', + standalone: true, + template: ` + <div class="border rounded-xl p-4"> + <h4>{{ title() }}</h4> + <ng-content /> + </div> + `, +}) +export class PlanChecklistComponent { + readonly title = input<string>('Plan'); +} +``` + +## How Specs Are Detected + +The chat component checks each message for a `ui` field containing a valid spec: + +```json +{ + "type": "tool", + "content": "...", + "ui": { + "root": "plan", + "elements": { + "plan": { + "type": "plan-checklist", + "props": { "title": "My Plan" } + } + } + } +} +``` + +The `type` in the spec is matched against the view registry to resolve the Angular component. From 8714627641a3e83290a9e39877d9d3fd1550c64f Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:03:55 -0700 Subject: [PATCH 14/17] feat(cockpit): add approval-card view component to interrupts example Register an approval-card view with the chat component so the LangGraph interrupts demo can render a generative UI card with Approve, Edit, and Cancel action buttons. --- .../angular/src/app/interrupts.component.ts | 12 ++++- .../src/app/views/approval-card.component.ts | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts diff --git a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts index b6683d5b5..6b4a99862 100644 --- a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts +++ b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts @@ -1,8 +1,10 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; -import { ChatComponent, ChatInterruptPanelComponent, type InterruptAction } from '@cacheplane/chat'; +import { ChatComponent, ChatInterruptPanelComponent, type InterruptAction, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { ApprovalCardComponent } from './views/approval-card.component'; /** * InterruptsComponent demonstrates human-in-the-loop with `agent()`. @@ -22,7 +24,7 @@ import { environment } from '../environments/environment'; imports: [ChatComponent, ChatInterruptPanelComponent], template: ` <div class="flex flex-col h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> + <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> @if (stream.interrupt()) { <div class="p-4" style="border-top: 1px solid var(--chat-border, #333);"> <chat-interrupt-panel [ref]="stream" (action)="onInterruptAction($event)" /> @@ -32,6 +34,12 @@ import { environment } from '../environments/environment'; `, }) export class InterruptsComponent { + readonly ui = views({ + 'approval-card': ApprovalCardComponent, + }); + + readonly uiStore = signalStateStore({}); + /** * The streaming resource with interrupt support. * diff --git a/cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts b/cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts new file mode 100644 index 000000000..40f0c6dba --- /dev/null +++ b/cockpit/langgraph/interrupts/angular/src/app/views/approval-card.component.ts @@ -0,0 +1,52 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'approval-card', + standalone: true, + template: ` + <div class="border rounded-xl my-2 overflow-hidden" style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <!-- Warning header --> + <div class="flex items-center gap-2 px-4 py-2 text-sm font-semibold" + style="background: color-mix(in srgb, var(--chat-warning-text, #f59e0b) 15%, transparent); color: var(--chat-warning-text, #f59e0b);"> + <svg class="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> + <path stroke-linecap="round" stroke-linejoin="round" + d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" /> + </svg> + Requires Approval + </div> + + <!-- Body --> + <div class="px-4 py-3"> + <p class="text-sm mb-4" style="color: var(--chat-text, #e0e0e0);">{{ description() }}</p> + + <div class="flex items-center gap-2"> + <button + (click)="emit()('approve')" + class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors" + style="background: var(--chat-success, #16a34a); color: #fff;" + onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'"> + Approve + </button> + <button + (click)="emit()('edit')" + class="px-3 py-1.5 text-sm font-medium rounded-lg border transition-colors" + style="border-color: var(--chat-border, #333); color: var(--chat-text, #e0e0e0); background: transparent;" + onmouseover="this.style.background='var(--chat-bg-hover, #222)'" onmouseout="this.style.background='transparent'"> + Edit + </button> + <button + (click)="emit()('cancel')" + class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors" + style="color: var(--chat-text-muted, #777); background: transparent;" + onmouseover="this.style.background='var(--chat-bg-hover, #222)'" onmouseout="this.style.background='transparent'"> + Cancel + </button> + </div> + </div> + </div> + `, +}) +export class ApprovalCardComponent { + readonly description = input<string>(''); + readonly emit = input<(event: string) => void>(() => {}); +} From 56e1c33f8bc5566681ead82939ad52b196061144 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:04:36 -0700 Subject: [PATCH 15/17] feat(cockpit): add view component for filesystem example Register file-preview view component with the chat widget to enable generative UI rendering of file operation results. --- .../angular/src/app/filesystem.component.ts | 12 ++++++++-- .../src/app/views/file-preview.component.ts | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts 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 699ec60fb..70f6eb4d4 100644 --- a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts +++ b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts @@ -1,7 +1,9 @@ import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { FilePreviewComponent } from './views/file-preview.component'; /** * Represents a file operation extracted from agent tool calls. @@ -30,7 +32,7 @@ interface FileOperation { imports: [ChatComponent], template: ` <div class="flex h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> + <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> <aside class="w-72 shrink-0 border-l overflow-y-auto p-4 space-y-2" style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> <h3 class="text-xs font-semibold uppercase tracking-wide" @@ -70,6 +72,12 @@ export class FilesystemComponent { assistantId: environment.streamingAssistantId, }); + readonly ui = views({ + 'file-preview': FilePreviewComponent, + }); + + readonly uiStore = signalStateStore({}); + /** * Reactive list of file operations derived from the message history. * diff --git a/cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts b/cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts new file mode 100644 index 000000000..7017936ad --- /dev/null +++ b/cockpit/deep-agents/filesystem/angular/src/app/views/file-preview.component.ts @@ -0,0 +1,23 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'file-preview', + standalone: true, + template: ` + <div class="rounded-xl my-2 overflow-hidden" + style="border: 1px solid var(--chat-border); background: var(--chat-bg-alt);"> + <div class="flex items-center justify-between px-4 py-2" + style="border-bottom: 1px solid var(--chat-border); background: var(--chat-bg);"> + <span class="text-sm font-mono truncate" style="color: var(--chat-text);">{{ path() }}</span> + <span class="text-xs ml-2 shrink-0" style="color: var(--chat-text-muted);">{{ size() }}</span> + </div> + <pre class="whitespace-pre-wrap break-words font-mono text-xs leading-relaxed p-4 m-0 overflow-x-auto" + style="color: var(--chat-text); background: var(--chat-bg-alt);">{{ content() }}</pre> + </div> + `, +}) +export class FilePreviewComponent { + readonly path = input<string>(''); + readonly content = input<string>(''); + readonly size = input<string>(''); +} From 6d171675341c1757106cd1624a2b5c4d2988f410 Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:05:03 -0700 Subject: [PATCH 16/17] feat(cockpit): add step-pipeline view component to durable-execution example Register a step-pipeline view with the chat component so the LangGraph durable-execution demo can render a horizontal pipeline with status indicators (complete/active/pending) as generative UI. --- .../src/app/durable-execution.component.ts | 12 +++- .../src/app/views/step-pipeline.component.ts | 67 +++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts 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 f5a63e1be..7efe8d46c 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 @@ -1,7 +1,9 @@ import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { StepPipelineComponent } from './views/step-pipeline.component'; /** * Pipeline step definition for the vertical progress indicator. @@ -37,7 +39,7 @@ const STEP_LABELS: Record<string, string> = { imports: [ChatComponent], template: ` <div class="flex h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> + <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> <aside class="w-64 shrink-0 border-l overflow-y-auto p-4" style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> <h3 class="text-xs font-semibold uppercase tracking-wide mb-6" @@ -92,6 +94,12 @@ const STEP_LABELS: Record<string, string> = { `, }) export class DurableExecutionComponent { + readonly ui = views({ + 'step-pipeline': StepPipelineComponent, + }); + + readonly uiStore = signalStateStore({}); + protected readonly stream = agent({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, 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 new file mode 100644 index 000000000..d7a3db81d --- /dev/null +++ b/cockpit/langgraph/durable-execution/angular/src/app/views/step-pipeline.component.ts @@ -0,0 +1,67 @@ +import { Component, input } from '@angular/core'; + +interface PipelineStep { + label: string; + status: 'pending' | 'active' | 'complete'; +} + +@Component({ + selector: 'step-pipeline', + standalone: true, + template: ` + <div class="border rounded-xl p-4 my-2 overflow-x-auto" style="border-color: var(--chat-border); background: var(--chat-bg-alt);"> + <div class="flex items-center gap-0 min-w-max"> + @for (step of steps(); track step.label; let i = $index; let last = $last) { + <!-- Step node --> + <div class="flex flex-col items-center gap-1.5"> + <!-- Status circle --> + @switch (step.status) { + @case ('complete') { + <div class="w-8 h-8 rounded-full flex items-center justify-center" + style="background: var(--chat-success, #16a34a);"> + <svg class="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="3"> + <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /> + </svg> + </div> + } + @case ('active') { + <div class="w-8 h-8 rounded-full border-2 flex items-center justify-center animate-spin" + style="border-color: var(--chat-warning-text, #f59e0b); animation-duration: 1.2s;"> + <div class="w-2 h-2 rounded-full" style="background: var(--chat-warning-text, #f59e0b);"></div> + </div> + } + @default { + <div class="w-8 h-8 rounded-full border-2 flex items-center justify-center" + style="border-color: var(--chat-text-muted, #555);"> + <div class="w-2 h-2 rounded-full" style="background: var(--chat-text-muted, #555);"></div> + </div> + } + } + <!-- Label --> + <span class="text-xs whitespace-nowrap" + [style.color]="step.status === 'active' + ? 'var(--chat-warning-text, #f59e0b)' + : step.status === 'complete' + ? 'var(--chat-success, #16a34a)' + : 'var(--chat-text-muted, #777)'" + [class.font-semibold]="step.status === 'active'"> + {{ step.label }} + </span> + </div> + + <!-- Connecting line --> + @if (!last) { + <div class="w-10 h-0.5 -mt-5 mx-1" + [style.background]="step.status === 'complete' + ? 'var(--chat-success, #16a34a)' + : 'var(--chat-text-muted, #555)'"> + </div> + } + } + </div> + </div> + `, +}) +export class StepPipelineComponent { + readonly steps = input<PipelineStep[]>([]); +} From 803466905166184fc250ce2e8f3c90ff3d19710d Mon Sep 17 00:00:00 2001 From: Brian Love <brian@liveloveapp.com> Date: Tue, 7 Apr 2026 16:05:20 -0700 Subject: [PATCH 17/17] feat(cockpit): add view component for sandboxes example Register code-execution view component with the chat widget to enable generative UI rendering of code execution results with stdout/stderr. --- .../angular/src/app/sandboxes.component.ts | 12 +++++- .../src/app/views/code-execution.component.ts | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts 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 1f349ca8b..ff8b1b097 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts @@ -1,7 +1,9 @@ import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +import { ChatComponent, views } from '@cacheplane/chat'; +import { signalStateStore } from '@cacheplane/render'; import { agent } from '@cacheplane/angular'; import { environment } from '../environments/environment'; +import { CodeExecutionComponent } from './views/code-execution.component'; /** * Represents a parsed code execution: the code that was run and its output. @@ -28,7 +30,7 @@ interface CodeExecution { imports: [ChatComponent], template: ` <div class="flex h-screen"> - <chat [ref]="stream" class="flex-1 min-w-0" /> + <chat [ref]="stream" [views]="ui" [store]="uiStore" class="flex-1 min-w-0" /> <aside class="w-80 shrink-0 border-l overflow-y-auto p-4 space-y-3" style="border-color: var(--chat-border, #333); background: var(--chat-bg, #171717); color: var(--chat-text, #e0e0e0);"> <h3 class="text-xs font-semibold uppercase tracking-wide" @@ -64,6 +66,12 @@ export class SandboxesComponent { assistantId: environment.streamingAssistantId, }); + readonly ui = views({ + 'code-execution': CodeExecutionComponent, + }); + + readonly uiStore = signalStateStore({}); + /** * Derived signal: extracts code executions from the message stream. * diff --git a/cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts b/cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts new file mode 100644 index 000000000..5e81550d7 --- /dev/null +++ b/cockpit/deep-agents/sandboxes/angular/src/app/views/code-execution.component.ts @@ -0,0 +1,38 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'code-execution', + standalone: true, + template: ` + <div class="rounded-xl my-2 overflow-hidden" + style="border: 1px solid var(--chat-border); background: var(--chat-bg-alt);"> + <div class="px-4 py-2 text-xs font-semibold uppercase tracking-wide" + style="border-bottom: 1px solid var(--chat-border); background: var(--chat-bg); color: var(--chat-text-muted);"> + Code Execution + </div> + <pre class="whitespace-pre-wrap break-words font-mono text-xs leading-relaxed p-4 m-0 overflow-x-auto" + style="color: var(--chat-text); background: var(--chat-bg-alt);">{{ code() }}</pre> + @if (stdout()) { + <div class="px-4 py-2" + style="border-top: 1px solid var(--chat-border);"> + <div class="text-xs font-semibold mb-1" style="color: var(--chat-success, #4ade80);">stdout</div> + <pre class="whitespace-pre-wrap break-words font-mono text-xs leading-relaxed p-2 m-0 rounded" + style="background: var(--chat-bg); color: var(--chat-success, #4ade80);">{{ stdout() }}</pre> + </div> + } + @if (stderr()) { + <div class="px-4 py-2" + style="border-top: 1px solid var(--chat-border);"> + <div class="text-xs font-semibold mb-1" style="color: var(--chat-error-text, #f87171);">stderr</div> + <pre class="whitespace-pre-wrap break-words font-mono text-xs leading-relaxed p-2 m-0 rounded" + style="background: var(--chat-bg); color: var(--chat-error-text, #f87171);">{{ stderr() }}</pre> + </div> + } + </div> + `, +}) +export class CodeExecutionComponent { + readonly code = input<string>(''); + readonly stdout = input<string>(''); + readonly stderr = input<string>(''); +}