@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.
Files changed (214) hide show
  1. package/.docs/organized/code-examples/waterfall.md +15 -7
  2. package/.docs/organized/code-examples/with-a2a.md +9 -21
  3. package/.docs/organized/code-examples/with-ag-ui.md +11 -8
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +10 -10
  5. package/.docs/organized/code-examples/with-artifacts.md +12 -10
  6. package/.docs/organized/code-examples/with-assistant-transport.md +11 -12
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +83 -54
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +14 -11
  9. package/.docs/organized/code-examples/with-cloud.md +9 -10
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +61 -16
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +17 -12
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +13 -13
  13. package/.docs/organized/code-examples/with-expo.md +25 -21
  14. package/.docs/organized/code-examples/with-external-store.md +8 -8
  15. package/.docs/organized/code-examples/with-ffmpeg.md +17 -12
  16. package/.docs/organized/code-examples/with-generative-ui.md +9 -9
  17. package/.docs/organized/code-examples/with-google-adk.md +8 -8
  18. package/.docs/organized/code-examples/with-heat-graph.md +5 -5
  19. package/.docs/organized/code-examples/with-interactables.md +10 -25
  20. package/.docs/organized/code-examples/with-langchain.md +437 -0
  21. package/.docs/organized/code-examples/with-langgraph.md +16 -16
  22. package/.docs/organized/code-examples/with-livekit.md +18 -13
  23. package/.docs/organized/code-examples/with-opencode.md +105 -62
  24. package/.docs/organized/code-examples/with-parent-id-grouping.md +10 -10
  25. package/.docs/organized/code-examples/with-react-hook-form.md +220 -148
  26. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  27. package/.docs/organized/code-examples/with-react-router.md +12 -12
  28. package/.docs/organized/code-examples/with-store.md +8 -5
  29. package/.docs/organized/code-examples/with-tanstack.md +10 -10
  30. package/.docs/organized/code-examples/with-tap-runtime.md +10 -6
  31. package/.docs/raw/docs/(docs)/cli.mdx +2 -1
  32. package/.docs/raw/docs/(docs)/copilots/assistant-frame.mdx +1 -0
  33. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +10 -3
  34. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +8 -3
  35. package/.docs/raw/docs/(docs)/copilots/make-assistant-visible.mdx +1 -0
  36. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +1 -0
  37. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +1 -0
  38. package/.docs/raw/docs/(docs)/copilots/use-assistant-instructions.mdx +1 -0
  39. package/.docs/raw/docs/(docs)/devtools.mdx +1 -0
  40. package/.docs/raw/docs/(docs)/index.mdx +1 -0
  41. package/.docs/raw/docs/(docs)/installation.mdx +1 -0
  42. package/.docs/raw/docs/(docs)/rtl.mdx +80 -0
  43. package/.docs/raw/docs/(reference)/api-reference/adapters/attachments.mdx +34 -0
  44. package/.docs/raw/docs/(reference)/api-reference/adapters/feedback-speech.mdx +41 -0
  45. package/.docs/raw/docs/(reference)/api-reference/adapters/index.mdx +26 -0
  46. package/.docs/raw/docs/(reference)/api-reference/adapters/persistence.mdx +34 -0
  47. package/.docs/raw/docs/(reference)/api-reference/adapters/runtime.mdx +31 -0
  48. package/.docs/raw/docs/(reference)/api-reference/context-providers/index.mdx +20 -0
  49. package/.docs/raw/docs/(reference)/api-reference/hooks/index.mdx +26 -0
  50. package/.docs/raw/docs/(reference)/api-reference/hooks/model-context.mdx +72 -0
  51. package/.docs/raw/docs/(reference)/api-reference/hooks/runtimes.mdx +41 -0
  52. package/.docs/raw/docs/(reference)/api-reference/hooks/state.mdx +48 -0
  53. package/.docs/raw/docs/(reference)/api-reference/hooks/utilities.mdx +30 -0
  54. package/.docs/raw/docs/(reference)/api-reference/integrations/index.mdx +23 -0
  55. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +21 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +7 -0
  57. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
  58. package/.docs/raw/docs/(reference)/api-reference/primitives/index.mdx +65 -0
  59. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
  60. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +50 -6
  61. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +15 -0
  62. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +36 -1
  63. package/.docs/raw/docs/(reference)/api-reference/runtimes/index.mdx +38 -0
  64. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +9 -0
  65. package/.docs/raw/docs/(reference)/migrations/v0-14.mdx +144 -6
  66. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +231 -3
  67. package/.docs/raw/docs/cloud/ai-sdk.mdx +221 -3
  68. package/.docs/raw/docs/cloud/langgraph.mdx +274 -2
  69. package/.docs/raw/docs/{(docs)/guides → guides}/attachments.mdx +41 -36
  70. package/.docs/raw/docs/guides/branching.mdx +76 -0
  71. package/.docs/raw/docs/guides/chain-of-thought.mdx +166 -0
  72. package/.docs/raw/docs/{(docs)/guides → guides}/context-api.mdx +50 -22
  73. package/.docs/raw/docs/{(docs)/guides → guides}/dictation.mdx +2 -0
  74. package/.docs/raw/docs/guides/editing.mdx +102 -0
  75. package/.docs/raw/docs/guides/index.mdx +103 -0
  76. package/.docs/raw/docs/{(docs)/guides → guides}/interactables.mdx +49 -0
  77. package/.docs/raw/docs/{(docs)/guides → guides}/latex.mdx +51 -8
  78. package/.docs/raw/docs/guides/mentions.mdx +520 -0
  79. package/.docs/raw/docs/{(docs)/guides → guides}/message-timing.mdx +8 -2
  80. package/.docs/raw/docs/{(docs)/guides → guides}/multi-agent.mdx +64 -4
  81. package/.docs/raw/docs/{(docs)/guides → guides}/quoting.mdx +10 -17
  82. package/.docs/raw/docs/guides/slash-commands.mdx +361 -0
  83. package/.docs/raw/docs/guides/speech.mdx +156 -0
  84. package/.docs/raw/docs/{(docs)/guides → guides}/suggestions.mdx +21 -83
  85. package/.docs/raw/docs/{(docs)/guides → guides}/tool-ui.mdx +108 -36
  86. package/.docs/raw/docs/{(docs)/guides → guides}/tools.mdx +131 -35
  87. package/.docs/raw/docs/{(docs)/guides → guides}/voice.mdx +39 -0
  88. package/.docs/raw/docs/ink/index.mdx +1 -3
  89. package/.docs/raw/docs/ink/migration.mdx +1 -3
  90. package/.docs/raw/docs/ink/primitives.mdx +37 -1
  91. package/.docs/raw/docs/integrations/attachments/custom-adapter.mdx +520 -0
  92. package/.docs/raw/docs/integrations/auth/better-auth.mdx +191 -0
  93. package/.docs/raw/docs/integrations/auth/clerk.mdx +172 -0
  94. package/.docs/raw/docs/integrations/auth/next-auth.mdx +196 -0
  95. package/.docs/raw/docs/integrations/frameworks/ai-sdk.mdx +79 -0
  96. package/.docs/raw/docs/integrations/frameworks/mastra/full-stack.mdx +188 -0
  97. package/.docs/raw/docs/integrations/frameworks/mastra/overview.mdx +57 -0
  98. package/.docs/raw/docs/integrations/frameworks/mastra/separate-server.mdx +201 -0
  99. package/.docs/raw/docs/integrations/gateways/index.mdx +157 -0
  100. package/.docs/raw/docs/integrations/index.mdx +173 -0
  101. package/.docs/raw/docs/integrations/observability/helicone.mdx +130 -0
  102. package/.docs/raw/docs/integrations/observability/langfuse.mdx +156 -0
  103. package/.docs/raw/docs/integrations/observability/langsmith.mdx +146 -0
  104. package/.docs/raw/docs/integrations/persistence/custom-adapter.mdx +712 -0
  105. package/.docs/raw/docs/integrations/tools/mcp.mdx +267 -0
  106. package/.docs/raw/docs/primitives/action-bar.mdx +1 -0
  107. package/.docs/raw/docs/primitives/assistant-modal.mdx +1 -0
  108. package/.docs/raw/docs/primitives/attachment.mdx +1 -0
  109. package/.docs/raw/docs/primitives/branch-picker.mdx +1 -0
  110. package/.docs/raw/docs/primitives/chain-of-thought.mdx +90 -85
  111. package/.docs/raw/docs/primitives/composer.mdx +96 -63
  112. package/.docs/raw/docs/primitives/error.mdx +1 -0
  113. package/.docs/raw/docs/primitives/index.mdx +2 -1
  114. package/.docs/raw/docs/primitives/message.mdx +68 -5
  115. package/.docs/raw/docs/primitives/selection-toolbar.mdx +1 -0
  116. package/.docs/raw/docs/primitives/suggestion.mdx +1 -0
  117. package/.docs/raw/docs/primitives/thread-list.mdx +39 -0
  118. package/.docs/raw/docs/primitives/thread.mdx +16 -13
  119. package/.docs/raw/docs/react-native/index.mdx +1 -3
  120. package/.docs/raw/docs/react-native/migration.mdx +1 -3
  121. package/.docs/raw/docs/runtimes/a2a/client-and-hooks.mdx +396 -0
  122. package/.docs/raw/docs/runtimes/a2a/overview.mdx +60 -0
  123. package/.docs/raw/docs/runtimes/a2a/quickstart.mdx +216 -0
  124. package/.docs/raw/docs/runtimes/ag-ui/overview.mdx +70 -0
  125. package/.docs/raw/docs/runtimes/ag-ui/quickstart.mdx +243 -0
  126. package/.docs/raw/docs/runtimes/ag-ui/runtime-options.mdx +123 -0
  127. package/.docs/raw/docs/runtimes/ai-sdk/overview.mdx +52 -0
  128. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +71 -131
  129. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +69 -63
  130. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +365 -101
  131. package/.docs/raw/docs/runtimes/concepts/adapters.mdx +265 -0
  132. package/.docs/raw/docs/runtimes/concepts/architecture.mdx +125 -0
  133. package/.docs/raw/docs/runtimes/concepts/stability.mdx +67 -0
  134. package/.docs/raw/docs/runtimes/concepts/threads.mdx +428 -0
  135. package/.docs/raw/docs/runtimes/custom/assistant-transport.mdx +703 -0
  136. package/.docs/raw/docs/runtimes/custom/data-stream.mdx +323 -0
  137. package/.docs/raw/docs/runtimes/custom/external-store.mdx +253 -1236
  138. package/.docs/raw/docs/runtimes/custom/local-runtime.mdx +746 -0
  139. package/.docs/raw/docs/runtimes/custom/overview.mdx +71 -0
  140. package/.docs/raw/docs/runtimes/google-adk/api.mdx +256 -0
  141. package/.docs/raw/docs/runtimes/google-adk/hooks.mdx +717 -0
  142. package/.docs/raw/docs/runtimes/google-adk/overview.mdx +69 -0
  143. package/.docs/raw/docs/runtimes/google-adk/quickstart.mdx +229 -0
  144. package/.docs/raw/docs/runtimes/langchain.mdx +533 -0
  145. package/.docs/raw/docs/runtimes/langgraph/generative-ui.mdx +305 -0
  146. package/.docs/raw/docs/runtimes/langgraph/interrupts.mdx +104 -0
  147. package/.docs/raw/docs/runtimes/langgraph/overview.mdx +84 -0
  148. package/.docs/raw/docs/runtimes/langgraph/quickstart.mdx +496 -0
  149. package/.docs/raw/docs/runtimes/langgraph/streaming.mdx +127 -0
  150. package/.docs/raw/docs/runtimes/langgraph/threads.mdx +113 -0
  151. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +3 -3
  152. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +0 -23
  153. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
  154. package/.docs/raw/docs/runtimes/opencode/hooks.mdx +191 -0
  155. package/.docs/raw/docs/runtimes/opencode/overview.mdx +48 -0
  156. package/.docs/raw/docs/runtimes/opencode/quickstart.mdx +119 -0
  157. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +74 -198
  158. package/.docs/raw/docs/ui/accordion.mdx +1 -0
  159. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -0
  160. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -0
  161. package/.docs/raw/docs/ui/attachment.mdx +1 -0
  162. package/.docs/raw/docs/ui/badge.mdx +1 -0
  163. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +200 -0
  164. package/.docs/raw/docs/ui/context-display.mdx +1 -0
  165. package/.docs/raw/docs/ui/diff-viewer.mdx +1 -0
  166. package/.docs/raw/docs/ui/directive-text.mdx +114 -0
  167. package/.docs/raw/docs/ui/file.mdx +1 -0
  168. package/.docs/raw/docs/ui/image.mdx +1 -0
  169. package/.docs/raw/docs/ui/markdown.mdx +2 -14
  170. package/.docs/raw/docs/ui/mermaid.mdx +1 -0
  171. package/.docs/raw/docs/ui/message-timing.mdx +3 -2
  172. package/.docs/raw/docs/ui/model-selector.mdx +1 -0
  173. package/.docs/raw/docs/ui/part-grouping.mdx +325 -313
  174. package/.docs/raw/docs/ui/quote.mdx +1 -0
  175. package/.docs/raw/docs/ui/reasoning.mdx +69 -32
  176. package/.docs/raw/docs/ui/scrollbar.mdx +1 -0
  177. package/.docs/raw/docs/ui/select.mdx +1 -0
  178. package/.docs/raw/docs/ui/sources.mdx +1 -0
  179. package/.docs/raw/docs/ui/streamdown.mdx +1 -0
  180. package/.docs/raw/docs/ui/syntax-highlighting.mdx +1 -0
  181. package/.docs/raw/docs/ui/tabs.mdx +1 -0
  182. package/.docs/raw/docs/ui/thread-list.mdx +17 -0
  183. package/.docs/raw/docs/ui/thread.mdx +56 -1
  184. package/.docs/raw/docs/ui/tool-fallback.mdx +1 -0
  185. package/.docs/raw/docs/ui/tool-group.mdx +39 -11
  186. package/.docs/raw/docs/ui/voice.mdx +1 -0
  187. package/.docs/raw/docs/utilities/heat-graph.mdx +1 -0
  188. package/.docs/raw/docs/utilities/react-o11y.mdx +278 -0
  189. package/.docs/raw/docs/utilities/tw-shimmer.mdx +1 -0
  190. package/dist/utils/logger.js +1 -1
  191. package/dist/utils/logger.js.map +1 -1
  192. package/package.json +4 -4
  193. package/src/tools/tests/path-traversal.test.ts +1 -1
  194. package/src/utils/logger.ts +1 -1
  195. package/.docs/raw/docs/(docs)/guides/branching.mdx +0 -65
  196. package/.docs/raw/docs/(docs)/guides/chain-of-thought.mdx +0 -164
  197. package/.docs/raw/docs/(docs)/guides/editing.mdx +0 -66
  198. package/.docs/raw/docs/(docs)/guides/mentions.mdx +0 -406
  199. package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +0 -275
  200. package/.docs/raw/docs/(docs)/guides/speech.mdx +0 -38
  201. package/.docs/raw/docs/runtimes/a2a/index.mdx +0 -298
  202. package/.docs/raw/docs/runtimes/assistant-transport.mdx +0 -1033
  203. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +0 -268
  204. package/.docs/raw/docs/runtimes/custom/local.mdx +0 -1464
  205. package/.docs/raw/docs/runtimes/data-stream.mdx +0 -422
  206. package/.docs/raw/docs/runtimes/google-adk/index.mdx +0 -686
  207. package/.docs/raw/docs/runtimes/helicone.mdx +0 -61
  208. package/.docs/raw/docs/runtimes/langgraph/index.mdx +0 -607
  209. package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +0 -12
  210. package/.docs/raw/docs/runtimes/langserve.mdx +0 -116
  211. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -218
  212. package/.docs/raw/docs/runtimes/mastra/overview.mdx +0 -18
  213. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -217
  214. package/.docs/raw/docs/ui/mention.mdx +0 -168
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  title: Quote Selected Text
3
3
  description: Let users select and quote text from messages. Full guide including backend handling and programmatic API.
