From 8903c836dad4db46ce6ad2050adc10d2704932f9 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Mon, 18 May 2026 21:17:38 -0700 Subject: [PATCH] Update ThreadPlane positioning tagline --- README.md | 6 ++- apps/website/content/AGENTS.md.template | 2 +- apps/website/content/CLAUDE.md.template | 2 +- .../agent/getting-started/introduction.mdx | 2 +- apps/website/emails/angular-download.ts | 2 +- apps/website/emails/drip-chat-followup.ts | 2 +- apps/website/emails/email-wrapper.ts | 2 +- apps/website/emails/whitepaper-download.ts | 2 +- apps/website/public/AGENTS.md | 2 +- apps/website/public/CLAUDE.md | 2 +- apps/website/public/assets/arch-diagram.svg | 2 +- apps/website/public/assets/hero.svg | 2 +- apps/website/public/whitepaper-preview.html | 8 +-- apps/website/public/whitepaper.pdf | Bin 442854 -> 441000 bytes .../public/whitepapers/angular-preview.html | 8 +-- apps/website/public/whitepapers/angular.pdf | Bin 500730 -> 499510 bytes apps/website/public/whitepapers/chat.pdf | Bin 443203 -> 443203 bytes apps/website/public/whitepapers/render.pdf | Bin 497329 -> 497329 bytes apps/website/scripts/generate-whitepaper.ts | 46 +++++++++--------- apps/website/src/app/angular/page.tsx | 10 ++-- .../src/app/api/email-preview/route.ts | 2 +- .../src/app/api/whitepaper-signup/route.ts | 4 +- apps/website/src/app/layout.tsx | 17 +++++-- apps/website/src/app/opengraph-image.tsx | 21 ++++---- apps/website/src/app/page.tsx | 16 +++--- .../src/components/landing/EcosystemStrip.tsx | 2 +- .../src/components/landing/Hero.spec.tsx | 6 ++- apps/website/src/components/landing/Hero.tsx | 14 +++--- .../src/components/landing/HomeFAQ.tsx | 4 +- apps/website/src/components/shared/Footer.tsx | 3 +- apps/website/src/lib/docs-config.ts | 2 +- apps/website/src/lib/docs.spec.ts | 1 + apps/website/src/lib/positioning.ts | 13 +++++ apps/website/src/lib/site-metadata.spec.ts | 43 ++++++++++++++++ apps/website/src/lib/site-metadata.ts | 8 +++ apps/website/src/lib/solutions-data.ts | 2 +- docs/gtm/icp.md | 3 +- docs/gtm/messaging.md | 10 ++-- 38 files changed, 174 insertions(+), 97 deletions(-) create mode 100644 apps/website/src/lib/positioning.ts create mode 100644 apps/website/src/lib/site-metadata.spec.ts diff --git a/README.md b/README.md index 30d253b09..10e1fc50f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- Agent UI primitives and LangGraph streaming adapters for Angular + Agent UI for Angular — Production-ready chat, threads, and generative UI for AI agents.

@@ -27,7 +27,9 @@ --- -`agent()` is the Angular equivalent of LangGraph's React `useStream()` hook, projected into a runtime-neutral `Agent` contract consumed by `@ngaf/chat`. Drop it into any Angular 20+ component, point it at your LangGraph Platform endpoint, and get signal-driven access to messages, status, tool calls, interrupts, subagents, regenerate, and thread history. +ThreadPlane is a production-ready agent UI framework for Angular. Use `@ngaf/chat` for chat surfaces, `@ngaf/langgraph` for LangGraph-backed agents, `@ngaf/ag-ui` for AG-UI event streams, and `@ngaf/render` for generative UI that stays inside your Angular design system. + +When you are building on LangGraph, `agent()` is the Angular equivalent of LangGraph's React `useStream()` hook, projected into a runtime-neutral `Agent` contract consumed by `@ngaf/chat`. Drop it into any Angular 20+ component, point it at your LangGraph Platform endpoint, and get signal-driven access to messages, status, tool calls, interrupts, subagents, regenerate, and thread history. --- diff --git a/apps/website/content/AGENTS.md.template b/apps/website/content/AGENTS.md.template index a867ab1b6..9a1b12897 100644 --- a/apps/website/content/AGENTS.md.template +++ b/apps/website/content/AGENTS.md.template @@ -1,6 +1,6 @@ # Agent UI for Angular v@VERSION@ -Agent UI primitives and LangGraph streaming adapters for Angular. +Production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI for Angular agent apps. ## Install npm install @ngaf/chat @ngaf/langgraph diff --git a/apps/website/content/CLAUDE.md.template b/apps/website/content/CLAUDE.md.template index a867ab1b6..9a1b12897 100644 --- a/apps/website/content/CLAUDE.md.template +++ b/apps/website/content/CLAUDE.md.template @@ -1,6 +1,6 @@ # Agent UI for Angular v@VERSION@ -Agent UI primitives and LangGraph streaming adapters for Angular. +Production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI for Angular agent apps. ## Install npm install @ngaf/chat @ngaf/langgraph diff --git a/apps/website/content/docs/agent/getting-started/introduction.mdx b/apps/website/content/docs/agent/getting-started/introduction.mdx index 56eeffd1a..798fdf12a 100644 --- a/apps/website/content/docs/agent/getting-started/introduction.mdx +++ b/apps/website/content/docs/agent/getting-started/introduction.mdx @@ -1,6 +1,6 @@ # Introduction -Agent is the Signal-native streaming library for Angular 20+ — built natively for LangGraph, without React translation layers. Build streaming AI applications with Angular Signals, connect to LangGraph agents, and ship production-ready frontends for your AI products. +Agent is the Angular adapter layer for production-ready agent UI surfaces: chat, durable threads, interrupts, subagents, planning, memory, and generative UI. Build complete Angular 20+ agent applications with Signals, connect them to LangGraph agents, and keep the UI inside Angular templates, dependency injection, and your design system. This guide walks you through the complete workflow: build a LangGraph agent in Python, run it locally, connect it to an Angular app with agent(), and deploy to production. diff --git a/apps/website/emails/angular-download.ts b/apps/website/emails/angular-download.ts index 31093ee78..e3d60e6d8 100644 --- a/apps/website/emails/angular-download.ts +++ b/apps/website/emails/angular-download.ts @@ -5,7 +5,7 @@ const DOWNLOAD_URL = 'https://threadplane.ai/whitepapers/angular.pdf'; export function angularDownloadHtml(name?: string): string { return wrapEmail({ body: ` -

