Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<p align="center">
<img
src="https://threadplane.ai/assets/hero.svg"
alt="Threadplane — the AI agent UI framework for Angular"
alt="Threadplane — the open-source thread-plane for agents"
width="100%"
/>
</p>

<p align="center">
<em>The AI agent UI framework for Angular.</em>
<em>The open-source thread-plane for agents.</em>
</p>

<p align="center">
Expand All @@ -33,8 +33,8 @@

---

**Threadplane is the open-source Angular AI agent UI framework.** Chat, durable
threads, human approvals, tool progress, subagents, and generative UI — built on
**Threadplane is the open-source thread-plane for agents.** Chat, durable
threads, persistence, human approvals, tool progress, subagents, and generative UI — built on
Angular Signals and dependency injection, for Angular 20–22. Your backend stays
where it is: Threadplane adapts a LangGraph or AG-UI agent into a runtime-neutral
`Agent` contract that the UI consumes, and renders generated UI with the design
Expand Down
5 changes: 5 additions & 0 deletions apps/website/e2e/home-hero.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ test.describe('homepage hero', () => {
await page.goto('/');
const demo = page.locator('[data-hero-demo]');
await expect(demo.locator('img')).toHaveAttribute('src', '/screenshots/hero-walkthrough-poster.webp');
// The frame mounts on a 25%-visibility threshold, and the three-line H1
// leaves the stage short of that at the default viewport. Scroll it in, as
// the mobile sibling below already does, so this asserts the mount and not
// the fold position.
await demo.scrollIntoViewIfNeeded();
await expect(demo.locator('iframe')).toHaveAttribute('src', 'https://demo.threadplane.ai/hero');
});