4
+ platforms: ["react"]
4
5
  ---
5
6
 
6
7
  import { QuoteComposerSample } from "@/components/docs/samples/quote-composer";
@@ -9,17 +10,11 @@ import { QuoteComposerSample } from "@/components/docs/samples/quote-composer";
9
10
 
10
11
  ## Get Started
11
12
 
12
- The **[Quote](/docs/ui/quote)** registry component gives you everything you need out of the box, including a floating selection toolbar, composer quote preview, and inline quote display.
13
+ Install and wire up the `quote` registry component by following the [Quote component page](/docs/ui/quote). It covers the install command, component placement, and the `injectQuoteContext` helper for forwarding quote data to the LLM.
13
14
 
14
- <InstallCommand shadcn={["quote"]} />
15
-
16
- It ships three composable pieces:
17
-
18
- - **QuoteBlock** renders quoted text inline in user messages
19
- - **SelectionToolbar** is a floating toolbar that appears on text selection
20
- - **ComposerQuotePreview** shows the pending quote inside the composer
21
-
22
- See the [Quote component page](/docs/ui/quote) for full setup steps and API reference.
15
+ <Callout type="warn">
16
+ **Limitations:** only one quote can be active at a time. `setQuote` replaces the previous quote instead of appending. The floating toolbar only appears when the selection is entirely within a single message part; cross-message and cross-part selections are ignored.
17
+ </Callout>
23
18
 