Your Enterprise Guide to Agent Streaming

+

Your Enterprise Guide to Agent UI in Angular

${name ? `Hi ${esc(name)}, t` : 'T'}he guide covers six chapters: the last-mile problem, the agent() API, thread persistence, interrupt flows, full LangGraph feature coverage, and deterministic testing.

Download the Guide diff --git a/apps/website/emails/drip-chat-followup.ts b/apps/website/emails/drip-chat-followup.ts index e61d144a5..63323afdf 100644 --- a/apps/website/emails/drip-chat-followup.ts +++ b/apps/website/emails/drip-chat-followup.ts @@ -8,7 +8,7 @@ export function dripChatFollowupHtml(day: number): { subject: string; html: stri body: `

Chat Guide Follow-up

Did you read Chapter 2 on batteries-included components?

-

Chapter 2 covers the batteries-included component library — message bubbles, streaming indicators, thread lists, and input controls that are pre-wired to LangGraph state. Drop them in and your chat UI works on day one.

+

Chapter 2 covers the batteries-included component library — message bubbles, streaming indicators, thread lists, and input controls that are pre-wired to agent state. Drop them in and your chat UI works on day one.

Read the Docs → `, showUnsubscribe: true, diff --git a/apps/website/emails/email-wrapper.ts b/apps/website/emails/email-wrapper.ts index 499589bac..014af7971 100644 --- a/apps/website/emails/email-wrapper.ts +++ b/apps/website/emails/email-wrapper.ts @@ -18,7 +18,7 @@ export function wrapEmail(opts: {
${opts.body}
-

Agent UI for Angular — Signal-native streaming for LangGraph.

+

Agent UI for Angular — Production-ready chat, threads, and generative UI for AI agents.

${opts.showUnsubscribe ? '

Unsubscribe

' : ''}
diff --git a/apps/website/emails/whitepaper-download.ts b/apps/website/emails/whitepaper-download.ts index 78879de2c..f0386cd66 100644 --- a/apps/website/emails/whitepaper-download.ts +++ b/apps/website/emails/whitepaper-download.ts @@ -5,7 +5,7 @@ const DOWNLOAD_URL = 'https://threadplane.ai/whitepaper.pdf'; export function whitepaperDownloadHtml(name?: string): string { return wrapEmail({ body: ` -

Your Angular Agent Readiness Guide

+

Your Enterprise Agent UI Guide for Angular

${name ? `Hi ${esc(name)}, t` : 'T'}he guide covers six production-readiness dimensions: streaming state, thread persistence, tool-call rendering, human approval flows, generative UI, and deterministic testing.

Download the Guide diff --git a/apps/website/public/AGENTS.md b/apps/website/public/AGENTS.md index 7d6935072..544b2463c 100644 --- a/apps/website/public/AGENTS.md +++ b/apps/website/public/AGENTS.md @@ -1,6 +1,6 @@ # Agent UI for Angular v0.0.40 -Agent UI primitives and LangGraph streaming adapters for Angular. +Production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI for Angular agent apps. ## Install npm install @ngaf/chat @ngaf/langgraph diff --git a/apps/website/public/CLAUDE.md b/apps/website/public/CLAUDE.md index 7d6935072..544b2463c 100644 --- a/apps/website/public/CLAUDE.md +++ b/apps/website/public/CLAUDE.md @@ -1,6 +1,6 @@ # Agent UI for Angular v0.0.40 -Agent UI primitives and LangGraph streaming adapters for Angular. +Production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI for Angular agent apps. ## Install npm install @ngaf/chat @ngaf/langgraph diff --git a/apps/website/public/assets/arch-diagram.svg b/apps/website/public/assets/arch-diagram.svg index 02f9d2023..e4c923b09 100644 --- a/apps/website/public/assets/arch-diagram.svg +++ b/apps/website/public/assets/arch-diagram.svg @@ -33,7 +33,7 @@ font-size="15" font-weight="700" fill="#6C8EFF">agent() Signal-native state + font-size="11" fill="#4A527A">Agent UI state @ngaf/langgraph diff --git a/apps/website/public/assets/hero.svg b/apps/website/public/assets/hero.svg index 2c3f1d39f..928409810 100644 --- a/apps/website/public/assets/hero.svg +++ b/apps/website/public/assets/hero.svg @@ -24,7 +24,7 @@ font-style="italic" fill="#8B96C8" opacity="0.9" - >Agent UI for Angular — for LangGraph + >Agent UI for Angular — Production-ready agent UI diff --git a/apps/website/public/whitepaper-preview.html b/apps/website/public/whitepaper-preview.html index ab247c7fa..df46dbebc 100644 --- a/apps/website/public/whitepaper-preview.html +++ b/apps/website/public/whitepaper-preview.html @@ -20,9 +20,9 @@
-
@ngaf/langgraph · Production Readiness Guide
-

From
Prototype
to
Production

-

The Angular Agent Readiness Guide

+
ThreadPlane · Enterprise Angular Agent UI
+

Agent
UI
for
Angular

+

Production-ready chat, threads, and generative UI for AI agents

threadplane.ai · 2026
@@ -65,7 +65,7 @@

-
@ngaf/langgraph · Enterprise Guide
-

The
Enterprise
Guide
to
Agent
Streaming
in
Angular

-

Ship LangGraph agents in Angular — without building the plumbing

+
ThreadPlane · Angular Agent UI Guide
+

The
Enterprise
Guide
to
Agent
UI
in
Angular

+

Ship LangGraph and AG-UI-compatible agents without building the plumbing

threadplane.ai · 2026
@@ -145,7 +145,7 @@

The Alternative

With `agent()`:

readonly chat = agent({ assistantId: 'chat_agent' });
 
-

The signal-native design eliminates the subscription lifecycle entirely. When the component destroys, the signals become inert. No `takeUntilDestroyed()`. No `ngOnDestroy`. The framework handles it.

+

The Angular signals-based design eliminates the subscription lifecycle entirely. When the component destroys, the signals become inert. No `takeUntilDestroyed()`. No `ngOnDestroy`. The framework handles it.

Chapter 3
diff --git a/apps/website/public/whitepapers/angular.pdf b/apps/website/public/whitepapers/angular.pdf index 3a2520306e08435d7412179d32bf38c87f0544a8..7c44548ad267d435dfd1bec57f2112dfa1d4122b 100644 GIT binary patch delta 34618 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�v&e^o?A|{ft&j$ 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 diff --git a/apps/website/public/whitepapers/chat.pdf b/apps/website/public/whitepapers/chat.pdf index a5c19f982ce1c33c653d21abe17017d22d24b376..ae438d20cc5a8b5077849888c2148df811a6974c 100644 GIT binary patch delta 298 zcmX@yCVjX~dcs>RO9K-F3nN2q0|Rv+(&W2>L$vgSr&n!l>$vVHvCTsuVVKkqv*v69=(mg(olg z#Xni?cjjb?-+Ytj{?3{_;WzJOkw00ChMVjEL^4jU{lmNY>EAV{wam;E3>1PCH1hIO zQVk%$R8zss97EJ>`o)usqSG&&WMt8`#E`YnR4_Nd5VZt~8ct_C#V9Ilgf3}lINcs7 b!D<9lW76(?iV=vJfS4JGS(ZDWV*LpKMu%dW delta 280 zcmX@yCVjX~dcs>R3nOD=3qx~l0|Rv+(&WE@2tsne|RPf|H+AO(%Q{FGD! z2r!-Af09v@)l5^t%zXNOAXC@^Lyg7siy$?YnhNFy(-}`Ois~Aot1&bLN*ZB^8UaO( V+nrA_0x=U1GXpWpa_3X5KLLl(UK#)Z diff --git a/apps/website/public/whitepapers/render.pdf b/apps/website/public/whitepapers/render.pdf index 8cd4adedabb128db87dbc8827accfd3e21efdc0a..daea95f2c742b5118a2b4dd6459fedf59a129397 100644 GIT binary patch delta 375 zcmZY3KTE@45C(9bR2@pvBxtFily__5-%GT~8;81d)WyN2lhQ5`8U*R;AmZwFb3Z`Q zPMMrs^dktO;NTz%4tDoIr{=h4`Edu&cvK&c>epW@32l=Ol2xLHtY6;T&${iSv!3$W zJ0XP;HG}_?iyHD8aBuQM1tz~+IN@CzCSrD9u)(hm8vN0~f9!8_-w|N=Mc#Spf?6^|%Qu33sxXh;u-ec68eqVr5eflePMv>_^c^P@9U*TiS zW;C7dz|UAV{RAIS;4nX9)^vM*Aj?XCF_YK80?CN!3)cfp_`uJoG5t*$qwsWX0Y<** z>Vk|_{DuZd$}G&L7xFUdY`-JOxc8B+xq^X0kb*{DeoCqV1Q=^7m|I|ongB&Dr*HVe zDB@sYfFWz9sbFD(2g1K3{*H=a$W&&bnAZ7t# M*6m(j+0qvR0F)zW?EnA( diff --git a/apps/website/scripts/generate-whitepaper.ts b/apps/website/scripts/generate-whitepaper.ts index 67dd779de..33ce91e79 100644 --- a/apps/website/scripts/generate-whitepaper.ts +++ b/apps/website/scripts/generate-whitepaper.ts @@ -67,9 +67,9 @@ interface WhitepaperConfig { const WHITEPAPERS: Record = { overview: { id: 'overview', - title: 'From Prototype to Production', - subtitle: 'The Angular Agent Readiness Guide', - eyebrow: '@ngaf/langgraph · Production Readiness Guide', + title: 'Agent UI for Angular', + subtitle: 'Production-ready chat, threads, and generative UI for AI agents', + eyebrow: 'ThreadPlane · Enterprise Angular Agent UI', coverGradient: 'linear-gradient(135deg, #fafbfc 0%, #eaf3ff 100%)', outputPdf: 'apps/website/public/whitepaper.pdf', outputHtml: 'apps/website/public/whitepaper-preview.html', @@ -77,15 +77,15 @@ const WHITEPAPERS: Record = { { id: 'streaming-state', title: 'Streaming State Management', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Streaming State Management -Context: Angular teams building LangGraph-powered agents must wire SSE event streams into reactive UI. Without the right primitives, they end up with custom zone-patching, manual subscription management, and brittle token accumulation logic that breaks under load. +Context: Angular teams building AI agent applications must wire streaming transports into reactive chat, thread, approval, and generative UI surfaces. Without the right primitives, they end up with custom zone-patching, manual subscription management, and brittle token accumulation logic that breaks under load. Cover: - Why streaming state is hard in Angular (zone.js, change detection, timing) -- The signals-native approach: how agent() exposes messages() as Signal +- The Angular signals approach: how agent() exposes messages() as Signal - How isLoading() lets developers drive loading UI without polling - Code example: minimal agent() setup (TypeScript snippet, 8-12 lines) - Production checklist item: "Are your message signals OnPush-compatible?" @@ -95,7 +95,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'thread-persistence', title: 'Thread Persistence', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Thread Persistence @@ -114,7 +114,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'tool-call-rendering', title: 'Tool-Call Rendering', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Tool-Call Rendering @@ -133,7 +133,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'human-approval-flows', title: 'Human Approval Flows', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Human Approval Flows (Interrupts) @@ -153,7 +153,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'generative-ui', title: 'Generative UI', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Generative UI @@ -172,7 +172,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'deterministic-testing', title: 'Deterministic Testing', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "Agent UI for Angular: Production-ready chat, threads, and generative UI for AI agents". Chapter topic: Deterministic Testing @@ -193,9 +193,9 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi angular: { id: 'angular', - title: 'The Enterprise Guide to Agent Streaming in Angular', - subtitle: 'Ship LangGraph agents in Angular — without building the plumbing', - eyebrow: '@ngaf/langgraph · Enterprise Guide', + title: 'The Enterprise Guide to Agent UI in Angular', + subtitle: 'Ship LangGraph and AG-UI-compatible agents without building the plumbing', + eyebrow: 'ThreadPlane · Angular Agent UI Guide', coverGradient: 'linear-gradient(135deg, #fafbfc 0%, #eaf3ff 100%)', outputPdf: 'apps/website/public/whitepapers/angular.pdf', outputHtml: 'apps/website/public/whitepapers/angular-preview.html', @@ -203,11 +203,11 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'last-mile-problem', title: 'The Last-Mile Problem', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: The Last-Mile Problem -Context: Teams have built powerful LangGraph backends with sophisticated agent graphs, tool calling, and memory. Then they hit Angular. SSE streams don't integrate cleanly with zone.js. Signals and change detection are at odds with streaming event sequences. The backend works — the frontend gap is real and expensive. +Context: Teams have built powerful agent backends with sophisticated graphs, tool calling, memory, and AG-UI-compatible interaction patterns. Then they hit Angular. SSE streams don't integrate cleanly with zone.js. Signals and change detection are at odds with streaming event sequences. The backend works — the frontend gap is real and expensive. Cover: - Why SSE + Angular zones is a zone pollution problem, not a configuration problem @@ -222,16 +222,16 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'agent-api', title: 'The agent() API', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: The agent() API -Context: @ngaf/langgraph exposes a signal-native API for streaming LangGraph agents into Angular components. The core primitive is agent() — a function that returns reactive signals wired directly to the agent stream, with no manual subscription management, no zone-patching, and no token accumulation logic. +Context: @ngaf/langgraph exposes an Angular signals-based API for connecting LangGraph agents to Angular components. The core primitive is agent() — a function that returns reactive signals wired directly to the agent stream, with no manual subscription management, no zone-patching, and no token accumulation logic. Cover: - How agent() returns a LangGraphAgent with typed signals: messages(), isLoading(), error(), interrupt(), and langGraph* raw signals - The provideAgent() provider and how it configures the agent endpoint and stream transport -- Why the signal-native design works with OnPush change detection out of the box +- Why the Angular signals design works with OnPush change detection out of the box - How to bind agent state directly in Angular templates without async pipe or manual subscriptions - Code example: minimal agent() setup with template binding (10-14 lines) - The contrast: what the equivalent hand-rolled code looks like vs. agent() in 3 lines @@ -241,7 +241,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'thread-persistence-memory', title: 'Thread Persistence & Memory', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: Thread Persistence & Memory @@ -261,7 +261,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'interrupt-approval-flows', title: 'Interrupt & Approval Flows', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: Interrupt & Approval Flows @@ -280,7 +280,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'full-langgraph-coverage', title: 'Full LangGraph Feature Coverage', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: Full LangGraph Feature Coverage @@ -299,7 +299,7 @@ Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engi { id: 'deterministic-testing-angular', title: 'Deterministic Testing', - prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent Streaming in Angular". + prompt: `Write a 400-600 word chapter for an engineering white paper titled "The Enterprise Guide to Agent UI in Angular". Chapter topic: Deterministic Testing diff --git a/apps/website/src/app/angular/page.tsx b/apps/website/src/app/angular/page.tsx index 139cc048a..59a88133f 100644 --- a/apps/website/src/app/angular/page.tsx +++ b/apps/website/src/app/angular/page.tsx @@ -9,11 +9,11 @@ import { FeatureBlock } from '../../components/landing/FeatureBlock'; import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock'; import { FinalCTA } from '../../components/landing/FinalCTA'; import { AngularCodeShowcase } from '../../components/landing/angular/AngularCodeShowcase'; -import { createPageMetadata } from '../../lib/site-metadata'; +import { createPageMetadata, SHORT_POSITIONING_DESCRIPTION } from '../../lib/site-metadata'; export const metadata = createPageMetadata({ - title: '@ngaf/langgraph — Agent Streaming for Angular', - description: 'Ship LangGraph agents in Angular. Signal-native streaming, thread persistence, interrupts, and deterministic testing.', + title: '@ngaf/langgraph — Agent UI for Angular', + description: SHORT_POSITIONING_DESCRIPTION, pathname: '/angular', type: 'website', }); @@ -39,7 +39,7 @@ export default async function AngularPage() { letterSpacing: '-0.02em', }} > - Signal-native streaming for Angular. + LangGraph agent UI for Angular.

- Ship LangGraph agents inside your Angular 20+ app. Thread state, interrupts, branch/history, and tool progress — all surfaced as signals. + Ship LangGraph agents inside your Angular 20+ app with headless chat, durable threads, interrupts, branch/history, tool progress, and generative UI.

diff --git a/apps/website/src/app/api/email-preview/route.ts b/apps/website/src/app/api/email-preview/route.ts index 273198076..d10f0eb4d 100644 --- a/apps/website/src/app/api/email-preview/route.ts +++ b/apps/website/src/app/api/email-preview/route.ts @@ -12,7 +12,7 @@ import { dripWhitepaperFollowupHtml } from '../../../../emails/drip-whitepaper-f const TEMPLATES: Record { subject: string; html: string }> = { 'whitepaper-download': () => ({ - subject: 'Your Angular Agent Readiness Guide', + subject: 'Your Enterprise Agent UI Guide for Angular', html: whitepaperDownloadHtml('Brian'), }), 'newsletter-welcome': () => ({ diff --git a/apps/website/src/app/api/whitepaper-signup/route.ts b/apps/website/src/app/api/whitepaper-signup/route.ts index 26821d71e..db8c4ba4b 100644 --- a/apps/website/src/app/api/whitepaper-signup/route.ts +++ b/apps/website/src/app/api/whitepaper-signup/route.ts @@ -23,8 +23,8 @@ const DOWNLOAD_EMAILS: Record string> = { }; const DOWNLOAD_SUBJECTS: Record = { - overview: 'Your Angular Agent Readiness Guide', - angular: 'Your Enterprise Guide to Agent Streaming', + overview: 'Your Enterprise Agent UI Guide for Angular', + angular: 'Your Enterprise Guide to Agent UI in Angular', render: 'Your Enterprise Guide to Generative UI', chat: 'Your Enterprise Guide to Agent Chat Interfaces', }; diff --git a/apps/website/src/app/layout.tsx b/apps/website/src/app/layout.tsx index 17321ba24..82194bd21 100644 --- a/apps/website/src/app/layout.tsx +++ b/apps/website/src/app/layout.tsx @@ -4,7 +4,14 @@ import './global.css'; import { Nav } from '../components/shared/Nav'; import { Footer } from '../components/shared/Footer'; import { AnnouncementToast } from '../components/shared/AnnouncementToast'; -import { DEFAULT_SOCIAL_IMAGE, SITE_NAME, SITE_ORIGIN } from '../lib/site-metadata'; +import { + DEFAULT_META_DESCRIPTION, + DEFAULT_SOCIAL_IMAGE, + LONG_SUBHEAD, + PRIMARY_TAGLINE, + SITE_NAME, + SITE_ORIGIN, +} from '../lib/site-metadata'; const garamond = EB_Garamond({ subsets: ['latin'], @@ -25,14 +32,14 @@ const mono = JetBrains_Mono({ export const metadata: Metadata = { metadataBase: new URL(SITE_ORIGIN), - title: 'Agent UI for Angular — Signal-Native Streaming for Angular + LangGraph', - description: 'The enterprise Angular agent framework for LangChain. Signal-native streaming, thread persistence, and production patterns for Angular 20+.', + title: PRIMARY_TAGLINE, + description: DEFAULT_META_DESCRIPTION, icons: { icon: 'data:image/svg+xml,🛩️', }, openGraph: { title: 'Agent UI for Angular', - description: 'Signal-native streaming for LangGraph — production patterns your Angular team can own.', + description: LONG_SUBHEAD, type: 'website', siteName: SITE_NAME, url: '/', @@ -41,7 +48,7 @@ export const metadata: Metadata = { twitter: { card: 'summary_large_image', title: 'Agent UI for Angular', - description: 'Signal-native streaming for LangGraph — production patterns your Angular team can own.', + description: LONG_SUBHEAD, images: [DEFAULT_SOCIAL_IMAGE], }, }; diff --git a/apps/website/src/app/opengraph-image.tsx b/apps/website/src/app/opengraph-image.tsx index 47d7c5385..76f7269ba 100644 --- a/apps/website/src/app/opengraph-image.tsx +++ b/apps/website/src/app/opengraph-image.tsx @@ -6,10 +6,11 @@ * file in any route folder. */ import { ImageResponse } from 'next/og'; +import { POSITIONING_PROOF_POINTS, PRIMARY_TAGLINE, SHORT_POSITIONING_DESCRIPTION } from '../lib/positioning'; // Node runtime (not edge) so we can read the bundled Garamond TTF off disk. export const runtime = 'nodejs'; -export const alt = 'Agent UI for Angular — Signal-native streaming for Angular + LangGraph'; +export const alt = PRIMARY_TAGLINE; export const size = { width: 1200, height: 630 }; export const contentType = 'image/png'; @@ -127,8 +128,7 @@ export default async function OpenGraphImage() { marginBottom: 'auto', }} > - Build fullstack agentic Angular apps with signal-native streaming, - runtime adapters, generative UI, and production-ready primitives. + {SHORT_POSITIONING_DESCRIPTION}
{/* Footer row — pill trust signals + wordmark */} @@ -141,9 +141,11 @@ export default async function OpenGraphImage() { }} >
- MIT - LangGraph + AG-UI - Angular 20+ + {POSITIONING_PROOF_POINTS.slice(0, 3).map((proofPoint, index) => ( + + {proofPoint} + + ))}
- provideAgent + agent() give you signals for messages, status, errors, and interrupts. LangGraph and AG-UI adapters share the contract — swap runtimes without rewriting the UI. + provideAgent + agent() give you headless chat, durable threads, interrupts, tool progress, and generative UI. LangGraph and AG-UI adapters share the contract, so teams can swap runtimes without rewriting the Angular surface. } bullets={[ - 'Token-level streaming straight into Angular signals', - 'Thread state, interrupts, tool progress, branch/history', + 'Headless chat and durable thread state', + 'Interrupts, tool progress, branch/history', 'Adapters: LangGraph (@ngaf/langgraph), AG-UI (@ngaf/ag-ui)', - 'One contract, swappable runtimes', + 'One Angular UI layer, swappable runtimes', ]} supportingCards={[ { title: 'provideAgent', description: 'Wire the agent into your app.config.ts.' }, diff --git a/apps/website/src/components/landing/EcosystemStrip.tsx b/apps/website/src/components/landing/EcosystemStrip.tsx index 194663199..ddb1b384f 100644 --- a/apps/website/src/components/landing/EcosystemStrip.tsx +++ b/apps/website/src/components/landing/EcosystemStrip.tsx @@ -144,7 +144,7 @@ export function EcosystemStrip() { margin: 0, }} > - NGAF gives Angular teams signal-native primitives for LangGraph, AG-UI, chat, and generative UI without locking the backend to one provider. + NGAF gives Angular teams production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI without locking the backend to one provider.

diff --git a/apps/website/src/components/landing/Hero.spec.tsx b/apps/website/src/components/landing/Hero.spec.tsx index 2a1747052..2a18585df 100644 --- a/apps/website/src/components/landing/Hero.spec.tsx +++ b/apps/website/src/components/landing/Hero.spec.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { describe, expect, it, vi, beforeEach } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/react'; +import { HERO_SUBHEAD, POSITIONING_PROOF_POINTS } from '../../lib/positioning'; const trackMock = vi.hoisted(() => vi.fn()); const writeTextMock = vi.hoisted(() => vi.fn().mockResolvedValue(undefined)); @@ -50,7 +51,10 @@ describe('Hero', () => { render(); expect(screen.getByRole('heading', { level: 1 }).textContent) .toMatch(/Ship production agent UIs in Angular\./); - expect(screen.getByText(/Signal-native chat, threads, interrupts/)).toBeTruthy(); + expect(screen.getByText(HERO_SUBHEAD)).toBeTruthy(); + for (const proofPoint of POSITIONING_PROOF_POINTS) { + expect(screen.getByText(proofPoint)).toBeTruthy(); + } }); it('primary CTA copies the install command and fires cta_id=hero_install', async () => { diff --git a/apps/website/src/components/landing/Hero.tsx b/apps/website/src/components/landing/Hero.tsx index 216ea86c3..155371a5d 100644 --- a/apps/website/src/components/landing/Hero.tsx +++ b/apps/website/src/components/landing/Hero.tsx @@ -10,6 +10,7 @@ import { BrowserFrame } from '../ui/BrowserFrame'; import { Pill } from '../ui/Pill'; import { track } from '../../lib/analytics/client'; import { analyticsEvents } from '../../lib/analytics/events'; +import { HERO_SUBHEAD, POSITIONING_PROOF_POINTS } from '../../lib/positioning'; const INSTALL_COMMAND = 'npm install @ngaf/chat'; const COPY_FEEDBACK_MS = 1500; @@ -104,7 +105,7 @@ export function Hero() { maxWidth: '54ch', }} > - Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, app telemetry off by default, no React rewrite. + {HERO_SUBHEAD}

@@ -119,12 +120,11 @@ export function Hero() { marginBottom: 12, }} > - MIT - Angular-native Signals - LangGraph + AG-UI - A2UI-compatible - Self-hostable - App telemetry off by default + {POSITIONING_PROOF_POINTS.map((proofPoint, index) => ( + + {proofPoint} + + ))}

- The enterprise Angular agent framework for LangChain. Signal-native streaming built for production Angular 20+. + {SHORT_POSITIONING_DESCRIPTION}

{/* Social links */} diff --git a/apps/website/src/lib/docs-config.ts b/apps/website/src/lib/docs-config.ts index 04db8d8d6..ac8e3e458 100644 --- a/apps/website/src/lib/docs-config.ts +++ b/apps/website/src/lib/docs-config.ts @@ -31,7 +31,7 @@ export const docsConfig: DocsLibrary[] = [ { id: 'agent', title: 'Agent', - description: 'Streaming state management for LangGraph agents', + description: 'Production agent state for Angular UI', sections: [ { title: 'Getting Started', diff --git a/apps/website/src/lib/docs.spec.ts b/apps/website/src/lib/docs.spec.ts index 288f5b6aa..cd9a17a0a 100644 --- a/apps/website/src/lib/docs.spec.ts +++ b/apps/website/src/lib/docs.spec.ts @@ -135,6 +135,7 @@ describe('website docs bindings', () => { '@ngaf/a2ui', '@ngaf/ag-ui', '@ngaf/chat', + '@ngaf/chat/debug', '@ngaf/chat/testing', '@ngaf/langgraph', '@ngaf/licensing', diff --git a/apps/website/src/lib/positioning.ts b/apps/website/src/lib/positioning.ts new file mode 100644 index 000000000..fc9750cf6 --- /dev/null +++ b/apps/website/src/lib/positioning.ts @@ -0,0 +1,13 @@ +export const PRIMARY_TAGLINE = 'Agent UI for Angular — Production-ready chat, threads, and generative UI for AI agents.'; +export const LONG_SUBHEAD = 'The enterprise-grade Angular UI framework for LangGraph and AG-UI-compatible agents: headless chat, durable threads, interrupts, subagents, planning, memory, and generative UI through json-render and A2UI-compatible specs.'; +export const HERO_SUBHEAD = 'Build enterprise-grade agent experiences in Angular: headless chat, durable threads, interrupts, subagents, planning, memory, and generative UI for LangGraph, AG-UI, json-render, and A2UI-compatible backends.'; +export const POSITIONING_PROOF_POINTS = [ + 'LangGraph + AG-UI', + 'Durable threads', + 'Interrupts', + 'Subagents', + 'Planning + memory', + 'json-render + A2UI', +] as const; +export const SHORT_POSITIONING_DESCRIPTION = 'Production-ready chat, durable threads, interrupts, subagents, planning, memory, and generative UI for Angular agent apps.'; +export const DEFAULT_META_DESCRIPTION = SHORT_POSITIONING_DESCRIPTION; diff --git a/apps/website/src/lib/site-metadata.spec.ts b/apps/website/src/lib/site-metadata.spec.ts new file mode 100644 index 000000000..990e52f54 --- /dev/null +++ b/apps/website/src/lib/site-metadata.spec.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; +import { + DEFAULT_META_DESCRIPTION, + HERO_SUBHEAD, + LONG_SUBHEAD, + POSITIONING_PROOF_POINTS, + PRIMARY_TAGLINE, + SHORT_POSITIONING_DESCRIPTION, + createPageMetadata, +} from './site-metadata'; + +describe('site positioning copy', () => { + it('exports the approved primary tagline and supporting copy', () => { + expect(PRIMARY_TAGLINE).toBe('Agent UI for Angular — Production-ready chat, threads, and generative UI for AI agents.'); + expect(LONG_SUBHEAD).toContain('enterprise-grade Angular UI framework'); + expect(LONG_SUBHEAD).toContain('LangGraph and AG-UI-compatible agents'); + expect(LONG_SUBHEAD).toContain('json-render and A2UI-compatible specs'); + expect(HERO_SUBHEAD).toContain('headless chat, durable threads, interrupts, subagents, planning, memory'); + expect(POSITIONING_PROOF_POINTS).toEqual([ + 'LangGraph + AG-UI', + 'Durable threads', + 'Interrupts', + 'Subagents', + 'Planning + memory', + 'json-render + A2UI', + ]); + expect(DEFAULT_META_DESCRIPTION).toBe(SHORT_POSITIONING_DESCRIPTION); + }); + + it('uses canonical copy in generated page metadata', () => { + const metadata = createPageMetadata({ + title: PRIMARY_TAGLINE, + description: DEFAULT_META_DESCRIPTION, + pathname: '/', + type: 'website', + }); + + expect(metadata.title).toBe(PRIMARY_TAGLINE); + expect(metadata.description).toBe(DEFAULT_META_DESCRIPTION); + expect(metadata.openGraph?.description).toBe(DEFAULT_META_DESCRIPTION); + expect(metadata.twitter?.description).toBe(DEFAULT_META_DESCRIPTION); + }); +}); diff --git a/apps/website/src/lib/site-metadata.ts b/apps/website/src/lib/site-metadata.ts index bc34fa29f..404125130 100644 --- a/apps/website/src/lib/site-metadata.ts +++ b/apps/website/src/lib/site-metadata.ts @@ -6,6 +6,14 @@ import { getAllPosts } from './blog'; export const SITE_ORIGIN = 'https://threadplane.ai'; export const SITE_NAME = 'Agent UI for Angular'; export const DEFAULT_SOCIAL_IMAGE = '/opengraph-image'; +export { + DEFAULT_META_DESCRIPTION, + HERO_SUBHEAD, + LONG_SUBHEAD, + POSITIONING_PROOF_POINTS, + PRIMARY_TAGLINE, + SHORT_POSITIONING_DESCRIPTION, +} from './positioning'; export function getCanonicalPath(pathname: string): string { if (pathname === '/') return '/'; diff --git a/apps/website/src/lib/solutions-data.ts b/apps/website/src/lib/solutions-data.ts index 1b301a6d2..19f6abb1e 100644 --- a/apps/website/src/lib/solutions-data.ts +++ b/apps/website/src/lib/solutions-data.ts @@ -64,7 +64,7 @@ export const SOLUTIONS: SolutionConfig[] = [ { library: 'Agent', pkg: '@ngaf/langgraph', - role: 'Signal-native streaming with first-class interrupt support. Every agent action can require human approval before execution. Thread persistence preserves the full record of every tool call and state transition.', + role: 'Production agent state with first-class interrupt support. Every agent action can require human approval before execution. Durable thread persistence preserves the full record of every tool call and state transition.', }, { library: 'Render', diff --git a/docs/gtm/icp.md b/docs/gtm/icp.md index 24964b60d..12e527005 100644 --- a/docs/gtm/icp.md +++ b/docs/gtm/icp.md @@ -23,7 +23,8 @@ These are the signals to instrument first in Spec 1. Baseline rates between them - Angular Reddit, ng-conf community, Angular Discord/Slack equivalents, LangChain forum, AG-UI / A2UI communities, dev.to/medium. **What they need to see in 30 seconds** -- "Not another agent runtime. The Angular UI layer for LangGraph/AG-UI/A2UI." +- "Agent UI for Angular — Production-ready chat, threads, and generative UI for AI agents." +- `LangGraph + AG-UI · Durable threads · Interrupts · Subagents · Planning + memory · json-render + A2UI` - One install command. - A working demo they can clone. diff --git a/docs/gtm/messaging.md b/docs/gtm/messaging.md index 866ea5abe..e7b058594 100644 --- a/docs/gtm/messaging.md +++ b/docs/gtm/messaging.md @@ -4,21 +4,21 @@ ## Positioning statement (durable) -> For Angular teams building AI agents on LangGraph, AG-UI, or custom backends, ThreadPlane is the open-source agent UI framework that turns streaming agent events into production-grade Angular experiences: chat, tool progress, approvals, threads, generative UI, fallbacks, observability, and tests. Unlike React-first agent UI stacks or raw streaming SDKs, ThreadPlane is signal-native, DI-friendly, design-system-first, self-hostable, and built for enterprise Angular apps. +> For Angular teams building AI agents on LangGraph, AG-UI, or custom backends, ThreadPlane is the open-source agent UI framework that turns streaming agent events into production-ready Angular experiences: chat, durable threads, interrupts, subagents, planning, memory, generative UI, fallbacks, observability, and tests. Unlike React-first agent UI stacks or raw streaming SDKs, ThreadPlane is Angular-native, DI-friendly, design-system-first, self-hostable, and built for enterprise Angular apps. ## Hero (locked for Spec 2 to implement) **H1:** Ship production agent UIs in Angular. -**Subhead:** Signal-native chat, threads, interrupts, tool progress, and generative UI for LangGraph, AG-UI, and A2UI. MIT-licensed, self-hostable, app telemetry off by default, no React rewrite. +**Subhead:** Build enterprise-grade agent experiences in Angular: headless chat, durable threads, interrupts, subagents, planning, memory, and generative UI for LangGraph, AG-UI, json-render, and A2UI-compatible backends. **Primary CTA:** `Install @ngaf/chat` (copy-to-clipboard, fires `marketing:cta_click` with `cta_id=hero_install`, `track=developer`). **Secondary CTA:** `Talk to our engineers` (routes to `/contact?source=home_hero&track=enterprise`, fires `marketing:cta_click` with `cta_id=hero_talk_to_engineers`, `track=enterprise`). -**Proof row:** `MIT · Angular-native Signals · LangGraph + AG-UI · A2UI-compatible · Self-hostable · App telemetry off by default` +**Proof row:** `LangGraph + AG-UI · Durable threads · Interrupts · Subagents · Planning + memory · json-render + A2UI` -**Subline under proof row:** *Not another backend agent runtime. Keep LangGraph, Genkit, Mastra, CrewAI, or your own service. ThreadPlane solves the Angular UI layer.* +**Subline under proof row:** *Not another backend agent runtime. Keep LangGraph, Genkit, Mastra, CrewAI, or your own service. ThreadPlane solves the production Angular UI layer.* ## The five durable differentiation points @@ -64,7 +64,7 @@ Hidden attribution fields (populated by URL params + referrer): `source_page`, ` ## Launch narrative (Spec 6 spine) -> Angular teams are building agents, but the last mile is still messy: streaming state, tool progress, interrupts, threads, generated UI, fallbacks, and tests. React has mature examples. Backend agent frameworks have protocols. Angular teams need something that speaks Signals, DI, templates, standalone components, and enterprise design systems. ThreadPlane is an MIT-licensed agent UI framework for Angular that connects LangGraph, AG-UI, A2UI, and custom backends to production-ready Angular surfaces. +> Angular teams are building agents, but the last mile is still messy: streaming state, tool progress, interrupts, durable threads, subagents, planning, memory, generated UI, fallbacks, and tests. React has mature examples. Backend agent frameworks have protocols. Angular teams need something that speaks Signals, DI, templates, standalone components, and enterprise design systems. ThreadPlane is an MIT-licensed agent UI framework for Angular that connects LangGraph, AG-UI, A2UI, and custom backends to production-ready Angular surfaces. ## Avoid