Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4cd7fb7
docs: spec for docs visual design — callouts, diagram kit, audit
blove Sep 1, 2026
e854b60
docs: implementation plan for docs visual design
blove Sep 1, 2026
4343087
feat(website): header-band docs callouts, retiring the left border
blove Sep 1, 2026
ad6a593
fix(website): callout band a11y — AA warning contrast + sr-only kind …
blove Sep 1, 2026
732b017
feat(website): SVG diagram kit primitives on a dot grid
blove Sep 1, 2026
94fe735
refactor(website): harden diagram-kit API — typed arrows, contracts, …
blove Sep 1, 2026
5c58a4c
feat(website): StackDiagram — the canonical stack schematic
blove Sep 1, 2026
a01ae11
fix(website): StackDiagram text fit + arrow gap; harden highlight tests
blove Sep 1, 2026
d1992b5
refactor(website): replace ascii + HTML arch diagrams with StackDiagram
blove Sep 1, 2026
7b104b5
fix(website): align prose and captions with the swapped-in StackDiagram
blove Sep 1, 2026
e308e32
docs(website): How-it-fits stack diagrams for langgraph, chat, runtim…
blove Sep 1, 2026
ff5cdd8
fix(website): honest runtimes caption; retire the chat intro's stale …
blove Sep 1, 2026
e0d7f55
docs(website): kit pipeline diagram for AG-UI architecture page
blove Sep 1, 2026
cf1dc9d
docs(website): A2UI message-flow diagram for overview and intro
blove Sep 1, 2026
fd44de7
fix(website): surface store belongs to chat, not a2ui, in A2uiMessage…
blove Sep 2, 2026
db5a706
docs(website): render pipeline + render-vs-a2ui kit diagrams
blove Sep 2, 2026
c5f4104
fix(website): render diagrams — break edges under pills, honest entry…
blove Sep 2, 2026
a924099
docs(website): middleware + telemetry How-it-fits diagrams
blove Sep 2, 2026
0d3f6c4
fix(website): vertical middleware diagram — pill no longer overlaps n…
blove Sep 2, 2026
eb657d3
fix(website): honest telemetry destinations; runtime-neutral middlewa…
blove Sep 2, 2026
138cd20
feat(website): homepage architecture section with the master stack di…
blove Sep 2, 2026
739201d
fix(website): StackDiagramSection reuses SectionHeader; distinct head…
blove Sep 2, 2026
a99931c
feat(website): stack diagram sections on the adapter landing pages
blove Sep 2, 2026
682e906
fix(website): ag-ui section copy — distinct angle, seven-runtime caption
blove Sep 2, 2026
19dec6e
chore(website): final polish — token font, barrel wiring, honest labels
blove Sep 2, 2026
e67f1ba
test(website): gate the permalink-glyph e2e on workspace hydration
blove Sep 2, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ The whole protocol is seventeen events; you can hold it in your head.

The seams come first, before any code.

<AgUiArchDiagram />
<StackDiagram highlight="ag-ui" caption="Backend speaks AG-UI over SSE → the adapter exposes the Agent contract → the chat UI renders." />

Three boxes. Two seams.
Two seams: the wire, and the contract.

**The backend.** Whatever you want, as long as it can emit AG-UI events.
LangGraph, CrewAI, Mastra, Microsoft Agent Framework, Pydantic AI, AG2, AWS Strands, Agno. They all have first-party or partnership adapters.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ They describe different topologies.
LangGraph addresses a platform API and names a graph on it.
AG-UI addresses one HTTP endpoint that streams Server-Sent Events, and the endpoint is the whole address space.

<AgUiArchDiagram />
<StackDiagram highlight="contract" caption="Two adapters, one contract — the UI cannot tell which runtime answered." />

One SSE route to allowlist, proxy, and monitor is a smaller operational surface than a platform API with a run lifecycle behind it.
Whether that simplicity is a feature depends entirely on the next section.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

It does not render Angular components. It does not register handler functions. It does not decide how an agent should respond to a button click. Those jobs sit in `@threadplane/chat` and `@threadplane/render`.

## How it fits

<A2uiMessageFlow />

## What the package owns

The public entry point exports four groups of tools:
Expand Down
18 changes: 1 addition & 17 deletions apps/website/content/docs/ag-ui/concepts/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,7 @@

