@assistant-ui/mcp-docs-server 0.1.29 → 0.1.31
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 +15 -7
- package/.docs/organized/code-examples/with-a2a.md +9 -21
- package/.docs/organized/code-examples/with-ag-ui.md +11 -8
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
- package/.docs/organized/code-examples/with-artifacts.md +12 -10
- package/.docs/organized/code-examples/with-assistant-transport.md +11 -12
- package/.docs/organized/code-examples/with-chain-of-thought.md +83 -54
- package/.docs/organized/code-examples/with-cloud-standalone.md +14 -11
- package/.docs/organized/code-examples/with-cloud.md +9 -10
- package/.docs/organized/code-examples/with-custom-thread-list.md +61 -16
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +17 -12
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +13 -13
- package/.docs/organized/code-examples/with-expo.md +25 -21
- package/.docs/organized/code-examples/with-external-store.md +8 -8
- package/.docs/organized/code-examples/with-ffmpeg.md +17 -12
- package/.docs/organized/code-examples/with-generative-ui.md +9 -9
- package/.docs/organized/code-examples/with-google-adk.md +8 -8
- package/.docs/organized/code-examples/with-heat-graph.md +5 -5
- package/.docs/organized/code-examples/with-interactables.md +10 -25
- package/.docs/organized/code-examples/with-langchain.md +437 -0
- package/.docs/organized/code-examples/with-langgraph.md +16 -16
- package/.docs/organized/code-examples/with-livekit.md +18 -13
- package/.docs/organized/code-examples/with-opencode.md +105 -62
- package/.docs/organized/code-examples/with-parent-id-grouping.md +10 -10
- package/.docs/organized/code-examples/with-react-hook-form.md +220 -148
- package/.docs/organized/code-examples/with-react-ink.md +2 -2
- package/.docs/organized/code-examples/with-react-router.md +12 -12
- package/.docs/organized/code-examples/with-store.md +8 -5
- package/.docs/organized/code-examples/with-tanstack.md +10 -10
- package/.docs/organized/code-examples/with-tap-runtime.md +10 -6
- package/.docs/raw/docs/(docs)/cli.mdx +2 -1
- package/.docs/raw/docs/(docs)/copilots/assistant-frame.mdx +1 -0
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +10 -3
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +8 -3
- package/.docs/raw/docs/(docs)/copilots/make-assistant-visible.mdx +1 -0
- package/.docs/raw/docs/(docs)/copilots/model-context.mdx +1 -0
- package/.docs/raw/docs/(docs)/copilots/motivation.mdx +1 -0
- package/.docs/raw/docs/(docs)/copilots/use-assistant-instructions.mdx +1 -0
- package/.docs/raw/docs/(docs)/devtools.mdx +1 -0
- package/.docs/raw/docs/(docs)/index.mdx +1 -0
- package/.docs/raw/docs/(docs)/installation.mdx +1 -0
- package/.docs/raw/docs/(docs)/rtl.mdx +80 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +34 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/feedback-speech.mdx +41 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +26 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +34 -0
- package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +31 -0
- package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +20 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +26 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +72 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +41 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +48 -0
- package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +30 -0
- package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +23 -0
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +21 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +7 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
- package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +65 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +50 -6
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +15 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +36 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +38 -0
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +9 -0
- package/.docs/raw/docs/(reference)/migrations/v0-14.mdx +144 -6
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +231 -3
- package/.docs/raw/docs/cloud/ai-sdk.mdx +221 -3
- package/.docs/raw/docs/cloud/langgraph.mdx +274 -2
- package/.docs/raw/docs/{(docs)/guides → guides}/attachments.mdx +41 -36
- package/.docs/raw/docs/guides/branching.mdx +76 -0
- package/.docs/raw/docs/guides/chain-of-thought.mdx +166 -0
- package/.docs/raw/docs/{(docs)/guides → guides}/context-api.mdx +50 -22
- package/.docs/raw/docs/{(docs)/guides → guides}/dictation.mdx +2 -0
- package/.docs/raw/docs/guides/editing.mdx +102 -0
- package/.docs/raw/docs/guides/index.mdx +103 -0
- package/.docs/raw/docs/{(docs)/guides → guides}/interactables.mdx +49 -0
- package/.docs/raw/docs/{(docs)/guides → guides}/latex.mdx +51 -8
- package/.docs/raw/docs/guides/mentions.mdx +520 -0
- package/.docs/raw/docs/{(docs)/guides → guides}/message-timing.mdx +8 -2
- package/.docs/raw/docs/{(docs)/guides → guides}/multi-agent.mdx +64 -4
- package/.docs/raw/docs/{(docs)/guides → guides}/quoting.mdx +10 -17
- package/.docs/raw/docs/guides/slash-commands.mdx +361 -0
- package/.docs/raw/docs/guides/speech.mdx +156 -0
- package/.docs/raw/docs/{(docs)/guides → guides}/suggestions.mdx +21 -83
- package/.docs/raw/docs/{(docs)/guides → guides}/tool-ui.mdx +108 -36
- package/.docs/raw/docs/{(docs)/guides → guides}/tools.mdx +131 -35
- package/.docs/raw/docs/{(docs)/guides → guides}/voice.mdx +39 -0
- package/.docs/raw/docs/ink/index.mdx +1 -3
- package/.docs/raw/docs/ink/migration.mdx +1 -3
- package/.docs/raw/docs/ink/primitives.mdx +37 -1
- package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +520 -0
- package/.docs/raw/docs/integrations/auth/better-auth.mdx +191 -0
- package/.docs/raw/docs/integrations/auth/clerk.mdx +172 -0
- package/.docs/raw/docs/integrations/auth/next-auth.mdx +196 -0
- package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +79 -0
- package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +188 -0
- package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +57 -0
- package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +201 -0
- package/.docs/raw/docs/integrations/gateways/index.mdx +157 -0
- package/.docs/raw/docs/integrations/index.mdx +173 -0
- package/.docs/raw/docs/integrations/observability/helicone.mdx +130 -0
- package/.docs/raw/docs/integrations/observability/langfuse.mdx +156 -0
- package/.docs/raw/docs/integrations/observability/langsmith.mdx +146 -0
- package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +712 -0
- package/.docs/raw/docs/integrations/tools/mcp.mdx +267 -0
- package/.docs/raw/docs/primitives/action-bar.mdx +1 -0
- package/.docs/raw/docs/primitives/assistant-modal.mdx +1 -0
- package/.docs/raw/docs/primitives/attachment.mdx +1 -0
- package/.docs/raw/docs/primitives/branch-picker.mdx +1 -0
- package/.docs/raw/docs/primitives/chain-of-thought.mdx +90 -85
- package/.docs/raw/docs/primitives/composer.mdx +96 -63
- package/.docs/raw/docs/primitives/error.mdx +1 -0
- package/.docs/raw/docs/primitives/index.mdx +2 -1
- package/.docs/raw/docs/primitives/message.mdx +68 -5
- package/.docs/raw/docs/primitives/selection-toolbar.mdx +1 -0
- package/.docs/raw/docs/primitives/suggestion.mdx +1 -0
- package/.docs/raw/docs/primitives/thread-list.mdx +39 -0
- package/.docs/raw/docs/primitives/thread.mdx +16 -13
- package/.docs/raw/docs/react-native/index.mdx +1 -3
- package/.docs/raw/docs/react-native/migration.mdx +1 -3
- package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +396 -0
- package/.docs/raw/docs/runtimes/a2a/overview.mdx +60 -0
- package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +216 -0
- package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +70 -0
- package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +243 -0
- package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +123 -0
- package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +52 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +71 -131
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +69 -63
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +365 -101
- package/.docs/raw/docs/runtimes/concepts/adapters.mdx +265 -0
- package/.docs/raw/docs/runtimes/concepts/architecture.mdx +125 -0
- package/.docs/raw/docs/runtimes/concepts/stability.mdx +67 -0
- package/.docs/raw/docs/runtimes/concepts/threads.mdx +428 -0
- package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +703 -0
- package/.docs/raw/docs/runtimes/custom/data-stream.mdx +323 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +253 -1236
- package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +746 -0
- package/.docs/raw/docs/runtimes/custom/overview.mdx +71 -0
- package/.docs/raw/docs/runtimes/google-adk/api.mdx +256 -0
- package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +717 -0
- package/.docs/raw/docs/runtimes/google-adk/overview.mdx +69 -0
- package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +229 -0
- package/.docs/raw/docs/runtimes/langchain.mdx +533 -0
- package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +305 -0
- package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +104 -0
- package/.docs/raw/docs/runtimes/langgraph/overview.mdx +84 -0
- package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +496 -0
- package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +127 -0
- package/.docs/raw/docs/runtimes/langgraph/threads.mdx +113 -0
- package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +3 -3
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -23
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
- package/.docs/raw/docs/runtimes/opencode/hooks.mdx +191 -0
- package/.docs/raw/docs/runtimes/opencode/overview.mdx +48 -0
- package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +119 -0
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +74 -198
- package/.docs/raw/docs/ui/accordion.mdx +1 -0
- package/.docs/raw/docs/ui/assistant-modal.mdx +1 -0
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -0
- package/.docs/raw/docs/ui/attachment.mdx +1 -0
- package/.docs/raw/docs/ui/badge.mdx +1 -0
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +200 -0
- package/.docs/raw/docs/ui/context-display.mdx +1 -0
- package/.docs/raw/docs/ui/diff-viewer.mdx +1 -0
- package/.docs/raw/docs/ui/directive-text.mdx +114 -0
- package/.docs/raw/docs/ui/file.mdx +1 -0
- package/.docs/raw/docs/ui/image.mdx +1 -0
- package/.docs/raw/docs/ui/markdown.mdx +2 -14
- package/.docs/raw/docs/ui/mermaid.mdx +1 -0
- package/.docs/raw/docs/ui/message-timing.mdx +3 -2
- package/.docs/raw/docs/ui/model-selector.mdx +1 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +325 -313
- package/.docs/raw/docs/ui/quote.mdx +1 -0
- package/.docs/raw/docs/ui/reasoning.mdx +69 -32
- package/.docs/raw/docs/ui/scrollbar.mdx +1 -0
- package/.docs/raw/docs/ui/select.mdx +1 -0
- package/.docs/raw/docs/ui/sources.mdx +1 -0
- package/.docs/raw/docs/ui/streamdown.mdx +1 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -0
- package/.docs/raw/docs/ui/tabs.mdx +1 -0
- package/.docs/raw/docs/ui/thread-list.mdx +17 -0
- package/.docs/raw/docs/ui/thread.mdx +56 -1
- package/.docs/raw/docs/ui/tool-fallback.mdx +1 -0
- package/.docs/raw/docs/ui/tool-group.mdx +39 -11
- package/.docs/raw/docs/ui/voice.mdx +1 -0
- package/.docs/raw/docs/utilities/heat-graph.mdx +1 -0
- package/.docs/raw/docs/utilities/react-o11y.mdx +278 -0
- package/.docs/raw/docs/utilities/tw-shimmer.mdx +1 -0
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +4 -4
- package/src/tools/tests/path-traversal.test.ts +1 -1
- package/src/utils/logger.ts +1 -1
- package/.docs/raw/docs/(docs)/guides/branching.mdx +0 -65
- package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +0 -164
- package/.docs/raw/docs/(docs)/guides/editing.mdx +0 -66
- package/.docs/raw/docs/(docs)/guides/mentions.mdx +0 -406
- package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +0 -275
- package/.docs/raw/docs/(docs)/guides/speech.mdx +0 -38
- package/.docs/raw/docs/runtimes/a2a/index.mdx +0 -298
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +0 -1033
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +0 -268
- package/.docs/raw/docs/runtimes/custom/local.mdx +0 -1464
- package/.docs/raw/docs/runtimes/data-stream.mdx +0 -422
- package/.docs/raw/docs/runtimes/google-adk/index.mdx +0 -686
- package/.docs/raw/docs/runtimes/helicone.mdx +0 -61
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +0 -607
- package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +0 -12
- package/.docs/raw/docs/runtimes/langserve.mdx +0 -116
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -218
- package/.docs/raw/docs/runtimes/mastra/overview.mdx +0 -18
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -217
- package/.docs/raw/docs/ui/mention.mdx +0 -168
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Attachments
|
|
3
3
|
description: Let users attach files, images, and documents to messages.
|
|
4
|
+
platforms: ["react"]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
import { AttachmentSample } from "@/components/docs/samples/attachment";
|
|
@@ -57,37 +58,7 @@ const runtime = useChatRuntime();
|
|
|
57
58
|
|
|
58
59
|
### Add UI Components
|
|
59
60
|
|
|
60
|
-
Integrate attachment components into your chat interface
|
|
61
|
-
|
|
62
|
-
```tsx title="/components/assistant-ui/thread.tsx"
|
|
63
|
-
// In your Composer component
|
|
64
|
-
import {
|
|
65
|
-
ComposerAttachments,
|
|
66
|
-
ComposerAddAttachment,
|
|
67
|
-
} from "@/components/assistant-ui/attachment";
|
|
68
|
-
|
|
69
|
-
const Composer = () => {
|
|
70
|
-
return (
|
|
71
|
-
<ComposerPrimitive.Root>
|
|
72
|
-
<ComposerAttachments />
|
|
73
|
-
<ComposerAddAttachment />
|
|
74
|
-
<ComposerPrimitive.Input placeholder="Type a message..." />
|
|
75
|
-
</ComposerPrimitive.Root>
|
|
76
|
-
);
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
// In your UserMessage component
|
|
80
|
-
import { UserMessageAttachments } from "@/components/assistant-ui/attachment";
|
|
81
|
-
|
|
82
|
-
const UserMessage = () => {
|
|
83
|
-
return (
|
|
84
|
-
<MessagePrimitive.Root>
|
|
85
|
-
<UserMessageAttachments />
|
|
86
|
-
<MessagePrimitive.Parts />
|
|
87
|
-
</MessagePrimitive.Root>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
```
|
|
61
|
+
Integrate the attachment components into your chat interface. See [Attachment UI components](/docs/ui/attachment) for the full install and usage guide.
|
|
91
62
|
|
|
92
63
|
</Step>
|
|
93
64
|
</Steps>
|
|
@@ -143,7 +114,7 @@ const compositeAdapter = new CompositeAttachmentAdapter([
|
|
|
143
114
|
|
|
144
115
|
## Creating Custom Attachment Adapters
|
|
145
116
|
|
|
146
|
-
Build your own adapters for specialized file handling. Below are complete examples for common use cases.
|
|
117
|
+
Build your own adapters for specialized file handling. Below are complete examples for common use cases. For `PendingAttachment` and `CompleteAttachment` type definitions, see [Attachment types](/docs/ui/attachment#attachment-types).
|
|
147
118
|
|
|
148
119
|
### Vision-Capable Image Adapter
|
|
149
120
|
|
|
@@ -364,7 +335,7 @@ Provide real-time upload progress using async generators:
|
|
|
364
335
|
|
|
365
336
|
```tsx
|
|
366
337
|
class UploadAttachmentAdapter implements AttachmentAdapter {
|
|
367
|
-
accept = "
|
|
338
|
+
accept = "*";
|
|
368
339
|
|
|
369
340
|
async *add({ file }: { file: File }) {
|
|
370
341
|
const id = generateId();
|
|
@@ -391,8 +362,8 @@ class UploadAttachmentAdapter implements AttachmentAdapter {
|
|
|
391
362
|
} as PendingAttachment;
|
|
392
363
|
}
|
|
393
364
|
|
|
394
|
-
//
|
|
395
|
-
|
|
365
|
+
// Yield final progress so the 100% state reaches the composer
|
|
366
|
+
yield {
|
|
396
367
|
id,
|
|
397
368
|
type: "file",
|
|
398
369
|
name: file.name,
|
|
@@ -490,6 +461,40 @@ class ValidatedImageAdapter implements AttachmentAdapter {
|
|
|
490
461
|
}
|
|
491
462
|
```
|
|
492
463
|
|
|
464
|
+
To surface failures in the UI, subscribe to `composer.attachmentAddError`. It fires whenever an add operation produces a failure, in either of two ways:
|
|
465
|
+
|
|
466
|
+
1. `addAttachment()` rejects: no adapter is configured, the file type does not match `accept`, or the adapter's `add()` throws.
|
|
467
|
+
2. `addAttachment()` resolves but the adapter returned (or, for async-iterator adapters, yielded) an attachment whose `status.reason === "error"`. The promise resolves successfully, yet the event still fires so the UI can react.
|
|
468
|
+
|
|
469
|
+
The event payload carries a `reason` discriminator and a human-readable `message`, so you can branch UI on the failure mode:
|
|
470
|
+
|
|
471
|
+
| `reason` | When It Fires |
|
|
472
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------ |
|
|
473
|
+
| `no-adapter` | `addAttachment(File)` was called but no `AttachmentAdapter` is configured. |
|
|
474
|
+
| `not-accepted` | The file's content type (or filename extension) did not match `adapter.accept`. External `CreateAttachment` descriptors also trigger this when their `contentType` does not match `adapter.accept`. |
|
|
475
|
+
| `adapter-error` | The adapter's `add()` threw, or returned/yielded an attachment with `status.reason === "error"`. If the adapter produced any attachment before failing, the errored attachment is also visible in `composer.attachments`; if it threw before producing one, the event is the only signal. |
|
|
476
|
+
|
|
477
|
+
```tsx
|
|
478
|
+
import { toast } from "sonner"; // or your toast library of choice
|
|
479
|
+
import { useAuiEvent } from "@assistant-ui/react";
|
|
480
|
+
|
|
481
|
+
function AttachmentErrorToast() {
|
|
482
|
+
useAuiEvent("composer.attachmentAddError", ({ reason, message, error }) => {
|
|
483
|
+
if (reason === "not-accepted") {
|
|
484
|
+
toast.error("This file type is not supported.");
|
|
485
|
+
} else if (reason === "no-adapter") {
|
|
486
|
+
toast.error("Attachments are not configured for this composer.");
|
|
487
|
+
} else {
|
|
488
|
+
if (error) console.error(error); // underlying Error, useful for logging
|
|
489
|
+
toast.error(message || "Attachment failed to upload.");
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
`attachmentId` is included when the failure is associated with an attachment that was registered (typically `adapter-error` cases). It is `undefined` for `no-adapter` and `not-accepted` failures because those reject before any attachment is registered.
|
|
497
|
+
|
|
493
498
|
### External Source Attachments
|
|
494
499
|
|
|
495
500
|
Add attachments from external sources (URLs, API data, CMS references) without needing a `File` object or an `AttachmentAdapter`:
|
|
@@ -513,7 +518,7 @@ await aui.composer().addAttachment({
|
|
|
513
518
|
});
|
|
514
519
|
```
|
|
515
520
|
|
|
516
|
-
External attachments are added as complete attachments directly
|
|
521
|
+
External attachments are added as complete attachments directly. They bypass the `AttachmentAdapter`'s `add()` step (no upload), but `adapter.accept` is still enforced when an `AttachmentAdapter` is configured: a `CreateAttachment` whose `contentType` does not match `adapter.accept` is rejected and emits `composer.attachmentAddError`. If `contentType` is omitted, the descriptor's filename extension is matched against `adapter.accept` only when `accept` itself contains explicit extension entries (e.g. `.png,.pdf`); MIME-wildcard `accept` strings such as `image/*` always require a matching `contentType`. When no `AttachmentAdapter` is configured, external attachments are added without any content-type check, and they can be removed without an adapter.
|
|
517
522
|
|
|
518
523
|
### Multiple File Selection
|
|
519
524
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Message Branching
|
|
3
|
+
description: Navigate between alternative message versions created by editing or reloading.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
import { BranchingSample } from "@/components/docs/samples/branching";
|
|
8
|
+
|
|
9
|
+
Branching lets users navigate between alternative versions of a message. A new branch is created when:
|
|
10
|
+
|
|
11
|
+
- A user message is edited
|
|
12
|
+
- An assistant message is reloaded (reload creates a new branch on the same message)
|
|
13
|
+
|
|
14
|
+
Branches are automatically tracked by assistant-ui by observing changes to the `messages` array.
|
|
15
|
+
|
|
16
|
+
## Shortest Working Pattern
|
|
17
|
+
|
|
18
|
+
<BranchingSample />
|
|
19
|
+
|
|
20
|
+
Place a branch picker inside your message component:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import { BranchPickerPrimitive } from "@assistant-ui/react";
|
|
24
|
+
|
|
25
|
+
const BranchPicker = () => (
|
|
26
|
+
<BranchPickerPrimitive.Root hideWhenSingleBranch>
|
|
27
|
+
<BranchPickerPrimitive.Previous />
|
|
28
|
+
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
29
|
+
<BranchPickerPrimitive.Next />
|
|
30
|
+
</BranchPickerPrimitive.Root>
|
|
31
|
+
);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`BranchPickerPrimitive.Previous` and `.Next` automatically disable at branch boundaries and while a run is in flight (unless the runtime supports `switchBranchDuringRun`). For the full primitive API, see [BranchPickerPrimitive](/docs/primitives/branch-picker).
|
|
35
|
+
|
|
36
|
+
## Triggering Reload
|
|
37
|
+
|
|
38
|
+
`ActionBarPrimitive.Reload` creates a new branch on an assistant message and re-runs from there:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { ActionBarPrimitive, MessagePrimitive } from "@assistant-ui/react";
|
|
42
|
+
|
|
43
|
+
const AssistantMessage = () => (
|
|
44
|
+
<MessagePrimitive.Root>
|
|
45
|
+
<MessagePrimitive.Parts />
|
|
46
|
+
<ActionBarPrimitive.Root>
|
|
47
|
+
<ActionBarPrimitive.Reload />
|
|
48
|
+
</ActionBarPrimitive.Root>
|
|
49
|
+
</MessagePrimitive.Root>
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`Reload` is disabled while `thread.isRunning` or `thread.isDisabled` is true. See [ActionBarPrimitive](/docs/primitives/action-bar) for the full reference.
|
|
54
|
+
|
|
55
|
+
## Programmatic Branch Navigation
|
|
56
|
+
|
|
57
|
+
For headless or keyboard-shortcut flows, navigate directly to a branch by id via `aui.message().switchToBranch`:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useAui } from "@assistant-ui/react";
|
|
61
|
+
|
|
62
|
+
const SwitchToBranch = ({ branchId }: { branchId: string }) => {
|
|
63
|
+
const aui = useAui();
|
|
64
|
+
return (
|
|
65
|
+
<button onClick={() => aui.message().switchToBranch({ branchId })}>
|
|
66
|
+
Go to branch
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This must be called inside a message context (e.g. nested within `MessagePrimitive.Root`).
|
|
73
|
+
|
|
74
|
+
## Grouped Parts After Branching
|
|
75
|
+
|
|
76
|
+
Each branch is a distinct message version with its own content parts. `MessagePrimitive.GroupedParts` provides hierarchical adjacent grouping of those parts, useful when a message mixes tool calls and text across branches. See the [MessagePrimitive](/docs/primitives/message) reference for `GroupedParts` usage.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Chain of Thought
|
|
3
|
+
description: Group reasoning and tool calls into a collapsible accordion UI.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
LLMs often produce reasoning steps and tool calls in succession. Chain of Thought lets you visually group these consecutive parts into a single collapsible accordion, giving users a clean "thinking" UI.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
When a model like OpenAI's `o4-mini` responds, it may emit a sequence of reasoning tokens and tool calls before producing its final text answer. Use `MessagePrimitive.GroupedParts` to group those adjacent reasoning and tool-call parts into a single collapsible "thinking" section.
|
|
12
|
+
|
|
13
|
+
<Callout type="info">
|
|
14
|
+
The older `components.ChainOfThought` prop on `MessagePrimitive.Parts` and `components` prop on `ChainOfThoughtPrimitive.Parts` are legacy APIs. They still work for existing code, but new code should use `MessagePrimitive.GroupedParts`.
|
|
15
|
+
</Callout>
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
<Steps>
|
|
20
|
+
<Step>
|
|
21
|
+
|
|
22
|
+
### Wire GroupedParts into your assistant message
|
|
23
|
+
|
|
24
|
+
Return the same top-level group for reasoning and tool calls, with nested groups for each type:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {
|
|
28
|
+
MessagePrimitive,
|
|
29
|
+
} from "@assistant-ui/react";
|
|
30
|
+
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
31
|
+
import {
|
|
32
|
+
Reasoning,
|
|
33
|
+
ReasoningContent,
|
|
34
|
+
ReasoningRoot,
|
|
35
|
+
ReasoningText,
|
|
36
|
+
ReasoningTrigger,
|
|
37
|
+
} from "@/components/assistant-ui/reasoning";
|
|
38
|
+
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
|
|
39
|
+
import {
|
|
40
|
+
ToolGroupContent,
|
|
41
|
+
ToolGroupRoot,
|
|
42
|
+
ToolGroupTrigger,
|
|
43
|
+
} from "@/components/assistant-ui/tool-group";
|
|
44
|
+
import type { FC } from "react";
|
|
45
|
+
|
|
46
|
+
const AssistantMessage: FC = () => {
|
|
47
|
+
return (
|
|
48
|
+
<MessagePrimitive.Root>
|
|
49
|
+
<MessagePrimitive.GroupedParts
|
|
50
|
+
groupBy={(part) => {
|
|
51
|
+
if (part.type === "reasoning")
|
|
52
|
+
return ["group-chainOfThought", "group-reasoning"];
|
|
53
|
+
if (part.type === "tool-call")
|
|
54
|
+
return ["group-chainOfThought", "group-tool"];
|
|
55
|
+
return null;
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
{({ part, children }) => {
|
|
59
|
+
switch (part.type) {
|
|
60
|
+
case "group-chainOfThought":
|
|
61
|
+
return <div className="my-2">{children}</div>;
|
|
62
|
+
case "group-reasoning": {
|
|
63
|
+
const running = part.status.type === "running";
|
|
64
|
+
return (
|
|
65
|
+
<ReasoningRoot defaultOpen={running}>
|
|
66
|
+
<ReasoningTrigger active={running} />
|
|
67
|
+
<ReasoningContent aria-busy={running}>
|
|
68
|
+
<ReasoningText>{children}</ReasoningText>
|
|
69
|
+
</ReasoningContent>
|
|
70
|
+
</ReasoningRoot>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
case "group-tool":
|
|
74
|
+
return (
|
|
75
|
+
<ToolGroupRoot>
|
|
76
|
+
<ToolGroupTrigger
|
|
77
|
+
count={part.indices.length}
|
|
78
|
+
active={part.status.type === "running"}
|
|
79
|
+
/>
|
|
80
|
+
<ToolGroupContent>{children}</ToolGroupContent>
|
|
81
|
+
</ToolGroupRoot>
|
|
82
|
+
);
|
|
83
|
+
case "text":
|
|
84
|
+
return <MarkdownText />;
|
|
85
|
+
case "reasoning":
|
|
86
|
+
return <Reasoning {...part} />;
|
|
87
|
+
case "tool-call":
|
|
88
|
+
return part.toolUI ?? <ToolFallback {...part} />;
|
|
89
|
+
default:
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}}
|
|
93
|
+
</MessagePrimitive.GroupedParts>
|
|
94
|
+
</MessagePrimitive.Root>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
</Step>
|
|
100
|
+
<Step>
|
|
101
|
+
|
|
102
|
+
### Use a Reasoning Model
|
|
103
|
+
|
|
104
|
+
Chain of Thought is most useful with models that produce reasoning tokens (e.g. OpenAI `o4-mini`). Here's an example backend route using the AI SDK:
|
|
105
|
+
|
|
106
|
+
```tsx title="app/api/chat/route.ts"
|
|
107
|
+
import { openai } from "@ai-sdk/openai";
|
|
108
|
+
import { streamText, convertToModelMessages } from "ai";
|
|
109
|
+
|
|
110
|
+
export async function POST(req: Request) {
|
|
111
|
+
const { messages } = await req.json();
|
|
112
|
+
|
|
113
|
+
const result = streamText({
|
|
114
|
+
model: openai("o4-mini"),
|
|
115
|
+
messages: await convertToModelMessages(messages),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return result.toUIMessageStreamResponse();
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
</Step>
|
|
123
|
+
</Steps>
|
|
124
|
+
|
|
125
|
+
## LangGraph
|
|
126
|
+
|
|
127
|
+
Chain-of-thought parts are surfaced by the AI SDK's built-in reasoning stream. LangGraph does not emit reasoning tokens in that format, so reasoning grouping will not activate automatically. If you want to display reasoning text from a LangGraph agent, emit it as a custom data part from your graph and render it with `makeAssistantDataUI`. See [generative UI with LangGraph](/docs/runtimes/langgraph/generative-ui) for details.
|
|
128
|
+
|
|
129
|
+
## Legacy: ChainOfThoughtPrimitive
|
|
130
|
+
|
|
131
|
+
### Reading Collapsed State
|
|
132
|
+
|
|
133
|
+
For existing `ChainOfThoughtPrimitive` code, use `AuiIf` to conditionally render based on the accordion state:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { AuiIf, ChainOfThoughtPrimitive } from "@assistant-ui/react";
|
|
137
|
+
import { ChevronDownIcon, ChevronRightIcon } from "lucide-react";
|
|
138
|
+
|
|
139
|
+
const ChainOfThoughtAccordionTrigger = () => {
|
|
140
|
+
return (
|
|
141
|
+
<ChainOfThoughtPrimitive.AccordionTrigger className="flex w-full cursor-pointer items-center gap-2 px-4 py-2 text-sm">
|
|
142
|
+
<AuiIf condition={(s) => s.chainOfThought.collapsed}>
|
|
143
|
+
<ChevronRightIcon className="size-4" />
|
|
144
|
+
</AuiIf>
|
|
145
|
+
<AuiIf condition={(s) => !s.chainOfThought.collapsed}>
|
|
146
|
+
<ChevronDownIcon className="size-4" />
|
|
147
|
+
</AuiIf>
|
|
148
|
+
Thinking
|
|
149
|
+
</ChainOfThoughtPrimitive.AccordionTrigger>
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### API Reference
|
|
155
|
+
|
|
156
|
+
For lower-level legacy compatibility details, see the [`ChainOfThought` primitive reference](/docs/primitives/chain-of-thought).
|
|
157
|
+
|
|
158
|
+
## Full Example
|
|
159
|
+
|
|
160
|
+
See the complete [with-chain-of-thought example](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-chain-of-thought) for a working implementation with tool calls and reasoning.
|
|
161
|
+
|
|
162
|
+
## Related Guides
|
|
163
|
+
|
|
164
|
+
- [Reasoning](/docs/ui/reasoning) — reasoning UI primitives for grouped parts
|
|
165
|
+
- [Generative UI](/docs/guides/tool-ui) — custom UI for tool calls
|
|
166
|
+
- [Tools](/docs/guides/tools) — defining and using tools
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Context API
|
|
3
3
|
description: Read and update assistant state to build custom components.
|
|
4
|
+
platforms: ["react"]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
The Context API provides direct access to assistant-ui's state management system, enabling you to build custom components that integrate seamlessly with the assistant runtime.
|
|
@@ -211,6 +212,8 @@ aui.attachment().getState();
|
|
|
211
212
|
// ThreadList actions
|
|
212
213
|
aui.threads().switchToNewThread();
|
|
213
214
|
aui.threads().switchToThread(threadId);
|
|
215
|
+
aui.threads().reload();
|
|
216
|
+
await aui.threads().getLoadThreadsPromise();
|
|
214
217
|
aui.threads().getState();
|
|
215
218
|
|
|
216
219
|
// ThreadListItem actions
|
|
@@ -233,9 +236,9 @@ aui.chainOfThought().getState();
|
|
|
233
236
|
aui.chainOfThought().setCollapsed(collapsed);
|
|
234
237
|
aui.chainOfThought().part({ index: 0 });
|
|
235
238
|
|
|
236
|
-
// ModelContext actions
|
|
237
|
-
aui.modelContext().getState();
|
|
239
|
+
// ModelContext actions — see /docs/copilots/model-context for full usage
|
|
238
240
|
aui.modelContext().register(provider);
|
|
241
|
+
aui.modelContext().getState();
|
|
239
242
|
|
|
240
243
|
// Tools actions
|
|
241
244
|
aui.tools().setToolUI(toolName, render);
|
|
@@ -355,9 +358,14 @@ const partByToolCall = aui.message().part({ toolCallId: "call_123" });
|
|
|
355
358
|
// Access attachment by index
|
|
356
359
|
const attachment = aui.composer().attachment({ index: 0 }).getState();
|
|
357
360
|
|
|
358
|
-
// Access thread list item by ID or
|
|
361
|
+
// Access thread list item by ID, index, or the "main" selector
|
|
359
362
|
const threadItem = aui.threads().item({ id: "thread_123" });
|
|
360
363
|
const threadByIndex = aui.threads().item({ index: 0 });
|
|
364
|
+
const archivedThread = aui.threads().item({ index: 0, archived: true });
|
|
365
|
+
|
|
366
|
+
// Traverse to the main thread directly
|
|
367
|
+
const mainThread = aui.threads().thread("main");
|
|
368
|
+
const message = aui.threads().thread("main").message({ id: "msg_123" });
|
|
361
369
|
```
|
|
362
370
|
|
|
363
371
|
## Common Patterns
|
|
@@ -490,9 +498,9 @@ const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
|
490
498
|
|
|
491
499
|
| Scope | Key State Properties | Description |
|
|
492
500
|
| -------------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------- |
|
|
493
|
-
| ThreadList | `mainThreadId`, `newThreadId`, `threadIds`, `archivedThreadIds`, `isLoading`, `threadItems`
|
|
494
|
-
| ThreadListItem | `id`, `title`, `status`, `remoteId`, `externalId`
|
|
495
|
-
| Thread | `isRunning
|
|
501
|
+
| ThreadList | `mainThreadId`, `newThreadId`, `threadIds`, `archivedThreadIds`, `isLoading`, `threadItems` (`readonly ThreadListItemState[]`) | Manages all available conversation threads |
|
|
502
|
+
| ThreadListItem | `id`, `title`, `status`, `remoteId`, `externalId`, `custom?: Record<string, unknown>` | Individual thread metadata and status; `custom` carries arbitrary per-thread metadata set by remote runtimes |
|
|
503
|
+
| Thread | `isRunning` (may be explicitly set by the runtime rather than derived from last-message status), `isLoading`, `isDisabled`, `isEmpty`, `messages`, `capabilities`, `suggestions` | Active conversation state and message history |
|
|
496
504
|
| Message | `role`, `content`, `status`, `attachments`, `parts`, `parentId`, `branchNumber`, `branchCount`, `isLast`, `index` | Individual message content and metadata |
|
|
497
505
|
| Part | `type`, `status`, `text`, `toolCallId`, `toolName` | Content parts within messages (text, tool calls) |
|
|
498
506
|
| ChainOfThought | `parts`, `collapsed`, `status` | Reasoning steps grouped within a message |
|
|
@@ -500,13 +508,15 @@ const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
|
500
508
|
| Attachment | `id`, `type`, `name`, `contentType`, `status` | File attachments metadata and content |
|
|
501
509
|
| Suggestions | `suggestions` | Collection of follow-up message suggestions |
|
|
502
510
|
| Suggestion | `title`, `label`, `prompt` | Individual suggestion with title, label, and prompt |
|
|
503
|
-
| ModelContext | *(empty — use `register()` / `getToolCallParams()` methods)*
|
|
511
|
+
| ModelContext | *(empty — use `register()` / `getToolCallParams()` methods; see [Model Context](/docs/copilots/model-context))* | System instructions, tools, and context providers |
|
|
504
512
|
|
|
505
513
|
### Available Actions by Scope
|
|
506
514
|
|
|
515
|
+
The table below covers the most commonly used actions. For the full catalog, see the [API Reference](/docs/api-reference/overview).
|
|
516
|
+
|
|
507
517
|
| Scope | Actions | Use Cases |
|
|
508
518
|
| -------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
509
|
-
| ThreadList | `switchToNewThread()`, `switchToThread(id)`, `item(selector)`, `thread("main")`, `getState()`
|
|
519
|
+
| ThreadList | `switchToNewThread()`, `switchToThread(id)`, `reload()`, `getLoadThreadsPromise()`, `item(selector)`, `thread("main")`, `getState()` | Thread navigation, creation, and sync |
|
|
510
520
|
| ThreadListItem | `switchTo()`, `rename(title)`, `archive()`, `unarchive()`, `delete()`, `getState()` | Thread management operations |
|
|
511
521
|
| Thread | `append(message)`, `startRun(config)`, `resumeRun(config)`, `cancelRun()`, `reset()`, `export()`, `import(repository)`, `message(selector)`, `composer()`, `getState()` | Message handling and conversation control |
|
|
512
522
|
| Message | `reload()`, `speak()`, `stopSpeaking()`, `submitFeedback(feedback)`, `switchToBranch(options)`, `getCopyText()`, `part(selector)`, `attachment(selector)`, `composer()`, `setIsCopied(value)`, `setIsHovering(value)`, `getState()` | Message interactions and regeneration |
|
|
@@ -516,20 +526,38 @@ const isRunning = useAuiState((s) => s.thread.isRunning);
|
|
|
516
526
|
| Attachment | `remove()`, `getState()` | File management |
|
|
517
527
|
| Suggestions | `suggestion({ index })`, `getState()` | Access follow-up suggestions |
|
|
518
528
|
| Suggestion | `getState()` | Read individual suggestion data |
|
|
519
|
-
| ModelContext | `register(provider)`, `getState()` | Register model
|
|
520
|
-
|
|
521
|
-
###
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
529
|
+
| ModelContext | `register(provider)`, `getState()` | Register providers; full details in [Model Context](/docs/copilots/model-context) |
|
|
530
|
+
|
|
531
|
+
### Events vs State Observation
|
|
532
|
+
|
|
533
|
+
`useAuiEvent` is the escape hatch for **transient occurrences that are not derivable from state**. State-derivable transitions (attachment list changing, run progress, thread switching) should be observed with `useAuiState`, not subscribed via events.
|
|
534
|
+
|
|
535
|
+
The rule of thumb:
|
|
536
|
+
|
|
537
|
+
1. Can you read the new value from state right now? → use `useAuiState`.
|
|
538
|
+
2. Are you the caller and want immediate feedback? → catch the rejection / read the return value.
|
|
539
|
+
3. Did something happen that has no representation in state at all? → use `useAuiEvent`.
|
|
540
|
+
|
|
541
|
+
Most existing events are kept for backward compatibility but duplicate state. They are marked `@deprecated` in the type definitions; new code should follow the rule above.
|
|
542
|
+
|
|
543
|
+
#### Currently Recommended (Truly Transient)
|
|
544
|
+
|
|
545
|
+
| Event | When It Fires |
|
|
546
|
+
| ----------------------------- | ---------------------------------------------------------------------------- |
|
|
547
|
+
| `composer.attachmentAddError` | An `addAttachment()` call failed. Payload `reason` discriminates `no-adapter` / `not-accepted` / `adapter-error`. `no-adapter` and `not-accepted` are non-state-derivable. `adapter-error` is partially state-derivable: if the adapter produced any attachment before failing, the errored attachment also appears in `composer.attachments` with `status.reason === "error"`. The event additionally surfaces a human-readable `message` (and the underlying `Error` instance via the low-level `runtime.unstable_on("attachmentAddError")` API; `useAuiEvent` payloads omit it because raw `Error` objects are not store-serializable). |
|
|
548
|
+
| `thread.modelContextUpdate` | The model context provider notified a change. The model context lives in a provider, not in thread state, so this event has no state-derivable equivalent. |
|
|
549
|
+
|
|
550
|
+
#### Legacy (State-Derivable, Prefer `useAuiState`)
|
|
551
|
+
|
|
552
|
+
These events fire at the same transition you can observe via state. They are kept for backward compatibility but new code should observe state instead.
|
|
553
|
+
|
|
554
|
+
| Legacy Event | Observe Instead |
|
|
555
|
+
| -------------------------------------------- | -------------------------------------------------------------- |
|
|
556
|
+
| `composer.send` | composer `text` clearing |
|
|
557
|
+
| `composer.attachmentAdd` | composer `attachments` |
|
|
558
|
+
| `thread.runStart` / `runEnd` | thread `isRunning` flipping to `true` / `false` |
|
|
559
|
+
| `thread.initialize` | thread `messages` becoming non-empty (or `isEmpty` flipping) |
|
|
560
|
+
| `threadListItem.switchedTo` / `switchedAway` | compare `s.threads.mainThreadId` against `s.threadListItem.id` |
|
|
533
561
|
|
|
534
562
|
## Troubleshooting
|
|
535
563
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Message Editing
|
|
3
|
+
description: Allow users to edit their messages with custom editor interfaces.
|
|
4
|
+
platforms: ["react"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Mental Model
|
|
8
|
+
|
|
9
|
+
Editing re-submits a message from a past point in the conversation and creates a new branch. The messages after the edited one are discarded, and the assistant generates a fresh response from that point forward. Each user message has an independent edit composer; only one can be active at a time.
|
|
10
|
+
|
|
11
|
+
The recommended way to wire this up is via the `children` render prop on `ThreadPrimitive.Messages`, branching on `message.role` and on `message.composer.isEditing` to swap in an edit composer when needed.
|
|
12
|
+
|
|
13
|
+
## Enabling Edit Support
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import {
|
|
17
|
+
ActionBarPrimitive,
|
|
18
|
+
ComposerPrimitive,
|
|
19
|
+
MessagePrimitive,
|
|
20
|
+
ThreadPrimitive,
|
|
21
|
+
} from "@assistant-ui/react";
|
|
22
|
+
|
|
23
|
+
const Thread = () => {
|
|
24
|
+
return (
|
|
25
|
+
<ThreadPrimitive.Root>
|
|
26
|
+
<ThreadPrimitive.Viewport>
|
|
27
|
+
<ThreadPrimitive.Messages>
|
|
28
|
+
{({ message }) => {
|
|
29
|
+
if (message.role === "user") {
|
|
30
|
+
if (message.composer.isEditing) return <UserEditComposer />;
|
|
31
|
+
return <UserMessage />;
|
|
32
|
+
}
|
|
33
|
+
return <AssistantMessage />;
|
|
34
|
+
}}
|
|
35
|
+
</ThreadPrimitive.Messages>
|
|
36
|
+
</ThreadPrimitive.Viewport>
|
|
37
|
+
</ThreadPrimitive.Root>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const UserMessage = () => {
|
|
42
|
+
return (
|
|
43
|
+
<MessagePrimitive.Root>
|
|
44
|
+
{/* message content */}
|
|
45
|
+
<ActionBarPrimitive.Root>
|
|
46
|
+
<ActionBarPrimitive.Edit />
|
|
47
|
+
</ActionBarPrimitive.Root>
|
|
48
|
+
</MessagePrimitive.Root>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const UserEditComposer = () => {
|
|
53
|
+
return (
|
|
54
|
+
<MessagePrimitive.Root>
|
|
55
|
+
<ComposerPrimitive.Root>
|
|
56
|
+
<ComposerPrimitive.Input />
|
|
57
|
+
<ComposerPrimitive.Cancel />
|
|
58
|
+
<ComposerPrimitive.Send />
|
|
59
|
+
</ComposerPrimitive.Root>
|
|
60
|
+
</MessagePrimitive.Root>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const AssistantMessage = () => {
|
|
65
|
+
return <MessagePrimitive.Root>{/* message content */}</MessagePrimitive.Root>;
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`ActionBarPrimitive.Edit` calls `aui.composer().beginEdit()` under the hood and is disabled when the composer is already in edit mode.
|
|
70
|
+
|
|
71
|
+
`ComposerPrimitive.Cancel` calls `aui.composer().cancel()`, which exits edit mode and restores the original message content. See [Composer primitives](/docs/primitives/composer) for the full composer API.
|
|
72
|
+
|
|
73
|
+
## Detecting Edit Mode
|
|
74
|
+
|
|
75
|
+
The `isEditing` flag is available on both `ThreadComposerState` and `EditComposerState`, so `useAuiState((s) => s.composer.isEditing)` works inside any composer context. The more idiomatic path is to rely on the `UserEditComposer` slot in the render function (shown above), which scopes the component tree automatically and avoids manual state checks.
|
|
76
|
+
|
|
77
|
+
## Imperative API
|
|
78
|
+
|
|
79
|
+
`aui.composer().beginEdit()` is the programmatic entry point for entering edit mode on a message. Use it for headless or keyboard-shortcut-driven flows where `ActionBarPrimitive.Edit` is not rendered:
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { useAui } from "@assistant-ui/react";
|
|
83
|
+
|
|
84
|
+
const EditButton = () => {
|
|
85
|
+
const aui = useAui();
|
|
86
|
+
return (
|
|
87
|
+
<button onClick={() => aui.composer().beginEdit()}>Edit</button>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`aui.composer().cancel()` exits edit mode without re-submitting.
|
|
93
|
+
|
|
94
|
+
## Editing While Streaming
|
|
95
|
+
|
|
96
|
+
If a user triggers edit mode while the assistant is still generating a response, the in-progress run is cancelled and a new branch is started from the edited message. The hook does not block this. If your UI should prevent editing during streaming, gate the edit button on the thread run state before rendering `ActionBarPrimitive.Edit` or calling `beginEdit()`.
|
|
97
|
+
|
|
98
|
+
## References
|
|
99
|
+
|
|
100
|
+
- [ActionBar primitives](/docs/primitives/action-bar) — `ActionBarPrimitive.Edit` and related actions
|
|
101
|
+
- [Composer primitives](/docs/primitives/composer) — full composer anatomy including `Cancel`, `Send`, and `Input`
|
|
102
|
+
- [Message primitives](/docs/primitives/message) — `MessagePrimitive.Root` and content parts
|