@assistant-ui/mcp-docs-server 0.1.29 → 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 (53) hide show
  1. package/.docs/organized/code-examples/waterfall.md +15 -7
  2. package/.docs/organized/code-examples/with-a2a.md +8 -20
  3. package/.docs/organized/code-examples/with-ag-ui.md +9 -6
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +8 -8
  5. package/.docs/organized/code-examples/with-artifacts.md +10 -8
  6. package/.docs/organized/code-examples/with-assistant-transport.md +9 -10
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +7 -7
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +13 -10
  9. package/.docs/organized/code-examples/with-cloud.md +8 -9
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +8 -8
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +15 -10
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +11 -11
  13. package/.docs/organized/code-examples/with-expo.md +20 -16
  14. package/.docs/organized/code-examples/with-external-store.md +7 -7
  15. package/.docs/organized/code-examples/with-ffmpeg.md +15 -10
  16. package/.docs/organized/code-examples/with-generative-ui.md +7 -7
  17. package/.docs/organized/code-examples/with-google-adk.md +6 -6
  18. package/.docs/organized/code-examples/with-heat-graph.md +5 -5
  19. package/.docs/organized/code-examples/with-interactables.md +8 -23
  20. package/.docs/organized/code-examples/with-langchain.md +437 -0
  21. package/.docs/organized/code-examples/with-langgraph.md +15 -15
  22. package/.docs/organized/code-examples/with-livekit.md +15 -10
  23. package/.docs/organized/code-examples/with-opencode.md +8 -10
  24. package/.docs/organized/code-examples/with-parent-id-grouping.md +8 -8
  25. package/.docs/organized/code-examples/with-react-hook-form.md +219 -147
  26. package/.docs/organized/code-examples/with-react-ink.md +2 -2
  27. package/.docs/organized/code-examples/with-react-router.md +10 -10
  28. package/.docs/organized/code-examples/with-store.md +8 -5
  29. package/.docs/organized/code-examples/with-tanstack.md +8 -8
  30. package/.docs/organized/code-examples/with-tap-runtime.md +9 -5
  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)/rtl.mdx +79 -0
  34. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +149 -40
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +2 -0
  36. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +4 -0
  37. package/.docs/raw/docs/cloud/ai-sdk-assistant-ui.mdx +1 -1
  38. package/.docs/raw/docs/primitives/composer.mdx +94 -62
  39. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +57 -0
  40. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +47 -1
  41. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1 -1
  42. package/.docs/raw/docs/runtimes/langchain/comparison.mdx +60 -0
  43. package/.docs/raw/docs/runtimes/langchain/index.mdx +210 -0
  44. package/.docs/raw/docs/runtimes/langgraph/index.mdx +155 -63
  45. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +12 -4
  46. package/.docs/raw/docs/ui/composer-trigger-popover.mdx +199 -0
  47. package/.docs/raw/docs/ui/directive-text.mdx +113 -0
  48. package/.docs/raw/docs/ui/reasoning.mdx +13 -9
  49. package/dist/utils/logger.js +1 -1
  50. package/dist/utils/logger.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/utils/logger.ts +1 -1
  53. 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 text into the message), slash commands trigger **actions** — the `/command` text is removed from the composer and a callback fires.
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
- Command executed ← User selects command from popover
13
+ Callback fired ← User selects command from popover
14
14
  ↓
