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: 3 additions & 5 deletions apps/website/content/docs/chat/a2ui/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -255,18 +255,16 @@ Pass the built-in A2UI catalog to chat:
```ts
import { Component } from '@angular/core';
import { ChatComponent, a2uiBasicCatalog } from '@threadplane/chat';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';

@Component({
standalone: true,
imports: [ChatComponent],
providers: [provideAgent({ apiUrl: '/api/langgraph', assistantId: 'support' })],
template: `<chat [agent]="chat" [views]="catalog" />`,
})
export class SupportChatComponent {
protected readonly chat = agent({
apiUrl: '/api/langgraph',
assistantId: 'support',
});
protected readonly chat = injectAgent();

protected readonly catalog = a2uiBasicCatalog();
}
Expand Down
17 changes: 10 additions & 7 deletions apps/website/content/docs/chat/components/chat-debug.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ import { ChatDebugComponent } from '@threadplane/chat/debug';

```typescript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatDebugComponent } from '@threadplane/chat/debug';

@Component({
selector: 'app-debug-page',
standalone: true,
imports: [ChatDebugComponent],
providers: [
provideAgent({
apiUrl: '/api/langgraph',
assistantId: 'chat',
threadId: signal(localStorage.getItem('threadId')),
onThreadId: (id) => localStorage.setItem('threadId', id),
}),
],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<chat-debug
Expand All @@ -36,12 +44,7 @@ export class DebugPageComponent {
protected readonly replayCheckpointId = signal<string | null>(null);
protected readonly forkSourceCheckpointId = signal<string | null>(null);

protected readonly chat = agent({
apiUrl: '/api/langgraph',
assistantId: 'chat',
threadId: signal(localStorage.getItem('threadId')),
onThreadId: (id) => localStorage.setItem('threadId', id),
});
protected readonly chat = injectAgent();

replay(checkpointId: string) {
this.replayCheckpointId.set(checkpointId);
Expand Down
21 changes: 16 additions & 5 deletions apps/website/content/docs/chat/components/chat-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ import { ChatInputComponent, submitMessage } from '@threadplane/chat';
| `agent` | `Agent` | **Required** | The agent to submit messages to |
| `submitOnEnter` | `boolean` | `true` | When `true`, pressing Enter submits the message. Shift+Enter inserts a newline. When `false`, Enter always inserts a newline. |
| `placeholder` | `string` | `''` | Placeholder text shown when the input is empty |
| `showStopButton` | `boolean` | `true` | When `true`, shows a stop button in place of the send button while the agent is streaming. |

### Outputs

| Output | Type | Description |
|--------|------|-------------|
| `submitted` | `string` | Emits the trimmed message text after successful submission |
| `stopped` | `void` | Emits when the user clicks the stop button to halt a streaming response |

## Behavior

Expand Down Expand Up @@ -99,6 +101,17 @@ The function calls `agent.submit({ message: trimmed })` under the hood.

## Slots

The component template exposes content-projection slots arranged around the input pill:

| Slot | Selector | Description |
|------|----------|-------------|
| Banner | `[chatInputBanner]` | Rendered above the input pill (e.g., a notice or status banner). |
| Attachments | `[chatInputAttachments]` | Rendered above the input pill, below the banner (e.g., attachment chips). |
| Leading | `[chatInputLeading]` | Rendered inside the pill, before the textarea. |
| Model select | `[chatInputModelSelect]` | Rendered in the controls row, before the trailing slot and send button. |
| Trailing | `[chatInputTrailing]` | Rendered in the controls row, after the model-select slot and before the send button. |
| Footer | `[chatInputFooter]` | Rendered below the input pill. |

### `[chatInputModelSelect]`

Projects content into the controls row of the input pill, between `[chatInputTrailing]` and the send button. Designed for `<chat-select>` (a model picker), but accepts any element.
Expand Down Expand Up @@ -136,13 +149,14 @@ The component includes accessibility attributes:

```typescript
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatInputComponent } from '@threadplane/chat';

@Component({
selector: 'app-input-demo',
standalone: true,
imports: [ChatInputComponent],
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="border rounded-lg p-4">
Expand All @@ -161,10 +175,7 @@ import { ChatInputComponent } from '@threadplane/chat';
`,
})
export class InputDemoComponent {
chatRef = agent({
assistantId: 'chat',
threadId: signal(null),
});
chatRef = injectAgent();

lastMessage = signal('');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,15 @@ const interrupt = getInterrupt(chatRef); // Interrupt<any> | undefined

```typescript
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatComponent, ChatInterruptPanelComponent } from '@threadplane/chat';
import type { InterruptAction } from '@threadplane/chat';

@Component({
selector: 'app-interrupt-demo',
standalone: true,
imports: [ChatComponent, ChatInterruptPanelComponent],
providers: [provideAgent({ assistantId: 'interrupt_agent', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="height: 100vh; display: flex; flex-direction: column;">
Expand All @@ -145,10 +146,7 @@ import type { InterruptAction } from '@threadplane/chat';
`,
})
export class InterruptDemoComponent {
chatRef = agent({
assistantId: 'interrupt_agent',
threadId: signal(null),
});
chatRef = injectAgent();

onInterruptAction(action: InterruptAction) {
if (action === 'accept') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ChatMessageListComponent

`ChatMessageListComponent` is the core primitive for rendering chat messages. It iterates over the messages from an `Agent` and renders each one using a matching `MessageTemplateDirective`. This gives you full control over how each message type is displayed.
`ChatMessageListComponent` is the core primitive for rendering chat messages. It receives an `agent` input and reads `agent.messages()`, rendering each message using a matching `MessageTemplateDirective`. This gives you full control over how each message type is displayed.

**Selector:** `chat-message-list`

Expand All @@ -16,7 +16,7 @@ import {

## How It Works

1. The component reads `injectAgent().messages()` to get the current message list
1. The component receives an `agent` input and reads `agent.messages()` to get the current message list
2. For each message, it calls `getMessageType()` to determine the template type
3. It finds the matching `MessageTemplateDirective` among its content children
4. It renders the message using `ngTemplateOutlet` with the message as the implicit context
Expand Down Expand Up @@ -139,7 +139,7 @@ For custom templates, you can access `message.content` directly and handle the t
```typescript
import { Component, inject, ChangeDetectionStrategy, signal } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { agent } from '@threadplane/langgraph';
import { injectAgent } from '@threadplane/langgraph';
import {
ChatMessageListComponent,
MessageTemplateDirective,
Expand Down
13 changes: 6 additions & 7 deletions apps/website/content/docs/chat/components/chat-popup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,22 @@ If you need the chat to always be visible, use [`<chat>`](/docs/chat/components/

```typescript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatPopupComponent } from '@threadplane/chat';

@Component({
selector: 'app-root',
standalone: true,
imports: [ChatPopupComponent],
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<!-- your app content here -->
<chat-popup [agent]="chatAgent" />
`,
})
export class AppComponent {
protected readonly chatAgent = agent({
assistantId: 'chat',
threadId: signal(null),
});
protected readonly chatAgent = injectAgent();
}
```

Expand All @@ -52,7 +50,7 @@ export class AppComponent {
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `agent` | `Agent` | **Required** | The agent providing streaming state |
| `open` | `boolean` | `false` | Two-way bindable. Controls whether the chat window is open |
| `open` | `boolean` (two-way) | `false` | Two-way bindable. Controls whether the chat window is open |

### Outputs

Expand Down Expand Up @@ -84,10 +82,11 @@ Control the open state from your component:
<button (click)="popup.openWindow()">Open Chat</button>
<chat-popup [(open)]="chatOpen" [agent]="chatAgent" #popup />
`,
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
})
export class AppComponent {
chatOpen = false;
chatAgent = agent({ assistantId: 'chat', threadId: signal(null) });
chatAgent = injectAgent();
}
```

Expand Down
4 changes: 4 additions & 0 deletions apps/website/content/docs/chat/components/chat-select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ interface ChatSelectOption {
}
```

### Outputs

`value` is a signal-based `model<string>('')`, so Angular auto-creates a `valueChange` output (`string`) that emits the new selection. Bind it directly with `(valueChange)`, or use the `[(value)]` two-way shorthand below.

### Two-way binding

Use `[(value)]` to bind a writable signal:
Expand Down
13 changes: 6 additions & 7 deletions apps/website/content/docs/chat/components/chat-sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ If you need a floating overlay that does not affect layout, use [`<chat-popup>`]

```typescript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatSidebarComponent } from '@threadplane/chat';

@Component({
selector: 'app-shell',
standalone: true,
imports: [ChatSidebarComponent],
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<chat-sidebar [agent]="chatAgent">
Expand All @@ -38,10 +39,7 @@ import { ChatSidebarComponent } from '@threadplane/chat';
`,
})
export class AppShellComponent {
protected readonly chatAgent = agent({
assistantId: 'chat',
threadId: signal(null),
});
protected readonly chatAgent = injectAgent();
}
```

Expand All @@ -68,7 +66,7 @@ In push-content mode, `ChatSidebarComponent` uses `display: flex` on its host. T
| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `agent` | `Agent` | **Required** | The agent providing streaming state |
| `open` | `boolean` | `false` | Two-way bindable. Controls whether the sidebar is open |
| `open` | `boolean` (two-way) | `false` | Two-way bindable. Controls whether the sidebar is open |
| `pushContent` | `boolean` | `false` | When `true`, the sidebar shifts projected content rather than overlaying it |

### Outputs
Expand Down Expand Up @@ -107,10 +105,11 @@ In push-content mode, `ChatSidebarComponent` uses `display: flex` on its host. T
<main><router-outlet /></main>
</chat-sidebar>
`,
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
})
export class AppShellComponent {
sidebarOpen = signal(false);
chatAgent = agent({ assistantId: 'chat', threadId: signal(null) });
chatAgent = injectAgent();
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The `ChatSubagentsComponent` primitive iterates over active subagent streams fro

```typescript
import { Component, signal, ChangeDetectionStrategy } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import {
ChatComponent,
ChatSubagentsComponent,
Expand All @@ -88,6 +88,7 @@ import {
selector: 'app-subagent-demo',
standalone: true,
imports: [ChatComponent, ChatSubagentsComponent, ChatSubagentCardComponent],
providers: [provideAgent({ assistantId: 'multi_agent', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="height: 100vh; display: flex; flex-direction: column;">
Expand All @@ -109,10 +110,7 @@ import {
`,
})
export class SubagentDemoComponent {
chatRef = agent({
assistantId: 'multi_agent',
threadId: signal(null),
});
chatRef = injectAgent();
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ChatToolCallsComponent } from '@threadplane/chat';
| `[message]` | `Message \| undefined` | `undefined` | Filter to calls referenced by this message's `tool_use` content blocks |
| `[grouping]` | `'auto' \| 'none'` | `'auto'` | Auto-collapse adjacent same-name calls into a strip |
| `[groupSummary]` | `(name: string, count: number) => string` | built-in registry | Override the default strip label |
| `[excludeToolNames]` | `readonly string[]` | `[]` | Tool names whose groups are hidden. Used by chat compositions to filter out internal/orchestration tools (e.g. GenUI dispatchers). |

## Default group summaries

Expand Down
12 changes: 6 additions & 6 deletions apps/website/content/docs/chat/components/chat-trace.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ For a fully managed tool call display, see [`<chat-tool-call-card>`](/docs/chat/

| Input | Type | Default | Description |
|-------|------|---------|-------------|
| `state` | `'pending' \| 'running' \| 'done' \| 'error'` | `'pending'` | Current execution state. Controls the status icon and auto-expand behavior |
| `state` | `TraceState` | `'pending'` | Current execution state. Controls the status icon and auto-expand behavior. `TraceState` is exported from `@threadplane/chat` and is `'pending' \| 'running' \| 'done' \| 'error'`. |

### Outputs

None. State transitions are driven by the `state` input.

### Slots

All slots are optional.

| Slot | Selector | Description |
|------|----------|-------------|
| Default | (none) | Main trace content. Shown when the trace is expanded |
Expand Down Expand Up @@ -90,13 +92,14 @@ When `state` transitions from any value to `'running'`, the trace automatically

```typescript
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
import { agent } from '@threadplane/langgraph';
import { injectAgent, provideAgent } from '@threadplane/langgraph';
import { ChatTraceComponent, ChatMessageListComponent } from '@threadplane/chat';

@Component({
selector: 'app-traced-chat',
standalone: true,
imports: [ChatMessageListComponent, ChatTraceComponent],
providers: [provideAgent({ assistantId: 'chat', threadId: signal(null) })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div style="height: 100vh; display: flex; flex-direction: column;">
Expand All @@ -116,10 +119,7 @@ import { ChatTraceComponent, ChatMessageListComponent } from '@threadplane/chat'
`,
})
export class TracedChatComponent {
protected readonly chatAgent = agent({
assistantId: 'chat',
threadId: signal(null),
});
protected readonly chatAgent = injectAgent();
}
```

Expand Down
10 changes: 10 additions & 0 deletions apps/website/content/docs/chat/components/chat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,22 @@ export class ChatPageComponent {
| `handlers` | `Record<string, (params: Record<string, unknown>) => unknown \| Promise<unknown>>` | `{}` | Event handlers for generative UI specs and A2UI `functionCall` actions. Handlers run in Angular injection context — `inject()` is available inside handler functions. |
| `threads` | `Thread[]` | `[]` | List of threads to display in the sidebar. Each thread must have an `id` property. |
| `activeThreadId` | `string` | `''` | The ID of the currently active thread, used for highlighting in the sidebar. |
| `welcomeDisabled` | `boolean` | `false` | When `true`, suppresses the welcome screen shown for an empty conversation. |
| `modelOptions` | `readonly ChatSelectOption[]` | `[]` | When non-empty, renders a `<chat-select>` model picker inside the input pill, wired to the two-way `selectedModel`. Leave empty to project your own `<chat-select chatInputModelSelect>`. |
| `showModelPicker` | `boolean` | `true` | When `false`, hides the auto-rendered model picker even when `modelOptions` is non-empty. No effect on a consumer-projected `<chat-select>`. |
| `selectedModel` | `string` (two-way) | `''` | The selected model `value`, bound to the auto-rendered model picker. Use with `[(selectedModel)]`. |
| `modelPickerPlaceholder` | `string` | `'Choose a model'` | Placeholder shown in the auto-rendered model picker when no option matches. |
| `genuiToolNames` | `readonly string[]` | `['generate_a2ui_schema', 'generate_json_render_spec', 'render_spec']` | Tool names whose calls produce a rendered GenUI surface rather than visible text. Used to filter `<chat-tool-calls>` and detect GenUI turns. |

### Outputs

| Output | Type | Description |
|--------|------|-------------|
| `threadSelected` | `string` | Emits when a thread is clicked in the sidebar. Payload is the thread ID. |
| `renderEvent` | `ChatRenderEvent` | Emits render lifecycle events from generative UI surfaces. |
| `regenerate` | `void` | Emits when the user clicks the regenerate button on an assistant message. |
| `rate` | `{ messageIndex: number; rating: 'up' \| 'down' }` | Emits when the user rates an assistant message. |
| `messageCopy` | `{ messageIndex: number; content: string }` | Emits when the user copies an assistant message. |

### Thread Type

Expand Down
Loading