@assistant-ui/mcp-docs-server 0.1.9 → 0.1.11
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/with-ai-sdk-v5.md +26 -26
- package/.docs/organized/code-examples/with-assistant-transport.md +29 -29
- package/.docs/organized/code-examples/with-cloud.md +21 -21
- package/.docs/organized/code-examples/with-external-store.md +18 -18
- package/.docs/organized/code-examples/with-ffmpeg.md +22 -22
- package/.docs/organized/code-examples/with-langgraph.md +35 -120
- package/.docs/organized/code-examples/with-parent-id-grouping.md +18 -18
- package/.docs/organized/code-examples/with-react-hook-form.md +27 -27
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +40 -8
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +64 -68
- package/.docs/raw/docs/getting-started.mdx +541 -152
- package/.docs/raw/docs/guides/Attachments.mdx +21 -0
- package/.docs/raw/docs/guides/ToolUI.mdx +112 -37
- package/.docs/raw/docs/guides/Tools.mdx +170 -6
- package/.docs/raw/docs/migrations/react-langgraph-v0-7.mdx +324 -0
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +2 -2
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +218 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +31 -24
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +55 -20
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +8 -3
- package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +1 -1
- package/.docs/raw/docs/ui/AssistantModal.mdx +21 -0
- package/.docs/raw/docs/ui/AssistantSidebar.mdx +21 -0
- package/.docs/raw/docs/ui/Attachment.mdx +21 -0
- package/.docs/raw/docs/ui/Markdown.mdx +22 -1
- package/.docs/raw/docs/ui/Mermaid.mdx +22 -1
- package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +43 -2
- package/.docs/raw/docs/ui/Thread.mdx +374 -5
- package/.docs/raw/docs/ui/ThreadList.mdx +48 -2
- package/.docs/raw/docs/ui/ToolFallback.mdx +21 -0
- package/package.json +7 -7
- package/.docs/raw/docs/migrations/v0-7.mdx +0 -188
- package/.docs/raw/docs/migrations/v0-8.mdx +0 -160
- package/.docs/raw/docs/migrations/v0-9.mdx +0 -75
- package/.docs/raw/docs/ui/primitives/Thread.mdx +0 -197
|
@@ -96,12 +96,16 @@ npm install \
|
|
|
96
96
|
@assistant-ui/react \
|
|
97
97
|
@assistant-ui/react-markdown \
|
|
98
98
|
@assistant-ui/styles \
|
|
99
|
-
@radix-ui/react-
|
|
99
|
+
@radix-ui/react-avatar \
|
|
100
|
+
@radix-ui/react-dialog \
|
|
100
101
|
@radix-ui/react-slot \
|
|
102
|
+
@radix-ui/react-tooltip \
|
|
103
|
+
class-variance-authority \
|
|
104
|
+
clsx \
|
|
101
105
|
lucide-react \
|
|
106
|
+
motion \
|
|
102
107
|
remark-gfm \
|
|
103
|
-
|
|
104
|
-
clsx
|
|
108
|
+
zustand
|
|
105
109
|
```
|
|
106
110
|
|
|
107
111
|
</Step>
|
|
@@ -192,59 +196,75 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
|
192
196
|
```
|
|
193
197
|
|
|
194
198
|
```tsx title="components/assistant-ui/thread.tsx"
|
|
195
|
-
import {
|
|
196
|
-
ActionBarPrimitive,
|
|
197
|
-
BranchPickerPrimitive,
|
|
198
|
-
ComposerPrimitive,
|
|
199
|
-
MessagePrimitive,
|
|
200
|
-
ThreadPrimitive,
|
|
201
|
-
} from "@assistant-ui/react";
|
|
202
|
-
import type { FC } from "react";
|
|
203
199
|
import {
|
|
204
200
|
ArrowDownIcon,
|
|
201
|
+
ArrowUpIcon,
|
|
205
202
|
CheckIcon,
|
|
206
203
|
ChevronLeftIcon,
|
|
207
204
|
ChevronRightIcon,
|
|
208
205
|
CopyIcon,
|
|
209
206
|
PencilIcon,
|
|
210
207
|
RefreshCwIcon,
|
|
211
|
-
|
|
208
|
+
Square,
|
|
212
209
|
} from "lucide-react";
|
|
213
|
-
|
|
210
|
+
|
|
211
|
+
import {
|
|
212
|
+
ActionBarPrimitive,
|
|
213
|
+
BranchPickerPrimitive,
|
|
214
|
+
ComposerPrimitive,
|
|
215
|
+
ErrorPrimitive,
|
|
216
|
+
MessagePrimitive,
|
|
217
|
+
ThreadPrimitive,
|
|
218
|
+
} from "@assistant-ui/react";
|
|
219
|
+
|
|
220
|
+
import type { FC } from "react";
|
|
221
|
+
import { LazyMotion, MotionConfig, domAnimation } from "motion/react";
|
|
222
|
+
import * as m from "motion/react-m";
|
|
214
223
|
|
|
215
224
|
import { Button } from "@/components/ui/button";
|
|
216
225
|
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
226
|
+
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
|
|
217
227
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
228
|
+
import {
|
|
229
|
+
ComposerAddAttachment,
|
|
230
|
+
ComposerAttachments,
|
|
231
|
+
UserMessageAttachments,
|
|
232
|
+
} from "@/components/assistant-ui/attachment";
|
|
233
|
+
|
|
234
|
+
import { cn } from "@/lib/utils";
|
|
218
235
|
|
|
219
236
|
export const Thread: FC = () => {
|
|
220
237
|
return (
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
<ThreadPrimitive.Viewport className="aui-thread-viewport">
|
|
228
|
-
<ThreadWelcome />
|
|
229
|
-
|
|
230
|
-
<ThreadPrimitive.Messages
|
|
231
|
-
components={{
|
|
232
|
-
UserMessage: UserMessage,
|
|
233
|
-
EditComposer: EditComposer,
|
|
234
|
-
AssistantMessage: AssistantMessage,
|
|
238
|
+
<LazyMotion features={domAnimation}>
|
|
239
|
+
<MotionConfig reducedMotion="user">
|
|
240
|
+
<ThreadPrimitive.Root
|
|
241
|
+
className="aui-root aui-thread-root"
|
|
242
|
+
style={{
|
|
243
|
+
["--thread-max-width" as string]: "44rem",
|
|
235
244
|
}}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
>
|
|
246
|
+
<ThreadPrimitive.Viewport className="aui-thread-viewport">
|
|
247
|
+
<ThreadPrimitive.If empty>
|
|
248
|
+
<ThreadWelcome />
|
|
249
|
+
</ThreadPrimitive.If>
|
|
250
|
+
|
|
251
|
+
<ThreadPrimitive.Messages
|
|
252
|
+
components={{
|
|
253
|
+
UserMessage,
|
|
254
|
+
EditComposer,
|
|
255
|
+
AssistantMessage,
|
|
256
|
+
}}
|
|
257
|
+
/>
|
|
258
|
+
|
|
259
|
+
<ThreadPrimitive.If empty={false}>
|
|
260
|
+
<div className="aui-thread-viewport-spacer" />
|
|
261
|
+
</ThreadPrimitive.If>
|
|
262
|
+
|
|
263
|
+
<Composer />
|
|
264
|
+
</ThreadPrimitive.Viewport>
|
|
265
|
+
</ThreadPrimitive.Root>
|
|
266
|
+
</MotionConfig>
|
|
267
|
+
</LazyMotion>
|
|
248
268
|
);
|
|
249
269
|
};
|
|
250
270
|
|
|
@@ -264,146 +284,179 @@ const ThreadScrollToBottom: FC = () => {
|
|
|
264
284
|
|
|
265
285
|
const ThreadWelcome: FC = () => {
|
|
266
286
|
return (
|
|
267
|
-
<
|
|
268
|
-
<div className="aui-thread-welcome-
|
|
269
|
-
<div className="aui-thread-welcome-
|
|
270
|
-
<
|
|
287
|
+
<div className="aui-thread-welcome-root">
|
|
288
|
+
<div className="aui-thread-welcome-center">
|
|
289
|
+
<div className="aui-thread-welcome-message">
|
|
290
|
+
<m.div
|
|
291
|
+
initial={{ opacity: 0, y: 10 }}
|
|
292
|
+
animate={{ opacity: 1, y: 0 }}
|
|
293
|
+
exit={{ opacity: 0, y: 10 }}
|
|
294
|
+
className="aui-thread-welcome-message-motion-1"
|
|
295
|
+
>
|
|
296
|
+
Hello there!
|
|
297
|
+
</m.div>
|
|
298
|
+
<m.div
|
|
299
|
+
initial={{ opacity: 0, y: 10 }}
|
|
300
|
+
animate={{ opacity: 1, y: 0 }}
|
|
301
|
+
exit={{ opacity: 0, y: 10 }}
|
|
302
|
+
transition={{ delay: 0.1 }}
|
|
303
|
+
className="aui-thread-welcome-message-motion-2"
|
|
304
|
+
>
|
|
271
305
|
How can I help you today?
|
|
272
|
-
</
|
|
306
|
+
</m.div>
|
|
273
307
|
</div>
|
|
274
|
-
<ThreadWelcomeSuggestions />
|
|
275
308
|
</div>
|
|
276
|
-
|
|
309
|
+
<ThreadSuggestions />
|
|
310
|
+
</div>
|
|
277
311
|
);
|
|
278
312
|
};
|
|
279
313
|
|
|
280
|
-
const
|
|
314
|
+
const ThreadSuggestions: FC = () => {
|
|
281
315
|
return (
|
|
282
316
|
<div className="aui-thread-welcome-suggestions">
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
317
|
+
{[
|
|
318
|
+
{
|
|
319
|
+
title: "What's the weather",
|
|
320
|
+
label: "in San Francisco?",
|
|
321
|
+
action: "What's the weather in San Francisco?",
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
title: "Explain React hooks",
|
|
325
|
+
label: "like useState and useEffect",
|
|
326
|
+
action: "Explain React hooks like useState and useEffect",
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
title: "Write a SQL query",
|
|
330
|
+
label: "to find top customers",
|
|
331
|
+
action: "Write a SQL query to find top customers",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
title: "Create a meal plan",
|
|
335
|
+
label: "for healthy weight loss",
|
|
336
|
+
action: "Create a meal plan for healthy weight loss",
|
|
337
|
+
},
|
|
338
|
+
].map((suggestedAction, index) => (
|
|
339
|
+
<m.div
|
|
340
|
+
initial={{ opacity: 0, y: 20 }}
|
|
341
|
+
animate={{ opacity: 1, y: 0 }}
|
|
342
|
+
exit={{ opacity: 0, y: 20 }}
|
|
343
|
+
transition={{ delay: 0.05 * index }}
|
|
344
|
+
key={`suggested-action-${suggestedAction.title}-${index}`}
|
|
345
|
+
className="aui-thread-welcome-suggestion-display"
|
|
346
|
+
>
|
|
347
|
+
<ThreadPrimitive.Suggestion
|
|
348
|
+
prompt={suggestedAction.action}
|
|
349
|
+
send
|
|
350
|
+
asChild
|
|
351
|
+
>
|
|
352
|
+
<Button
|
|
353
|
+
variant="ghost"
|
|
354
|
+
className="aui-thread-welcome-suggestion"
|
|
355
|
+
aria-label={suggestedAction.action}
|
|
356
|
+
>
|
|
357
|
+
<span className="aui-thread-welcome-suggestion-text-1">
|
|
358
|
+
{suggestedAction.title}
|
|
359
|
+
</span>
|
|
360
|
+
<span className="aui-thread-welcome-suggestion-text-2">
|
|
361
|
+
{suggestedAction.label}
|
|
362
|
+
</span>
|
|
363
|
+
</Button>
|
|
364
|
+
</ThreadPrimitive.Suggestion>
|
|
365
|
+
</m.div>
|
|
366
|
+
))}
|
|
303
367
|
</div>
|
|
304
368
|
);
|
|
305
369
|
};
|
|
306
370
|
|
|
307
371
|
const Composer: FC = () => {
|
|
308
372
|
return (
|
|
309
|
-
<
|
|
310
|
-
<
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
373
|
+
<div className="aui-composer-wrapper">
|
|
374
|
+
<ThreadScrollToBottom />
|
|
375
|
+
<ComposerPrimitive.Root className="aui-composer-root">
|
|
376
|
+
<ComposerAttachments />
|
|
377
|
+
<ComposerPrimitive.Input
|
|
378
|
+
placeholder="Send a message..."
|
|
379
|
+
className="aui-composer-input"
|
|
380
|
+
rows={1}
|
|
381
|
+
autoFocus
|
|
382
|
+
aria-label="Message input"
|
|
383
|
+
/>
|
|
384
|
+
<ComposerAction />
|
|
385
|
+
</ComposerPrimitive.Root>
|
|
386
|
+
</div>
|
|
318
387
|
);
|
|
319
388
|
};
|
|
320
389
|
|
|
321
390
|
const ComposerAction: FC = () => {
|
|
322
391
|
return (
|
|
323
|
-
|
|
392
|
+
<div className="aui-composer-action-wrapper">
|
|
393
|
+
<ComposerAddAttachment />
|
|
394
|
+
|
|
324
395
|
<ThreadPrimitive.If running={false}>
|
|
325
396
|
<ComposerPrimitive.Send asChild>
|
|
326
397
|
<TooltipIconButton
|
|
327
|
-
tooltip="Send"
|
|
398
|
+
tooltip="Send message"
|
|
399
|
+
side="bottom"
|
|
400
|
+
type="submit"
|
|
328
401
|
variant="default"
|
|
402
|
+
size="icon"
|
|
329
403
|
className="aui-composer-send"
|
|
404
|
+
aria-label="Send message"
|
|
330
405
|
>
|
|
331
|
-
<
|
|
406
|
+
<ArrowUpIcon className="aui-composer-send-icon" />
|
|
332
407
|
</TooltipIconButton>
|
|
333
408
|
</ComposerPrimitive.Send>
|
|
334
409
|
</ThreadPrimitive.If>
|
|
410
|
+
|
|
335
411
|
<ThreadPrimitive.If running>
|
|
336
412
|
<ComposerPrimitive.Cancel asChild>
|
|
337
|
-
<
|
|
338
|
-
|
|
413
|
+
<Button
|
|
414
|
+
type="button"
|
|
339
415
|
variant="default"
|
|
416
|
+
size="icon"
|
|
340
417
|
className="aui-composer-cancel"
|
|
418
|
+
aria-label="Stop generating"
|
|
341
419
|
>
|
|
342
|
-
<
|
|
343
|
-
</
|
|
420
|
+
<Square className="aui-composer-cancel-icon" />
|
|
421
|
+
</Button>
|
|
344
422
|
</ComposerPrimitive.Cancel>
|
|
345
423
|
</ThreadPrimitive.If>
|
|
346
|
-
|
|
347
|
-
);
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
const UserMessage: FC = () => {
|
|
351
|
-
return (
|
|
352
|
-
<MessagePrimitive.Root className="aui-user-message-root">
|
|
353
|
-
<UserActionBar />
|
|
354
|
-
|
|
355
|
-
<div className="aui-user-message-content">
|
|
356
|
-
<MessagePrimitive.Parts />
|
|
357
|
-
</div>
|
|
358
|
-
|
|
359
|
-
<BranchPicker className="aui-user-branch-picker" />
|
|
360
|
-
</MessagePrimitive.Root>
|
|
361
|
-
);
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
const UserActionBar: FC = () => {
|
|
365
|
-
return (
|
|
366
|
-
<ActionBarPrimitive.Root
|
|
367
|
-
hideWhenRunning
|
|
368
|
-
autohide="not-last"
|
|
369
|
-
className="aui-user-action-bar-root"
|
|
370
|
-
>
|
|
371
|
-
<ActionBarPrimitive.Edit asChild>
|
|
372
|
-
<TooltipIconButton tooltip="Edit">
|
|
373
|
-
<PencilIcon />
|
|
374
|
-
</TooltipIconButton>
|
|
375
|
-
</ActionBarPrimitive.Edit>
|
|
376
|
-
</ActionBarPrimitive.Root>
|
|
424
|
+
</div>
|
|
377
425
|
);
|
|
378
426
|
};
|
|
379
427
|
|
|
380
|
-
const
|
|
428
|
+
const MessageError: FC = () => {
|
|
381
429
|
return (
|
|
382
|
-
<
|
|
383
|
-
<
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
<Button variant="ghost">Cancel</Button>
|
|
388
|
-
</ComposerPrimitive.Cancel>
|
|
389
|
-
<ComposerPrimitive.Send asChild>
|
|
390
|
-
<Button>Send</Button>
|
|
391
|
-
</ComposerPrimitive.Send>
|
|
392
|
-
</div>
|
|
393
|
-
</ComposerPrimitive.Root>
|
|
430
|
+
<MessagePrimitive.Error>
|
|
431
|
+
<ErrorPrimitive.Root className="aui-message-error-root">
|
|
432
|
+
<ErrorPrimitive.Message className="aui-message-error-message" />
|
|
433
|
+
</ErrorPrimitive.Root>
|
|
434
|
+
</MessagePrimitive.Error>
|
|
394
435
|
);
|
|
395
436
|
};
|
|
396
437
|
|
|
397
438
|
const AssistantMessage: FC = () => {
|
|
398
439
|
return (
|
|
399
|
-
<MessagePrimitive.Root
|
|
400
|
-
<div
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
440
|
+
<MessagePrimitive.Root asChild>
|
|
441
|
+
<div
|
|
442
|
+
className="aui-assistant-message-root"
|
|
443
|
+
data-role="assistant"
|
|
444
|
+
>
|
|
445
|
+
<div className="aui-assistant-message-content">
|
|
446
|
+
<MessagePrimitive.Parts
|
|
447
|
+
components={{
|
|
448
|
+
Text: MarkdownText,
|
|
449
|
+
tools: { Fallback: ToolFallback },
|
|
450
|
+
}}
|
|
451
|
+
/>
|
|
452
|
+
<MessageError />
|
|
453
|
+
</div>
|
|
405
454
|
|
|
406
|
-
|
|
455
|
+
<div className="aui-assistant-message-footer">
|
|
456
|
+
<BranchPicker />
|
|
457
|
+
<AssistantActionBar />
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
407
460
|
</MessagePrimitive.Root>
|
|
408
461
|
);
|
|
409
462
|
};
|
|
@@ -435,6 +488,72 @@ const AssistantActionBar: FC = () => {
|
|
|
435
488
|
);
|
|
436
489
|
};
|
|
437
490
|
|
|
491
|
+
const UserMessage: FC = () => {
|
|
492
|
+
return (
|
|
493
|
+
<MessagePrimitive.Root asChild>
|
|
494
|
+
<div
|
|
495
|
+
className="aui-user-message-root"
|
|
496
|
+
data-role="user"
|
|
497
|
+
>
|
|
498
|
+
<UserMessageAttachments />
|
|
499
|
+
|
|
500
|
+
<div className="aui-user-message-content-wrapper">
|
|
501
|
+
<div className="aui-user-message-content">
|
|
502
|
+
<MessagePrimitive.Parts />
|
|
503
|
+
</div>
|
|
504
|
+
<div className="aui-user-action-bar-wrapper">
|
|
505
|
+
<UserActionBar />
|
|
506
|
+
</div>
|
|
507
|
+
</div>
|
|
508
|
+
|
|
509
|
+
<BranchPicker className="aui-user-branch-picker" />
|
|
510
|
+
</div>
|
|
511
|
+
</MessagePrimitive.Root>
|
|
512
|
+
);
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const UserActionBar: FC = () => {
|
|
516
|
+
return (
|
|
517
|
+
<ActionBarPrimitive.Root
|
|
518
|
+
hideWhenRunning
|
|
519
|
+
autohide="not-last"
|
|
520
|
+
className="aui-user-action-bar-root"
|
|
521
|
+
>
|
|
522
|
+
<ActionBarPrimitive.Edit asChild>
|
|
523
|
+
<TooltipIconButton tooltip="Edit" className="aui-user-action-edit">
|
|
524
|
+
<PencilIcon />
|
|
525
|
+
</TooltipIconButton>
|
|
526
|
+
</ActionBarPrimitive.Edit>
|
|
527
|
+
</ActionBarPrimitive.Root>
|
|
528
|
+
);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const EditComposer: FC = () => {
|
|
532
|
+
return (
|
|
533
|
+
<div className="aui-edit-composer-wrapper">
|
|
534
|
+
<ComposerPrimitive.Root className="aui-edit-composer-root">
|
|
535
|
+
<ComposerPrimitive.Input
|
|
536
|
+
className="aui-edit-composer-input"
|
|
537
|
+
autoFocus
|
|
538
|
+
/>
|
|
539
|
+
|
|
540
|
+
<div className="aui-edit-composer-footer">
|
|
541
|
+
<ComposerPrimitive.Cancel asChild>
|
|
542
|
+
<Button variant="ghost" size="sm" aria-label="Cancel edit">
|
|
543
|
+
Cancel
|
|
544
|
+
</Button>
|
|
545
|
+
</ComposerPrimitive.Cancel>
|
|
546
|
+
<ComposerPrimitive.Send asChild>
|
|
547
|
+
<Button size="sm" aria-label="Update message">
|
|
548
|
+
Update
|
|
549
|
+
</Button>
|
|
550
|
+
</ComposerPrimitive.Send>
|
|
551
|
+
</div>
|
|
552
|
+
</ComposerPrimitive.Root>
|
|
553
|
+
</div>
|
|
554
|
+
);
|
|
555
|
+
};
|
|
556
|
+
|
|
438
557
|
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
439
558
|
className,
|
|
440
559
|
...rest
|
|
@@ -461,20 +580,290 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
|
461
580
|
</BranchPickerPrimitive.Root>
|
|
462
581
|
);
|
|
463
582
|
};
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
```tsx title="components/assistant-ui/attachment.tsx"
|
|
586
|
+
"use client";
|
|
587
|
+
|
|
588
|
+
import { PropsWithChildren, useEffect, useState, type FC } from "react";
|
|
589
|
+
import Image from "next/image";
|
|
590
|
+
import { XIcon, PlusIcon, FileText } from "lucide-react";
|
|
591
|
+
import {
|
|
592
|
+
AttachmentPrimitive,
|
|
593
|
+
ComposerPrimitive,
|
|
594
|
+
MessagePrimitive,
|
|
595
|
+
useAssistantState,
|
|
596
|
+
useAssistantApi,
|
|
597
|
+
} from "@assistant-ui/react";
|
|
598
|
+
import { useShallow } from "zustand/shallow";
|
|
599
|
+
import {
|
|
600
|
+
Tooltip,
|
|
601
|
+
TooltipContent,
|
|
602
|
+
TooltipTrigger,
|
|
603
|
+
} from "@/components/ui/tooltip";
|
|
604
|
+
import {
|
|
605
|
+
Dialog,
|
|
606
|
+
DialogTitle,
|
|
607
|
+
DialogContent,
|
|
608
|
+
DialogTrigger,
|
|
609
|
+
} from "@/components/ui/dialog";
|
|
610
|
+
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
|
611
|
+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
612
|
+
import { cn } from "@/lib/utils";
|
|
613
|
+
|
|
614
|
+
const useFileSrc = (file: File | undefined) => {
|
|
615
|
+
const [src, setSrc] = useState<string | undefined>(undefined);
|
|
616
|
+
|
|
617
|
+
useEffect(() => {
|
|
618
|
+
if (!file) {
|
|
619
|
+
setSrc(undefined);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const objectUrl = URL.createObjectURL(file);
|
|
624
|
+
setSrc(objectUrl);
|
|
625
|
+
|
|
626
|
+
return () => {
|
|
627
|
+
URL.revokeObjectURL(objectUrl);
|
|
628
|
+
};
|
|
629
|
+
}, [file]);
|
|
630
|
+
|
|
631
|
+
return src;
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const useAttachmentSrc = () => {
|
|
635
|
+
const { file, src } = useAssistantState(
|
|
636
|
+
useShallow(({ attachment }): { file?: File; src?: string } => {
|
|
637
|
+
if (attachment.type !== "image") return {};
|
|
638
|
+
if (attachment.file) return { file: attachment.file };
|
|
639
|
+
const src = attachment.content?.filter((c) => c.type === "image")[0]
|
|
640
|
+
?.image;
|
|
641
|
+
if (!src) return {};
|
|
642
|
+
return { src };
|
|
643
|
+
}),
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
return useFileSrc(file) ?? src;
|
|
647
|
+
};
|
|
648
|
+
|
|
649
|
+
type AttachmentPreviewProps = {
|
|
650
|
+
src: string;
|
|
651
|
+
};
|
|
464
652
|
|
|
465
|
-
const
|
|
653
|
+
const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
|
|
654
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
466
655
|
return (
|
|
467
|
-
<
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
656
|
+
<Image
|
|
657
|
+
src={src}
|
|
658
|
+
alt="Image Preview"
|
|
659
|
+
width={1}
|
|
660
|
+
height={1}
|
|
661
|
+
className={
|
|
662
|
+
isLoaded
|
|
663
|
+
? "aui-attachment-preview-image-loaded"
|
|
664
|
+
: "aui-attachment-preview-image-loading"
|
|
665
|
+
}
|
|
666
|
+
onLoadingComplete={() => setIsLoaded(true)}
|
|
667
|
+
priority={false}
|
|
668
|
+
/>
|
|
669
|
+
);
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
|
|
673
|
+
const src = useAttachmentSrc();
|
|
674
|
+
|
|
675
|
+
if (!src) return children;
|
|
676
|
+
|
|
677
|
+
return (
|
|
678
|
+
<Dialog>
|
|
679
|
+
<DialogTrigger className="aui-attachment-preview-trigger" asChild>
|
|
680
|
+
{children}
|
|
681
|
+
</DialogTrigger>
|
|
682
|
+
<DialogContent className="aui-attachment-preview-dialog-content">
|
|
683
|
+
<DialogTitle className="aui-sr-only">
|
|
684
|
+
Image Attachment Preview
|
|
685
|
+
</DialogTitle>
|
|
686
|
+
<div className="aui-attachment-preview">
|
|
687
|
+
<AttachmentPreview src={src} />
|
|
688
|
+
</div>
|
|
689
|
+
</DialogContent>
|
|
690
|
+
</Dialog>
|
|
691
|
+
);
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const AttachmentThumb: FC = () => {
|
|
695
|
+
const isImage = useAssistantState(
|
|
696
|
+
({ attachment }) => attachment.type === "image",
|
|
697
|
+
);
|
|
698
|
+
const src = useAttachmentSrc();
|
|
699
|
+
|
|
700
|
+
return (
|
|
701
|
+
<Avatar className="aui-attachment-tile-avatar">
|
|
702
|
+
<AvatarImage
|
|
703
|
+
src={src}
|
|
704
|
+
alt="Attachment preview"
|
|
705
|
+
className="aui-attachment-tile-image"
|
|
706
|
+
/>
|
|
707
|
+
<AvatarFallback delayMs={isImage ? 200 : 0}>
|
|
708
|
+
<FileText className="aui-attachment-tile-fallback-icon" />
|
|
709
|
+
</AvatarFallback>
|
|
710
|
+
</Avatar>
|
|
476
711
|
);
|
|
477
712
|
};
|
|
713
|
+
|
|
714
|
+
const AttachmentUI: FC = () => {
|
|
715
|
+
const api = useAssistantApi();
|
|
716
|
+
const isComposer = api.attachment.source === "composer";
|
|
717
|
+
|
|
718
|
+
const isImage = useAssistantState(
|
|
719
|
+
({ attachment }) => attachment.type === "image",
|
|
720
|
+
);
|
|
721
|
+
const typeLabel = useAssistantState(({ attachment }) => {
|
|
722
|
+
const type = attachment.type;
|
|
723
|
+
switch (type) {
|
|
724
|
+
case "image":
|
|
725
|
+
return "Image";
|
|
726
|
+
case "document":
|
|
727
|
+
return "Document";
|
|
728
|
+
case "file":
|
|
729
|
+
return "File";
|
|
730
|
+
default:
|
|
731
|
+
const _exhaustiveCheck: never = type;
|
|
732
|
+
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
<Tooltip>
|
|
738
|
+
<AttachmentPrimitive.Root
|
|
739
|
+
className={cn(
|
|
740
|
+
"aui-attachment-root",
|
|
741
|
+
isImage && "aui-attachment-root-composer",
|
|
742
|
+
)}
|
|
743
|
+
>
|
|
744
|
+
<AttachmentPreviewDialog>
|
|
745
|
+
<TooltipTrigger asChild>
|
|
746
|
+
<div
|
|
747
|
+
className={cn(
|
|
748
|
+
"aui-attachment-tile",
|
|
749
|
+
isComposer && "aui-attachment-tile-composer",
|
|
750
|
+
)}
|
|
751
|
+
role="button"
|
|
752
|
+
id="attachment-tile"
|
|
753
|
+
aria-label={`${typeLabel} attachment`}
|
|
754
|
+
>
|
|
755
|
+
<AttachmentThumb />
|
|
756
|
+
</div>
|
|
757
|
+
</TooltipTrigger>
|
|
758
|
+
</AttachmentPreviewDialog>
|
|
759
|
+
{isComposer && <AttachmentRemove />}
|
|
760
|
+
</AttachmentPrimitive.Root>
|
|
761
|
+
<TooltipContent side="top">
|
|
762
|
+
<AttachmentPrimitive.Name />
|
|
763
|
+
</TooltipContent>
|
|
764
|
+
</Tooltip>
|
|
765
|
+
);
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const AttachmentRemove: FC = () => {
|
|
769
|
+
return (
|
|
770
|
+
<AttachmentPrimitive.Remove asChild>
|
|
771
|
+
<TooltipIconButton
|
|
772
|
+
tooltip="Remove file"
|
|
773
|
+
className="aui-attachment-tile-remove"
|
|
774
|
+
side="top"
|
|
775
|
+
>
|
|
776
|
+
<XIcon className="aui-attachment-remove-icon" />
|
|
777
|
+
</TooltipIconButton>
|
|
778
|
+
</AttachmentPrimitive.Remove>
|
|
779
|
+
);
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
export const UserMessageAttachments: FC = () => {
|
|
783
|
+
return (
|
|
784
|
+
<div className="aui-user-message-attachments-end">
|
|
785
|
+
<MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
|
|
786
|
+
</div>
|
|
787
|
+
);
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
export const ComposerAttachments: FC = () => {
|
|
791
|
+
return (
|
|
792
|
+
<div className="aui-composer-attachments">
|
|
793
|
+
<ComposerPrimitive.Attachments
|
|
794
|
+
components={{ Attachment: AttachmentUI }}
|
|
795
|
+
/>
|
|
796
|
+
</div>
|
|
797
|
+
);
|
|
798
|
+
};
|
|
799
|
+
|
|
800
|
+
export const ComposerAddAttachment: FC = () => {
|
|
801
|
+
return (
|
|
802
|
+
<ComposerPrimitive.AddAttachment asChild>
|
|
803
|
+
<TooltipIconButton
|
|
804
|
+
tooltip="Add Attachment"
|
|
805
|
+
side="bottom"
|
|
806
|
+
variant="ghost"
|
|
807
|
+
size="icon"
|
|
808
|
+
className="aui-composer-add-attachment"
|
|
809
|
+
aria-label="Add Attachment"
|
|
810
|
+
>
|
|
811
|
+
<PlusIcon className="aui-attachment-add-icon" />
|
|
812
|
+
</TooltipIconButton>
|
|
813
|
+
</ComposerPrimitive.AddAttachment>
|
|
814
|
+
);
|
|
815
|
+
};
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
```tsx title="components/assistant-ui/tool-fallback.tsx"
|
|
819
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
820
|
+
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|
821
|
+
import { useState } from "react";
|
|
822
|
+
import { Button } from "@/components/ui/button";
|
|
823
|
+
|
|
824
|
+
export const ToolFallback: ToolCallMessagePartComponent = ({
|
|
825
|
+
toolName,
|
|
826
|
+
argsText,
|
|
827
|
+
result,
|
|
828
|
+
}) => {
|
|
829
|
+
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
830
|
+
return (
|
|
831
|
+
<div className="aui-tool-fallback-root">
|
|
832
|
+
<div className="aui-tool-fallback-header">
|
|
833
|
+
<CheckIcon className="aui-tool-fallback-icon" />
|
|
834
|
+
<p className="aui-tool-fallback-title">
|
|
835
|
+
Used tool: <b>{toolName}</b>
|
|
836
|
+
</p>
|
|
837
|
+
<Button onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
838
|
+
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
839
|
+
</Button>
|
|
840
|
+
</div>
|
|
841
|
+
{!isCollapsed && (
|
|
842
|
+
<div className="aui-tool-fallback-content">
|
|
843
|
+
<div className="aui-tool-fallback-args-root">
|
|
844
|
+
<pre className="aui-tool-fallback-args-value">
|
|
845
|
+
{argsText}
|
|
846
|
+
</pre>
|
|
847
|
+
</div>
|
|
848
|
+
{result !== undefined && (
|
|
849
|
+
<div className="aui-tool-fallback-result-root">
|
|
850
|
+
<p className="aui-tool-fallback-result-header">
|
|
851
|
+
Result:
|
|
852
|
+
</p>
|
|
853
|
+
<pre className="aui-tool-fallback-result-content">
|
|
854
|
+
{typeof result === "string"
|
|
855
|
+
? result
|
|
856
|
+
: JSON.stringify(result, null, 2)}
|
|
857
|
+
</pre>
|
|
858
|
+
</div>
|
|
859
|
+
)}
|
|
860
|
+
</div>
|
|
861
|
+
)}
|
|
862
|
+
</div>
|
|
863
|
+
);
|
|
864
|
+
};
|
|
865
|
+
```
|
|
866
|
+
|
|
478
867
|
```
|
|
479
868
|
|
|
480
869
|
```tsx title="components/assistant-ui/thread-list.tsx"
|
|
@@ -525,9 +914,9 @@ const ThreadListItem: FC = () => {
|
|
|
525
914
|
|
|
526
915
|
const ThreadListItemTitle: FC = () => {
|
|
527
916
|
return (
|
|
528
|
-
<
|
|
917
|
+
<span className="aui-thread-list-item-title">
|
|
529
918
|
<ThreadListItemPrimitive.Title fallback="New Chat" />
|
|
530
|
-
</
|
|
919
|
+
</span>
|
|
531
920
|
);
|
|
532
921
|
};
|
|
533
922
|
|
|
@@ -552,13 +941,13 @@ const ThreadListItemArchive: FC = () => {
|
|
|
552
941
|
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
553
942
|
|
|
554
943
|
import {
|
|
555
|
-
CodeHeaderProps,
|
|
944
|
+
type CodeHeaderProps,
|
|
556
945
|
MarkdownTextPrimitive,
|
|
557
946
|
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
558
947
|
useIsMarkdownCodeBlock,
|
|
559
948
|
} from "@assistant-ui/react-markdown";
|
|
560
949
|
import remarkGfm from "remark-gfm";
|
|
561
|
-
import { FC, memo, useState } from "react";
|
|
950
|
+
import { type FC, memo, useState } from "react";
|
|
562
951
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
563
952
|
|
|
564
953
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
@@ -684,7 +1073,8 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
684
1073
|
```tsx title="components/assistant-ui/tooltip-icon-button.tsx"
|
|
685
1074
|
"use client";
|
|
686
1075
|
|
|
687
|
-
import {
|
|
1076
|
+
import { ComponentPropsWithRef, forwardRef } from "react";
|
|
1077
|
+
import { Slottable } from "@radix-ui/react-slot";
|
|
688
1078
|
|
|
689
1079
|
import {
|
|
690
1080
|
Tooltip,
|
|
@@ -693,9 +1083,8 @@ import {
|
|
|
693
1083
|
} from "@/components/ui/tooltip";
|
|
694
1084
|
import { Button } from "@/components/ui/button";
|
|
695
1085
|
import { cn } from "@/lib/utils";
|
|
696
|
-
import { Slottable } from "@radix-ui/react-slot";
|
|
697
1086
|
|
|
698
|
-
export type TooltipIconButtonProps =
|
|
1087
|
+
export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
|
|
699
1088
|
tooltip: string;
|
|
700
1089
|
side?: "top" | "bottom" | "left" | "right";
|
|
701
1090
|
};
|
|
@@ -1075,7 +1464,7 @@ const MyApp = () => {
|
|
|
1075
1464
|
|
|
1076
1465
|
return (
|
|
1077
1466
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
1078
|
-
<div
|
|
1467
|
+
<div>
|
|
1079
1468
|
<ThreadList />
|
|
1080
1469
|
<Thread />
|
|
1081
1470
|
</div>
|