24
19
  ---
25
20
 
@@ -132,7 +127,7 @@ function CustomQuoteDisplay() {
132
127
 
133
128
  ## Programmatic API
134
129
 
135
- Set or clear quotes via the composer runtime:
130
+ Set or clear quotes via `useAui` from `@assistant-ui/react`. Call `aui.thread().composer().setQuote()` when your component is rendered outside of a specific thread context, or `aui.composer().setQuote()` when it is rendered inside a thread:
136
131
 
137
132
  ```tsx
138
133
  import { useAui } from "@assistant-ui/react";
@@ -162,12 +157,10 @@ function MyComponent() {
162
157
 
163
158
  ## Design Notes
164
159
 
165
- - **Single quote:** `setQuote` replaces the current quote instead of appending. Only one quote can be active at a time.
166
- - **Snapshot text:** The selected text is captured when the quote is created and is not linked to the source message afterward.
167
- - **Cross-message selection:** The toolbar only appears when the selection stays within a single message.
168
- - **Streaming messages:** The toolbar still works while a message is streaming because it relies on the captured selection rather than message status.
169
- - **`isEmpty` unchanged:** A quote by itself does not make the composer non-empty. The user still needs to type a reply.
170
- - **Scroll hides toolbar:** The toolbar hides on scroll because its position would otherwise become stale.
160
+ - **snapshot text:** the selected text is captured when the quote is created and is not linked to the source message afterward.
161
+ - **streaming messages:** the toolbar still works while a message is streaming because it relies on the captured selection rather than message status.
162
+ - **`isEmpty` unchanged:** a quote by itself does not make the composer non-empty; the user still needs to type a reply.
163
+ - **scroll hides toolbar:** the toolbar hides on scroll because its position would otherwise become stale.
171
164
 
172
165
  ## Related
173
166
 
@@ -0,0 +1,361 @@
1
+ ---
2
+ title: Slash Commands
3
+ description: Let users type / in the composer to trigger predefined actions from a popover picker.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ Slash commands let users type `/` in the composer to open a popover, browse available commands, and execute one. Unlike [mentions](/docs/guides/mentions) (which only insert a directive into the message), slash commands additionally fire an **action callback** at the moment of selection.
8
+
9
+ ## How It Works
10
+
11
+ ```
12
+ User types "/" → Trigger detected → Adapter provides commands
13
+ ↓
14
+ Callback fired ← User selects command from popover
15
+ ↓
16
+ Directive chip left in composer (or removed if removeOnExecute)
17
+ ```
18
+
19
+ The slash command system is built on the same [trigger popover architecture](#trigger-popover-architecture) as mentions. A slash command declares its behavior with a `<TriggerPopover.Action>` sub-primitive whose `onExecute` callback fires when an item is chosen.
20
+
21
+ By default `Action` leaves a directive chip in the composer — giving the user (and the LLM) an audit trail of which commands were invoked. Pass `removeOnExecute` to strip the `/command` text entirely.
22
+
23
+ ## Quick Start
24
+
25
+ ### 1. Define Commands with `unstable_useSlashCommandAdapter`
26
+
27
+ Declare commands (data + `execute` bundled together, like `useAssistantTool`). The hook returns `{ adapter, action }` — wire both into a single `<TriggerPopover>`:
28
+
29
+ ```tsx
30
+ import {
31
+ ComposerPrimitive,
32
+ unstable_useSlashCommandAdapter,
33
+ type Unstable_SlashCommand,
34
+ } from "@assistant-ui/react";
35
+
36
+ const SLASH_COMMANDS: readonly Unstable_SlashCommand[] = [
37
+ {
38
+ id: "summarize",
39
+ description: "Summarize the conversation",
40
+ execute: () => console.log("Summarize!"),
41
+ },
42
+ {
43
+ id: "translate",
44
+ description: "Translate text to another language",
45
+ execute: () => console.log("Translate!"),
46
+ },
47
+ {
48
+ id: "help",
49
+ description: "List all available commands",
50
+ execute: () => console.log("Help!"),
51
+ },
52
+ ];
53
+
54
+ function MyComposer() {
55
+ const slash = unstable_useSlashCommandAdapter({ commands: SLASH_COMMANDS });
56
+
57
+ return (
58
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
59
+ <ComposerPrimitive.Root>
60
+ <ComposerPrimitive.Input placeholder="Type / for commands..." />
61
+ <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
62
+
63
+ <ComposerPrimitive.Unstable_TriggerPopover
64
+ char="/"
65
+ adapter={slash.adapter}
66
+ className="popover"
67
+ >
68
+ <ComposerPrimitive.Unstable_TriggerPopover.Action {...slash.action} />
69
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
70
+ {(items) =>
71
+ items.map((item, index) => (
72
+ <ComposerPrimitive.Unstable_TriggerPopoverItem
73
+ key={item.id}
74
+ item={item}
75
+ index={index}
76
+ className="popover-item"
77
+ >
78
+ <strong>{item.label}</strong>
79
+ {item.description && <span>{item.description}</span>}
80
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
81
+ ))
82
+ }
83
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
84
+ </ComposerPrimitive.Unstable_TriggerPopover>
85
+ </ComposerPrimitive.Root>
86
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
87
+ );
88
+ }
89
+ ```
90
+
91
+ The label defaults to `/${id}`; override via `label` on the command. Icons are strings that your `iconMap` on the picker UI resolves to components (see [ComposerTriggerPopover](/docs/ui/composer-trigger-popover)).
92
+
93
+ ### `unstable_useSlashCommandAdapter` options
94
+
95
+ | Option | Type | Default | Description |
96
+ | --- | --- | --- | --- |
97
+ | `commands` | `Unstable_SlashCommand[]` | — | Command definitions — each has `id`, optional `label`, `description`, `icon`, and an `execute` callback (required) |
98
+ | `removeOnExecute` | `boolean` | `false` | When `true`, strips the trigger text from the composer after executing instead of leaving a directive chip |
99
+ | `iconMap` | `Record<string, IconComponent>` | — | Maps `metadata.icon` / category `id` strings to React icon components; forwarded to `ComposerTriggerPopover` |
100
+ | `fallbackIcon` | `IconComponent` | — | Fallback when no `iconMap` entry matches; forwarded to `ComposerTriggerPopover` |
101
+
102
+ The hook returns `{ adapter, action, iconMap?, fallbackIcon? }` — spread directly into `<ComposerTriggerPopover char="/" {...slash} />` for one-line wiring.
103
+
104
+ ### 2. Controlling the Chip
105
+
106
+ By default, a selected `/summarize` is converted into a directive chip (`:command[/summarize]{name=summarize}`) in the composer text and the command's `execute` fires. This keeps an audit trail of which commands were invoked.
107
+
108
+ To strip the trigger text entirely — useful for purely transient commands — pass `removeOnExecute` on the hook options:
109
+
110
+ ```tsx
111
+ const slash = unstable_useSlashCommandAdapter({
112
+ commands: SLASH_COMMANDS,
113
+ removeOnExecute: true,
114
+ });
115
+ ```
116
+
117
+ ### 3. Custom Dispatch
118
+
119
+ For side effects on top of `execute` (logging, analytics, intercept), wrap the hook's `onExecute`:
120
+
121
+ ```tsx
122
+ <ComposerPrimitive.Unstable_TriggerPopover.Action
123
+ onExecute={(item) => {
124
+ logCommandUsed(item.id);
125
+ slash.action.onExecute(item);
126
+ }}
127
+ />
128
+ ```
129
+
130
+ ## Categorized Commands
131
+
132
+ For **categorized navigation** (drill-down into groups), return categories from `categories()` and items from `categoryItems()`. The popover shows categories first, then items within the selected category:
133
+
134
+ ```ts
135
+ import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
136
+
137
+ const adapter: Unstable_TriggerAdapter = {
138
+ categories() {
139
+ return [
140
+ { id: "actions", label: "Actions" },
141
+ { id: "export", label: "Export" },
142
+ ];
143
+ },
144
+
145
+ categoryItems(categoryId) {
146
+ if (categoryId === "actions") {
147
+ return [
148
+ { id: "summarize", type: "command", label: "/summarize", description: "Summarize the conversation" },
149
+ { id: "translate", type: "command", label: "/translate", description: "Translate text" },
150
+ ];
151
+ }
152
+ if (categoryId === "export") {
153
+ return [
154
+ { id: "pdf", type: "command", label: "/export pdf", description: "Export as PDF" },
155
+ { id: "markdown", type: "command", label: "/export md", description: "Export as Markdown" },
156
+ ];
157
+ }
158
+ return [];
159
+ },
160
+
161
+ // Optional — enables search across all categories
162
+ search(query) {
163
+ const lower = query.toLowerCase();
164
+ const all = [...this.categoryItems("actions"), ...this.categoryItems("export")];
165
+ return all.filter(
166
+ (item) => item.label.toLowerCase().includes(lower) || item.description?.toLowerCase().includes(lower),
167
+ );
168
+ },
169
+ };
170
+ ```
171
+
172
+ When using a categorized adapter, add `TriggerPopoverCategories` to your popover UI:
173
+
174
+ ```tsx
175
+ const commandHandlers: Record<string, () => void> = {
176
+ summarize: () => {/* ... */},
177
+ pdf: () => {/* ... */},
178
+ };
179
+
180
+ <ComposerPrimitive.Unstable_TriggerPopover
181
+ char="/"
182
+ adapter={adapter}
183
+ >
184
+ <ComposerPrimitive.Unstable_TriggerPopover.Action
185
+ formatter={unstable_defaultDirectiveFormatter}
186
+ onExecute={(item) => commandHandlers[item.id]?.()}
187
+ />
188
+ <ComposerPrimitive.Unstable_TriggerPopoverBack>← Back</ComposerPrimitive.Unstable_TriggerPopoverBack>
189
+ <ComposerPrimitive.Unstable_TriggerPopoverCategories>
190
+ {(categories) => categories.map((cat) => (
191
+ <ComposerPrimitive.Unstable_TriggerPopoverCategoryItem key={cat.id} categoryId={cat.id}>
192
+ {cat.label}
193
+ </ComposerPrimitive.Unstable_TriggerPopoverCategoryItem>
194
+ ))}
195
+ </ComposerPrimitive.Unstable_TriggerPopoverCategories>
196
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
197
+ {(items) => items.map((item, index) => (
198
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
199
+ {item.label}
200
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
201
+ ))}
202
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
203
+ </ComposerPrimitive.Unstable_TriggerPopover>
204
+ ```
205
+
206
+ ## Combining with Mentions
207
+
208
+ Slash commands and mentions live under the same `TriggerPopoverRoot`. Declare one `TriggerPopover` per trigger — each with its own behavior sub-primitive:
209
+
210
+ ```tsx
211
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
212
+ <ComposerPrimitive.Root>
213
+ <ComposerPrimitive.Input placeholder="Type @ to mention, / for commands..." />
214
+ <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
215
+
216
+ {/* @ mention popover */}
217
+ <ComposerPrimitive.Unstable_TriggerPopover
218
+ char="@"
219
+ adapter={mention.adapter}
220
+ >
221
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive {...mention.directive} />
222
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
223
+ {(items) => items.map((item) => (
224
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item}>
225
+ {item.label}
226
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
227
+ ))}
228
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
229
+ </ComposerPrimitive.Unstable_TriggerPopover>
230
+
231
+ {/* / slash command popover */}
232
+ <ComposerPrimitive.Unstable_TriggerPopover
233
+ char="/"
234
+ adapter={slash.adapter}
235
+ >
236
+ <ComposerPrimitive.Unstable_TriggerPopover.Action {...slash.action} />
237
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
238
+ {(items) => items.map((item, index) => (
239
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
240
+ {item.label}
241
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
242
+ ))}
243
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
244
+ </ComposerPrimitive.Unstable_TriggerPopover>
245
+ </ComposerPrimitive.Root>
246
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
247
+ ```
248
+
249
+ Each `TriggerPopover` is its own scope — the `@` popover and the `/` popover read state from their own declaration and never collide. Keyboard events route to whichever popover is currently active.
250
+
251
+ ## Commands with Arguments
252
+
253
+ Some commands accept inline arguments typed after the command word — for example `/translate en` or `/ask what is TypeScript`. Because the adapter's `search` method receives the full text after `/`, you can split on the first space to separate the command from its arguments:
254
+
255
+ ```tsx
256
+ const SLASH_COMMANDS: readonly Unstable_SlashCommand[] = [
257
+ {
258
+ id: "translate",
259
+ description: "Translate to a language, e.g. /translate en",
260
+ execute: () => {/* arguments extracted separately, see below */},
261
+ },
262
+ {
263
+ id: "ask",
264
+ description: "Ask about a topic, e.g. /ask what is TypeScript",
265
+ execute: () => {/* arguments extracted separately, see below */},
266
+ },
267
+ ];
268
+
269
+ function MyComposer() {
270
+ const composerRef = useRef<HTMLTextAreaElement>(null);
271
+ const slash = unstable_useSlashCommandAdapter({
272
+ commands: SLASH_COMMANDS.map((cmd) => ({
273
+ ...cmd,
274
+ execute: () => {
275
+ // Read the full composer text to extract arguments
276
+ const raw = composerRef.current?.value ?? "";
277
+ // Match "/<id> <args>" at start of input
278
+ const match = raw.match(new RegExp(`^\\/${cmd.id}\\s+(.*)`));
279
+ const args = match?.[1]?.trim() ?? "";
280
+ handleCommand(cmd.id, args);
281
+ },
282
+ })),
283
+ removeOnExecute: true,
284
+ });
285
+
286
+ return (
287
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
288
+ <ComposerPrimitive.Root>
289
+ <ComposerPrimitive.Input ref={composerRef} placeholder="Type / for commands..." />
290
+ <ComposerPrimitive.Unstable_TriggerPopover char="/" adapter={slash.adapter}>
291
+ <ComposerPrimitive.Unstable_TriggerPopover.Action {...slash.action} />
292
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
293
+ {(items) => items.map((item, i) => (
294
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={i}>
295
+ <strong>{item.label}</strong>
296
+ {item.description && <span>{item.description}</span>}
297
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
298
+ ))}
299
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
300
+ </ComposerPrimitive.Unstable_TriggerPopover>
301
+ <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
302
+ </ComposerPrimitive.Root>
303
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
304
+ );
305
+ }
306
+ ```
307
+
308
+ `removeOnExecute: true` strips the `/translate en` text from the composer so the argument is consumed by the handler rather than sent to the LLM.
309
+
310
+ ## Async Command Loading
311
+
312
+ The adapter interface is synchronous, but the command list can come from any async source. Load commands into state (or a query cache) and pass the current snapshot to the hook. Because `unstable_useSlashCommandAdapter` re-runs on every render, the adapter always reflects the latest list.
313
+
314
+ **With React state:**
315
+
316
+ ```tsx
317
+ function MyComposer() {
318
+ const [commands, setCommands] = useState<Unstable_SlashCommand[]>([]);
319
+
320
+ useEffect(() => {
321
+ fetchAvailableCommands().then(setCommands);
322
+ }, []);
323
+
324
+ const slash = unstable_useSlashCommandAdapter({ commands });
325
+
326
+ return (/* ... */);
327
+ }
328
+ ```
329
+
330
+ **With React Query:**
331
+
332
+ ```tsx
333
+ function MyComposer() {
334
+ const { data: commands = [] } = useQuery({
335
+ queryKey: ["slash-commands"],
336
+ queryFn: fetchAvailableCommands,
337
+ });
338
+
339
+ const slash = unstable_useSlashCommandAdapter({ commands });
340
+
341
+ return (/* ... */);
342
+ }
343
+ ```
344
+
345
+ ## Keyboard Navigation
346
+
347
+ See [ComposerTriggerPopover keyboard navigation](/docs/ui/composer-trigger-popover#keyboard-navigation) for the full key bindings table.
348
+
349
+ ## Trigger Popover Architecture
350
+
351
+ Both mentions and slash commands are built on a generic trigger popover system where each `Unstable_TriggerPopover` declares one trigger character, an adapter, and exactly one behavior sub-primitive (`Directive` or `Action`). Multiple triggers coexist under a single `Unstable_TriggerPopoverRoot`. See the [Composer Primitives](/docs/primitives/composer) reference for the complete API.
352
+
353
+ ## Primitives Reference
354
+
355
+ See the [Composer Primitives](/docs/primitives/composer) reference for the full list of trigger popover primitives and their props.
356
+
357
+ ## Related
358
+
359
+ - [Mentions Guide](/docs/guides/mentions) — `@`-mention system built on the same architecture
360
+ - [Suggestions Guide](/docs/guides/suggestions) — static follow-up prompts (different from slash commands)
361
+ - [Composer Primitives](/docs/primitives/composer) — underlying composer primitives
@@ -0,0 +1,156 @@
1
+ ---
2
+ title: Text-to-Speech (Speech Synthesis)
3
+ description: Read messages aloud with Web Speech API or a custom TTS adapter.
4
+ platforms: ["react"]
5
+ ---
6
+
7
+ import { SpeechSample } from "@/components/docs/samples/speech";
8
+
9
+ assistant-ui supports text-to-speech via the `SpeechSynthesisAdapter` interface. When a speech adapter is configured, users can trigger playback for any assistant message.
10
+
11
+ <SpeechSample />
12
+
13
+ ## SpeechSynthesisAdapter
14
+
15
+ The `SpeechSynthesisAdapter` interface has a single method:
16
+
17
+ ```tsx
18
+ import type { SpeechSynthesisAdapter } from "@assistant-ui/react";
19
+
20
+ type SpeechSynthesisAdapter = {
21
+ speak: (text: string) => SpeechSynthesisAdapter.Utterance;
22
+ };
23
+ ```
24
+
25
+ `speak` is called with the plain text of an assistant message and must return an `Utterance` object:
26
+
27
+ ```tsx
28
+ type Utterance = {
29
+ status: SpeechSynthesisAdapter.Status;
30
+ cancel: () => void;
31
+ subscribe: (callback: () => void) => Unsubscribe;
32
+ };
33
+
34
+ type Status =
35
+ | { type: "starting" | "running" }
36
+ | { type: "ended"; reason: "finished" | "cancelled" | "error"; error?: unknown };
37
+ ```
38
+
39
+ Currently the following built-in adapter is available:
40
+
41
+ - `WebSpeechSynthesisAdapter`: uses the browser's `Web Speech API` (`SpeechSynthesis`)
42
+
43
+ ## WebSpeechSynthesisAdapter
44
+
45
+ ```tsx
46
+ import { WebSpeechSynthesisAdapter } from "@assistant-ui/react";
47
+
48
+ const runtime = useChatRuntime({
49
+ adapters: {
50
+ speech: new WebSpeechSynthesisAdapter(),
51
+ },
52
+ });
53
+ ```
54
+
55
+ ## UI
56
+
57
+ The default action bar does not include a speech button. Add `ActionBarPrimitive.Speak` and `ActionBarPrimitive.StopSpeaking` to your assistant message action bar:
58
+
59
+ ```tsx
60
+ import { ActionBarPrimitive, useMessageTTS } from "@assistant-ui/react";
61
+ import { AudioLinesIcon, StopCircleIcon } from "lucide-react";
62
+
63
+ const AssistantActionBar = () => {
64
+ const isSpeaking = useMessageTTS();
65
+
66
+ return (
67
+ <ActionBarPrimitive.Root>
68
+ {!isSpeaking && (
69
+ <ActionBarPrimitive.Speak>
70
+ <AudioLinesIcon />
71
+ </ActionBarPrimitive.Speak>
72
+ )}
73
+ {isSpeaking && (
74
+ <ActionBarPrimitive.StopSpeaking>
75
+ <StopCircleIcon />
76
+ </ActionBarPrimitive.StopSpeaking>
77
+ )}
78
+ <ActionBarPrimitive.Copy />
79
+ </ActionBarPrimitive.Root>
80
+ );
81
+ };
82
+ ```
83
+
84
+ `ActionBarPrimitive.Speak` is automatically disabled when no speech adapter is configured.
85
+
86
+ ## Custom Adapters
87
+
88
+ Implement `SpeechSynthesisAdapter` to call any external TTS API:
89
+
90
+ ```tsx title="lib/custom-tts-adapter.ts"
91
+ import type { SpeechSynthesisAdapter } from "@assistant-ui/react";
92
+
93
+ export class CustomTTSAdapter implements SpeechSynthesisAdapter {
94
+ private apiUrl: string;
95
+
96
+ constructor(options: { apiUrl: string }) {
97
+ this.apiUrl = options.apiUrl;
98
+ }
99
+
100
+ speak(text: string): SpeechSynthesisAdapter.Utterance {
101
+ const subscribers = new Set<() => void>();
102
+ let status: SpeechSynthesisAdapter.Status = { type: "starting" };
103
+ let audio: HTMLAudioElement | null = null;
104
+
105
+ const notify = () => {
106
+ for (const cb of subscribers) cb();
107
+ };
108
+
109
+ const finish = (reason: "finished" | "cancelled" | "error", error?: unknown) => {
110
+ if (status.type === "ended") return;
111
+ status = { type: "ended", reason, error };
112
+ notify();
113
+ };
114
+
115
+ fetch(this.apiUrl, {
116
+ method: "POST",
117
+ headers: { "Content-Type": "application/json" },
118
+ body: JSON.stringify({ text }),
119
+ })
120
+ .then((res) => res.blob())
121
+ .then((blob) => {
122
+ audio = new Audio(URL.createObjectURL(blob));
123
+ status = { type: "running" };
124
+ notify();
125
+ audio.onended = () => finish("finished");
126
+ audio.onerror = (e) => finish("error", e);
127
+ audio.play();
128
+ })
129
+ .catch((err) => finish("error", err));
130
+
131
+ return {
132
+ get status() { return status; },
133
+ cancel: () => {
134
+ audio?.pause();
135
+ finish("cancelled");
136
+ },
137
+ subscribe: (cb) => {
138
+ subscribers.add(cb);
139
+ return () => subscribers.delete(cb);
140
+ },
141
+ };
142
+ }
143
+ }
144
+ ```
145
+
146
+ Wire it up the same way as the built-in adapter:
147
+
148
+ ```tsx
149
+ import { CustomTTSAdapter } from "@/lib/custom-tts-adapter";
150
+
151
+ const runtime = useChatRuntime({
152
+ adapters: {
153
+ speech: new CustomTTSAdapter({ apiUrl: "/api/tts" }),
154
+ },
155
+ });
156
+ ```