@assistant-ui/mcp-docs-server 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/organized/code-examples/waterfall.md +801 -0
- package/.docs/organized/code-examples/with-ag-ui.md +39 -27
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +39 -29
- package/.docs/organized/code-examples/with-artifacts.md +467 -0
- package/.docs/organized/code-examples/with-assistant-transport.md +32 -25
- package/.docs/organized/code-examples/with-chain-of-thought.md +42 -33
- package/.docs/organized/code-examples/with-cloud-standalone.md +674 -0
- package/.docs/organized/code-examples/with-cloud.md +35 -28
- package/.docs/organized/code-examples/with-custom-thread-list.md +35 -28
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +42 -31
- package/.docs/organized/code-examples/with-expo.md +2012 -0
- package/.docs/organized/code-examples/with-external-store.md +32 -26
- package/.docs/organized/code-examples/with-ffmpeg.md +32 -28
- package/.docs/organized/code-examples/with-langgraph.md +97 -39
- package/.docs/organized/code-examples/with-parent-id-grouping.md +33 -26
- package/.docs/organized/code-examples/with-react-hook-form.md +63 -61
- package/.docs/organized/code-examples/with-react-router.md +38 -31
- package/.docs/organized/code-examples/with-store.md +17 -25
- package/.docs/organized/code-examples/with-tanstack.md +36 -26
- package/.docs/organized/code-examples/with-tap-runtime.md +11 -25
- package/.docs/raw/docs/(docs)/cli.mdx +13 -6
- package/.docs/raw/docs/(docs)/guides/attachments.mdx +26 -3
- package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +5 -5
- package/.docs/raw/docs/(docs)/guides/context-api.mdx +53 -52
- package/.docs/raw/docs/(docs)/guides/dictation.mdx +0 -2
- package/.docs/raw/docs/(docs)/guides/message-timing.mdx +169 -0
- package/.docs/raw/docs/(docs)/guides/quoting.mdx +327 -0
- package/.docs/raw/docs/(docs)/guides/speech.mdx +0 -1
- package/.docs/raw/docs/(docs)/index.mdx +12 -2
- package/.docs/raw/docs/(docs)/installation.mdx +8 -2
- package/.docs/raw/docs/(docs)/llm.mdx +9 -7
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +1 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +2 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +27 -27
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +60 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +78 -4
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +32 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/selection-toolbar.mdx +61 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +1 -1
- package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +1 -6
- package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +2 -2
- package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +1 -6
- package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +1 -5
- package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +17 -17
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +209 -0
- package/.docs/raw/docs/cloud/ai-sdk.mdx +296 -0
- package/.docs/raw/docs/cloud/authorization.mdx +178 -79
- package/.docs/raw/docs/cloud/{persistence/langgraph.mdx → langgraph.mdx} +2 -2
- package/.docs/raw/docs/cloud/overview.mdx +29 -39
- package/.docs/raw/docs/react-native/adapters.mdx +118 -0
- package/.docs/raw/docs/react-native/custom-backend.mdx +210 -0
- package/.docs/raw/docs/react-native/hooks.mdx +364 -0
- package/.docs/raw/docs/react-native/index.mdx +332 -0
- package/.docs/raw/docs/react-native/primitives.mdx +653 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +60 -15
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +103 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +25 -2
- package/.docs/raw/docs/runtimes/data-stream.mdx +1 -3
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +113 -9
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +1 -4
- package/.docs/raw/docs/ui/attachment.mdx +4 -2
- package/.docs/raw/docs/ui/context-display.mdx +147 -0
- package/.docs/raw/docs/ui/message-timing.mdx +92 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +1 -1
- package/.docs/raw/docs/ui/reasoning.mdx +4 -4
- package/.docs/raw/docs/ui/scrollbar.mdx +2 -2
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +55 -50
- package/.docs/raw/docs/ui/thread.mdx +16 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/tools/tests/integration.test.ts +2 -2
- package/src/tools/tests/json-parsing.test.ts +1 -1
- package/src/tools/tests/mcp-protocol.test.ts +1 -3
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -108
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Quote Selected Text
|
|
3
|
+
description: Let users select and quote text from messages, similar to Claude's quoting experience.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Allow users to select text in assistant messages and reply with a quote reference — just like Claude, ChatGPT, and other modern AI interfaces.
|
|
7
|
+
|
|
8
|
+
## How It Works
|
|
9
|
+
|
|
10
|
+
1. User selects text in a message
|
|
11
|
+
2. A floating toolbar appears near the selection with a **Quote** button
|
|
12
|
+
3. User clicks it — a quote preview appears in the composer
|
|
13
|
+
4. User types their reply and sends
|
|
14
|
+
5. The sent message displays the quoted text above the user's reply
|
|
15
|
+
|
|
16
|
+
Quote data is stored in `metadata.custom.quote` and is **not** injected into message content. Your backend decides how to present the quoted context to the LLM.
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### 1. Add the Floating Selection Toolbar
|
|
21
|
+
|
|
22
|
+
Place `SelectionToolbarPrimitive` inside your `ThreadPrimitive.Root`. It renders a floating toolbar near the user's text selection — only when text is selected within a message.
|
|
23
|
+
|
|
24
|
+
```tsx {1,13-20}
|
|
25
|
+
import { SelectionToolbarPrimitive, ThreadPrimitive } from "@assistant-ui/react";
|
|
26
|
+
import { QuoteIcon } from "lucide-react";
|
|
27
|
+
|
|
28
|
+
const Thread = () => {
|
|
29
|
+
return (
|
|
30
|
+
<ThreadPrimitive.Root>
|
|
31
|
+
<ThreadPrimitive.Viewport>
|
|
32
|
+
<ThreadPrimitive.Messages components={{ ... }} />
|
|
33
|
+
...
|
|
34
|
+
</ThreadPrimitive.Viewport>
|
|
35
|
+
|
|
36
|
+
{/* Floating toolbar — appears on text selection */}
|
|
37
|
+
<SelectionToolbarPrimitive.Root className="flex items-center gap-1 rounded-lg border bg-popover px-1 py-1 shadow-md">
|
|
38
|
+
<SelectionToolbarPrimitive.Quote className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-sm hover:bg-accent">
|
|
39
|
+
<QuoteIcon className="size-3.5" />
|
|
40
|
+
Quote
|
|
41
|
+
</SelectionToolbarPrimitive.Quote>
|
|
42
|
+
</SelectionToolbarPrimitive.Root>
|
|
43
|
+
</ThreadPrimitive.Root>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The `Root` component:
|
|
49
|
+
- Listens for `mouseup` and `keyup` events to detect text selections
|
|
50
|
+
- Validates the selection is within a single message (cross-message selections are ignored)
|
|
51
|
+
- Renders a portal positioned above the selection
|
|
52
|
+
- Prevents `mousedown` from clearing the selection when clicking the toolbar
|
|
53
|
+
- Hides automatically on scroll or when the selection is cleared
|
|
54
|
+
|
|
55
|
+
### 2. Show Quote Preview in Composer
|
|
56
|
+
|
|
57
|
+
Add `ComposerPrimitive.Quote` inside the composer to show what's being quoted:
|
|
58
|
+
|
|
59
|
+
```tsx {1,8-13}
|
|
60
|
+
import { ComposerPrimitive } from "@assistant-ui/react";
|
|
61
|
+
|
|
62
|
+
const Composer = () => {
|
|
63
|
+
return (
|
|
64
|
+
<ComposerPrimitive.Root>
|
|
65
|
+
{/* Quote preview — only renders when a quote is set */}
|
|
66
|
+
<ComposerPrimitive.Quote className="flex items-start gap-2 border-l-4 border-primary/40 bg-muted/50 px-3 py-2 text-sm">
|
|
67
|
+
<ComposerPrimitive.QuoteText className="line-clamp-2 flex-1 italic text-muted-foreground" />
|
|
68
|
+
<ComposerPrimitive.QuoteDismiss>
|
|
69
|
+
×
|
|
70
|
+
</ComposerPrimitive.QuoteDismiss>
|
|
71
|
+
</ComposerPrimitive.Quote>
|
|
72
|
+
|
|
73
|
+
<ComposerPrimitive.Input placeholder="Send a message..." />
|
|
74
|
+
<ComposerPrimitive.Send />
|
|
75
|
+
</ComposerPrimitive.Root>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Display Quotes in Sent Messages
|
|
81
|
+
|
|
82
|
+
Use `useMessageQuote()` to render quoted text in user messages:
|
|
83
|
+
|
|
84
|
+
```tsx {1,4-5,11}
|
|
85
|
+
import { MessagePrimitive, useMessageQuote } from "@assistant-ui/react";
|
|
86
|
+
|
|
87
|
+
const QuoteBlock = () => {
|
|
88
|
+
const quote = useMessageQuote();
|
|
89
|
+
if (!quote) return null;
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div className="mb-1 border-l-4 border-primary/40 pl-3 text-xs italic text-muted-foreground">
|
|
93
|
+
{quote.text}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const UserMessage = () => {
|
|
99
|
+
return (
|
|
100
|
+
<MessagePrimitive.Root>
|
|
101
|
+
<QuoteBlock />
|
|
102
|
+
<MessagePrimitive.Parts />
|
|
103
|
+
</MessagePrimitive.Root>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Backend Handling
|
|
109
|
+
|
|
110
|
+
The quote is stored in message metadata — **not** in message content. This gives your backend full control over how to present quoted context to the LLM.
|
|
111
|
+
|
|
112
|
+
### Data Shape
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
type QuoteInfo = {
|
|
116
|
+
readonly text: string; // selected plain text
|
|
117
|
+
readonly messageId: string; // source message ID
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Stored at: message.metadata.custom.quote
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Example: Prepend as Markdown Blockquote
|
|
124
|
+
|
|
125
|
+
A simple approach is to prepend the quoted text as a `>` blockquote before converting to model messages:
|
|
126
|
+
|
|
127
|
+
```typescript title="app/api/chat/route.ts" {1,9}
|
|
128
|
+
import { convertToModelMessages, streamText } from "ai";
|
|
129
|
+
import type { UIMessage } from "ai";
|
|
130
|
+
|
|
131
|
+
export async function POST(req: Request) {
|
|
132
|
+
const { messages } = await req.json();
|
|
133
|
+
|
|
134
|
+
const result = streamText({
|
|
135
|
+
model: myModel,
|
|
136
|
+
messages: await convertToModelMessages(injectQuoteContext(messages)),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return result.toUIMessageStreamResponse();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function injectQuoteContext(messages: UIMessage[]): UIMessage[] {
|
|
143
|
+
return messages.map((msg) => {
|
|
144
|
+
const quote = (msg.metadata as Record<string, unknown>)?.custom;
|
|
145
|
+
if (
|
|
146
|
+
!quote ||
|
|
147
|
+
typeof quote !== "object" ||
|
|
148
|
+
!("quote" in (quote as Record<string, unknown>))
|
|
149
|
+
)
|
|
150
|
+
return msg;
|
|
151
|
+
|
|
152
|
+
const q = (quote as Record<string, unknown>).quote;
|
|
153
|
+
if (
|
|
154
|
+
!q ||
|
|
155
|
+
typeof q !== "object" ||
|
|
156
|
+
!("text" in (q as Record<string, unknown>))
|
|
157
|
+
)
|
|
158
|
+
return msg;
|
|
159
|
+
|
|
160
|
+
const text = (q as { text: unknown }).text;
|
|
161
|
+
if (typeof text !== "string") return msg;
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
...msg,
|
|
165
|
+
parts: [{ type: "text" as const, text: `> ${text}\n\n` }, ...msg.parts],
|
|
166
|
+
};
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Example: Claude-Style Citation Source
|
|
172
|
+
|
|
173
|
+
For Claude's API, you can pass the quoted text as a citation source. This enables Claude to produce citations that reference the quoted text:
|
|
174
|
+
|
|
175
|
+
```typescript title="app/api/chat/route.ts"
|
|
176
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
177
|
+
|
|
178
|
+
const client = new Anthropic();
|
|
179
|
+
|
|
180
|
+
export async function POST(req: Request) {
|
|
181
|
+
const { messages } = await req.json();
|
|
182
|
+
|
|
183
|
+
// Transform messages: extract quotes into Claude source blocks
|
|
184
|
+
const claudeMessages = messages.map((msg) => {
|
|
185
|
+
const quote = msg.metadata?.custom?.quote;
|
|
186
|
+
if (!quote?.text) {
|
|
187
|
+
return { role: msg.role, content: extractText(msg) };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
role: "user",
|
|
192
|
+
content: [
|
|
193
|
+
{
|
|
194
|
+
type: "text",
|
|
195
|
+
text: quote.text,
|
|
196
|
+
cache_control: { type: "ephemeral" },
|
|
197
|
+
citations: { enabled: true },
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: extractText(msg),
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const response = await client.messages.create({
|
|
208
|
+
model: "claude-sonnet-4-5-20250929",
|
|
209
|
+
max_tokens: 1024,
|
|
210
|
+
messages: claudeMessages,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// ... stream response back
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Example: OpenAI-Style System Context
|
|
218
|
+
|
|
219
|
+
For OpenAI's API, inject the quote as additional context in the user message:
|
|
220
|
+
|
|
221
|
+
```typescript title="app/api/chat/route.ts"
|
|
222
|
+
function injectQuoteForOpenAI(messages) {
|
|
223
|
+
return messages.map((msg) => {
|
|
224
|
+
const quote = msg.metadata?.custom?.quote;
|
|
225
|
+
if (!quote?.text || msg.role !== "user") return msg;
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
...msg,
|
|
229
|
+
content: `[Referring to: "${quote.text}"]\n\n${msg.content}`,
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Programmatic API
|
|
236
|
+
|
|
237
|
+
You can set or clear quotes programmatically via the composer runtime:
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { useAui } from "@assistant-ui/react";
|
|
241
|
+
|
|
242
|
+
function MyComponent() {
|
|
243
|
+
const aui = useAui();
|
|
244
|
+
|
|
245
|
+
const quoteText = () => {
|
|
246
|
+
aui.thread().composer.setQuote({
|
|
247
|
+
text: "The text to quote",
|
|
248
|
+
messageId: "msg-123",
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const clearQuote = () => {
|
|
253
|
+
aui.thread().composer.setQuote(undefined);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<>
|
|
258
|
+
<button onClick={quoteText}>Set Quote</button>
|
|
259
|
+
<button onClick={clearQuote}>Clear Quote</button>
|
|
260
|
+
</>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## API Reference
|
|
266
|
+
|
|
267
|
+
### SelectionToolbarPrimitive.Root
|
|
268
|
+
|
|
269
|
+
A floating container that appears when text is selected within a message. Renders as a portal positioned above the selection.
|
|
270
|
+
|
|
271
|
+
- Listens for `mouseup` / `keyup` to detect selection
|
|
272
|
+
- Validates selection is within a single message
|
|
273
|
+
- Hides on scroll or when selection is cleared
|
|
274
|
+
- Prevents `mousedown` from clearing the selection
|
|
275
|
+
- Provides selection context to child components
|
|
276
|
+
|
|
277
|
+
### SelectionToolbarPrimitive.Quote
|
|
278
|
+
|
|
279
|
+
A button inside the floating toolbar that captures the selection as a quote.
|
|
280
|
+
|
|
281
|
+
- Reads selection info from the toolbar context (not `window.getSelection()`)
|
|
282
|
+
- Stores `{ text, messageId }` in the thread composer
|
|
283
|
+
- Clears the text selection after quoting
|
|
284
|
+
|
|
285
|
+
### ComposerPrimitive.Quote
|
|
286
|
+
|
|
287
|
+
A container that only renders when a quote is set.
|
|
288
|
+
|
|
289
|
+
### ComposerPrimitive.QuoteText
|
|
290
|
+
|
|
291
|
+
Renders the quoted text. Defaults to `<span>`.
|
|
292
|
+
|
|
293
|
+
### ComposerPrimitive.QuoteDismiss
|
|
294
|
+
|
|
295
|
+
A button that clears the quote by calling `setQuote(undefined)`. Supports `asChild`.
|
|
296
|
+
|
|
297
|
+
### useMessageQuote()
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
const quote: QuoteInfo | undefined = useMessageQuote();
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Returns the quote attached to the current message, or `undefined`.
|
|
304
|
+
|
|
305
|
+
### ComposerRuntime.setQuote()
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
setQuote(quote: QuoteInfo | undefined): void
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Set or clear the quote on the composer. The quote is automatically cleared when the message is sent.
|
|
312
|
+
|
|
313
|
+
## Design Notes
|
|
314
|
+
|
|
315
|
+
- **Single quote** — `setQuote` replaces, not appends. Only one quote at a time.
|
|
316
|
+
- **Snapshot text** — The selected text is captured at quote time, not linked to the source message.
|
|
317
|
+
- **Cross-message selection** — Rejected. The toolbar only appears when the selection is within a single message.
|
|
318
|
+
- **Streaming messages** — The floating toolbar works during streaming since it reads from the captured selection, not message status.
|
|
319
|
+
- **`isEmpty` unchanged** — A quote alone doesn't make the composer non-empty. The user must type a reply.
|
|
320
|
+
- **Scroll hides toolbar** — The floating toolbar hides when any scroll event occurs, since the position would become stale.
|
|
321
|
+
|
|
322
|
+
## Related
|
|
323
|
+
|
|
324
|
+
- [Message Editing](/docs/guides/editing) — Edit user messages
|
|
325
|
+
- [Thread Component](/docs/ui/thread) — Main chat container
|
|
326
|
+
- [ComposerPrimitive](/docs/reference/primitives/Composer) — Composer primitive reference
|
|
327
|
+
- [ActionBarPrimitive](/docs/reference/primitives/ActionBar) — Action bar primitive reference
|
|
@@ -7,6 +7,10 @@ import { Sparkles, PanelsTopLeft, Database, Terminal, Bot } from "lucide-react";
|
|
|
7
7
|
|
|
8
8
|
assistant-ui helps you create beautiful, enterprise-grade AI chat interfaces in minutes. Whether you're building a ChatGPT clone, a customer support chatbot, an AI assistant, or a complex multi-agent application, assistant-ui provides the frontend primitive components and state management layers to focus on what makes your application unique.
|
|
9
9
|
|
|
10
|
+
<Callout type="tip">
|
|
11
|
+
Already using the AI SDK with your own UI? Add [cloud persistence with just one hook](/docs/cloud/ai-sdk), no UI library required.
|
|
12
|
+
</Callout>
|
|
13
|
+
|
|
10
14
|
## Key Features
|
|
11
15
|
|
|
12
16
|
<Cards>
|
|
@@ -38,13 +42,19 @@ npx assistant-ui@latest create
|
|
|
38
42
|
This creates a new project with everything configured. Or choose a template:
|
|
39
43
|
|
|
40
44
|
```sh
|
|
45
|
+
# Minimal starter
|
|
46
|
+
npx assistant-ui@latest create -t minimal
|
|
47
|
+
|
|
41
48
|
# Assistant Cloud - with persistence and thread management
|
|
42
49
|
npx assistant-ui@latest create -t cloud
|
|
43
50
|
|
|
44
|
-
#
|
|
51
|
+
# Assistant Cloud + Clerk authentication
|
|
52
|
+
npx assistant-ui@latest create -t cloud-clerk
|
|
53
|
+
|
|
54
|
+
# LangGraph starter template
|
|
45
55
|
npx assistant-ui@latest create -t langgraph
|
|
46
56
|
|
|
47
|
-
# MCP
|
|
57
|
+
# MCP starter template
|
|
48
58
|
npx assistant-ui@latest create -t mcp
|
|
49
59
|
```
|
|
50
60
|
|
|
@@ -24,13 +24,19 @@ npx assistant-ui@latest create
|
|
|
24
24
|
Or choose a template:
|
|
25
25
|
|
|
26
26
|
```sh
|
|
27
|
+
# Minimal starter
|
|
28
|
+
npx assistant-ui@latest create -t minimal
|
|
29
|
+
|
|
27
30
|
# Assistant Cloud - with persistence and thread management
|
|
28
31
|
npx assistant-ui@latest create -t cloud
|
|
29
32
|
|
|
30
|
-
#
|
|
33
|
+
# Assistant Cloud + Clerk authentication
|
|
34
|
+
npx assistant-ui@latest create -t cloud-clerk
|
|
35
|
+
|
|
36
|
+
# LangGraph starter template
|
|
31
37
|
npx assistant-ui@latest create -t langgraph
|
|
32
38
|
|
|
33
|
-
# MCP
|
|
39
|
+
# MCP starter template
|
|
34
40
|
npx assistant-ui@latest create -t mcp
|
|
35
41
|
```
|
|
36
42
|
|
|
@@ -78,18 +78,20 @@ Once installed, your AI assistant will understand everything about assistant-ui
|
|
|
78
78
|
### Quick Install (CLI)
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
npx assistant-ui
|
|
81
|
+
npx add-mcp @assistant-ui/mcp-docs-server
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Or specify your IDE directly:
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
npx assistant-ui
|
|
88
|
-
npx assistant-ui
|
|
89
|
-
npx assistant-ui
|
|
90
|
-
npx assistant-ui
|
|
91
|
-
npx assistant-ui
|
|
92
|
-
npx assistant-ui
|
|
87
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a claude-code
|
|
88
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a claude-desktop
|
|
89
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a codex
|
|
90
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a cursor
|
|
91
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a gemini-cli
|
|
92
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a opencode
|
|
93
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a vscode
|
|
94
|
+
npx add-mcp @assistant-ui/mcp-docs-server -a zed
|
|
93
95
|
```
|
|
94
96
|
|
|
95
97
|
### Manual Installation
|
|
@@ -261,7 +261,7 @@ import { useCallback } from "react";
|
|
|
261
261
|
|
|
262
262
|
const useEditAction = () => {
|
|
263
263
|
const aui = useAui();
|
|
264
|
-
const disabled = useAuiState((
|
|
264
|
+
const disabled = useAuiState((s) => s.composer.isEditing);
|
|
265
265
|
const callback = useCallback(() => aui.composer().beginEdit(), [aui]);
|
|
266
266
|
if (disabled) return null;
|
|
267
267
|
return callback;
|
|
@@ -156,10 +156,10 @@ Show a different icon for a few seconds after the message is copied.
|
|
|
156
156
|
|
|
157
157
|
```tsx
|
|
158
158
|
<ActionBarPrimitive.Copy>
|
|
159
|
-
<AuiIf condition={(
|
|
159
|
+
<AuiIf condition={(s) => !s.message.isCopied}>
|
|
160
160
|
<CopyIcon />
|
|
161
161
|
</AuiIf>
|
|
162
|
-
<AuiIf condition={(
|
|
162
|
+
<AuiIf condition={(s) => s.message.isCopied}>
|
|
163
163
|
<CopySuccessIcon />
|
|
164
164
|
</AuiIf>
|
|
165
165
|
</ActionBarPrimitive.Copy>
|
|
@@ -11,7 +11,7 @@ Conditionally render children based on assistant state.
|
|
|
11
11
|
```tsx
|
|
12
12
|
import { AuiIf } from "@assistant-ui/react";
|
|
13
13
|
|
|
14
|
-
<AuiIf condition={(
|
|
14
|
+
<AuiIf condition={(s) => s.thread.isEmpty}>
|
|
15
15
|
<WelcomeScreen />
|
|
16
16
|
</AuiIf>
|
|
17
17
|
```
|
|
@@ -80,20 +80,20 @@ The condition function receives an `AssistantState` object with the following pr
|
|
|
80
80
|
|
|
81
81
|
```tsx
|
|
82
82
|
// Show welcome screen when thread is empty
|
|
83
|
-
<AuiIf condition={(
|
|
83
|
+
<AuiIf condition={(s) => s.thread.isEmpty}>
|
|
84
84
|
<WelcomeScreen />
|
|
85
85
|
</AuiIf>
|
|
86
86
|
|
|
87
87
|
// Show loading indicator while running
|
|
88
|
-
<AuiIf condition={(
|
|
88
|
+
<AuiIf condition={(s) => s.thread.isRunning}>
|
|
89
89
|
<LoadingSpinner />
|
|
90
90
|
</AuiIf>
|
|
91
91
|
|
|
92
92
|
// Conditional send/cancel button
|
|
93
|
-
<AuiIf condition={(
|
|
93
|
+
<AuiIf condition={(s) => !s.thread.isRunning}>
|
|
94
94
|
<ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
|
|
95
95
|
</AuiIf>
|
|
96
|
-
<AuiIf condition={(
|
|
96
|
+
<AuiIf condition={(s) => s.thread.isRunning}>
|
|
97
97
|
<ComposerPrimitive.Cancel>Cancel</ComposerPrimitive.Cancel>
|
|
98
98
|
</AuiIf>
|
|
99
99
|
```
|
|
@@ -102,32 +102,32 @@ The condition function receives an `AssistantState` object with the following pr
|
|
|
102
102
|
|
|
103
103
|
```tsx
|
|
104
104
|
// Show avatar only for assistant messages
|
|
105
|
-
<AuiIf condition={(
|
|
105
|
+
<AuiIf condition={(s) => s.message.role === "assistant"}>
|
|
106
106
|
<AssistantAvatar />
|
|
107
107
|
</AuiIf>
|
|
108
108
|
|
|
109
109
|
// Show disclaimer on last message
|
|
110
|
-
<AuiIf condition={(
|
|
110
|
+
<AuiIf condition={(s) => s.message.isLast}>
|
|
111
111
|
<Disclaimer />
|
|
112
112
|
</AuiIf>
|
|
113
113
|
|
|
114
114
|
// Toggle copy icon based on copied state
|
|
115
115
|
<ActionBarPrimitive.Copy>
|
|
116
|
-
<AuiIf condition={(
|
|
116
|
+
<AuiIf condition={(s) => !s.message.isCopied}>
|
|
117
117
|
<CopyIcon />
|
|
118
118
|
</AuiIf>
|
|
119
|
-
<AuiIf condition={(
|
|
119
|
+
<AuiIf condition={(s) => s.message.isCopied}>
|
|
120
120
|
<CheckIcon />
|
|
121
121
|
</AuiIf>
|
|
122
122
|
</ActionBarPrimitive.Copy>
|
|
123
123
|
|
|
124
124
|
// Show speak/stop button based on speech state
|
|
125
|
-
<AuiIf condition={(
|
|
125
|
+
<AuiIf condition={(s) => s.message.speech == null}>
|
|
126
126
|
<ActionBarPrimitive.Speak>
|
|
127
127
|
<SpeakIcon />
|
|
128
128
|
</ActionBarPrimitive.Speak>
|
|
129
129
|
</AuiIf>
|
|
130
|
-
<AuiIf condition={(
|
|
130
|
+
<AuiIf condition={(s) => s.message.speech != null}>
|
|
131
131
|
<ActionBarPrimitive.StopSpeaking>
|
|
132
132
|
<StopIcon />
|
|
133
133
|
</ActionBarPrimitive.StopSpeaking>
|
|
@@ -138,12 +138,12 @@ The condition function receives an `AssistantState` object with the following pr
|
|
|
138
138
|
|
|
139
139
|
```tsx
|
|
140
140
|
// Show placeholder when composer is empty
|
|
141
|
-
<AuiIf condition={(
|
|
141
|
+
<AuiIf condition={(s) => s.composer.isEmpty}>
|
|
142
142
|
<PlaceholderText />
|
|
143
143
|
</AuiIf>
|
|
144
144
|
|
|
145
145
|
// Show attachment preview when editing
|
|
146
|
-
<AuiIf condition={(
|
|
146
|
+
<AuiIf condition={(s) => s.composer.isEditing}>
|
|
147
147
|
<EditingIndicator />
|
|
148
148
|
</AuiIf>
|
|
149
149
|
```
|
|
@@ -152,15 +152,15 @@ The condition function receives an `AssistantState` object with the following pr
|
|
|
152
152
|
|
|
153
153
|
```tsx
|
|
154
154
|
// Combine multiple conditions
|
|
155
|
-
<AuiIf condition={(
|
|
156
|
-
!thread.isRunning && message.role === "assistant"
|
|
155
|
+
<AuiIf condition={(s) =>
|
|
156
|
+
!s.thread.isRunning && s.message.role === "assistant"
|
|
157
157
|
}>
|
|
158
158
|
<ActionBar />
|
|
159
159
|
</AuiIf>
|
|
160
160
|
|
|
161
161
|
// Custom logic
|
|
162
|
-
<AuiIf condition={(
|
|
163
|
-
thread.messages.length > 0 && !thread.isRunning
|
|
162
|
+
<AuiIf condition={(s) =>
|
|
163
|
+
s.thread.messages.length > 0 && !s.thread.isRunning
|
|
164
164
|
}>
|
|
165
165
|
<FollowUpSuggestions />
|
|
166
166
|
</AuiIf>
|
|
@@ -173,7 +173,7 @@ You can import the `AuiIf.Condition` type for typing your condition functions:
|
|
|
173
173
|
```tsx
|
|
174
174
|
import { AuiIf } from "@assistant-ui/react";
|
|
175
175
|
|
|
176
|
-
const isThreadEmpty: AuiIf.Condition = (
|
|
176
|
+
const isThreadEmpty: AuiIf.Condition = (s) => s.thread.isEmpty;
|
|
177
177
|
|
|
178
178
|
<AuiIf condition={isThreadEmpty}>
|
|
179
179
|
<WelcomeScreen />
|
|
@@ -188,12 +188,12 @@ const isThreadEmpty: AuiIf.Condition = ({ thread }) => thread.isEmpty;
|
|
|
188
188
|
|
|
189
189
|
| Before | After |
|
|
190
190
|
|--------|-------|
|
|
191
|
-
| `<ThreadPrimitive.If empty>` | `<AuiIf condition={(
|
|
192
|
-
| `<ThreadPrimitive.If running>` | `<AuiIf condition={(
|
|
193
|
-
| `<ThreadPrimitive.If running={false}>` | `<AuiIf condition={(
|
|
194
|
-
| `<MessagePrimitive.If user>` | `<AuiIf condition={(
|
|
195
|
-
| `<MessagePrimitive.If assistant>` | `<AuiIf condition={(
|
|
196
|
-
| `<MessagePrimitive.If copied>` | `<AuiIf condition={(
|
|
197
|
-
| `<MessagePrimitive.If speaking>` | `<AuiIf condition={(
|
|
198
|
-
| `<MessagePrimitive.If last>` | `<AuiIf condition={(
|
|
199
|
-
| `<ComposerPrimitive.If editing>` | `<AuiIf condition={(
|
|
191
|
+
| `<ThreadPrimitive.If empty>` | `<AuiIf condition={(s) => s.thread.isEmpty}>` |
|
|
192
|
+
| `<ThreadPrimitive.If running>` | `<AuiIf condition={(s) => s.thread.isRunning}>` |
|
|
193
|
+
| `<ThreadPrimitive.If running={false}>` | `<AuiIf condition={(s) => !s.thread.isRunning}>` |
|
|
194
|
+
| `<MessagePrimitive.If user>` | `<AuiIf condition={(s) => s.message.role === "user"}>` |
|
|
195
|
+
| `<MessagePrimitive.If assistant>` | `<AuiIf condition={(s) => s.message.role === "assistant"}>` |
|
|
196
|
+
| `<MessagePrimitive.If copied>` | `<AuiIf condition={(s) => s.message.isCopied}>` |
|
|
197
|
+
| `<MessagePrimitive.If speaking>` | `<AuiIf condition={(s) => s.message.speech != null}>` |
|
|
198
|
+
| `<MessagePrimitive.If last>` | `<AuiIf condition={(s) => s.message.isLast}>` |
|
|
199
|
+
| `<ComposerPrimitive.If editing>` | `<AuiIf condition={(s) => s.composer.isEditing}>` |
|
|
@@ -20,6 +20,10 @@ import { ComposerPrimitive } from "@assistant-ui/react";
|
|
|
20
20
|
// creating a new message
|
|
21
21
|
const Composer = () => (
|
|
22
22
|
<ComposerPrimitive.Root>
|
|
23
|
+
<ComposerPrimitive.Quote>
|
|
24
|
+
<ComposerPrimitive.QuoteText />
|
|
25
|
+
<ComposerPrimitive.QuoteDismiss />
|
|
26
|
+
</ComposerPrimitive.Quote>
|
|
23
27
|
<ComposerPrimitive.Attachments />
|
|
24
28
|
<ComposerPrimitive.AddAttachment />
|
|
25
29
|
<ComposerPrimitive.Input />
|
|
@@ -80,14 +84,32 @@ This primitive renders a `<textarea>` element unless `asChild` is set.
|
|
|
80
84
|
{
|
|
81
85
|
name: "asChild",
|
|
82
86
|
},
|
|
87
|
+
{
|
|
88
|
+
name: "submitMode",
|
|
89
|
+
type: '"enter" | "ctrlEnter" | "none"',
|
|
90
|
+
default: '"enter"',
|
|
91
|
+
description:
|
|
92
|
+
'Controls how the Enter key submits messages. "enter": plain Enter submits (Shift+Enter for newline). "ctrlEnter": Ctrl/Cmd+Enter submits (plain Enter for newline). "none": keyboard submission disabled.',
|
|
93
|
+
},
|
|
83
94
|
]}
|
|
84
95
|
/>
|
|
85
96
|
|
|
86
97
|
#### Keyboard Shortcuts
|
|
87
98
|
|
|
99
|
+
Default (`submitMode="enter"`):
|
|
100
|
+
|
|
88
101
|
| Key | Description |
|
|
89
102
|
| --- | --- |
|
|
90
103
|
| <Kbd>Enter</Kbd> | Sends the message. |
|
|
104
|
+
| <Kbd>Shift + Enter</Kbd> | Inserts a newline. |
|
|
105
|
+
| <Kbd>Escape</Kbd> | Sends a cancel action. |
|
|
106
|
+
|
|
107
|
+
With `submitMode="ctrlEnter"`:
|
|
108
|
+
|
|
109
|
+
| Key | Description |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| <Kbd>Ctrl/Cmd + Enter</Kbd> | Sends the message. |
|
|
112
|
+
| <Kbd>Enter</Kbd> | Inserts a newline. |
|
|
91
113
|
| <Kbd>Escape</Kbd> | Sends a cancel action. |
|
|
92
114
|
|
|
93
115
|
### Send
|
|
@@ -274,6 +296,44 @@ This primitive renders a `<span>` element.
|
|
|
274
296
|
</ComposerPrimitive.If>
|
|
275
297
|
```
|
|
276
298
|
|
|
299
|
+
### Quote
|
|
300
|
+
|
|
301
|
+
A container for displaying a quote preview in the composer. Only renders when a quote is set via `composer.setQuote()`.
|
|
302
|
+
|
|
303
|
+
This primitive renders a `<div>` element.
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
<ComposerPrimitive.Quote className="flex items-start gap-2 bg-muted/60 px-3 py-2">
|
|
307
|
+
<ComposerPrimitive.QuoteText className="line-clamp-2 flex-1 text-sm" />
|
|
308
|
+
<ComposerPrimitive.QuoteDismiss>×</ComposerPrimitive.QuoteDismiss>
|
|
309
|
+
</ComposerPrimitive.Quote>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### QuoteText
|
|
313
|
+
|
|
314
|
+
Renders the quoted text content. Only renders when a quote is set.
|
|
315
|
+
|
|
316
|
+
This primitive renders a `<span>` element.
|
|
317
|
+
|
|
318
|
+
### QuoteDismiss
|
|
319
|
+
|
|
320
|
+
A button that clears the current quote from the composer by calling `setQuote(undefined)`.
|
|
321
|
+
|
|
322
|
+
This primitive renders a `<button>` element unless `asChild` is set.
|
|
323
|
+
|
|
324
|
+
<ParametersTable
|
|
325
|
+
type="ComposerPrimitiveQuoteDismissProps"
|
|
326
|
+
parameters={[
|
|
327
|
+
{
|
|
328
|
+
name: "asChild",
|
|
329
|
+
},
|
|
330
|
+
]}
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
<Callout type="info">
|
|
334
|
+
See the [Quoting guide](/docs/guides/quoting) for a complete walkthrough including the floating selection toolbar and backend handling.
|
|
335
|
+
</Callout>
|
|
336
|
+
|
|
277
337
|
### If
|
|
278
338
|
|
|
279
339
|
Renders children if a condition is met.
|