@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.
- package/.docs/organized/code-examples/waterfall.md +18 -10
- package/.docs/organized/code-examples/with-a2a.md +12 -24
- package/.docs/organized/code-examples/with-ag-ui.md +14 -11
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +12 -12
- package/.docs/organized/code-examples/with-artifacts.md +14 -12
- package/.docs/organized/code-examples/with-assistant-transport.md +13 -14
- package/.docs/organized/code-examples/with-chain-of-thought.md +11 -11
- package/.docs/organized/code-examples/with-cloud-standalone.md +17 -14
- package/.docs/organized/code-examples/with-cloud.md +12 -13
- package/.docs/organized/code-examples/with-custom-thread-list.md +12 -12
- package/.docs/organized/code-examples/with-elevenlabs-conversational.md +19 -14
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +15 -15
- package/.docs/organized/code-examples/with-expo.md +27 -23
- package/.docs/organized/code-examples/with-external-store.md +11 -11
- package/.docs/organized/code-examples/with-ffmpeg.md +19 -14
- package/.docs/organized/code-examples/with-generative-ui.md +11 -11
- package/.docs/organized/code-examples/with-google-adk.md +10 -10
- package/.docs/organized/code-examples/with-heat-graph.md +8 -8
- package/.docs/organized/code-examples/with-interactables.md +12 -27
- package/.docs/organized/code-examples/with-langchain.md +437 -0
- package/.docs/organized/code-examples/with-langgraph.md +20 -20
- package/.docs/organized/code-examples/with-livekit.md +59 -18
- package/.docs/organized/code-examples/with-opencode.md +2392 -0
- package/.docs/organized/code-examples/with-parent-id-grouping.md +12 -12
- package/.docs/organized/code-examples/with-react-hook-form.md +223 -151
- package/.docs/organized/code-examples/with-react-ink.md +3 -3
- package/.docs/organized/code-examples/with-react-router.md +15 -15
- package/.docs/organized/code-examples/with-store.md +11 -8
- package/.docs/organized/code-examples/with-tanstack.md +14 -14
- package/.docs/organized/code-examples/with-tap-runtime.md +13 -9
- package/.docs/raw/docs/(docs)/guides/mentions.mdx +248 -109
- package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +112 -92
- package/.docs/raw/docs/(docs)/guides/voice.mdx +10 -3
- package/.docs/raw/docs/(docs)/rtl.mdx +79 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +4 -0
- package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
- package/.docs/raw/docs/primitives/composer.mdx +94 -62
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +57 -0
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +49 -3
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +39 -1
- package/.docs/raw/docs/runtimes/custom/local.mdx +65 -18
- package/.docs/raw/docs/runtimes/google-adk/index.mdx +62 -0
- package/.docs/raw/docs/runtimes/langchain/comparison.mdx +60 -0
- package/.docs/raw/docs/runtimes/langchain/index.mdx +210 -0
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +288 -60
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -4
- package/.docs/raw/docs/ui/composer-trigger-popover.mdx +199 -0
- package/.docs/raw/docs/ui/directive-text.mdx +113 -0
- package/.docs/raw/docs/ui/reasoning.mdx +13 -9
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +4 -4
- package/src/utils/logger.ts +1 -1
- package/.docs/raw/docs/ui/mention.mdx +0 -168
|
@@ -3,63 +3,68 @@ title: Slash Commands
|
|
|
3
3
|
description: Let users type / in the composer to trigger predefined actions from a popover picker.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Slash commands let users type `/` in the composer to open a popover, browse available commands, and execute one. Unlike [mentions](/docs/guides/mentions) (which insert
|
|
6
|
+
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.
|
|
7
7
|
|
|
8
8
|
## How It Works
|
|
9
9
|
|
|
10
10
|
```
|
|
11
11
|
User types "/" → Trigger detected → Adapter provides commands
|
|
12
12
|
↓
|
|
13
|
-
|
|
13
|
+
Callback fired ← User selects command from popover
|
|
14
14
|
↓
|
|
15
|
-
|
|
15
|
+
Directive chip left in composer (or removed if removeOnExecute)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
The slash command system is built on the same [trigger popover architecture](#trigger-popover-architecture) as mentions.
|
|
18
|
+
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.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
2. **SlashCommandRoot** — a convenience wrapper that pre-configures the trigger character (`/`) and action-based select behavior
|
|
20
|
+
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
21
|
|
|
23
22
|
## Quick Start
|
|
24
23
|
|
|
25
|
-
### 1. Define Commands
|
|
24
|
+
### 1. Define Commands with `unstable_useSlashCommandAdapter`
|
|
25
|
+
|
|
26
|
+
Declare commands (data + `execute` bundled together, like `useAssistantTool`). The hook returns `{ adapter, action }` — wire both into a single `<TriggerPopover>`:
|
|
26
27
|
|
|
27
28
|
```tsx
|
|
28
29
|
import {
|
|
29
|
-
unstable_useSlashCommandAdapter,
|
|
30
30
|
ComposerPrimitive,
|
|
31
|
+
unstable_useSlashCommandAdapter,
|
|
32
|
+
type Unstable_SlashCommand,
|
|
31
33
|
} from "@assistant-ui/react";
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
const COMMANDS = [
|
|
35
|
+
const SLASH_COMMANDS: readonly Unstable_SlashCommand[] = [
|
|
35
36
|
{
|
|
36
|
-
|
|
37
|
+
id: "summarize",
|
|
37
38
|
description: "Summarize the conversation",
|
|
38
39
|
execute: () => console.log("Summarize!"),
|
|
39
40
|
},
|
|
40
41
|
{
|
|
41
|
-
|
|
42
|
+
id: "translate",
|
|
42
43
|
description: "Translate text to another language",
|
|
43
44
|
execute: () => console.log("Translate!"),
|
|
44
45
|
},
|
|
45
46
|
{
|
|
46
|
-
|
|
47
|
+
id: "help",
|
|
47
48
|
description: "List all available commands",
|
|
49
|
+
execute: () => console.log("Help!"),
|
|
48
50
|
},
|
|
49
51
|
];
|
|
50
52
|
|
|
51
53
|
function MyComposer() {
|
|
52
|
-
const
|
|
53
|
-
commands: COMMANDS,
|
|
54
|
-
});
|
|
54
|
+
const slash = unstable_useSlashCommandAdapter({ commands: SLASH_COMMANDS });
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
|
-
<ComposerPrimitive.
|
|
57
|
+
<ComposerPrimitive.Unstable_TriggerPopoverRoot>
|
|
58
58
|
<ComposerPrimitive.Root>
|
|
59
59
|
<ComposerPrimitive.Input placeholder="Type / for commands..." />
|
|
60
60
|
<ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
|
|
61
61
|
|
|
62
|
-
<ComposerPrimitive.
|
|
62
|
+
<ComposerPrimitive.Unstable_TriggerPopover
|
|
63
|
+
char="/"
|
|
64
|
+
adapter={slash.adapter}
|
|
65
|
+
className="popover"
|
|
66
|
+
>
|
|
67
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Action {...slash.action} />
|
|
63
68
|
<ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
64
69
|
{(items) =>
|
|
65
70
|
items.map((item, index) => (
|
|
@@ -75,49 +80,49 @@ function MyComposer() {
|
|
|
75
80
|
))
|
|
76
81
|
}
|
|
77
82
|
</ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
78
|
-
</ComposerPrimitive.
|
|
83
|
+
</ComposerPrimitive.Unstable_TriggerPopover>
|
|
79
84
|
</ComposerPrimitive.Root>
|
|
80
|
-
</ComposerPrimitive.
|
|
85
|
+
</ComposerPrimitive.Unstable_TriggerPopoverRoot>
|
|
81
86
|
);
|
|
82
87
|
}
|
|
83
88
|
```
|
|
84
89
|
|
|
85
|
-
|
|
90
|
+
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)).
|
|
86
91
|
|
|
87
|
-
|
|
92
|
+
### 2. Controlling the Chip
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
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.
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
To strip the trigger text entirely — useful for purely transient commands — pass `removeOnExecute` on the hook options:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
const slash = unstable_useSlashCommandAdapter({
|
|
100
|
+
commands: SLASH_COMMANDS,
|
|
101
|
+
removeOnExecute: true,
|
|
102
|
+
});
|
|
93
103
|
```
|
|
94
104
|
|
|
95
|
-
|
|
105
|
+
### 3. Custom Dispatch
|
|
106
|
+
|
|
107
|
+
For side effects on top of `execute` (logging, analytics, intercept), wrap the hook's `onExecute`:
|
|
96
108
|
|
|
97
109
|
```tsx
|
|
98
|
-
<ComposerPrimitive.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
case "summarize": runSummarize(); break;
|
|
103
|
-
case "translate": runTranslate(); break;
|
|
104
|
-
}
|
|
110
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Action
|
|
111
|
+
onExecute={(item) => {
|
|
112
|
+
logCommandUsed(item.id);
|
|
113
|
+
slash.action.onExecute(item);
|
|
105
114
|
}}
|
|
106
|
-
|
|
115
|
+
/>
|
|
107
116
|
```
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
## Custom Adapter
|
|
112
|
-
|
|
113
|
-
The `unstable_useSlashCommandAdapter` hook uses a **flat list** — all commands show immediately when `/` is typed, with search filtering as the user types. This is the recommended UX for most cases.
|
|
118
|
+
## Categorized Commands
|
|
114
119
|
|
|
115
|
-
For **categorized navigation** (drill-down into groups),
|
|
120
|
+
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:
|
|
116
121
|
|
|
117
122
|
```ts
|
|
118
|
-
import type {
|
|
123
|
+
import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
|
|
119
124
|
|
|
120
|
-
const adapter:
|
|
125
|
+
const adapter: Unstable_TriggerAdapter = {
|
|
121
126
|
categories() {
|
|
122
127
|
return [
|
|
123
128
|
{ id: "actions", label: "Actions" },
|
|
@@ -155,7 +160,19 @@ const adapter: Unstable_SlashCommandAdapter = {
|
|
|
155
160
|
When using a categorized adapter, add `TriggerPopoverCategories` to your popover UI:
|
|
156
161
|
|
|
157
162
|
```tsx
|
|
158
|
-
<
|
|
163
|
+
const commandHandlers: Record<string, () => void> = {
|
|
164
|
+
summarize: () => {/* ... */},
|
|
165
|
+
pdf: () => {/* ... */},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
<ComposerPrimitive.Unstable_TriggerPopover
|
|
169
|
+
char="/"
|
|
170
|
+
adapter={adapter}
|
|
171
|
+
>
|
|
172
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Action
|
|
173
|
+
formatter={unstable_defaultDirectiveFormatter}
|
|
174
|
+
onExecute={(item) => commandHandlers[item.id]?.()}
|
|
175
|
+
/>
|
|
159
176
|
<ComposerPrimitive.Unstable_TriggerPopoverBack>← Back</ComposerPrimitive.Unstable_TriggerPopoverBack>
|
|
160
177
|
<ComposerPrimitive.Unstable_TriggerPopoverCategories>
|
|
161
178
|
{(categories) => categories.map((cat) => (
|
|
@@ -171,54 +188,53 @@ When using a categorized adapter, add `TriggerPopoverCategories` to your popover
|
|
|
171
188
|
</ComposerPrimitive.Unstable_TriggerPopoverItem>
|
|
172
189
|
))}
|
|
173
190
|
</ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
174
|
-
</ComposerPrimitive.
|
|
191
|
+
</ComposerPrimitive.Unstable_TriggerPopover>
|
|
175
192
|
```
|
|
176
193
|
|
|
177
194
|
## Combining with Mentions
|
|
178
195
|
|
|
179
|
-
Slash commands and mentions
|
|
196
|
+
Slash commands and mentions live under the same `TriggerPopoverRoot`. Declare one `TriggerPopover` per trigger — each with its own behavior sub-primitive:
|
|
180
197
|
|
|
181
198
|
```tsx
|
|
182
|
-
<ComposerPrimitive.
|
|
183
|
-
<ComposerPrimitive.
|
|
184
|
-
<ComposerPrimitive.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
<ComposerPrimitive.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
</ComposerPrimitive.Unstable_MentionRoot>
|
|
199
|
+
<ComposerPrimitive.Unstable_TriggerPopoverRoot>
|
|
200
|
+
<ComposerPrimitive.Root>
|
|
201
|
+
<ComposerPrimitive.Input placeholder="Type @ to mention, / for commands..." />
|
|
202
|
+
<ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
|
|
203
|
+
|
|
204
|
+
{/* @ mention popover */}
|
|
205
|
+
<ComposerPrimitive.Unstable_TriggerPopover
|
|
206
|
+
char="@"
|
|
207
|
+
adapter={mention.adapter}
|
|
208
|
+
>
|
|
209
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Directive {...mention.directive} />
|
|
210
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
211
|
+
{(items) => items.map((item) => (
|
|
212
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item}>
|
|
213
|
+
{item.label}
|
|
214
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItem>
|
|
215
|
+
))}
|
|
216
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
217
|
+
</ComposerPrimitive.Unstable_TriggerPopover>
|
|
218
|
+
|
|
219
|
+
{/* / slash command popover */}
|
|
220
|
+
<ComposerPrimitive.Unstable_TriggerPopover
|
|
221
|
+
char="/"
|
|
222
|
+
adapter={slash.adapter}
|
|
223
|
+
>
|
|
224
|
+
<ComposerPrimitive.Unstable_TriggerPopover.Action {...slash.action} />
|
|
225
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
226
|
+
{(items) => items.map((item, index) => (
|
|
227
|
+
<ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
|
|
228
|
+
{item.label}
|
|
229
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItem>
|
|
230
|
+
))}
|
|
231
|
+
</ComposerPrimitive.Unstable_TriggerPopoverItems>
|
|
232
|
+
</ComposerPrimitive.Unstable_TriggerPopover>
|
|
233
|
+
</ComposerPrimitive.Root>
|
|
234
|
+
</ComposerPrimitive.Unstable_TriggerPopoverRoot>
|
|
219
235
|
```
|
|
220
236
|
|
|
221
|
-
Each
|
|
237
|
+
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.
|
|
222
238
|
|
|
223
239
|
## Keyboard Navigation
|
|
224
240
|
|
|
@@ -236,15 +252,18 @@ Same keyboard bindings as mentions:
|
|
|
236
252
|
|
|
237
253
|
Both mentions and slash commands are built on a generic **trigger popover** system:
|
|
238
254
|
|
|
239
|
-
- `ComposerPrimitive.Unstable_TriggerPopoverRoot` —
|
|
240
|
-
- `ComposerPrimitive.
|
|
241
|
-
-
|
|
255
|
+
- `ComposerPrimitive.Unstable_TriggerPopoverRoot` — root provider that groups triggers and owns the input plugin registry
|
|
256
|
+
- `ComposerPrimitive.Unstable_TriggerPopover` — declares one trigger (id, char, adapter) and renders its popover container
|
|
257
|
+
- Behavior sub-primitives — exactly one per `TriggerPopover`:
|
|
258
|
+
- `Unstable_TriggerPopover.Directive` — writes a formatted directive on selection ("mention" path)
|
|
259
|
+
- `Unstable_TriggerPopover.Action` — fires a callback on selection ("slash" path); inserts a chip by default, strip with `removeOnExecute`
|
|
260
|
+
- Shared sub-primitives (`TriggerPopoverCategories`, `TriggerPopoverItems`, `TriggerPopoverBack`) live inside a `TriggerPopover`
|
|
242
261
|
|
|
243
|
-
|
|
262
|
+
You can declare any number of triggers under one root and mix behavior types.
|
|
244
263
|
|
|
245
264
|
### ComposerInput Plugin Protocol
|
|
246
265
|
|
|
247
|
-
Under the hood, each
|
|
266
|
+
Under the hood, each `TriggerPopover` registers a **ComposerInputPlugin** with the composer input. This is a generic protocol that decouples the input from any specific trigger:
|
|
248
267
|
|
|
249
268
|
```ts
|
|
250
269
|
type ComposerInputPlugin = {
|
|
@@ -253,15 +272,16 @@ type ComposerInputPlugin = {
|
|
|
253
272
|
};
|
|
254
273
|
```
|
|
255
274
|
|
|
256
|
-
The input iterates over registered plugins for keyboard events and cursor changes. This is what enables multiple
|
|
275
|
+
The input iterates over registered plugins for keyboard events and cursor changes. This is what enables multiple triggers to coexist without conflict.
|
|
257
276
|
|
|
258
277
|
## Primitives Reference
|
|
259
278
|
|
|
260
279
|
| Primitive | Description |
|
|
261
280
|
| --- | --- |
|
|
262
|
-
| `
|
|
263
|
-
| `
|
|
264
|
-
| `
|
|
281
|
+
| `Unstable_TriggerPopoverRoot` | Root — groups triggers, provides input plugin registry |
|
|
282
|
+
| `Unstable_TriggerPopover` | Declares a trigger and renders its popover container |
|
|
283
|
+
| `Unstable_TriggerPopover.Directive` | Behavior sub-primitive — inserts a formatted directive on selection |
|
|
284
|
+
| `Unstable_TriggerPopover.Action` | Behavior sub-primitive — runs `onExecute` on selection; chip-by-default |
|
|
265
285
|
| `Unstable_TriggerPopoverCategories` | Render-function for the top-level category list |
|
|
266
286
|
| `Unstable_TriggerPopoverCategoryItem` | Button that drills into a category (`role="option"`, auto `data-highlighted`) |
|
|
267
287
|
| `Unstable_TriggerPopoverItems` | Render-function for items within the active category or search results |
|
|
@@ -303,14 +303,21 @@ const runtime = useChatRuntime({
|
|
|
303
303
|
|
|
304
304
|
## Example: LiveKit
|
|
305
305
|
|
|
306
|
-
[LiveKit](https://livekit.io/) provides realtime voice via WebRTC rooms with transcription support.
|
|
306
|
+
[LiveKit](https://livekit.io/) provides realtime voice via WebRTC rooms with transcription support. Unlike fully-hosted agent services, LiveKit follows a "bring-your-own-agent" model: the browser adapter only joins a room, and you run a separate **agent worker** that joins the same room and handles STT, LLM, and TTS. Without an agent in the room, the client will connect successfully but have nothing to talk to.
|
|
307
|
+
|
|
308
|
+
### Prerequisites
|
|
309
|
+
|
|
310
|
+
1. **A LiveKit server** — create a [LiveKit Cloud](https://cloud.livekit.io/) project (grab the URL, API Key, and API Secret from the project settings) or self-host `livekit-server`.
|
|
311
|
+
2. **An agent worker** — built with the [LiveKit Agents SDK](https://docs.livekit.io/agents/) (Python or Node). The worker connects to your LiveKit server and is automatically dispatched into new rooms.
|
|
307
312
|
|
|
308
313
|
### Install Dependencies
|
|
309
314
|
|
|
310
315
|
```bash
|
|
311
|
-
npm install livekit-client
|
|
316
|
+
npm install livekit-client livekit-server-sdk
|
|
312
317
|
```
|
|
313
318
|
|
|
319
|
+
`livekit-client` powers the browser adapter; `livekit-server-sdk` is used server-side to mint access tokens.
|
|
320
|
+
|
|
314
321
|
### Usage
|
|
315
322
|
|
|
316
323
|
```tsx
|
|
@@ -330,4 +337,4 @@ const runtime = useChatRuntime({
|
|
|
330
337
|
});
|
|
331
338
|
```
|
|
332
339
|
|
|
333
|
-
See the `examples/with-livekit
|
|
340
|
+
See the [`with-livekit` example](https://github.com/assistant-ui/assistant-ui/tree/main/examples/with-livekit) for a complete implementation: the browser adapter, the token endpoint, and a minimal Python agent worker using the OpenAI Realtime API.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: RTL Support
|
|
3
|
+
description: Use assistant-ui with right-to-left languages like Arabic, Hebrew, and Persian.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Components shipped through `@assistant-ui/ui` (npm) and the shadcn registry (`npx shadcn@latest add https://r.assistant-ui.com/...`) use logical Tailwind classes (`ms-*`, `pe-*`, `text-start`, `end-*`, `border-s`, ...). They flip automatically under `dir="rtl"` and render byte-identically under `dir="ltr"` (the default) — there is nothing to opt out of.
|
|
7
|
+
|
|
8
|
+
If you scaffolded from one of our templates (e.g. `npx assistant-ui@latest create -t default`), the generated `components/` folder still uses physical classes (`ml-*`, `text-left`, ...). Run shadcn's built-in migration **once** to convert both the shadcn primitives and assistant-ui's wrappers:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
# shadcn primitives under components/ui/
|
|
12
|
+
npx shadcn@latest migrate rtl
|
|
13
|
+
|
|
14
|
+
# assistant-ui wrappers (pass a custom glob)
|
|
15
|
+
npx shadcn@latest migrate rtl 'components/assistant-ui/**/*.tsx'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Commit the diff and do not re-run. The upstream migration is not fully idempotent on repeat runs ([#9891](https://github.com/shadcn-ui/ui/pull/9891)); you may end up with duplicated `rtl:translate-x-*` classes.
|
|
19
|
+
|
|
20
|
+
After migrating, follow the setup below.
|
|
21
|
+
|
|
22
|
+
## Setup
|
|
23
|
+
|
|
24
|
+
### 1. Install the `direction` component
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx shadcn@latest add https://r.assistant-ui.com/direction.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This adds `components/ui/direction.tsx`, a thin re-export of Radix UI's `DirectionProvider` and `useDirection`. It ensures Radix popovers, dropdowns, and menus pick up the current direction.
|
|
31
|
+
|
|
32
|
+
### 2. Set `dir` on your root element
|
|
33
|
+
|
|
34
|
+
```tsx title="app/layout.tsx"
|
|
35
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
36
|
+
return (
|
|
37
|
+
<html lang="ar" dir="rtl">
|
|
38
|
+
<body>{children}</body>
|
|
39
|
+
</html>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Wrap your app with `DirectionProvider`
|
|
45
|
+
|
|
46
|
+
```tsx title="app/providers.tsx"
|
|
47
|
+
"use client";
|
|
48
|
+
|
|
49
|
+
import { DirectionProvider } from "@/components/ui/direction";
|
|
50
|
+
|
|
51
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
52
|
+
return <DirectionProvider dir="rtl">{children}</DirectionProvider>;
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For apps that need to switch direction at runtime, drive `dir` from state and also update `document.documentElement.dir` to keep Tailwind's `[dir=rtl]` selector in sync.
|
|
57
|
+
|
|
58
|
+
## How it works
|
|
59
|
+
|
|
60
|
+
Every physical class (`ml-4`, `text-left`, `right-3`, `border-l`, ...) in `@assistant-ui/ui` is authored in logical form (`ms-4`, `text-start`, `end-3`, `border-s`, ...). Logical properties resolve to the matching physical side based on the ancestor with a `dir` attribute:
|
|
61
|
+
|
|
62
|
+
| Class | `dir="ltr"` | `dir="rtl"` |
|
|
63
|
+
| ----- | ----------- | ----------- |
|
|
64
|
+
| `ms-4` | `margin-left: 1rem` | `margin-right: 1rem` |
|
|
65
|
+
| `pe-2` | `padding-right: 0.5rem` | `padding-left: 0.5rem` |
|
|
66
|
+
| `end-3` | `right: 0.75rem` | `left: 0.75rem` |
|
|
67
|
+
| `text-start` | `text-align: left` | `text-align: right` |
|
|
68
|
+
| `border-s` | `border-left` | `border-right` |
|
|
69
|
+
|
|
70
|
+
A handful of Tailwind utilities have no logical equivalent, so we ship both the LTR value and an `rtl:` override:
|
|
71
|
+
|
|
72
|
+
- `translate-x-*`: emits `-translate-x-*` plus `rtl:translate-x-*` (sign-flipped).
|
|
73
|
+
- `space-x-*` / `divide-x-*`: emits the original plus `rtl:space-x-reverse` / `rtl:divide-x-reverse`.
|
|
74
|
+
|
|
75
|
+
## Known edges
|
|
76
|
+
|
|
77
|
+
- **Radix `data-[side=left|right]:slide-in-from-*`** animations are intentionally preserved as physical. Radix's `DirectionProvider` already flips the emitted `data-side` value, so no rewrite is needed.
|
|
78
|
+
- **Third-party components and template scaffolds** may still use physical classes. Run `npx shadcn@latest migrate rtl` once per project (optionally with a path glob) to convert them. Do not re-run — see the note about upstream idempotency in the intro.
|
|
79
|
+
- **Text that mixes LTR and RTL content** (e.g., English code inside Arabic prose) relies on the browser's bidi algorithm. Wrap unambiguous spans with `<bdi>` or `dir="ltr"` if you need to pin direction locally.
|