The package takes an AG-UI `AbstractAgent`, listens to its protocol events, and exposes the runtime-neutral `Agent` contract that the chat components already understand.

```text
Angular component
|
v
@threadplane/chat Agent contract
| messages, status, isLoading, error, toolCalls, state, events$
|
v
@threadplane/ag-ui toAgent()
| reduces AG-UI events into Angular signals
|
v
@ag-ui/client AbstractAgent
|
v
AG-UI backend or in-process fake agent
```
<AgUiArchitecturePipeline />

## The boundary

Expand Down
18 changes: 5 additions & 13 deletions apps/website/content/docs/ag-ui/getting-started/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,10 @@ The [AG-UI demo](https://ag-ui.threadplane.ai) runs this exact chat surface agai

## How it fits

```text
@threadplane/chat
<chat>, <chat-message-list>, <chat-input>
@threadplane/chat/debug -> <chat-debug>
|
| Agent contract (signals + events$)
|
+--> @threadplane/langgraph
| LangGraphAgent -> LangGraph Platform
|
+--> @threadplane/ag-ui
toAgent(AbstractAgent) -> AG-UI backend
```
<StackDiagram
highlight="ag-ui"
caption="Backend speaks AG-UI over SSE → the adapter exposes the signal-shaped Agent contract → the chat UI renders."
/>

## What you get

Expand Down Expand Up @@ -57,3 +48,4 @@ Out of scope for now (use `@threadplane/langgraph` if you need LangGraph Platfor
- [Fake Agent](/docs/ag-ui/guides/fake-agent) - build UI without a backend.
- [Citations](/docs/ag-ui/guides/citations) - attach sources through `state.citations`.
- [Troubleshooting](/docs/ag-ui/guides/troubleshooting) - debug provider, stream, state, and citation issues.
- [Chat Debug](/docs/chat/components/chat-debug) - inspect the raw event stream with `<chat-debug>`.
9 changes: 1 addition & 8 deletions apps/website/content/docs/chat/a2ui/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,7 @@ When an assistant message starts with `---a2ui_JSON---`, the chat streaming pipe

## Runtime Flow

```text
assistant text starts with ---a2ui_JSON---
-> content classifier switches to A2UI mode
-> createA2uiMessageParser() parses JSONL messages
-> createA2uiSurfaceStore() applies those messages by surface id
-> ChatComponent passes surface + state into A2uiSurfaceComponent
-> A2uiSurfaceComponent renders progressive state through your catalog
```
<A2uiMessageFlow />

This is why A2UI sits between chat and render. Chat owns message streaming. A2UI owns the protocol shapes. `A2uiSurfaceComponent` turns the accumulated surface state into a render spec and delegates to `@threadplane/render`, so handlers, render events, and json-render state bindings use the same path for both the preferred `state` input and the legacy `surface` input.

Expand Down
23 changes: 8 additions & 15 deletions apps/website/content/docs/chat/getting-started/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
This guide explains the library's two-tier architecture, how it relates to the rest of the Threadplane stack, and when to reach for the all-in-one composition versus assembling primitives yourself.
</Callout>

## How it fits

<StackDiagram
highlight="chat"
caption="The chat components consume only the Agent contract — the runtime below is swappable."
/>

## Two-Tier Architecture

The library is organized into two layers: **primitives** and **compositions**.
Expand Down Expand Up @@ -42,21 +49,7 @@ Compositions are opinionated, styled components that combine primitives into rea

## How the Stack Fits Together

`@threadplane/chat` sits between your application and two other Threadplane libraries:

```
Your App
|
v
@threadplane/chat <-- UI components (this library)
| |
v v
@threadplane/langgraph @threadplane/render
(streaming state) (generative UI)
|
v
LangGraph Platform
```
`@threadplane/chat` sits between your application and the adapters below the Agent-contract seam shown above; alongside it, `@threadplane/render` turns specs in assistant messages into your components.

- **`@threadplane/langgraph`** provides `provideAgent()` and `injectAgent()` returning a `LangGraphAgent`, which satisfies the `Agent` contract consumed by `@threadplane/chat`. The base `Agent` contract exposes reactive Signals for `messages()`, `status()`, `isLoading()`, `error()`, `toolCalls()`, and `state()`, plus optional `interrupt()` and `subagents()`. Adapters can layer on more: LangGraph implements `AgentWithHistory`, adding `history()` for checkpoint time-travel.

Expand Down
11 changes: 4 additions & 7 deletions apps/website/content/docs/langgraph/concepts/agent-contract.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ The `Agent` contract is the spine between runtime adapters and chat UI.

The UI shouldn't care whether a response came from LangGraph Platform, AG-UI, a local mock, or a custom HTTP service. That's the point. The boundary is explicit: adapters translate runtime events into Angular signals and a small action surface.

```text
LangGraph Platform -- @threadplane/langgraph --+
+-- Agent -- @threadplane/chat
AG-UI backend ------ @threadplane/ag-ui -------+

custom backend ------ your adapter -----+
```
<StackDiagram
highlight="contract"
caption="Every adapter — including one you write — meets the UI at the same Agent contract."
/>

## The Contract Surface

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
This guide walks you through the complete workflow: build a LangGraph agent in Python, run it locally, connect it to an Angular app with `injectAgent()`, and deploy to production.
</Callout>

## How it fits

<StackDiagram
highlight="langgraph"
caption="The LangGraph adapter implements the Agent contract natively against LangGraph Platform — threads, runs, and checkpoints included."
/>

## What is `injectAgent()`?

`injectAgent()` is an Angular function that creates a reactive, streaming connection to a LangGraph agent. Configured globally via `provideAgent({...})` at bootstrap, it returns an object whose properties are Angular Signals — meaning your templates update automatically as the agent streams responses, token by token.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import {

There is no root `@threadplane/middleware` JavaScript entry point. Import from `@threadplane/middleware/langgraph`.

## How it fits

<MiddlewareHowItFits />

## What it does

The LangGraph entry points read a client tool catalog from graph state, convert it into OpenAI function-tool objects, bind those tool stubs onto your chat model, and route client-tool calls to `END` so the browser can execute them.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,7 @@ This matters because the tradeoff is not "which renderer is better." The tradeof

## The Layers

```text
@threadplane/render
renders a Spec using an Angular registry, state store, functions, and handlers

@threadplane/a2ui
defines A2UI v0.9 message and component types

@threadplane/chat
detects assistant content, manages streaming state, and mounts render or A2UI surfaces
```
<RenderVsA2ui />

Use `@threadplane/render` directly when your application already has a spec.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

`@threadplane/render` is the Angular rendering engine for [json-render](https://github.com/nicholasgriffintn/json-render) specs. It takes a declarative JSON specification and renders it into a live Angular component tree -- with reactive state, event handling, and conditional rendering built in.

## How it fits

<RenderHowItFits />

## Why @threadplane/render?

Building dynamic UIs from server-driven specs shows up everywhere -- AI apps, form builders, CMS-powered frontends. `@threadplane/render` bridges `@json-render/core` (a framework-agnostic spec evaluation engine) and Angular's component model.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ This section documents what happened when it was tested. On 2026-08-31 the adapt
These pages document runtimes as *backends measured against the adapter*. They are not a substitute for each vendor's own documentation, and Threadplane does not maintain any of the upstream AG-UI bridges described here.
</Callout>

## How it fits

<StackDiagram
highlight="runtimes"
caption="The same Angular surface above the seam — these pages record what three non-LangGraph runtimes did when the backend beneath it changed."
/>

## The runtimes

<CardGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ The important boundary is simple:

Don't treat the browser and Node entry points as interchangeable. They have different runtime assumptions and different privacy defaults.

## How it fits

<TelemetryHowItFits />

## Entry points

```ts
Expand Down
17 changes: 15 additions & 2 deletions apps/website/e2e/docs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ test.describe('Docs slug page', () => {

test('heading permalinks carry no glyph in the text, only a CSS ::before', async ({ page }) => {
await page.goto(route);

// The workspace shell re-renders the article on hydration; before that,
// computed styles on its subtree can read as empty on a cold CI server.
// Same gate the control-plane tests above use.
await expect(page.locator('[data-workspace-shell]')).toHaveAttribute(
'data-hydrated',
'true',
);

const h2 = page.locator('article h2').first();
await expect(h2).toBeVisible();

Expand All @@ -127,10 +136,14 @@ test.describe('Docs slug page', () => {
// ...which means the visible affordance hangs entirely on one CSS rule
// (`.docs-prose h2 .heading-anchor::before` in global.css). jsdom cannot
// resolve pseudo-element content, so this is the only place it is guarded.
// Poll: stylesheet application can trail hydration on a cold dev server.
const anchor = h2.locator('a.heading-anchor');
await expect(anchor).toHaveCount(1);
const glyph = await anchor.evaluate((el) => getComputedStyle(el, '::before').content);
expect(glyph).toBe('"#"');
await expect
.poll(() =>
anchor.evaluate((el) => getComputedStyle(el, '::before').content)
)
.toBe('"#"');
});

test('breadcrumb renders exactly once', async ({ page }) => {
Expand Down
10 changes: 10 additions & 0 deletions apps/website/src/app/ag-ui/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { MediumSwitcher } from '../../components/landing/MediumSwitcher';
import { BackendsGrid } from '../../components/landing/ag-ui/BackendsGrid';
import { StackDiagramSection } from '../../components/landing/StackDiagramSection';
import { createPageMetadata, SHORT_POSITIONING_DESCRIPTION } from '../../lib/site-metadata';
import { SECTION_MEDIA } from '../../lib/section-media';
import { buildPanes } from '../../lib/build-panes';
Expand Down Expand Up @@ -63,6 +64,15 @@ export default async function AgUiPage() {
</Container>
</Section>

<StackDiagramSection
id="ag-ui-architecture"
eyebrow="Architecture"
headline="The adapter is the only part that speaks AG-UI"
body="Everything above the seam is plain Angular — signals in, components out. toAgent() keeps the protocol at the boundary, so nothing in your UI changes when the backend does."
highlight="ag-ui"
caption="Four of the seven AG-UI runtimes shown; the rest speak the same protocol."
/>

<FeatureBlock
id="backends"
eyebrow="Runtime choice"
Expand Down
9 changes: 9 additions & 0 deletions apps/website/src/app/langgraph/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { MediumSwitcher } from '../../components/landing/MediumSwitcher';
import { LangGraphCodeShowcase } from '../../components/landing/langgraph/LangGraphCodeShowcase';
import { StackDiagramSection } from '../../components/landing/StackDiagramSection';
import { createPageMetadata, SHORT_POSITIONING_DESCRIPTION } from '../../lib/site-metadata';
import { SECTION_MEDIA } from '../../lib/section-media';
import { buildPanes } from '../../lib/build-panes';
Expand Down Expand Up @@ -55,6 +56,14 @@ export default async function LangGraphPage() {
</Container>
</Section>

<StackDiagramSection
id="langgraph-architecture"
eyebrow="Architecture"
headline="Native LangGraph, behind the Agent contract"
body="The adapter speaks LangGraph Platform directly — threads, runs, checkpoints — and hands your components the same signal-shaped contract every Threadplane surface consumes."
highlight="langgraph"
/>

<FeatureBlock
id="providers"
eyebrow="Providers"
Expand Down
9 changes: 9 additions & 0 deletions apps/website/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Hero } from '../components/landing/Hero';
import { LogoRibbon } from '../components/landing/LogoRibbon';
import { YesWall } from '../components/landing/YesWall';
import { FeatureBlock } from '../components/landing/FeatureBlock';
import { StackDiagramSection } from '../components/landing/StackDiagramSection';
import { DemoShowcase } from '../components/landing/DemoShowcase';
import { MediumSwitcher } from '../components/landing/MediumSwitcher';
import { SECTION_MEDIA } from '../lib/section-media';
Expand Down Expand Up @@ -37,6 +38,14 @@ export default async function HomePage() {
<LogoRibbon />
<YesWall />

<StackDiagramSection
id="architecture"
eyebrow="Architecture"
headline="Your UI talks to one contract, never to a runtime"
body="Your Angular components consume a signal-shaped Agent contract. Adapters implement it — swap the runtime underneath without touching the UI."
caption="The chat surface never imports a runtime SDK — only the contract."
/>

{/* Interactive demo showcase */}
<Section surface="canvas">
<Container>
Expand Down
Loading
Loading