@assistant-ui/mcp-docs-server 0.1.28 → 0.1.30

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 (56) hide show
  1. package/.docs/organized/code-examples/waterfall.md +18 -10
  2. package/.docs/organized/code-examples/with-a2a.md +12 -24
  3. package/.docs/organized/code-examples/with-ag-ui.md +14 -11
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +12 -12
  5. package/.docs/organized/code-examples/with-artifacts.md +14 -12
  6. package/.docs/organized/code-examples/with-assistant-transport.md +13 -14
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +11 -11
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +17 -14
  9. package/.docs/organized/code-examples/with-cloud.md +12 -13
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +12 -12
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +19 -14
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +15 -15
  13. package/.docs/organized/code-examples/with-expo.md +27 -23
  14. package/.docs/organized/code-examples/with-external-store.md +11 -11
  15. package/.docs/organized/code-examples/with-ffmpeg.md +19 -14
  16. package/.docs/organized/code-examples/with-generative-ui.md +11 -11
  17. package/.docs/organized/code-examples/with-google-adk.md +10 -10
  18. package/.docs/organized/code-examples/with-heat-graph.md +8 -8
  19. package/.docs/organized/code-examples/with-interactables.md +12 -27
  20. package/.docs/organized/code-examples/with-langchain.md +437 -0
  21. package/.docs/organized/code-examples/with-langgraph.md +20 -20
  22. package/.docs/organized/code-examples/with-livekit.md +59 -18
  23. package/.docs/organized/code-examples/with-opencode.md +2392 -0
  24. package/.docs/organized/code-examples/with-parent-id-grouping.md +12 -12
  25. package/.docs/organized/code-examples/with-react-hook-form.md +223 -151
  26. package/.docs/organized/code-examples/with-react-ink.md +3 -3
  27. package/.docs/organized/code-examples/with-react-router.md +15 -15
  28. package/.docs/organized/code-examples/with-store.md +11 -8
  29. package/.docs/organized/code-examples/with-tanstack.md +14 -14
  30. package/.docs/organized/code-examples/with-tap-runtime.md +13 -9
  31. package/.docs/raw/docs/(docs)/guides/mentions.mdx +248 -109
  32. package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +112 -92
  33. package/.docs/raw/docs/(docs)/guides/voice.mdx +10 -3
  34. package/.docs/raw/docs/(docs)/rtl.mdx +79 -0
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
  36. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
  37. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +4 -0
  38. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  39. package/.docs/raw/docs/primitives/composer.mdx +94 -62
  40. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +57 -0
  41. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +49 -3
  42. package/.docs/raw/docs/runtimes/custom/external-store.mdx +39 -1
  43. package/.docs/raw/docs/runtimes/custom/local.mdx +65 -18
  44. package/.docs/raw/docs/runtimes/google-adk/index.mdx +62 -0
  45. package/.docs/raw/docs/runtimes/langchain/comparison.mdx +60 -0
  46. package/.docs/raw/docs/runtimes/langchain/index.mdx +210 -0
  47. package/.docs/raw/docs/runtimes/langgraph/index.mdx +288 -60
  48. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -4
  49. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +199 -0
  50. package/.docs/raw/docs/ui/directive-text.mdx +113 -0
  51. package/.docs/raw/docs/ui/reasoning.mdx +13 -9
  52. package/dist/utils/logger.js +1 -1
  53. package/dist/utils/logger.js.map +1 -1
  54. package/package.json +4 -4
  55. package/src/utils/logger.ts +1 -1
  56. package/.docs/raw/docs/ui/mention.mdx +0 -168