Expand Down
7 changes: 4 additions & 3 deletions apps/website/e2e/website.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ async function expectNoHorizontalOverflow(
test('landing page renders hero headline', async ({ page }) => {
await page.goto('/');
await expect(page.locator('#hero-heading')).toBeVisible();
await expect(page.locator('#hero-heading')).toHaveText('The AI agent UI framework for Angular.');
const headline = await page.locator('#hero-heading').textContent();
expect(headline?.toLowerCase()).toContain('angular');
await expect(page.locator('#hero-heading')).toHaveText('The open-source thread-plane for agents.');
// The tagline stopped naming Angular on 2026-09-06, so the guard that the
// hero says which framework this is moved to the eyebrow directly above it.
await expect(page.locator('.hero-eyebrow')).toContainText('Angular');
});

test('landing page renders the dark proof band', async ({ page }) => {
Expand Down
8 changes: 6 additions & 2 deletions apps/website/src/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* If either changes, re-resolve them here.
*/
import { ImageResponse } from 'next/og';
import { HERO_H1, HERO_SUBHEAD, POSITIONING_PROOF_POINTS, PRIMARY_TAGLINE } from '../lib/positioning';
import { HERO_H1_LINES, HERO_SUBHEAD, POSITIONING_PROOF_POINTS, PRIMARY_TAGLINE } from '../lib/positioning';
import { loadCardFonts } from './og-font';

// Node runtime (not edge) so we can read the bundled Garamond TTF off disk.
Expand Down Expand Up @@ -159,6 +159,8 @@ export default async function OpenGraphImage() {
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: 34,
fontSize: 52,
fontWeight: 600,
Expand All @@ -167,7 +169,9 @@ export default async function OpenGraphImage() {
color: TOKENS.textPrimary,
}}
>
{HERO_H1}
{HERO_H1_LINES.map((line) => (
<div key={line} style={{ display: 'flex' }}>{line}</div>
))}
</div>

{/* What you get. Wrapped to two lines on purpose — see the header. */}
Expand Down
11 changes: 9 additions & 2 deletions apps/website/src/components/landing/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Button } from '../ui/Button';
import { trackCtaClick } from '../../lib/analytics/client';
import {
HERO_EYEBROW,
HERO_H1,
HERO_H1_LINES,
HERO_PRIMARY_LABEL,
HERO_SECONDARY_HREF,
HERO_SECONDARY_LABEL,
Expand All @@ -31,7 +31,14 @@ export function Hero() {
<Container>
<div className="hero-stack">
<Eyebrow tone="accent" className="hero-eyebrow">{HERO_EYEBROW}</Eyebrow>
<h1 id="hero-heading" className="hero-heading">{HERO_H1}</h1>
<h1 id="hero-heading" className="hero-heading">
{HERO_H1_LINES.map((line, i) => (
<React.Fragment key={line}>
{i > 0 ? ' ' : ''}
<span className="hero-heading-line">{line}</span>
</React.Fragment>
))}
</h1>
<p className="hero-subhead">
{HERO_SUBHEAD_SEGMENTS.map((segment) =>
segment.highlight ? (
Expand Down
14 changes: 10 additions & 4 deletions apps/website/src/lib/positioning.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
formatAngularRange,
HERO_EYEBROW,
HERO_H1,
HERO_H1_LINES,
HERO_PRIMARY_LABEL,
HERO_SECONDARY_HREF,
HERO_SECONDARY_LABEL,
Expand Down Expand Up @@ -46,16 +47,21 @@ function parses(code: string): boolean {

describe('positioning: hero copy', () => {
it('names the exact category in eyebrow, H1, title and description', () => {
expect(HERO_EYEBROW).toBe('Open-source · Angular · LangGraph & AG-UI');
expect(HERO_H1).toBe('The AI agent UI framework for Angular.');
expect(HERO_EYEBROW).toBe('Angular · LangGraph & AG-UI');
expect(HERO_H1).toBe('The open-source thread-plane for agents.');
expect(HERO_SUBHEAD).toBe('Chat, threads, approvals, and generative UI on Signals and DI. Your backend stays where it is.');
expect(HOME_TITLE).toBe('Threadplane — Angular AI Agent UI Framework');
expect(HOME_TITLE).toBe('Threadplane — The open-source thread-plane for agents');
expect(HOME_DESCRIPTION).toBe(
'Open-source Angular AI agent UI framework for LangGraph and AG-UI: chat, durable threads, human approvals, and generative UI with Signals and DI.',
'The open-source thread-plane for agents: chat, durable threads, persistence, human approvals, and generative UI for Angular, on LangGraph and AG-UI.',
);
expect(HOME_DESCRIPTION.length).toBeLessThanOrEqual(160);
});

it('H1 lines join back to HERO_H1 on three lines', () => {
expect(HERO_H1_LINES).toHaveLength(3);
expect(HERO_H1_LINES.join(' ')).toBe(HERO_H1);
});

it('subhead segments join back to HERO_SUBHEAD, with exactly one highlight', () => {
// The segments exist only so Hero.tsx can marker-highlight one phrase.
// If they ever stop reassembling the source-of-truth string, the rendered
Expand Down
17 changes: 12 additions & 5 deletions apps/website/src/lib/positioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
import { WEBSITE_SUPPORTED_ANGULAR_MAJORS } from '../components/pricing/angular-support.mjs';
import type { StageBeat } from './stage-beats';

export const HERO_EYEBROW = 'Open-source · Angular · LangGraph & AG-UI';
export const HERO_H1 = 'The AI agent UI framework for Angular.';
export const HERO_EYEBROW = 'Angular · LangGraph & AG-UI';
export const HERO_H1 = 'The open-source thread-plane for agents.';
/**
* The H1 broken where it is meant to break: three lines, one thought each.
* HERO_H1 stays the single source of truth — positioning.spec.ts asserts the
* lines join back to it with single spaces, so the rendered heading, the
* <title> and the social card cannot drift apart.
*/
export const HERO_H1_LINES: readonly string[] = ['The open-source', 'thread-plane', 'for agents.'];
export const HERO_SUBHEAD =
'Chat, threads, approvals, and generative UI on Signals and DI. Your backend stays where it is.';

Expand All @@ -28,13 +35,13 @@ export const HERO_SECONDARY_LABEL = 'See it running in the docs →';
export const HERO_SECONDARY_HREF = '/docs/chat/guides/generative-ui?mode=run';

/** Kept for layout.tsx default title and the OG image alt. */
export const PRIMARY_TAGLINE = 'Threadplane — Angular AI Agent UI Framework';
export const PRIMARY_TAGLINE = 'Threadplane — The open-source thread-plane for agents';
export const HOME_TITLE = PRIMARY_TAGLINE;
export const HOME_DESCRIPTION =
'Open-source Angular AI agent UI framework for LangGraph and AG-UI: chat, durable threads, human approvals, and generative UI with Signals and DI.';
'The open-source thread-plane for agents: chat, durable threads, persistence, human approvals, and generative UI for Angular, on LangGraph and AG-UI.';
/** Longer form used by layout.tsx OG/Twitter defaults and the About page. */
export const LONG_SUBHEAD =
'Threadplane is the open-source Angular AI agent UI framework: signal-native chat, durable threads, human approvals, tool progress, subagents, and generative UI for LangGraph and AG-UI backends — without replacing your backend or design system.';
'Threadplane is the open-source thread-plane for agents: signal-native chat, durable threads, persistence, human approvals, tool progress, subagents, and generative UI for Angular, on LangGraph and AG-UI — without replacing your backend or design system.';

// ── Trust line (values verified by positioning.spec.ts + angular-support-copy.spec.ts) ──
export function formatAngularRange(majors: readonly number[]): string {
Expand Down
8 changes: 4 additions & 4 deletions apps/website/src/lib/site-metadata.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { resolveWebsiteDir } from './website-dir';

describe('site positioning copy', () => {
it('exports the approved primary tagline and supporting copy', () => {
expect(PRIMARY_TAGLINE).toBe('Threadplane — Angular AI Agent UI Framework');
expect(LONG_SUBHEAD).toContain('open-source Angular AI agent UI framework');
expect(PRIMARY_TAGLINE).toBe('Threadplane — The open-source thread-plane for agents');
expect(LONG_SUBHEAD).toContain('open-source thread-plane for agents');
expect(LONG_SUBHEAD).toContain('LangGraph and AG-UI');
expect(HERO_SUBHEAD).toBe(
'Chat, threads, approvals, and generative UI on Signals and DI. Your backend stays where it is.',
Expand Down Expand Up @@ -65,7 +65,7 @@ describe('site positioning copy', () => {
type: 'website',
});

expect(metadata.title).toBe('Threadplane — Angular AI Agent UI Framework');
expect(metadata.title).toBe('Threadplane — The open-source thread-plane for agents');
expect(metadata.description).toBe(HOME_DESCRIPTION);
});
});
Expand Down Expand Up @@ -142,7 +142,7 @@ describe('createPageMetadata article fields', () => {
expect(openGraph.images[0].url).toBe('/opengraph-image');
expect(openGraph.images[0].width).toBe(1200);
expect(openGraph.images[0].height).toBe(630);
expect(openGraph.images[0].alt).toMatch(/agent UI framework for Angular/);
expect(openGraph.images[0].alt).toMatch(/open-source thread-plane for agents/);
});

it('accepts a page-specific social image', () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/website/src/lib/site-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const DEFAULT_SOCIAL_IMAGE_META = {
url: DEFAULT_SOCIAL_IMAGE,
width: 1200,
height: 630,
alt: 'Threadplane — the AI agent UI framework for Angular. Chat, threads, approvals, and generative UI on Signals and DI, for LangGraph and AG-UI.',
alt: 'Threadplane — the open-source thread-plane for agents. Chat, durable threads, persistence, human approvals, and generative UI for Angular, on LangGraph and AG-UI.',
} as const;
export {
CODING_AGENT_PROMPT,
Expand Down
4 changes: 4 additions & 0 deletions apps/website/src/styles/landing.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
margin-bottom: 24px;
letter-spacing: -0.02em;
}
/* One line per thought; the space between the spans keeps textContent equal to HERO_H1. */
.hero-heading-line {
display: block;
}
.hero-subhead {
font-family: var(--font-inter);
font-size: var(--text-body-lg);
Expand Down
4 changes: 2 additions & 2 deletions docs/gtm/messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## 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-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.
> For Angular teams building AI agents on LangGraph, AG-UI, or custom backends, Threadplane is the open-source thread-plane for agents: it 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)

Expand Down Expand Up @@ -63,7 +63,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, 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.
> 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 the MIT-licensed thread-plane for agents: it connects LangGraph, AG-UI, A2UI, and custom backends to production-ready Angular surfaces.

## Avoid

Expand Down
33 changes: 33 additions & 0 deletions docs/superpowers/specs/2026-09-06-tagline-thread-plane-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Tagline and description: "the open-source thread-plane for agents"

Date: 2026-09-06. Status: approved in conversation, implemented in the same branch.

## Decision

The public tagline changes from "The AI agent UI framework for Angular." to:

> The open-source thread-plane for agents.

The site description (meta description, npm/GitHub summary) becomes:

> The open-source thread-plane for agents: chat, durable threads, persistence, human approvals, and generative UI for Angular, on LangGraph and AG-UI.

Both strings were supplied by Brian. The description is 159 characters, inside the 160-character meta budget enforced by `positioning.spec.ts`.

## Scope ("everywhere")

`apps/website/src/lib/positioning.ts` stays the single source of truth for the website. Derived strings change with it:

- `HERO_H1` = the tagline, rendered on three lines from `HERO_H1_LINES` ("The open-source" / "thread-plane" / "for agents.") in the hero and the OG card. `HERO_EYEBROW` drops "Open-source" so the hero does not say it twice.
- `PRIMARY_TAGLINE` / `HOME_TITLE` = `Threadplane — The open-source thread-plane for agents` (em dash kept: `opengraph-image.tsx` splits on it for the brand name).
- `HOME_DESCRIPTION` = the description above.
- `LONG_SUBHEAD` (OG/Twitter default, About page) = the description expanded with the capability list, ending in "without replacing your backend or design system".
- `DEFAULT_SOCIAL_IMAGE_META.alt` in `site-metadata.ts` restated on the tagline.

Outside the website, every literal "the AI agent UI framework for Angular" is replaced with "the open-source thread-plane for agents": root `README.md` (hero alt, tagline line, lead paragraph), the seven `libs/*/README.md` intros, `docs/gtm/messaging.md` positioning statement, and `gtm.md` §1–2. The GitHub repository description is set to the tagline plus description.

Tests pinning the old strings (`positioning.spec.ts`, `site-metadata.spec.ts`, `e2e/website.spec.ts`) are updated to the new ones. The public-copy contract gate has no rule touching these phrases.

## Out of scope

npm `package.json` descriptions (they describe each package, not the product), historical specs/plans under `docs/superpowers`, blog posts, and the `hero.svg` artwork (it carries no text).
3 changes: 2 additions & 1 deletion gtm.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@

## 1. What we are

Threadplane is the production agent UI framework for Angular teams. It turns LangGraph, AG-UI, A2UI, and custom agent streams into real Angular experiences — chat, threads, tool progress, human approvals, generative UI, fallbacks, observability, tests — without rewriting in React or adopting a proprietary cloud.
Threadplane is the open-source thread-plane for agents, built for Angular teams. It turns LangGraph, AG-UI, A2UI, and custom agent streams into real Angular experiences — chat, threads, tool progress, human approvals, generative UI, fallbacks, observability, tests — without rewriting in React or adopting a proprietary cloud.

## 2. Category

- **Primary:** Threadplane
- **Tagline (2026-09-06):** "The open-source thread-plane for agents." The description that follows it everywhere: "The open-source thread-plane for agents: chat, durable threads, persistence, human approvals, and generative UI for Angular, on LangGraph and AG-UI."
- **Secondary (only after "Agent UI"):** Angular Agent UI Framework
- **Do not use:** "Threadplane" (ambiguous with backend runtimes and coding agents), "Enterprise Angular agent framework" (reads sales-first).

Expand Down
2 changes: 1 addition & 1 deletion libs/a2ui/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @threadplane/a2ui

The A2UI (Agent-to-UI) protocol layer behind generative UI in [Threadplane](https://github.com/cacheplane/angular-agent-framework), the AI agent UI framework for Angular, targeting the **A2UI v0.9.1 stable release**. It defines the wire format an agent uses to drive a UI surface, plus the parser and resolver that read it — framework-agnostic, pure TypeScript, no Angular dependency, usable in any TypeScript environment.
The A2UI (Agent-to-UI) protocol layer behind generative UI in [Threadplane](https://github.com/cacheplane/angular-agent-framework), the open-source thread-plane for agents, targeting the **A2UI v0.9.1 stable release**. It defines the wire format an agent uses to drive a UI surface, plus the parser and resolver that read it — framework-agnostic, pure TypeScript, no Angular dependency, usable in any TypeScript environment.

<p align="center">
<a href="https://www.npmjs.com/package/@threadplane/a2ui">
Expand Down
2 changes: 1 addition & 1 deletion libs/ag-ui/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @threadplane/ag-ui

The AG-UI adapter for [Threadplane](https://github.com/cacheplane/angular-agent-framework), the AI agent UI framework for Angular. Wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract that `@threadplane/chat` consumes, so any AG-UI-compatible backend drives the same chat surface.
The AG-UI adapter for [Threadplane](https://github.com/cacheplane/angular-agent-framework), the open-source thread-plane for agents. Wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract that `@threadplane/chat` consumes, so any AG-UI-compatible backend drives the same chat surface.

<p align="center">
<a href="https://www.npmjs.com/package/@threadplane/ag-ui">
Expand Down
2 changes: 1 addition & 1 deletion libs/chat/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @threadplane/chat

The chat surface of [Threadplane](https://github.com/cacheplane/angular-agent-framework), the AI agent UI framework for Angular. Headless primitives plus opinionated compositions read a runtime-neutral `Agent` contract, so the UI is built once and runs over LangGraph or AG-UI without changes. Angular 20–22, on Signals and DI.
The chat surface of [Threadplane](https://github.com/cacheplane/angular-agent-framework), the open-source thread-plane for agents. Headless primitives plus opinionated compositions read a runtime-neutral `Agent` contract, so the UI is built once and runs over LangGraph or AG-UI without changes. Angular 20–22, on Signals and DI.

<p>
<a href="https://www.npmjs.com/package/@threadplane/chat">
Expand Down
Loading
Loading