@assistant-ui/mcp-docs-server 0.1.10 → 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 +21 -21
- package/.docs/organized/code-examples/with-assistant-transport.md +24 -24
- package/.docs/organized/code-examples/with-cloud.md +18 -18
- package/.docs/organized/code-examples/with-external-store.md +15 -15
- package/.docs/organized/code-examples/with-ffmpeg.md +18 -18
- package/.docs/organized/code-examples/with-langgraph.md +18 -18
- package/.docs/organized/code-examples/with-parent-id-grouping.md +15 -15
- package/.docs/organized/code-examples/with-react-hook-form.md +23 -23
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +22 -2
- package/.docs/raw/docs/getting-started.mdx +541 -150
- package/.docs/raw/docs/guides/Attachments.mdx +21 -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/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 +9 -3
- package/.docs/raw/docs/ui/ThreadList.mdx +48 -2
- package/.docs/raw/docs/ui/ToolFallback.mdx +21 -0
- package/package.json +5 -5
|
@@ -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,144 +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
|
-
|
|
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
|
+
))}
|
|
301
367
|
</div>
|
|
302
368
|
);
|
|
303
369
|
};
|
|
304
370
|
|
|
305
371
|
const Composer: FC = () => {
|
|
306
372
|
return (
|
|
307
|
-
<
|
|
308
|
-
<
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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>
|
|
316
387
|
);
|
|
317
388
|
};
|
|
318
389
|
|
|
319
390
|
const ComposerAction: FC = () => {
|
|
320
391
|
return (
|
|
321
|
-
|
|
392
|
+
<div className="aui-composer-action-wrapper">
|
|
393
|
+
<ComposerAddAttachment />
|
|
394
|
+
|
|
322
395
|
<ThreadPrimitive.If running={false}>
|
|
323
396
|
<ComposerPrimitive.Send asChild>
|
|
324
397
|
<TooltipIconButton
|
|
325
|
-
tooltip="Send"
|
|
398
|
+
tooltip="Send message"
|
|
399
|
+
side="bottom"
|
|
400
|
+
type="submit"
|
|
326
401
|
variant="default"
|
|
402
|
+
size="icon"
|
|
327
403
|
className="aui-composer-send"
|
|
404
|
+
aria-label="Send message"
|
|
328
405
|
>
|
|
329
|
-
<
|
|
406
|
+
<ArrowUpIcon className="aui-composer-send-icon" />
|
|
330
407
|
</TooltipIconButton>
|
|
331
408
|
</ComposerPrimitive.Send>
|
|
332
409
|
</ThreadPrimitive.If>
|
|
410
|
+
|
|
333
411
|
<ThreadPrimitive.If running>
|
|
334
412
|
<ComposerPrimitive.Cancel asChild>
|
|
335
|
-
<
|
|
336
|
-
|
|
413
|
+
<Button
|
|
414
|
+
type="button"
|
|
337
415
|
variant="default"
|
|
416
|
+
size="icon"
|
|
338
417
|
className="aui-composer-cancel"
|
|
418
|
+
aria-label="Stop generating"
|
|
339
419
|
>
|
|
340
|
-
<
|
|
341
|
-
</
|
|
420
|
+
<Square className="aui-composer-cancel-icon" />
|
|
421
|
+
</Button>
|
|
342
422
|
</ComposerPrimitive.Cancel>
|
|
343
423
|
</ThreadPrimitive.If>
|
|
344
|
-
|
|
345
|
-
);
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
const UserMessage: FC = () => {
|
|
349
|
-
return (
|
|
350
|
-
<MessagePrimitive.Root className="aui-user-message-root">
|
|
351
|
-
<UserActionBar />
|
|
352
|
-
|
|
353
|
-
<div className="aui-user-message-content">
|
|
354
|
-
<MessagePrimitive.Parts />
|
|
355
|
-
</div>
|
|
356
|
-
|
|
357
|
-
<BranchPicker className="aui-user-branch-picker" />
|
|
358
|
-
</MessagePrimitive.Root>
|
|
359
|
-
);
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const UserActionBar: FC = () => {
|
|
363
|
-
return (
|
|
364
|
-
<ActionBarPrimitive.Root
|
|
365
|
-
hideWhenRunning
|
|
366
|
-
autohide="not-last"
|
|
367
|
-
className="aui-user-action-bar-root"
|
|
368
|
-
>
|
|
369
|
-
<ActionBarPrimitive.Edit asChild>
|
|
370
|
-
<TooltipIconButton tooltip="Edit">
|
|
371
|
-
<PencilIcon />
|
|
372
|
-
</TooltipIconButton>
|
|
373
|
-
</ActionBarPrimitive.Edit>
|
|
374
|
-
</ActionBarPrimitive.Root>
|
|
424
|
+
</div>
|
|
375
425
|
);
|
|
376
426
|
};
|
|
377
427
|
|
|
378
|
-
const
|
|
428
|
+
const MessageError: FC = () => {
|
|
379
429
|
return (
|
|
380
|
-
<
|
|
381
|
-
<
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
<Button variant="ghost">Cancel</Button>
|
|
386
|
-
</ComposerPrimitive.Cancel>
|
|
387
|
-
<ComposerPrimitive.Send asChild>
|
|
388
|
-
<Button>Send</Button>
|
|
389
|
-
</ComposerPrimitive.Send>
|
|
390
|
-
</div>
|
|
391
|
-
</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>
|
|
392
435
|
);
|
|
393
436
|
};
|
|
394
437
|
|
|
395
438
|
const AssistantMessage: FC = () => {
|
|
396
439
|
return (
|
|
397
|
-
<MessagePrimitive.Root
|
|
398
|
-
<div
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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>
|
|
403
454
|
|
|
404
|
-
|
|
455
|
+
<div className="aui-assistant-message-footer">
|
|
456
|
+
<BranchPicker />
|
|
457
|
+
<AssistantActionBar />
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
405
460
|
</MessagePrimitive.Root>
|
|
406
461
|
);
|
|
407
462
|
};
|
|
@@ -433,6 +488,72 @@ const AssistantActionBar: FC = () => {
|
|
|
433
488
|
);
|
|
434
489
|
};
|
|
435
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
|
+
|
|
436
557
|
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
437
558
|
className,
|
|
438
559
|
...rest
|
|
@@ -459,20 +580,290 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
|
459
580
|
</BranchPickerPrimitive.Root>
|
|
460
581
|
);
|
|
461
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
|
+
};
|
|
462
652
|
|
|
463
|
-
const
|
|
653
|
+
const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
|
|
654
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
464
655
|
return (
|
|
465
|
-
<
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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>
|
|
474
711
|
);
|
|
475
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
|
+
|
|
476
867
|
```
|
|
477
868
|
|
|
478
869
|
```tsx title="components/assistant-ui/thread-list.tsx"
|
|
@@ -523,9 +914,9 @@ const ThreadListItem: FC = () => {
|
|
|
523
914
|
|
|
524
915
|
const ThreadListItemTitle: FC = () => {
|
|
525
916
|
return (
|
|
526
|
-
<
|
|
917
|
+
<span className="aui-thread-list-item-title">
|
|
527
918
|
<ThreadListItemPrimitive.Title fallback="New Chat" />
|
|
528
|
-
</
|
|
919
|
+
</span>
|
|
529
920
|
);
|
|
530
921
|
};
|
|
531
922
|
|
|
@@ -550,13 +941,13 @@ const ThreadListItemArchive: FC = () => {
|
|
|
550
941
|
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
551
942
|
|
|
552
943
|
import {
|
|
553
|
-
CodeHeaderProps,
|
|
944
|
+
type CodeHeaderProps,
|
|
554
945
|
MarkdownTextPrimitive,
|
|
555
946
|
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
556
947
|
useIsMarkdownCodeBlock,
|
|
557
948
|
} from "@assistant-ui/react-markdown";
|
|
558
949
|
import remarkGfm from "remark-gfm";
|
|
559
|
-
import { FC, memo, useState } from "react";
|
|
950
|
+
import { type FC, memo, useState } from "react";
|
|
560
951
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
561
952
|
|
|
562
953
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
@@ -682,7 +1073,8 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
682
1073
|
```tsx title="components/assistant-ui/tooltip-icon-button.tsx"
|
|
683
1074
|
"use client";
|
|
684
1075
|
|
|
685
|
-
import {
|
|
1076
|
+
import { ComponentPropsWithRef, forwardRef } from "react";
|
|
1077
|
+
import { Slottable } from "@radix-ui/react-slot";
|
|
686
1078
|
|
|
687
1079
|
import {
|
|
688
1080
|
Tooltip,
|
|
@@ -691,9 +1083,8 @@ import {
|
|
|
691
1083
|
} from "@/components/ui/tooltip";
|
|
692
1084
|
import { Button } from "@/components/ui/button";
|
|
693
1085
|
import { cn } from "@/lib/utils";
|
|
694
|
-
import { Slottable } from "@radix-ui/react-slot";
|
|
695
1086
|
|
|
696
|
-
export type TooltipIconButtonProps =
|
|
1087
|
+
export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
|
|
697
1088
|
tooltip: string;
|
|
698
1089
|
side?: "top" | "bottom" | "left" | "right";
|
|
699
1090
|
};
|
|
@@ -1073,7 +1464,7 @@ const MyApp = () => {
|
|
|
1073
1464
|
|
|
1074
1465
|
return (
|
|
1075
1466
|
<AssistantRuntimeProvider runtime={runtime}>
|
|
1076
|
-
<div
|
|
1467
|
+
<div>
|
|
1077
1468
|
<ThreadList />
|
|
1078
1469
|
<Thread />
|
|
1079
1470
|
</div>
|