@@ -64,29 +64,29 @@
64
64
  "dependencies": {
65
65
  "@assistant-ui/react": "workspace:*",
66
66
  "@assistant-ui/react-markdown": "workspace:*",
67
- "@tailwindcss/vite": "^4.2.2",
68
- "@tanstack/react-router": "^1.168.10",
69
- "@tanstack/react-start": "^1.167.16",
67
+ "@tailwindcss/vite": "^4.2.4",
68
+ "@tanstack/react-router": "^1.168.23",
69
+ "@tanstack/react-start": "^1.167.42",
70
70
  "class-variance-authority": "^0.7.1",
71
71
  "clsx": "^2.1.1",
72
- "lucide-react": "^1.7.0",
73
- "nitro": "^3.0.1-alpha.2",
74
- "openai": "^6.33.0",
75
- "react": "^19.2.4",
76
- "react-dom": "^19.2.4",
72
+ "lucide-react": "^1.8.0",
73
+ "nitro": "^3.0.260311-beta",
74
+ "openai": "^6.34.0",
75
+ "react": "^19.2.5",
76
+ "react-dom": "^19.2.5",
77
77
  "remark-gfm": "^4.0.1",
78
78
  "tailwind-merge": "^3.5.0",
79
- "tailwindcss": "^4.2.2",
79
+ "tailwindcss": "^4.2.4",
80
80
  "vite-tsconfig-paths": "^6.1.1"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@assistant-ui/x-buildutils": "workspace:*",
84
- "@types/node": "^25.5.2",
84
+ "@types/node": "^25.6.0",
85
85
  "@types/react": "^19.2.14",
86
86
  "@types/react-dom": "^19.2.3",
87
87
  "@vitejs/plugin-react": "^6.0.1",
88
- "typescript": "5.9.3",
89
- "vite": "^8.0.5"
88
+ "typescript": "^6.0.3",
89
+ "vite": "^8.0.9"
90
90
  }
91
91
  }
92
92
 
@@ -143,8 +143,8 @@ npm run dev
143
143
  import { useState, type ReactNode } from "react";
144
144
  import {
145
145
  useExternalStoreRuntime,
146
- ThreadMessageLike,
147
- AppendMessage,
146
+ type ThreadMessageLike,
147
+ type AppendMessage,
148
148
  AssistantRuntimeProvider,
149
149
  } from "@assistant-ui/react";
150
150
  import { chatStream } from "@/server/chat";
@@ -368,6 +368,7 @@ export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
368
368
  <div className="flex items-center gap-2">
369
369
  <span className="text-muted-foreground text-sm">Controls:</span>
370
370
  <button