15
- "/command" text removed from composer
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. It has two layers:
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
- 1. **Adapter** — provides the list of available commands (flat list by default, or categorized for advanced use)
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
- // Define commands outside the component for a stable reference
34
- const COMMANDS = [
35
+ const SLASH_COMMANDS: readonly Unstable_SlashCommand[] = [
35
36
  {
36
- name: "summarize",
37
+ id: "summarize",
37
38
  description: "Summarize the conversation",
38
39
  execute: () => console.log("Summarize!"),
39
40
  },
40
41
  {
41
- name: "translate",
42
+ id: "translate",
42
43
  description: "Translate text to another language",
43
44
  execute: () => console.log("Translate!"),
44
45
  },
45
46
  {
46
- name: "help",
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 slashAdapter = unstable_useSlashCommandAdapter({
53
- commands: COMMANDS,
54
- });
54
+ const slash = unstable_useSlashCommandAdapter({ commands: SLASH_COMMANDS });
55
55
 
56
56
  return (
57
- <ComposerPrimitive.Unstable_SlashCommandRoot adapter={slashAdapter}>
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.Unstable_TriggerPopoverPopover className="popover">
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.Unstable_TriggerPopoverPopover>
83
+ </ComposerPrimitive.Unstable_TriggerPopover>
79
84
  </ComposerPrimitive.Root>
80
- </ComposerPrimitive.Unstable_SlashCommandRoot>
85
+ </ComposerPrimitive.Unstable_TriggerPopoverRoot>
81
86
  );
82
87
  }
83
88
  ```
84
89
 
85
- ### 2. Handle Command Selection
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
- There are two ways to handle command execution:
92
+ ### 2. Controlling the Chip
88
93
 
89
- **Via `execute` on each item** — define the action inline in the command definition:
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
- ```ts
92
- { name: "summarize", execute: () => runSummarize() }
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
- **Via `onSelect` prop** — handle all commands in one place:
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.Unstable_SlashCommandRoot
99
- adapter={slashAdapter}
100
- onSelect={(item) => {
101
- switch (item.id) {
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
- Both `execute` and `onSelect` fire when a command is selected. Use whichever pattern fits your code.
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), build the adapter manually. Return categories from `categories()` and items from `categoryItems()`. The popover will show categories first, then items within the selected category:
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 { Unstable_SlashCommandAdapter } from "@assistant-ui/core";
123
+ import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
119
124
 
120
- const adapter: Unstable_SlashCommandAdapter = {
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
- <ComposerPrimitive.Unstable_TriggerPopoverPopover>
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.Unstable_TriggerPopoverPopover>
191
+ </ComposerPrimitive.Unstable_TriggerPopover>
175
192
  ```
176
193
 
177
194
  ## Combining with Mentions
178
195
 
179
- Slash commands and mentions can coexist on the same composer. Nest both roots — the [plugin protocol](#trigger-popover-architecture) ensures they don't conflict:
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.Unstable_MentionRoot adapter={mentionAdapter}>
183
- <ComposerPrimitive.Unstable_SlashCommandRoot adapter={slashAdapter}>
184
- <ComposerPrimitive.Root>
185
- <ComposerPrimitive.Input placeholder="Type @ to mention, / for commands..." />
186
- <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>
187
-
188
- {/* Mention popover — shows when @ is typed */}
189
- <ComposerPrimitive.Unstable_MentionPopover>
190
- <ComposerPrimitive.Unstable_MentionCategories>
191
- {(categories) => categories.map((cat) => (
192
- <ComposerPrimitive.Unstable_MentionCategoryItem key={cat.id} categoryId={cat.id}>
193
- {cat.label}
194
- </ComposerPrimitive.Unstable_MentionCategoryItem>
195
- ))}
196
- </ComposerPrimitive.Unstable_MentionCategories>
197
- <ComposerPrimitive.Unstable_MentionItems>
198
- {(items) => items.map((item) => (
199
- <ComposerPrimitive.Unstable_MentionItem key={item.id} item={item}>
200
- {item.label}
201
- </ComposerPrimitive.Unstable_MentionItem>
202
- ))}
203
- </ComposerPrimitive.Unstable_MentionItems>
204
- </ComposerPrimitive.Unstable_MentionPopover>
205
-
206
- {/* Slash command popover — shows when / is typed */}
207
- <ComposerPrimitive.Unstable_TriggerPopoverPopover>
208
- <ComposerPrimitive.Unstable_TriggerPopoverItems>
209
- {(items) => items.map((item, index) => (
210
- <ComposerPrimitive.Unstable_TriggerPopoverItem key={item.id} item={item} index={index}>
211
- {item.label}
212
- </ComposerPrimitive.Unstable_TriggerPopoverItem>
213
- ))}
214
- </ComposerPrimitive.Unstable_TriggerPopoverItems>
215
- </ComposerPrimitive.Unstable_TriggerPopoverPopover>
216
- </ComposerPrimitive.Root>
217
- </ComposerPrimitive.Unstable_SlashCommandRoot>
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 root provides its own `TriggerPopoverContext`. When the user types `@`, the mention popover opens. When they type `/`, the slash command popover opens. Keyboard events route to whichever popover is active.
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` — the generic root, parameterized by trigger character and select behavior
240
- - `ComposerPrimitive.Unstable_MentionRoot` — preset with `trigger="@"` and `onSelect: insertDirective`
241
- - `ComposerPrimitive.Unstable_SlashCommandRoot` — preset with `trigger="/"` and `onSelect: action`
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
- The trigger popover primitives (`TriggerPopoverPopover`, `TriggerPopoverItems`, etc.) are shared across both. You can also use `TriggerPopoverRoot` directly to build custom trigger systems with other characters (e.g. `:` for emoji).
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 trigger root registers a **ComposerInputPlugin** with the composer input. This is a generic protocol that decouples the input from any specific trigger:
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 trigger roots to coexist without conflict.
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
- | `Unstable_SlashCommandRoot` | Convenience wrapper — `TriggerPopoverRoot` with `trigger="/"` and action behavior |
263
- | `Unstable_TriggerPopoverRoot` | Generic root — configurable trigger character and select behavior |
264
- | `Unstable_TriggerPopoverPopover` | Container — only renders when a trigger is active (`role="listbox"`) |
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 |
@@ -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.