371
+ type="button"
371
372
  onClick={() => {
372
373
  updateCurrentThreadMessages((prev) => [
373
374
  ...prev,
@@ -383,6 +384,7 @@ export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
383
384
  Add User Message
384
385
  </button>
385
386
  <button
387
+ type="button"
386
388
  onClick={() => {
387
389
  updateCurrentThreadMessages((prev) => [
388
390
  ...prev,
@@ -400,12 +402,14 @@ export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
400
402
  Add Assistant Message
401
403
  </button>
402
404
  <button
405
+ type="button"
403
406
  onClick={() => setIsRunning(!isRunning)}
404
407
  className="rounded-md bg-accent px-3 py-1 font-medium text-accent-foreground text-sm hover:bg-accent/80"
405
408
  >
406
409
  {isRunning ? "Stop" : "Start"} Running
407
410
  </button>
408
411
  <button
412
+ type="button"
409
413
  onClick={() => updateCurrentThreadMessages(() => [])}
410
414
  className="rounded-md bg-destructive px-3 py-1 font-medium text-sm text-white hover:bg-destructive/90"
411
415
  >
@@ -597,22 +601,22 @@ export default nextConfig;
597
601
  "@assistant-ui/ui": "workspace:*",
598
602
  "class-variance-authority": "^0.7.1",
599
603
  "clsx": "^2.1.1",
600
- "lucide-react": "^1.7.0",
601
- "next": "^16.2.2",
602
- "react": "^19.2.4",
603
- "react-dom": "^19.2.4",
604
+ "lucide-react": "^1.8.0",
605
+ "next": "^16.2.4",
606
+ "react": "^19.2.5",
607
+ "react-dom": "^19.2.5",
604
608
  "tailwind-merge": "^3.5.0"
605
609
  },
606
610
  "devDependencies": {
607
611
  "@assistant-ui/x-buildutils": "workspace:*",
608
- "@tailwindcss/postcss": "^4.2.2",
609
- "@types/node": "^25.5.2",
612
+ "@tailwindcss/postcss": "^4.2.4",
613
+ "@types/node": "^25.6.0",
610
614
  "@types/react": "^19.2.14",
611
615
  "@types/react-dom": "^19.2.3",
612
- "postcss": "^8.5.8",
613
- "tailwindcss": "^4.2.2",
616
+ "postcss": "^8.5.10",
617
+ "tailwindcss": "^4.2.4",
614
618
  "tw-animate-css": "^1.4.0",
615
- "typescript": "5.9.3"
619
+ "typescript": "^6.0.3"
616
620
  }
617
621
  }
618
622
 
@@ -17,30 +17,32 @@ User types "@" → Trigger detected → Adapter provides categories/items
17
17
 
18
18
  The mention system has three layers:
19
19
 
20
- 1. **Trigger detection** — watches the composer text for a trigger character (`@` by default) and extracts the query
20
+ 1. **Trigger detection** — the composer input watches for a trigger character (`@` by default) and extracts the query
21
21
  2. **Adapter** — provides the categories and items to display in the popover (e.g. registered tools)
22
22
  3. **Formatter** — serializes a selected item into directive text (`:type[label]{name=id}`) and parses it back for rendering
23
23
 
24
+ Under the hood, mentions are one kind of [trigger popover](/docs/guides/slash-commands#trigger-popover-architecture). A mention declares its behavior with a `<TriggerPopover.Directive>` sub-primitive, which writes the formatter-serialized directive into the composer on selection.
25
+
24
26
  ## Quick Start
25
27
 
26
- The fastest path is the pre-built [Mention UI component](/docs/ui/mention), which wires everything together with a single shadcn component:
28
+ The fastest path is the pre-built [Mention UI components](/docs/ui/composer-trigger-popover), which wire everything together with two shadcn components — the popover picker and the message-side chip renderer:
27
29
 
28
30
  ```bash
29
- npx shadcn@latest add "https://r.assistant-ui.com/composer-mention"
31
+ npx shadcn@latest add "https://r.assistant-ui.com/composer-trigger-popover" "https://r.assistant-ui.com/directive-text"
30
32
  ```
31
33
 
32
- See the [Mention UI guide](/docs/ui/mention) for setup steps.
34
+ See the [Composer Trigger Popover](/docs/ui/composer-trigger-popover) and [Directive Text](/docs/ui/directive-text) guides for setup steps.
33
35
 
34
36
  The rest of this guide covers the underlying concepts and customization points.
35
37
 
36
- ## Mention Adapter
38
+ ## Trigger Adapter
37
39
 
38
- A `Unstable_MentionAdapter` provides the data for the popover. All methods are **synchronous** — use external state management (React Query, SWR, local state) for async data, then expose loaded results through the adapter.
40
+ A `Unstable_TriggerAdapter` provides the data for the popover. All methods are **synchronous** — use external state management (React Query, SWR, local state) for async data, then expose loaded results through the adapter.
39
41
 
40
42
  ```ts
41
- import type { Unstable_MentionAdapter } from "@assistant-ui/core";
43
+ import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
42
44
 
43
- const myAdapter: Unstable_MentionAdapter = {
45
+ const myAdapter: Unstable_TriggerAdapter = {
44
46
  categories() {
45
47
  return [
46
48
  { id: "tools", label: "Tools" },
@@ -80,40 +82,142 @@ const myAdapter: Unstable_MentionAdapter = {
80
82
  };
81
83
  ```
82
84
 
83
- Pass the adapter to `MentionRoot`:
85
+ Pass the adapter to `TriggerPopover` and declare a `Directive` sub-primitive to bind the insertion behavior:
84
86
 
85
87
  ```tsx
86
- <ComposerPrimitive.Unstable_MentionRoot adapter={myAdapter}>
88
+ import { ComposerPrimitive } from "@assistant-ui/react";
89
+ import { unstable_defaultDirectiveFormatter } from "@assistant-ui/core";
90
+
91
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
87
92
  <ComposerPrimitive.Root>
88
93
  <ComposerPrimitive.Input placeholder="Type @ to mention..." />
94
+ <ComposerPrimitive.Unstable_TriggerPopover
95
+ char="@"
96
+ adapter={myAdapter}
97
+ >
98
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive
99
+ formatter={unstable_defaultDirectiveFormatter}
100
+ />
101
+ <ComposerPrimitive.Unstable_TriggerPopoverCategories>
102
+ {(categories) =>
103
+ categories.map((cat) => (
104
+ <ComposerPrimitive.Unstable_TriggerPopoverCategoryItem
105
+ key={cat.id}
106
+ categoryId={cat.id}
107
+ >
108
+ {cat.label}
109
+ </ComposerPrimitive.Unstable_TriggerPopoverCategoryItem>
110
+ ))
111
+ }
112
+ </ComposerPrimitive.Unstable_TriggerPopoverCategories>
113
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
114
+ {(items) =>
115
+ items.map((item) => (
116
+ <ComposerPrimitive.Unstable_TriggerPopoverItem
117
+ key={item.id}
118
+ item={item}
119
+ >
120
+ {item.label}
121
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
122
+ ))
123
+ }
124
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
125
+ </ComposerPrimitive.Unstable_TriggerPopover>
89
126
  </ComposerPrimitive.Root>
90
- </ComposerPrimitive.Unstable_MentionRoot>
127
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
91
128
  ```
92
129
 
93
- ### Built-in Tool Adapter
130
+ Exactly one behavior sub-primitive (`Directive` or `Action`) is allowed per `TriggerPopover`. The parent reads the registered behavior and wires the selection machinery.
94
131
 
95
- For the common case of mentioning registered tools, use `unstable_useToolMentionAdapter`:
132
+ ### Built-in Mention Adapter
133
+
134
+ `unstable_useMentionAdapter` covers the common cases: mention registered tools, add your own items, mix tools with custom items, or show multi-category drill-down.
135
+
136
+ **Tools from model context (default):**
96
137
 
97
138
  ```tsx
98
- import { unstable_useToolMentionAdapter } from "@assistant-ui/react";
139
+ import { unstable_useMentionAdapter } from "@assistant-ui/react";
140
+
141
+ const mention = unstable_useMentionAdapter();
142
+ // → { adapter, directive } — spread into <ComposerTriggerPopover {...mention} />
143
+ // Default: single "Tools" category reading from useAssistantTool registrations
144
+ ```
99
145
 
100
- const adapter = unstable_useToolMentionAdapter({
101
- // Format tool names for display (default: raw name)
102
- formatLabel: (name) =>
103
- name.replaceAll("_", " ").replace(/\b\w/g, (c) => c.toUpperCase()),
146
+ **Custom items only (no tools):**
104
147
 
105
- // Custom category label (default: "Tools")
106
- categoryLabel: "Tools",
148
+ ```tsx
149
+ const mention = unstable_useMentionAdapter({
150
+ items: [
151
+ { id: "alice", type: "user", label: "Alice", icon: "User" },
152
+ { id: "bob", type: "user", label: "Bob", icon: "User" },
153
+ ],
154
+ });
155
+ ```
107
156
 
108
- // Explicit tool list (overrides model context tools)
109
- // tools: [{ id: "search", type: "tool", label: "Search" }],
157
+ **Mix custom items with model-context tools (flat):**
110
158
 
111
- // Include model context tools alongside explicit tools
112
- // includeModelContextTools: true,
159
+ ```tsx
160
+ const mention = unstable_useMentionAdapter({
161
+ items: [{ id: "kb", type: "doc", label: "Knowledge Base", icon: "Book" }],
162
+ includeModelContextTools: true,
113
163
  });
114
164
  ```
115
165
 
116
- The adapter automatically reads tools from the model context (registered via `Tools()` or `useAssistantTool`). When `tools` is provided, model context tools are excluded unless `includeModelContextTools` is set to `true`.
166
+ **Multi-category drill-down:**
167
+
168
+ ```tsx
169
+ const mention = unstable_useMentionAdapter({
170
+ categories: [
171
+ {
172
+ id: "users",
173
+ label: "Users",
174
+ items: [
175
+ { id: "alice", type: "user", label: "Alice", icon: "User" },
176
+ { id: "bob", type: "user", label: "Bob", icon: "User" },
177
+ ],
178
+ },
179
+ {
180
+ id: "files",
181
+ label: "Files",
182
+ items: [
183
+ { id: "readme", type: "file", label: "README.md", icon: "FileText" },
184
+ ],
185
+ },
186
+ ],
187
+ // Tools auto-appended as their own category (default id "tools", label "Tools")
188
+ includeModelContextTools: true,
189
+ });
190
+ ```
191
+
192
+ **Tool formatting and category override:**
193
+
194
+ ```tsx
195
+ const mention = unstable_useMentionAdapter({
196
+ categories: [{ id: "users", label: "Users", items: [...] }],
197
+ includeModelContextTools: {
198
+ category: { id: "integrations", label: "Integrations" },
199
+ formatLabel: (name) =>
200
+ name.replaceAll("_", " ").replace(/\b\w/g, (c) => c.toUpperCase()),
201
+ icon: "Wrench",
202
+ },
203
+ });
204
+ ```
205
+
206
+ **Options summary:**
207
+
208
+ | Option | Type | Behavior |
209
+ | --- | --- | --- |
210
+ | `items` | `Unstable_Mention[]` | Flat list (ignored when `categories` is set) |
211
+ | `categories` | `{id, label, items}[]` | Drill-down groups |
212
+ | `includeModelContextTools` | `boolean \| object` | Default: `true` iff neither `items` nor `categories` |
213
+ | `formatter` | `Unstable_DirectiveFormatter` | Override directive serialization (default: `unstable_defaultDirectiveFormatter`) |
214
+ | `onInserted` | `(item) => void` | Fires after the directive is inserted into the composer |
215
+ | `iconMap` | `Record<string, IconComponent>` | Maps `metadata.icon` / category `id` strings to React components |
216
+ | `fallbackIcon` | `IconComponent` | Fallback when no entry in `iconMap` matches |
217
+
218
+ `icon` on each mention is a shortcut for `metadata.icon` that the picker UI resolves via `iconMap`. Dedup between custom items and model-context tools is by `id` — explicit items win.
219
+
220
+ The hook returns `{ adapter, directive, iconMap?, fallbackIcon? }` — spread into `<ComposerTriggerPopover {...mention} />` for one-line wiring. Callers consuming the raw primitives instead destructure: `mention.adapter`, `mention.directive.formatter`, etc.
117
221
 
118
222
  ## Directive Format
119
223
 
@@ -175,13 +279,17 @@ const slashFormatter: Unstable_DirectiveFormatter = {
175
279
  };
176
280
  ```
177
281
 
178
- Pass it to both the mention root and the message renderer:
282
+ Pass it to the trigger's `Directive` sub-primitive and the message renderer:
179
283
 
180
284
  ```tsx
181
285
  // Composer
182
- <ComposerPrimitive.Unstable_MentionRoot adapter={adapter} formatter={slashFormatter}>
286
+ <ComposerPrimitive.Unstable_TriggerPopover
287
+ char="@"
288
+ adapter={adapter}
289
+ >
290
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive formatter={slashFormatter} />
183
291
  ...
184
- </ComposerPrimitive.Unstable_MentionRoot>
292
+ </ComposerPrimitive.Unstable_TriggerPopover>
185
293
 
186
294
  // User messages
187
295
  const SlashDirectiveText = createDirectiveText(slashFormatter);
@@ -206,22 +314,29 @@ With **Lexical**, selected mentions appear as styled inline chips that behave as
206
314
  ```tsx
207
315
  import { LexicalComposerInput } from "@assistant-ui/react-lexical";
208
316
 
209
- <ComposerPrimitive.Unstable_MentionRoot adapter={adapter}>
317
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
210
318
  <ComposerPrimitive.Root>
211
319
  <LexicalComposerInput placeholder="Type @ to mention..." />
212
320
  <ComposerPrimitive.Send />
321
+ <ComposerPrimitive.Unstable_TriggerPopover
322
+ char="@"
323
+ adapter={adapter}
324
+ >
325
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive formatter={formatter} />
326
+ ...
327
+ </ComposerPrimitive.Unstable_TriggerPopover>
213
328
  </ComposerPrimitive.Root>
214
- </ComposerPrimitive.Unstable_MentionRoot>
329
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
215
330
  ```
216
331
 
217
- `LexicalComposerInput` auto-wires to the mention context — no extra props needed.
332
+ `LexicalComposerInput` automatically discovers every `Directive` trigger registered under `TriggerPopoverRoot` and renders their selections as inline chips.
218
333
 
219
334
  ## Rendering Mentions in Messages
220
335
 
221
336
  Use `DirectiveText` as the `Text` component for user messages so directives render as inline chips instead of raw syntax:
222
337
 
223
338
  ```tsx
224
- import { DirectiveText } from "@/components/assistant-ui/composer-mention";
339
+ import { DirectiveText } from "@/components/assistant-ui/directive-text";
225
340
 
226
341
  <MessagePrimitive.Parts
227
342
  components={{
@@ -235,7 +350,7 @@ For assistant messages, keep using your markdown renderer (e.g. `MarkdownText`)
235
350
  For a custom formatter, use `createDirectiveText`:
236
351
 
237
352
  ```tsx
238
- import { createDirectiveText } from "@/components/assistant-ui/composer-mention";
353
+ import { createDirectiveText } from "@/components/assistant-ui/directive-text";
239
354
 
240
355
  const MyDirectiveText = createDirectiveText(myFormatter);
241
356
  ```
@@ -273,134 +388,158 @@ You can use the extracted mentions to:
273
388
 
274
389
  ## Reading Mention State
275
390
 
276
- Use `unstable_useMentionContext` to programmatically access the mention popover state:
391
+ Use `unstable_useTriggerPopoverScopeContext` inside the `TriggerPopover` to programmatically access the popover state for that trigger:
277
392
 
278
393
  ```tsx
279
- import { unstable_useMentionContext } from "@assistant-ui/react";
280
-
281
- function MyComponent() {
282
- const mention = unstable_useMentionContext();
283
-
284
- // mention.open — whether the popover is visible
285
- // mention.query — current search text after "@"
286
- // mention.categories — filtered category list
287
- // mention.items — filtered item list
288
- // mention.highlightedIndex — keyboard-navigated index
289
- // mention.isSearchMode — true when global search is active
290
- // mention.selectItem(item) — programmatically select an item
291
- // mention.close() — close the popover
394
+ import { unstable_useTriggerPopoverScopeContext } from "@assistant-ui/react";
395
+
396
+ function MyPopoverContent() {
397
+ const scope = unstable_useTriggerPopoverScopeContext();
398
+
399
+ // scope.open — whether the popover is visible
400
+ // scope.query — current search text after the trigger
401
+ // scope.categories — filtered category list
402
+ // scope.items — filtered item list
403
+ // scope.highlightedIndex — keyboard-navigated index
404
+ // scope.isSearchMode — true when global search is active
405
+ // scope.selectItem(item) — programmatically select an item
406
+ // scope.close() — close the popover
407
+
408
+ return null;
292
409
  }
293
410
  ```
294
411
 
295
- This hook must be used within a `ComposerPrimitive.Unstable_MentionRoot`.
412
+ This hook must be used inside a `ComposerPrimitive.Unstable_TriggerPopover`.
413
+
414
+ To iterate every registered trigger (e.g. from a custom input implementation), use `unstable_useTriggerPopoverTriggers` inside `TriggerPopoverRoot`.
296
415
 
297
416
  ## Building a Custom Popover
298
417
 
299
- Use the mention primitives to build a fully custom popover:
418
+ Use the trigger popover primitives to build a fully custom popover:
300
419
 
301
420
  ```tsx
302
- <ComposerPrimitive.Unstable_MentionRoot adapter={adapter}>
421
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
303
422
  <ComposerPrimitive.Root>
304
423
  <ComposerPrimitive.Input />
305
424
 
306
- <ComposerPrimitive.Unstable_MentionPopover className="popover">
307
- <ComposerPrimitive.Unstable_MentionBack>
425
+ <ComposerPrimitive.Unstable_TriggerPopover
426
+ char="@"
427
+ adapter={adapter}
428
+ className="popover"
429
+ >
430
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive formatter={formatter} />
431
+
432
+ <ComposerPrimitive.Unstable_TriggerPopoverBack>
308
433
  ← Back
309
- </ComposerPrimitive.Unstable_MentionBack>
434
+ </ComposerPrimitive.Unstable_TriggerPopoverBack>
310
435
 
311
- <ComposerPrimitive.Unstable_MentionCategories>
436
+ <ComposerPrimitive.Unstable_TriggerPopoverCategories>
312
437
  {(categories) =>
313
438
  categories.map((cat) => (
314
- <ComposerPrimitive.Unstable_MentionCategoryItem
439
+ <ComposerPrimitive.Unstable_TriggerPopoverCategoryItem
315
440
  key={cat.id}
316
441
  categoryId={cat.id}
317
442
  >
318
443
  {cat.label}
319
- </ComposerPrimitive.Unstable_MentionCategoryItem>
444
+ </ComposerPrimitive.Unstable_TriggerPopoverCategoryItem>
320
445
  ))
321
446
  }
322
- </ComposerPrimitive.Unstable_MentionCategories>
447
+ </ComposerPrimitive.Unstable_TriggerPopoverCategories>
323
448
 
324
- <ComposerPrimitive.Unstable_MentionItems>
449
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
325
450
  {(items) =>
326
451
  items.map((item) => (
327
- <ComposerPrimitive.Unstable_MentionItem
452
+ <ComposerPrimitive.Unstable_TriggerPopoverItem
328
453
  key={item.id}
329
454
  item={item}
330
455
  >
331
456
  {item.label}
332
- </ComposerPrimitive.Unstable_MentionItem>
457
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
333
458
  ))
334
459
  }
335
- </ComposerPrimitive.Unstable_MentionItems>
336
- </ComposerPrimitive.Unstable_MentionPopover>
460
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
461
+ </ComposerPrimitive.Unstable_TriggerPopover>
337
462
 
338
463
  <ComposerPrimitive.Send />
339
464
  </ComposerPrimitive.Root>
340
- </ComposerPrimitive.Unstable_MentionRoot>
465
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
341
466
  ```
342
467
 
343
468
  ### Primitives Reference
344
469
 
345
470
  | Primitive | Description |
346
471
  | --- | --- |
347
- | `Unstable_MentionRoot` | Provider — wraps the composer with trigger detection, keyboard navigation, and popover state |
348
- | `Unstable_MentionPopover` | Container — only renders when a trigger is active (`role="listbox"`) |
349
- | `Unstable_MentionCategories` | Render-function for the top-level category list |
350
- | `Unstable_MentionCategoryItem` | Button that drills into a category (`role="option"`, auto `data-highlighted`) |
351
- | `Unstable_MentionItems` | Render-function for items within the active category or search results |
352
- | `Unstable_MentionItem` | Button that inserts a mention (`role="option"`, auto `data-highlighted`) |
353
- | `Unstable_MentionBack` | Button that navigates back from items to categories |
472
+ | `Unstable_TriggerPopoverRoot` | Root provider — groups one or more triggers, manages plugin registry |
473
+ | `Unstable_TriggerPopover` | Declares a trigger (id, char, adapter) and renders the popover container |
474
+ | `Unstable_TriggerPopover.Directive` | Behavior sub-primitive — inserts a formatted directive into the composer on selection |
475
+ | `Unstable_TriggerPopover.Action` | Behavior sub-primitive — runs a callback on selection; inserts a chip by default |
476
+ | `Unstable_TriggerPopoverCategories` | Render-function for the top-level category list |
477
+ | `Unstable_TriggerPopoverCategoryItem` | Button that drills into a category (`role="option"`, auto `data-highlighted`) |
478
+ | `Unstable_TriggerPopoverItems` | Render-function for items within the active category or search results |
479
+ | `Unstable_TriggerPopoverItem` | Button that selects an item (`role="option"`, auto `data-highlighted`) |
480
+ | `Unstable_TriggerPopoverBack` | Button that navigates back from items to categories |
354
481
 
355
482
  See the [Composer API reference](/docs/api-reference/primitives/composer) for full prop details.
356
483
 
357
484
  ## Combining with Slash Commands
358
485
 
359
- Mentions and [slash commands](/docs/guides/slash-commands) can coexist on the same composer. Both are built on the same [trigger popover architecture](/docs/guides/slash-commands#trigger-popover-architecture) — nest both roots and they work independently:
486
+ Mentions and [slash commands](/docs/guides/slash-commands) coexist on the same composer — they're both just triggers on the shared `TriggerPopoverRoot`:
360
487
 
361
488
  ```tsx
362
- <ComposerPrimitive.Unstable_MentionRoot adapter={mentionAdapter}>
363
- <ComposerPrimitive.Unstable_SlashCommandRoot adapter={slashAdapter}>
364
- <ComposerPrimitive.Root>
365
- <ComposerPrimitive.Input placeholder="Type @ to mention, / for commands..." />
366
- <ComposerPrimitive.Send />
367
-
368
- {/* Mention popover (shows on @) */}
369
- <ComposerPrimitive.Unstable_MentionPopover>
370
- <ComposerPrimitive.Unstable_MentionCategories>
371
- {(categories) => categories.map((cat) => (
372
- <ComposerPrimitive.Unstable_MentionCategoryItem key={cat.id} categoryId={cat.id}>
373
- {cat.label}
374
- </ComposerPrimitive.Unstable_MentionCategoryItem>
375
- ))}
376
- </ComposerPrimitive.Unstable_MentionCategories>
377
- <ComposerPrimitive.Unstable_MentionItems>
378
- {(items) => items.map((item) => (
379
- <ComposerPrimitive.Unstable_MentionItem key={item.id} item={item}>
380
- {item.label}
381
- </ComposerPrimitive.Unstable_MentionItem>
382
- ))}
383
- </ComposerPrimitive.Unstable_MentionItems>
384
- </ComposerPrimitive.Unstable_MentionPopover>
385
-
386
- {/* Slash command popover (shows on /) */}
387
- <ComposerPrimitive.Unstable_TriggerPopoverPopover>
388
- <ComposerPrimitive.Unstable_TriggerPopoverItems>
389
- {(items) => items.map((item, index) => (
390
- <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
391
- {item.label}
392
- </ComposerPrimitive.Unstable_TriggerPopoverItem>
393
- ))}
394
- </ComposerPrimitive.Unstable_TriggerPopoverItems>
395
- </ComposerPrimitive.Unstable_TriggerPopoverPopover>
396
- </ComposerPrimitive.Root>
397
- </ComposerPrimitive.Unstable_SlashCommandRoot>
398
- </ComposerPrimitive.Unstable_MentionRoot>
489
+ <ComposerPrimitive.Unstable_TriggerPopoverRoot>
490
+ <ComposerPrimitive.Root>
491
+ <ComposerPrimitive.Input placeholder="Type @ to mention, / for commands..." />
492
+ <ComposerPrimitive.Send />
493
+
494
+ {/* Mention popover (shows on @) */}
495
+ <ComposerPrimitive.Unstable_TriggerPopover
496
+ char="@"
497
+ adapter={mention.adapter}
498
+ >
499
+ <ComposerPrimitive.Unstable_TriggerPopover.Directive {...mention.directive} />
500
+ <ComposerPrimitive.Unstable_TriggerPopoverCategories>
501
+ {(categories) => categories.map((cat) => (
502
+ <ComposerPrimitive.Unstable_TriggerPopoverCategoryItem key={cat.id} categoryId={cat.id}>
503
+ {cat.label}
504
+ </ComposerPrimitive.Unstable_TriggerPopoverCategoryItem>
505
+ ))}
506
+ </ComposerPrimitive.Unstable_TriggerPopoverCategories>
507
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
508
+ {(items) => items.map((item) => (
509
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item}>
510
+ {item.label}
511
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
512
+ ))}
513
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
514
+ </ComposerPrimitive.Unstable_TriggerPopover>
515
+
516
+ {/* Slash command popover (shows on /) */}
517
+ <ComposerPrimitive.Unstable_TriggerPopover
518
+ char="/"
519
+ adapter={slashAdapter}
520
+ >
521
+ <ComposerPrimitive.Unstable_TriggerPopover.Action
522
+ formatter={unstable_defaultDirectiveFormatter}
523
+ onExecute={(item) => commandHandlers[item.id]?.()}
524
+ />
525
+ <ComposerPrimitive.Unstable_TriggerPopoverItems>
526
+ {(items) => items.map((item, index) => (
527
+ <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
528
+ {item.label}
529
+ </ComposerPrimitive.Unstable_TriggerPopoverItem>
530
+ ))}
531
+ </ComposerPrimitive.Unstable_TriggerPopoverItems>
532
+ </ComposerPrimitive.Unstable_TriggerPopover>
533
+ </ComposerPrimitive.Root>
534
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
399
535
  ```
400
536
 
537
+ Each `TriggerPopover` declares its own scope — its popover UI reads state only from that declaration, so `@` and `/` never collide.
538
+
401
539
  ## Related
402
540
 
403
- - [Mention UI Component](/docs/ui/mention) — pre-built shadcn component
541
+ - [ComposerTriggerPopover UI Component](/docs/ui/composer-trigger-popover) — pre-built shadcn component
542
+ - [DirectiveText UI Component](/docs/ui/directive-text) — renders mention chips in user messages
404
543
  - [Slash Commands Guide](/docs/guides/slash-commands) — `/` command system built on the same architecture
405
544
  - [Tools Guide](/docs/guides/tools) — register tools that appear in the mention picker
406
545
  - [Composer Primitives](/docs/primitives/composer) — underlying composer primitives