@assistant-ui/mcp-docs-server 0.1.19 → 0.1.21

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 (108) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
  2. package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
  3. package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
  4. package/.docs/organized/code-examples/with-cloud.md +71 -1745
  5. package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
  6. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
  7. package/.docs/organized/code-examples/with-external-store.md +67 -1624
  8. package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
  9. package/.docs/organized/code-examples/with-langgraph.md +95 -1893
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
  11. package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
  12. package/.docs/organized/code-examples/with-react-router.md +66 -1318
  13. package/.docs/organized/code-examples/with-store.md +31 -31
  14. package/.docs/organized/code-examples/with-tanstack.md +77 -861
  15. package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
  16. package/.docs/raw/docs/(docs)/cli.mdx +66 -0
  17. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
  18. package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
  19. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
  20. package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
  21. package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
  22. package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
  23. package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
  24. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
  25. package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
  26. package/.docs/raw/docs/(docs)/index.mdx +33 -410
  27. package/.docs/raw/docs/(docs)/installation.mdx +450 -0
  28. package/.docs/raw/docs/(docs)/llm.mdx +209 -0
  29. package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
  30. package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
  31. package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
  32. package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
  33. package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
  34. package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
  36. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
  37. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
  38. package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
  39. package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
  40. package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
  41. package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
  42. package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
  43. package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
  44. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
  45. package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
  46. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
  47. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
  48. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
  49. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
  50. package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
  51. package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
  52. package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
  53. package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
  54. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
  55. package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
  56. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
  57. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
  58. package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
  59. package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
  60. package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
  61. package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
  62. package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
  63. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
  64. package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
  65. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
  66. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
  67. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
  68. package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
  69. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
  70. package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
  71. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
  72. package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
  73. package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
  74. package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
  75. package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
  76. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
  77. package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
  78. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
  79. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
  80. package/.docs/raw/docs/ui/accordion.mdx +259 -0
  81. package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
  82. package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
  83. package/.docs/raw/docs/ui/attachment.mdx +0 -2
  84. package/.docs/raw/docs/ui/badge.mdx +138 -0
  85. package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
  86. package/.docs/raw/docs/ui/file.mdx +152 -0
  87. package/.docs/raw/docs/ui/image.mdx +100 -0
  88. package/.docs/raw/docs/ui/markdown.mdx +0 -1
  89. package/.docs/raw/docs/ui/mermaid.mdx +0 -1
  90. package/.docs/raw/docs/ui/model-selector.mdx +224 -0
  91. package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
  92. package/.docs/raw/docs/ui/reasoning.mdx +6 -5
  93. package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
  94. package/.docs/raw/docs/ui/select.mdx +245 -0
  95. package/.docs/raw/docs/ui/sources.mdx +6 -5
  96. package/.docs/raw/docs/ui/streamdown.mdx +348 -0
  97. package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
  98. package/.docs/raw/docs/ui/tabs.mdx +259 -0
  99. package/.docs/raw/docs/ui/thread-list.mdx +98 -16
  100. package/.docs/raw/docs/ui/thread.mdx +57 -73
  101. package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
  102. package/.docs/raw/docs/ui/tool-group.mdx +1 -3
  103. package/README.md +3 -3
  104. package/package.json +4 -4
  105. package/src/tools/tests/examples.test.ts +1 -1
  106. package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
  107. package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
  108. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +0 -219
@@ -33,6 +33,8 @@ export async function POST(req: Request) {
33
33
  @import "tailwindcss";
34
34
  @import "tw-animate-css";
35
35
 
36
+ @source "../../../packages/ui/src";
37
+
36
38
  @custom-variant dark (&:is(.dark *));
37
39
 
38
40
  @theme inline {
@@ -267,1741 +269,12 @@ export default function Home() {
267
269
  "lib": "@/lib",
268
270
  "hooks": "@/hooks"
269
271
  },
270
- "iconLibrary": "lucide"
271
- }
272
-
273
- ```
274
-
275
- ## components/assistant-ui/attachment.tsx
276
-
277
- ```tsx
278
- "use client";
279
-
280
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
281
- import Image from "next/image";
282
- import { XIcon, PlusIcon, FileText } from "lucide-react";
283
- import {
284
- AttachmentPrimitive,
285
- ComposerPrimitive,
286
- MessagePrimitive,
287
- useAssistantState,
288
- useAssistantApi,
289
- } from "@assistant-ui/react";
290
- import { useShallow } from "zustand/shallow";
291
- import {
292
- Tooltip,
293
- TooltipContent,
294
- TooltipTrigger,
295
- } from "@/components/ui/tooltip";
296
- import {
297
- Dialog,
298
- DialogTitle,
299
- DialogContent,
300
- DialogTrigger,
301
- } from "@/components/ui/dialog";
302
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
303
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
304
- import { cn } from "@/lib/utils";
305
-
306
- const useFileSrc = (file: File | undefined) => {
307
- const [src, setSrc] = useState<string | undefined>(undefined);
308
-
309
- useEffect(() => {
310
- if (!file) {
311
- setSrc(undefined);
312
- return;
313
- }
314
-
315
- const objectUrl = URL.createObjectURL(file);
316
- setSrc(objectUrl);
317
-
318
- return () => {
319
- URL.revokeObjectURL(objectUrl);
320
- };
321
- }, [file]);
322
-
323
- return src;
324
- };
325
-
326
- const useAttachmentSrc = () => {
327
- const { file, src } = useAssistantState(
328
- useShallow(({ attachment }): { file?: File; src?: string } => {
329
- if (attachment.type !== "image") return {};
330
- if (attachment.file) return { file: attachment.file };
331
- const src = attachment.content?.filter((c) => c.type === "image")[0]
332
- ?.image;
333
- if (!src) return {};
334
- return { src };
335
- }),
336
- );
337
-
338
- return useFileSrc(file) ?? src;
339
- };
340
-
341
- type AttachmentPreviewProps = {
342
- src: string;
343
- };
344
-
345
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
346
- const [isLoaded, setIsLoaded] = useState(false);
347
- return (
348
- <Image
349
- src={src}
350
- alt="Image Preview"
351
- width={1}
352
- height={1}
353
- className={
354
- isLoaded
355
- ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
356
- : "aui-attachment-preview-image-loading hidden"
357
- }
358
- onLoadingComplete={() => setIsLoaded(true)}
359
- priority={false}
360
- />
361
- );
362
- };
363
-
364
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
365
- const src = useAttachmentSrc();
366
-
367
- if (!src) return children;
368
-
369
- return (
370
- <Dialog>
371
- <DialogTrigger
372
- className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
373
- asChild
374
- >
375
- {children}
376
- </DialogTrigger>
377
- <DialogContent className="aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive">
378
- <DialogTitle className="aui-sr-only sr-only">
379
- Image Attachment Preview
380
- </DialogTitle>
381
- <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
382
- <AttachmentPreview src={src} />
383
- </div>
384
- </DialogContent>
385
- </Dialog>
386
- );
387
- };
388
-
389
- const AttachmentThumb: FC = () => {
390
- const isImage = useAssistantState(
391
- ({ attachment }) => attachment.type === "image",
392
- );
393
- const src = useAttachmentSrc();
394
-
395
- return (
396
- <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
397
- <AvatarImage
398
- src={src}
399
- alt="Attachment preview"
400
- className="aui-attachment-tile-image object-cover"
401
- />
402
- <AvatarFallback delayMs={isImage ? 200 : 0}>
403
- <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
404
- </AvatarFallback>
405
- </Avatar>
406
- );
407
- };
408
-
409
- const AttachmentUI: FC = () => {
410
- const api = useAssistantApi();
411
- const isComposer = api.attachment.source === "composer";
412
-
413
- const isImage = useAssistantState(
414
- ({ attachment }) => attachment.type === "image",
415
- );
416
- const typeLabel = useAssistantState(({ attachment }) => {
417
- const type = attachment.type;
418
- switch (type) {
419
- case "image":
420
- return "Image";
421
- case "document":
422
- return "Document";
423
- case "file":
424
- return "File";
425
- default:
426
- const _exhaustiveCheck: never = type;
427
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
428
- }
429
- });
430
-
431
- return (
432
- <Tooltip>
433
- <AttachmentPrimitive.Root
434
- className={cn(
435
- "aui-attachment-root relative",
436
- isImage &&
437
- "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
438
- )}
439
- >
440
- <AttachmentPreviewDialog>
441
- <TooltipTrigger asChild>
442
- <div
443
- className={cn(
444
- "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
445
- isComposer &&
446
- "aui-attachment-tile-composer border-foreground/20",
447
- )}
448
- role="button"
449
- id="attachment-tile"
450
- aria-label={`${typeLabel} attachment`}
451
- >
452
- <AttachmentThumb />
453
- </div>
454
- </TooltipTrigger>
455
- </AttachmentPreviewDialog>
456
- {isComposer && <AttachmentRemove />}
457
- </AttachmentPrimitive.Root>
458
- <TooltipContent side="top">
459
- <AttachmentPrimitive.Name />
460
- </TooltipContent>
461
- </Tooltip>
462
- );
463
- };
464
-
465
- const AttachmentRemove: FC = () => {
466
- return (
467
- <AttachmentPrimitive.Remove asChild>
468
- <TooltipIconButton
469
- tooltip="Remove file"
470
- className="aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive"
471
- side="top"
472
- >
473
- <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
474
- </TooltipIconButton>
475
- </AttachmentPrimitive.Remove>
476
- );
477
- };
478
-
479
- export const UserMessageAttachments: FC = () => {
480
- return (
481
- <div className="aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2">
482
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
483
- </div>
484
- );
485
- };
486
-
487
- export const ComposerAttachments: FC = () => {
488
- return (
489
- <div className="aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden">
490
- <ComposerPrimitive.Attachments
491
- components={{ Attachment: AttachmentUI }}
492
- />
493
- </div>
494
- );
495
- };
496
-
497
- export const ComposerAddAttachment: FC = () => {
498
- return (
499
- <ComposerPrimitive.AddAttachment asChild>
500
- <TooltipIconButton
501
- tooltip="Add Attachment"
502
- side="bottom"
503
- variant="ghost"
504
- size="icon"
505
- className="aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
506
- aria-label="Add Attachment"
507
- >
508
- <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
509
- </TooltipIconButton>
510
- </ComposerPrimitive.AddAttachment>
511
- );
512
- };
513
-
514
- ```
515
-
516
- ## components/assistant-ui/markdown-text.tsx
517
-
518
- ```tsx
519
- "use client";
520
-
521
- import "@assistant-ui/react-markdown/styles/dot.css";
522
-
523
- import {
524
- type CodeHeaderProps,
525
- MarkdownTextPrimitive,
526
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
527
- useIsMarkdownCodeBlock,
528
- } from "@assistant-ui/react-markdown";
529
- import remarkGfm from "remark-gfm";
530
- import { type FC, memo, useState } from "react";
531
- import { CheckIcon, CopyIcon } from "lucide-react";
532
-
533
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
534
- import { cn } from "@/lib/utils";
535
-
536
- const MarkdownTextImpl = () => {
537
- return (
538
- <MarkdownTextPrimitive
539
- remarkPlugins={[remarkGfm]}
540
- className="aui-md"
541
- components={defaultComponents}
542
- />
543
- );
544
- };
545
-
546
- export const MarkdownText = memo(MarkdownTextImpl);
547
-
548
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
549
- const { isCopied, copyToClipboard } = useCopyToClipboard();
550
- const onCopy = () => {
551
- if (!code || isCopied) return;
552
- copyToClipboard(code);
553
- };
554
-
555
- return (
556
- <div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 font-semibold text-foreground text-sm dark:bg-muted-foreground/20">
557
- <span className="aui-code-header-language lowercase [&>span]:text-xs">
558
- {language}
559
- </span>
560
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
561
- {!isCopied && <CopyIcon />}
562
- {isCopied && <CheckIcon />}
563
- </TooltipIconButton>
564
- </div>
565
- );
566
- };
567
-
568
- const useCopyToClipboard = ({
569
- copiedDuration = 3000,
570
- }: {
571
- copiedDuration?: number;
572
- } = {}) => {
573
- const [isCopied, setIsCopied] = useState<boolean>(false);
574
-
575
- const copyToClipboard = (value: string) => {
576
- if (!value) return;
577
-
578
- navigator.clipboard.writeText(value).then(() => {
579
- setIsCopied(true);
580
- setTimeout(() => setIsCopied(false), copiedDuration);
581
- });
582
- };
583
-
584
- return { isCopied, copyToClipboard };
585
- };
586
-
587
- const defaultComponents = memoizeMarkdownComponents({
588
- h1: ({ className, ...props }) => (
589
- <h1
590
- className={cn(
591
- "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
592
- className,
593
- )}
594
- {...props}
595
- />
596
- ),
597
- h2: ({ className, ...props }) => (
598
- <h2
599
- className={cn(
600
- "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
601
- className,
602
- )}
603
- {...props}
604
- />
605
- ),
606
- h3: ({ className, ...props }) => (
607
- <h3
608
- className={cn(
609
- "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
610
- className,
611
- )}
612
- {...props}
613
- />
614
- ),
615
- h4: ({ className, ...props }) => (
616
- <h4
617
- className={cn(
618
- "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
619
- className,
620
- )}
621
- {...props}
622
- />
623
- ),
624
- h5: ({ className, ...props }) => (
625
- <h5
626
- className={cn(
627
- "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
628
- className,
629
- )}
630
- {...props}
631
- />
632
- ),
633
- h6: ({ className, ...props }) => (
634
- <h6
635
- className={cn(
636
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
637
- className,
638
- )}
639
- {...props}
640
- />
641
- ),
642
- p: ({ className, ...props }) => (
643
- <p
644
- className={cn(
645
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
646
- className,
647
- )}
648
- {...props}
649
- />
650
- ),
651
- a: ({ className, ...props }) => (
652
- <a
653
- className={cn(
654
- "aui-md-a font-medium text-primary underline underline-offset-4",
655
- className,
656
- )}
657
- {...props}
658
- />
659
- ),
660
- blockquote: ({ className, ...props }) => (
661
- <blockquote
662
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
663
- {...props}
664
- />
665
- ),
666
- ul: ({ className, ...props }) => (
667
- <ul
668
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
669
- {...props}
670
- />
671
- ),
672
- ol: ({ className, ...props }) => (
673
- <ol
674
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
675
- {...props}
676
- />
677
- ),
678
- hr: ({ className, ...props }) => (
679
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
680
- ),
681
- table: ({ className, ...props }) => (
682
- <table
683
- className={cn(
684
- "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
685
- className,
686
- )}
687
- {...props}
688
- />
689
- ),
690
- th: ({ className, ...props }) => (
691
- <th
692
- className={cn(
693
- "aui-md-th bg-muted px-4 py-2 text-left font-bold first:rounded-tl-lg last:rounded-tr-lg [[align=center]]:text-center [[align=right]]:text-right",
694
- className,
695
- )}
696
- {...props}
697
- />
698
- ),
699
- td: ({ className, ...props }) => (
700
- <td
701
- className={cn(
702
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
703
- className,
704
- )}
705
- {...props}
706
- />
707
- ),
708
- tr: ({ className, ...props }) => (
709
- <tr
710
- className={cn(
711
- "aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg",
712
- className,
713
- )}
714
- {...props}
715
- />
716
- ),
717
- sup: ({ className, ...props }) => (
718
- <sup
719
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
720
- {...props}
721
- />
722
- ),
723
- pre: ({ className, ...props }) => (
724
- <pre
725
- className={cn(
726
- "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
727
- className,
728
- )}
729
- {...props}
730
- />
731
- ),
732
- code: function Code({ className, ...props }) {
733
- const isCodeBlock = useIsMarkdownCodeBlock();
734
- return (
735
- <code
736
- className={cn(
737
- !isCodeBlock &&
738
- "aui-md-inline-code rounded border bg-muted font-semibold",
739
- className,
740
- )}
741
- {...props}
742
- />
743
- );
744
- },
745
- CodeHeader,
746
- });
747
-
748
- ```
749
-
750
- ## components/assistant-ui/thread-list.tsx
751
-
752
- ```tsx
753
- import { Button } from "@/components/ui/button";
754
- import { Skeleton } from "@/components/ui/skeleton";
755
- import {
756
- AssistantIf,
757
- ThreadListItemMorePrimitive,
758
- ThreadListItemPrimitive,
759
- ThreadListPrimitive,
760
- } from "@assistant-ui/react";
761
- import { ArchiveIcon, MoreHorizontalIcon, PlusIcon } from "lucide-react";
762
- import type { FC } from "react";
763
-
764
- export const ThreadList: FC = () => {
765
- return (
766
- <ThreadListPrimitive.Root className="aui-root aui-thread-list-root flex flex-col gap-1">
767
- <ThreadListNew />
768
- <AssistantIf condition={({ threads }) => threads.isLoading}>
769
- <ThreadListSkeleton />
770
- </AssistantIf>
771
- <AssistantIf condition={({ threads }) => !threads.isLoading}>
772
- <ThreadListPrimitive.Items components={{ ThreadListItem }} />
773
- </AssistantIf>
774
- </ThreadListPrimitive.Root>
775
- );
776
- };
777
-
778
- const ThreadListNew: FC = () => {
779
- return (
780
- <ThreadListPrimitive.New asChild>
781
- <Button
782
- variant="outline"
783
- className="aui-thread-list-new h-9 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted data-active:bg-muted"
784
- >
785
- <PlusIcon className="size-4" />
786
- New Thread
787
- </Button>
788
- </ThreadListPrimitive.New>
789
- );
790
- };
791
-
792
- const ThreadListSkeleton: FC = () => {
793
- return (
794
- <div className="flex flex-col gap-1">
795
- {Array.from({ length: 5 }, (_, i) => (
796
- <div
797
- key={i}
798
- role="status"
799
- aria-label="Loading threads"
800
- className="aui-thread-list-skeleton-wrapper flex h-9 items-center px-3"
801
- >
802
- <Skeleton className="aui-thread-list-skeleton h-4 w-full" />
803
- </div>
804
- ))}
805
- </div>
806
- );
807
- };
808
-
809
- const ThreadListItem: FC = () => {
810
- return (
811
- <ThreadListItemPrimitive.Root className="aui-thread-list-item group flex h-9 items-center gap-2 rounded-lg transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none data-active:bg-muted">
812
- <ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger flex h-full min-w-0 flex-1 items-center truncate px-3 text-start text-sm">
813
- <ThreadListItemPrimitive.Title fallback="New Chat" />
814
- </ThreadListItemPrimitive.Trigger>
815
- <ThreadListItemMore />
816
- </ThreadListItemPrimitive.Root>
817
- );
818
- };
819
-
820
- const ThreadListItemMore: FC = () => {
821
- return (
822
- <ThreadListItemMorePrimitive.Root>
823
- <ThreadListItemMorePrimitive.Trigger asChild>
824
- <Button
825
- variant="ghost"
826
- size="icon"
827
- className="aui-thread-list-item-more mr-2 size-7 p-0 opacity-0 transition-opacity group-hover:opacity-100 data-[state=open]:bg-accent data-[state=open]:opacity-100 group-data-active:opacity-100"
828
- >
829
- <MoreHorizontalIcon className="size-4" />
830
- <span className="sr-only">More options</span>
831
- </Button>
832
- </ThreadListItemMorePrimitive.Trigger>
833
- <ThreadListItemMorePrimitive.Content
834
- side="bottom"
835
- align="start"
836
- className="aui-thread-list-item-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
837
- >
838
- <ThreadListItemPrimitive.Archive asChild>
839
- <ThreadListItemMorePrimitive.Item className="aui-thread-list-item-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
840
- <ArchiveIcon className="size-4" />
841
- Archive
842
- </ThreadListItemMorePrimitive.Item>
843
- </ThreadListItemPrimitive.Archive>
844
- </ThreadListItemMorePrimitive.Content>
845
- </ThreadListItemMorePrimitive.Root>
846
- );
847
- };
848
-
849
- ```
850
-
851
- ## components/assistant-ui/thread.tsx
852
-
853
- ```tsx
854
- import {
855
- ComposerAddAttachment,
856
- ComposerAttachments,
857
- UserMessageAttachments,
858
- } from "@/components/assistant-ui/attachment";
859
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
860
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
861
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
862
- import { Button } from "@/components/ui/button";
863
- import { cn } from "@/lib/utils";
864
- import {
865
- ActionBarMorePrimitive,
866
- ActionBarPrimitive,
867
- AssistantIf,
868
- BranchPickerPrimitive,
869
- ComposerPrimitive,
870
- ErrorPrimitive,
871
- MessagePrimitive,
872
- ThreadPrimitive,
873
- } from "@assistant-ui/react";
874
- import {
875
- ArrowDownIcon,
876
- ArrowUpIcon,
877
- CheckIcon,
878
- ChevronLeftIcon,
879
- ChevronRightIcon,
880
- CopyIcon,
881
- DownloadIcon,
882
- MoreHorizontalIcon,
883
- PencilIcon,
884
- RefreshCwIcon,
885
- SquareIcon,
886
- } from "lucide-react";
887
- import type { FC } from "react";
888
-
889
- export const Thread: FC = () => {
890
- return (
891
- <ThreadPrimitive.Root
892
- className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
893
- style={{
894
- ["--thread-max-width" as string]: "44rem",
895
- }}
896
- >
897
- <ThreadPrimitive.Viewport
898
- turnAnchor="top"
899
- className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
900
- >
901
- <AssistantIf condition={({ thread }) => thread.isEmpty}>
902
- <ThreadWelcome />
903
- </AssistantIf>
904
-
905
- <ThreadPrimitive.Messages
906
- components={{
907
- UserMessage,
908
- EditComposer,
909
- AssistantMessage,
910
- }}
911
- />
912
-
913
- <ThreadPrimitive.ViewportFooter className="aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible rounded-t-3xl bg-background pb-4 md:pb-6">
914
- <ThreadScrollToBottom />
915
- <Composer />
916
- </ThreadPrimitive.ViewportFooter>
917
- </ThreadPrimitive.Viewport>
918
- </ThreadPrimitive.Root>
919
- );
920
- };
921
-
922
- const ThreadScrollToBottom: FC = () => {
923
- return (
924
- <ThreadPrimitive.ScrollToBottom asChild>
925
- <TooltipIconButton
926
- tooltip="Scroll to bottom"
927
- variant="outline"
928
- className="aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent"
929
- >
930
- <ArrowDownIcon />
931
- </TooltipIconButton>
932
- </ThreadPrimitive.ScrollToBottom>
933
- );
934
- };
935
-
936
- const ThreadWelcome: FC = () => {
937
- return (
938
- <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
939
- <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
940
- <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
941
- <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
942
- Hello there!
943
- </h1>
944
- <p className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in text-muted-foreground text-xl delay-75 duration-200">
945
- How can I help you today?
946
- </p>
947
- </div>
948
- </div>
949
- <ThreadSuggestions />
950
- </div>
951
- );
952
- };
953
-
954
- const SUGGESTIONS = [
955
- {
956
- title: "What's the weather",
957
- label: "in San Francisco?",
958
- prompt: "What's the weather in San Francisco?",
959
- },
960
- {
961
- title: "Explain React hooks",
962
- label: "like useState and useEffect",
963
- prompt: "Explain React hooks like useState and useEffect",
964
- },
965
- ] as const;
966
-
967
- const ThreadSuggestions: FC = () => {
968
- return (
969
- <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
970
- {SUGGESTIONS.map((suggestion, index) => (
971
- <div
972
- key={suggestion.prompt}
973
- className="aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 @md:nth-[n+3]:block nth-[n+3]:hidden animate-in fill-mode-both duration-200"
974
- style={{ animationDelay: `${100 + index * 50}ms` }}
975
- >
976
- <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
977
- <Button
978
- variant="ghost"
979
- className="aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted"
980
- aria-label={suggestion.prompt}
981
- >
982
- <span className="aui-thread-welcome-suggestion-text-1 font-medium">
983
- {suggestion.title}
984
- </span>
985
- <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
986
- {suggestion.label}
987
- </span>
988
- </Button>
989
- </ThreadPrimitive.Suggestion>
990
- </div>
991
- ))}
992
- </div>
993
- );
994
- };
995
-
996
- const Composer: FC = () => {
997
- return (
998
- <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
999
- <ComposerPrimitive.AttachmentDropzone className="aui-composer-attachment-dropzone flex w-full flex-col rounded-2xl border border-input bg-background px-1 pt-2 outline-none transition-shadow has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50">
1000
- <ComposerAttachments />
1001
- <ComposerPrimitive.Input
1002
- placeholder="Send a message..."
1003
- className="aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0"
1004
- rows={1}
1005
- autoFocus
1006
- aria-label="Message input"
1007
- />
1008
- <ComposerAction />
1009
- </ComposerPrimitive.AttachmentDropzone>
1010
- </ComposerPrimitive.Root>
1011
- );
1012
- };
1013
-
1014
- const ComposerAction: FC = () => {
1015
- return (
1016
- <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
1017
- <ComposerAddAttachment />
1018
-
1019
- <AssistantIf condition={({ thread }) => !thread.isRunning}>
1020
- <ComposerPrimitive.Send asChild>
1021
- <TooltipIconButton
1022
- tooltip="Send message"
1023
- side="bottom"
1024
- type="submit"
1025
- variant="default"
1026
- size="icon"
1027
- className="aui-composer-send size-8 rounded-full"
1028
- aria-label="Send message"
1029
- >
1030
- <ArrowUpIcon className="aui-composer-send-icon size-4" />
1031
- </TooltipIconButton>
1032
- </ComposerPrimitive.Send>
1033
- </AssistantIf>
1034
-
1035
- <AssistantIf condition={({ thread }) => thread.isRunning}>
1036
- <ComposerPrimitive.Cancel asChild>
1037
- <Button
1038
- type="button"
1039
- variant="default"
1040
- size="icon"
1041
- className="aui-composer-cancel size-8 rounded-full"
1042
- aria-label="Stop generating"
1043
- >
1044
- <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
1045
- </Button>
1046
- </ComposerPrimitive.Cancel>
1047
- </AssistantIf>
1048
- </div>
1049
- );
1050
- };
1051
-
1052
- const MessageError: FC = () => {
1053
- return (
1054
- <MessagePrimitive.Error>
1055
- <ErrorPrimitive.Root className="aui-message-error-root mt-2 rounded-md border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200">
1056
- <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
1057
- </ErrorPrimitive.Root>
1058
- </MessagePrimitive.Error>
1059
- );
1060
- };
1061
-
1062
- const AssistantMessage: FC = () => {
1063
- return (
1064
- <MessagePrimitive.Root
1065
- className="aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-3 duration-150"
1066
- data-role="assistant"
1067
- >
1068
- <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
1069
- <MessagePrimitive.Parts
1070
- components={{
1071
- Text: MarkdownText,
1072
- tools: { Fallback: ToolFallback },
1073
- }}
1074
- />
1075
- <MessageError />
1076
- </div>
1077
-
1078
- <div className="aui-assistant-message-footer mt-1 ml-2 flex">
1079
- <BranchPicker />
1080
- <AssistantActionBar />
1081
- </div>
1082
- </MessagePrimitive.Root>
1083
- );
1084
- };
1085
-
1086
- const AssistantActionBar: FC = () => {
1087
- return (
1088
- <ActionBarPrimitive.Root
1089
- hideWhenRunning
1090
- autohide="not-last"
1091
- autohideFloat="single-branch"
1092
- className="aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-md data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm"
1093
- >
1094
- <ActionBarPrimitive.Copy asChild>
1095
- <TooltipIconButton tooltip="Copy">
1096
- <AssistantIf condition={({ message }) => message.isCopied}>
1097
- <CheckIcon />
1098
- </AssistantIf>
1099
- <AssistantIf condition={({ message }) => !message.isCopied}>
1100
- <CopyIcon />
1101
- </AssistantIf>
1102
- </TooltipIconButton>
1103
- </ActionBarPrimitive.Copy>
1104
- <ActionBarPrimitive.Reload asChild>
1105
- <TooltipIconButton tooltip="Refresh">
1106
- <RefreshCwIcon />
1107
- </TooltipIconButton>
1108
- </ActionBarPrimitive.Reload>
1109
- <ActionBarMorePrimitive.Root>
1110
- <ActionBarMorePrimitive.Trigger asChild>
1111
- <TooltipIconButton
1112
- tooltip="More"
1113
- className="data-[state=open]:bg-accent"
1114
- >
1115
- <MoreHorizontalIcon />
1116
- </TooltipIconButton>
1117
- </ActionBarMorePrimitive.Trigger>
1118
- <ActionBarMorePrimitive.Content
1119
- side="bottom"
1120
- align="start"
1121
- className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
1122
- >
1123
- <ActionBarPrimitive.ExportMarkdown asChild>
1124
- <ActionBarMorePrimitive.Item className="aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">
1125
- <DownloadIcon className="size-4" />
1126
- Export as Markdown
1127
- </ActionBarMorePrimitive.Item>
1128
- </ActionBarPrimitive.ExportMarkdown>
1129
- </ActionBarMorePrimitive.Content>
1130
- </ActionBarMorePrimitive.Root>
1131
- </ActionBarPrimitive.Root>
1132
- );
1133
- };
1134
-
1135
- const UserMessage: FC = () => {
1136
- return (
1137
- <MessagePrimitive.Root
1138
- className="aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-3 duration-150 [&:where(>*)]:col-start-2"
1139
- data-role="user"
1140
- >
1141
- <UserMessageAttachments />
1142
-
1143
- <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1144
- <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1145
- <MessagePrimitive.Parts />
1146
- </div>
1147
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1148
- <UserActionBar />
1149
- </div>
1150
- </div>
1151
-
1152
- <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1153
- </MessagePrimitive.Root>
1154
- );
1155
- };
1156
-
1157
- const UserActionBar: FC = () => {
1158
- return (
1159
- <ActionBarPrimitive.Root
1160
- hideWhenRunning
1161
- autohide="not-last"
1162
- className="aui-user-action-bar-root flex flex-col items-end"
1163
- >
1164
- <ActionBarPrimitive.Edit asChild>
1165
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1166
- <PencilIcon />
1167
- </TooltipIconButton>
1168
- </ActionBarPrimitive.Edit>
1169
- </ActionBarPrimitive.Root>
1170
- );
1171
- };
1172
-
1173
- const EditComposer: FC = () => {
1174
- return (
1175
- <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1176
- <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1177
- <ComposerPrimitive.Input
1178
- className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1179
- autoFocus
1180
- />
1181
- <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1182
- <ComposerPrimitive.Cancel asChild>
1183
- <Button variant="ghost" size="sm">
1184
- Cancel
1185
- </Button>
1186
- </ComposerPrimitive.Cancel>
1187
- <ComposerPrimitive.Send asChild>
1188
- <Button size="sm">Update</Button>
1189
- </ComposerPrimitive.Send>
1190
- </div>
1191
- </ComposerPrimitive.Root>
1192
- </MessagePrimitive.Root>
1193
- );
1194
- };
1195
-
1196
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1197
- className,
1198
- ...rest
1199
- }) => {
1200
- return (
1201
- <BranchPickerPrimitive.Root
1202
- hideWhenSingleBranch
1203
- className={cn(
1204
- "aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
1205
- className,
1206
- )}
1207
- {...rest}
1208
- >
1209
- <BranchPickerPrimitive.Previous asChild>
1210
- <TooltipIconButton tooltip="Previous">
1211
- <ChevronLeftIcon />
1212
- </TooltipIconButton>
1213
- </BranchPickerPrimitive.Previous>
1214
- <span className="aui-branch-picker-state font-medium">
1215
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1216
- </span>
1217
- <BranchPickerPrimitive.Next asChild>
1218
- <TooltipIconButton tooltip="Next">
1219
- <ChevronRightIcon />
1220
- </TooltipIconButton>
1221
- </BranchPickerPrimitive.Next>
1222
- </BranchPickerPrimitive.Root>
1223
- );
1224
- };
1225
-
1226
- ```
1227
-
1228
- ## components/assistant-ui/tool-fallback.tsx
1229
-
1230
- ```tsx
1231
- "use client";
1232
-
1233
- import { memo, useCallback, useRef, useState } from "react";
1234
- import {
1235
- AlertCircleIcon,
1236
- CheckIcon,
1237
- ChevronDownIcon,
1238
- LoaderIcon,
1239
- XCircleIcon,
1240
- } from "lucide-react";
1241
- import {
1242
- useScrollLock,
1243
- type ToolCallMessagePartStatus,
1244
- type ToolCallMessagePartComponent,
1245
- } from "@assistant-ui/react";
1246
- import {
1247
- Collapsible,
1248
- CollapsibleContent,
1249
- CollapsibleTrigger,
1250
- } from "@/components/ui/collapsible";
1251
- import { cn } from "@/lib/utils";
1252
-
1253
- const ANIMATION_DURATION = 200;
1254
-
1255
- export type ToolFallbackRootProps = Omit<
1256
- React.ComponentProps<typeof Collapsible>,
1257
- "open" | "onOpenChange"
1258
- > & {
1259
- open?: boolean;
1260
- onOpenChange?: (open: boolean) => void;
1261
- defaultOpen?: boolean;
1262
- };
1263
-
1264
- function ToolFallbackRoot({
1265
- className,
1266
- open: controlledOpen,
1267
- onOpenChange: controlledOnOpenChange,
1268
- defaultOpen = false,
1269
- children,
1270
- ...props
1271
- }: ToolFallbackRootProps) {
1272
- const collapsibleRef = useRef<HTMLDivElement>(null);
1273
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1274
- const lockScroll = useScrollLock(collapsibleRef, ANIMATION_DURATION);
1275
-
1276
- const isControlled = controlledOpen !== undefined;
1277
- const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
1278
-
1279
- const handleOpenChange = useCallback(
1280
- (open: boolean) => {
1281
- if (!open) {
1282
- lockScroll();
1283
- }
1284
- if (!isControlled) {
1285
- setUncontrolledOpen(open);
1286
- }
1287
- controlledOnOpenChange?.(open);
1288
- },
1289
- [lockScroll, isControlled, controlledOnOpenChange],
1290
- );
1291
-
1292
- return (
1293
- <Collapsible
1294
- ref={collapsibleRef}
1295
- data-slot="tool-fallback-root"
1296
- open={isOpen}
1297
- onOpenChange={handleOpenChange}
1298
- className={cn(
1299
- "aui-tool-fallback-root group/tool-fallback-root w-full rounded-lg border py-3",
1300
- className,
1301
- )}
1302
- style={
1303
- {
1304
- "--animation-duration": `${ANIMATION_DURATION}ms`,
1305
- } as React.CSSProperties
1306
- }
1307
- {...props}
1308
- >
1309
- {children}
1310
- </Collapsible>
1311
- );
1312
- }
1313
-
1314
- type ToolStatus = ToolCallMessagePartStatus["type"];
1315
-
1316
- const statusIconMap: Record<ToolStatus, React.ElementType> = {
1317
- running: LoaderIcon,
1318
- complete: CheckIcon,
1319
- incomplete: XCircleIcon,
1320
- "requires-action": AlertCircleIcon,
1321
- };
1322
-
1323
- function ToolFallbackTrigger({
1324
- toolName,
1325
- status,
1326
- className,
1327
- ...props
1328
- }: React.ComponentProps<typeof CollapsibleTrigger> & {
1329
- toolName: string;
1330
- status?: ToolCallMessagePartStatus;
1331
- }) {
1332
- const statusType = status?.type ?? "complete";
1333
- const isRunning = statusType === "running";
1334
- const isCancelled =
1335
- status?.type === "incomplete" && status.reason === "cancelled";
1336
-
1337
- const Icon = statusIconMap[statusType];
1338
- const label = isCancelled ? "Cancelled tool" : "Used tool";
1339
-
1340
- return (
1341
- <CollapsibleTrigger
1342
- data-slot="tool-fallback-trigger"
1343
- className={cn(
1344
- "aui-tool-fallback-trigger group/trigger flex w-full items-center gap-2 px-4 text-sm transition-colors",
1345
- className,
1346
- )}
1347
- {...props}
1348
- >
1349
- <Icon
1350
- data-slot="tool-fallback-trigger-icon"
1351
- className={cn(
1352
- "aui-tool-fallback-trigger-icon size-4 shrink-0",
1353
- isCancelled && "text-muted-foreground",
1354
- isRunning && "animate-spin",
1355
- )}
1356
- />
1357
- <span
1358
- data-slot="tool-fallback-trigger-label"
1359
- className={cn(
1360
- "aui-tool-fallback-trigger-label-wrapper relative inline-block grow text-left leading-none",
1361
- isCancelled && "text-muted-foreground line-through",
1362
- )}
1363
- >
1364
- <span>
1365
- {label}: <b>{toolName}</b>
1366
- </span>
1367
- {isRunning && (
1368
- <span
1369
- aria-hidden
1370
- data-slot="tool-fallback-trigger-shimmer"
1371
- className="aui-tool-fallback-trigger-shimmer shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
1372
- >
1373
- {label}: <b>{toolName}</b>
1374
- </span>
1375
- )}
1376
- </span>
1377
- <ChevronDownIcon
1378
- data-slot="tool-fallback-trigger-chevron"
1379
- className={cn(
1380
- "aui-tool-fallback-trigger-chevron size-4 shrink-0",
1381
- "transition-transform duration-(--animation-duration) ease-out",
1382
- "group-data-[state=closed]/trigger:-rotate-90",
1383
- "group-data-[state=open]/trigger:rotate-0",
1384
- )}
1385
- />
1386
- </CollapsibleTrigger>
1387
- );
1388
- }
1389
-
1390
- function ToolFallbackContent({
1391
- className,
1392
- children,
1393
- ...props
1394
- }: React.ComponentProps<typeof CollapsibleContent>) {
1395
- return (
1396
- <CollapsibleContent
1397
- data-slot="tool-fallback-content"
1398
- className={cn(
1399
- "aui-tool-fallback-content relative overflow-hidden text-sm outline-none",
1400
- "group/collapsible-content ease-out",
1401
- "data-[state=closed]:animate-collapsible-up",
1402
- "data-[state=open]:animate-collapsible-down",
1403
- "data-[state=closed]:fill-mode-forwards",
1404
- "data-[state=closed]:pointer-events-none",
1405
- "data-[state=open]:duration-(--animation-duration)",
1406
- "data-[state=closed]:duration-(--animation-duration)",
1407
- className,
1408
- )}
1409
- {...props}
1410
- >
1411
- <div className="mt-3 flex flex-col gap-2 border-t pt-2">{children}</div>
1412
- </CollapsibleContent>
1413
- );
1414
- }
1415
-
1416
- function ToolFallbackArgs({
1417
- argsText,
1418
- className,
1419
- ...props
1420
- }: React.ComponentProps<"div"> & {
1421
- argsText?: string;
1422
- }) {
1423
- if (!argsText) return null;
1424
-
1425
- return (
1426
- <div
1427
- data-slot="tool-fallback-args"
1428
- className={cn("aui-tool-fallback-args px-4", className)}
1429
- {...props}
1430
- >
1431
- <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1432
- {argsText}
1433
- </pre>
1434
- </div>
1435
- );
1436
- }
1437
-
1438
- function ToolFallbackResult({
1439
- result,
1440
- className,
1441
- ...props
1442
- }: React.ComponentProps<"div"> & {
1443
- result?: unknown;
1444
- }) {
1445
- if (result === undefined) return null;
1446
-
1447
- return (
1448
- <div
1449
- data-slot="tool-fallback-result"
1450
- className={cn(
1451
- "aui-tool-fallback-result border-t border-dashed px-4 pt-2",
1452
- className,
1453
- )}
1454
- {...props}
1455
- >
1456
- <p className="aui-tool-fallback-result-header font-semibold">Result:</p>
1457
- <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1458
- {typeof result === "string" ? result : JSON.stringify(result, null, 2)}
1459
- </pre>
1460
- </div>
1461
- );
1462
- }
1463
-
1464
- function ToolFallbackError({
1465
- status,
1466
- className,
1467
- ...props
1468
- }: React.ComponentProps<"div"> & {
1469
- status?: ToolCallMessagePartStatus;
1470
- }) {
1471
- if (status?.type !== "incomplete") return null;
1472
-
1473
- const error = status.error;
1474
- const errorText = error
1475
- ? typeof error === "string"
1476
- ? error
1477
- : JSON.stringify(error)
1478
- : null;
1479
-
1480
- if (!errorText) return null;
1481
-
1482
- const isCancelled = status.reason === "cancelled";
1483
- const headerText = isCancelled ? "Cancelled reason:" : "Error:";
1484
-
1485
- return (
1486
- <div
1487
- data-slot="tool-fallback-error"
1488
- className={cn("aui-tool-fallback-error px-4", className)}
1489
- {...props}
1490
- >
1491
- <p className="aui-tool-fallback-error-header font-semibold text-muted-foreground">
1492
- {headerText}
1493
- </p>
1494
- <p className="aui-tool-fallback-error-reason text-muted-foreground">
1495
- {errorText}
1496
- </p>
1497
- </div>
1498
- );
1499
- }
1500
-
1501
- const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1502
- toolName,
1503
- argsText,
1504
- result,
1505
- status,
1506
- }) => {
1507
- const isCancelled =
1508
- status?.type === "incomplete" && status.reason === "cancelled";
1509
-
1510
- return (
1511
- <ToolFallbackRoot
1512
- className={cn(isCancelled && "border-muted-foreground/30 bg-muted/30")}
1513
- >
1514
- <ToolFallbackTrigger toolName={toolName} status={status} />
1515
- <ToolFallbackContent>
1516
- <ToolFallbackError status={status} />
1517
- <ToolFallbackArgs
1518
- argsText={argsText}
1519
- className={cn(isCancelled && "opacity-60")}
1520
- />
1521
- {!isCancelled && <ToolFallbackResult result={result} />}
1522
- </ToolFallbackContent>
1523
- </ToolFallbackRoot>
1524
- );
1525
- };
1526
-
1527
- const ToolFallback = memo(
1528
- ToolFallbackImpl,
1529
- ) as unknown as ToolCallMessagePartComponent & {
1530
- Root: typeof ToolFallbackRoot;
1531
- Trigger: typeof ToolFallbackTrigger;
1532
- Content: typeof ToolFallbackContent;
1533
- Args: typeof ToolFallbackArgs;
1534
- Result: typeof ToolFallbackResult;
1535
- Error: typeof ToolFallbackError;
1536
- };
1537
-
1538
- ToolFallback.displayName = "ToolFallback";
1539
- ToolFallback.Root = ToolFallbackRoot;
1540
- ToolFallback.Trigger = ToolFallbackTrigger;
1541
- ToolFallback.Content = ToolFallbackContent;
1542
- ToolFallback.Args = ToolFallbackArgs;
1543
- ToolFallback.Result = ToolFallbackResult;
1544
- ToolFallback.Error = ToolFallbackError;
1545
-
1546
- export {
1547
- ToolFallback,
1548
- ToolFallbackRoot,
1549
- ToolFallbackTrigger,
1550
- ToolFallbackContent,
1551
- ToolFallbackArgs,
1552
- ToolFallbackResult,
1553
- ToolFallbackError,
1554
- };
1555
-
1556
- ```
1557
-
1558
- ## components/assistant-ui/tooltip-icon-button.tsx
1559
-
1560
- ```tsx
1561
- "use client";
1562
-
1563
- import { ComponentPropsWithRef, forwardRef } from "react";
1564
- import { Slottable } from "@radix-ui/react-slot";
1565
-
1566
- import {
1567
- Tooltip,
1568
- TooltipContent,
1569
- TooltipTrigger,
1570
- } from "@/components/ui/tooltip";
1571
- import { Button } from "@/components/ui/button";
1572
- import { cn } from "@/lib/utils";
1573
-
1574
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1575
- tooltip: string;
1576
- side?: "top" | "bottom" | "left" | "right";
1577
- };
1578
-
1579
- export const TooltipIconButton = forwardRef<
1580
- HTMLButtonElement,
1581
- TooltipIconButtonProps
1582
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1583
- return (
1584
- <Tooltip>
1585
- <TooltipTrigger asChild>
1586
- <Button
1587
- variant="ghost"
1588
- size="icon"
1589
- {...rest}
1590
- className={cn("aui-button-icon size-6 p-1", className)}
1591
- ref={ref}
1592
- >
1593
- <Slottable>{children}</Slottable>
1594
- <span className="aui-sr-only sr-only">{tooltip}</span>
1595
- </Button>
1596
- </TooltipTrigger>
1597
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1598
- </Tooltip>
1599
- );
1600
- });
1601
-
1602
- TooltipIconButton.displayName = "TooltipIconButton";
1603
-
1604
- ```
1605
-
1606
- ## components/ui/avatar.tsx
1607
-
1608
- ```tsx
1609
- "use client";
1610
-
1611
- import * as React from "react";
1612
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1613
-
1614
- import { cn } from "@/lib/utils";
1615
-
1616
- function Avatar({
1617
- className,
1618
- ...props
1619
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
1620
- return (
1621
- <AvatarPrimitive.Root
1622
- data-slot="avatar"
1623
- className={cn(
1624
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1625
- className,
1626
- )}
1627
- {...props}
1628
- />
1629
- );
1630
- }
1631
-
1632
- function AvatarImage({
1633
- className,
1634
- ...props
1635
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
1636
- return (
1637
- <AvatarPrimitive.Image
1638
- data-slot="avatar-image"
1639
- className={cn("aspect-square size-full", className)}
1640
- {...props}
1641
- />
1642
- );
1643
- }
1644
-
1645
- function AvatarFallback({
1646
- className,
1647
- ...props
1648
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
1649
- return (
1650
- <AvatarPrimitive.Fallback
1651
- data-slot="avatar-fallback"
1652
- className={cn(
1653
- "flex size-full items-center justify-center rounded-full bg-muted",
1654
- className,
1655
- )}
1656
- {...props}
1657
- />
1658
- );
1659
- }
1660
-
1661
- export { Avatar, AvatarImage, AvatarFallback };
1662
-
1663
- ```
1664
-
1665
- ## components/ui/button.tsx
1666
-
1667
- ```tsx
1668
- import * as React from "react";
1669
- import { Slot } from "@radix-ui/react-slot";
1670
- import { cva, type VariantProps } from "class-variance-authority";
1671
-
1672
- import { cn } from "@/lib/utils";
1673
-
1674
- const buttonVariants = cva(
1675
- "inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1676
- {
1677
- variants: {
1678
- variant: {
1679
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1680
- destructive:
1681
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1682
- outline:
1683
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1684
- secondary:
1685
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1686
- ghost:
1687
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1688
- link: "text-primary underline-offset-4 hover:underline",
1689
- },
1690
- size: {
1691
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1692
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1693
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1694
- icon: "size-9",
1695
- "icon-sm": "size-8",
1696
- "icon-lg": "size-10",
1697
- },
1698
- },
1699
- defaultVariants: {
1700
- variant: "default",
1701
- size: "default",
1702
- },
1703
- },
1704
- );
1705
-
1706
- function Button({
1707
- className,
1708
- variant = "default",
1709
- size = "default",
1710
- asChild = false,
1711
- ...props
1712
- }: React.ComponentProps<"button"> &
1713
- VariantProps<typeof buttonVariants> & {
1714
- asChild?: boolean;
1715
- }) {
1716
- const Comp = asChild ? Slot : "button";
1717
-
1718
- return (
1719
- <Comp
1720
- data-slot="button"
1721
- data-variant={variant}
1722
- data-size={size}
1723
- className={cn(buttonVariants({ variant, size, className }))}
1724
- {...props}
1725
- />
1726
- );
1727
- }
1728
-
1729
- export { Button, buttonVariants };
1730
-
1731
- ```
1732
-
1733
- ## components/ui/collapsible.tsx
1734
-
1735
- ```tsx
1736
- "use client";
1737
-
1738
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
1739
-
1740
- function Collapsible({
1741
- ...props
1742
- }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
1743
- return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
1744
- }
1745
-
1746
- function CollapsibleTrigger({
1747
- ...props
1748
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
1749
- return (
1750
- <CollapsiblePrimitive.CollapsibleTrigger
1751
- data-slot="collapsible-trigger"
1752
- {...props}
1753
- />
1754
- );
1755
- }
1756
-
1757
- function CollapsibleContent({
1758
- ...props
1759
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
1760
- return (
1761
- <CollapsiblePrimitive.CollapsibleContent
1762
- data-slot="collapsible-content"
1763
- {...props}
1764
- />
1765
- );
1766
- }
1767
-
1768
- export { Collapsible, CollapsibleTrigger, CollapsibleContent };
1769
-
1770
- ```
1771
-
1772
- ## components/ui/dialog.tsx
1773
-
1774
- ```tsx
1775
- "use client";
1776
-
1777
- import * as React from "react";
1778
- import * as DialogPrimitive from "@radix-ui/react-dialog";
1779
- import { XIcon } from "lucide-react";
1780
-
1781
- import { cn } from "@/lib/utils";
1782
-
1783
- function Dialog({
1784
- ...props
1785
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1786
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1787
- }
1788
-
1789
- function DialogTrigger({
1790
- ...props
1791
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1792
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1793
- }
1794
-
1795
- function DialogPortal({
1796
- ...props
1797
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1798
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1799
- }
1800
-
1801
- function DialogClose({
1802
- ...props
1803
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1804
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1805
- }
1806
-
1807
- function DialogOverlay({
1808
- className,
1809
- ...props
1810
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1811
- return (
1812
- <DialogPrimitive.Overlay
1813
- data-slot="dialog-overlay"
1814
- className={cn(
1815
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=open]:animate-in",
1816
- className,
1817
- )}
1818
- {...props}
1819
- />
1820
- );
1821
- }
1822
-
1823
- function DialogContent({
1824
- className,
1825
- children,
1826
- showCloseButton = true,
1827
- ...props
1828
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
1829
- showCloseButton?: boolean;
1830
- }) {
1831
- return (
1832
- <DialogPortal data-slot="dialog-portal">
1833
- <DialogOverlay />
1834
- <DialogPrimitive.Content
1835
- data-slot="dialog-content"
1836
- className={cn(
1837
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg outline-none duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
1838
- className,
1839
- )}
1840
- {...props}
1841
- >
1842
- {children}
1843
- {showCloseButton && (
1844
- <DialogPrimitive.Close
1845
- data-slot="dialog-close"
1846
- className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0"
1847
- >
1848
- <XIcon />
1849
- <span className="sr-only">Close</span>
1850
- </DialogPrimitive.Close>
1851
- )}
1852
- </DialogPrimitive.Content>
1853
- </DialogPortal>
1854
- );
1855
- }
1856
-
1857
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1858
- return (
1859
- <div
1860
- data-slot="dialog-header"
1861
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1862
- {...props}
1863
- />
1864
- );
1865
- }
1866
-
1867
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1868
- return (
1869
- <div
1870
- data-slot="dialog-footer"
1871
- className={cn(
1872
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1873
- className,
1874
- )}
1875
- {...props}
1876
- />
1877
- );
1878
- }
1879
-
1880
- function DialogTitle({
1881
- className,
1882
- ...props
1883
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1884
- return (
1885
- <DialogPrimitive.Title
1886
- data-slot="dialog-title"
1887
- className={cn("font-semibold text-lg leading-none", className)}
1888
- {...props}
1889
- />
1890
- );
1891
- }
1892
-
1893
- function DialogDescription({
1894
- className,
1895
- ...props
1896
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1897
- return (
1898
- <DialogPrimitive.Description
1899
- data-slot="dialog-description"
1900
- className={cn("text-muted-foreground text-sm", className)}
1901
- {...props}
1902
- />
1903
- );
1904
- }
1905
-
1906
- export {
1907
- Dialog,
1908
- DialogClose,
1909
- DialogContent,
1910
- DialogDescription,
1911
- DialogFooter,
1912
- DialogHeader,
1913
- DialogOverlay,
1914
- DialogPortal,
1915
- DialogTitle,
1916
- DialogTrigger,
1917
- };
1918
-
1919
- ```
1920
-
1921
- ## components/ui/skeleton.tsx
1922
-
1923
- ```tsx
1924
- import { cn } from "@/lib/utils";
1925
-
1926
- function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
1927
- return (
1928
- <div
1929
- data-slot="skeleton"
1930
- className={cn("animate-pulse rounded-md bg-accent", className)}
1931
- {...props}
1932
- />
1933
- );
1934
- }
1935
-
1936
- export { Skeleton };
1937
-
1938
- ```
1939
-
1940
- ## components/ui/tooltip.tsx
1941
-
1942
- ```tsx
1943
- "use client";
1944
-
1945
- import * as React from "react";
1946
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
1947
-
1948
- import { cn } from "@/lib/utils";
1949
-
1950
- function TooltipProvider({
1951
- delayDuration = 0,
1952
- ...props
1953
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
1954
- return (
1955
- <TooltipPrimitive.Provider
1956
- data-slot="tooltip-provider"
1957
- delayDuration={delayDuration}
1958
- {...props}
1959
- />
1960
- );
1961
- }
1962
-
1963
- function Tooltip({
1964
- ...props
1965
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
1966
- return (
1967
- <TooltipProvider>
1968
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
1969
- </TooltipProvider>
1970
- );
1971
- }
1972
-
1973
- function TooltipTrigger({
1974
- ...props
1975
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
1976
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
1977
- }
1978
-
1979
- function TooltipContent({
1980
- className,
1981
- sideOffset = 0,
1982
- children,
1983
- ...props
1984
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
1985
- return (
1986
- <TooltipPrimitive.Portal>
1987
- <TooltipPrimitive.Content
1988
- data-slot="tooltip-content"
1989
- sideOffset={sideOffset}
1990
- className={cn(
1991
- "fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-foreground px-3 py-1.5 text-background text-xs data-[state=closed]:animate-out",
1992
- className,
1993
- )}
1994
- {...props}
1995
- >
1996
- {children}
1997
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
1998
- </TooltipPrimitive.Content>
1999
- </TooltipPrimitive.Portal>
2000
- );
272
+ "iconLibrary": "lucide",
273
+ "registries": {
274
+ "@assistant-ui": "https://r.assistant-ui.com/{name}.json"
275
+ }
2001
276
  }
2002
277
 
2003
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
2004
-
2005
278
  ```
2006
279
 
2007
280
  ## lib/utils.ts
@@ -2043,31 +316,30 @@ export default nextConfig;
2043
316
  "start": "next start"
2044
317
  },
2045
318
  "dependencies": {
2046
- "@ai-sdk/openai": "^3.0.13",
319
+ "@ai-sdk/openai": "^3.0.25",
2047
320
  "@assistant-ui/react": "workspace:*",
2048
321
  "@assistant-ui/react-ai-sdk": "workspace:*",
2049
322
  "@assistant-ui/react-markdown": "workspace:*",
323
+ "@assistant-ui/ui": "workspace:*",
2050
324
  "@radix-ui/react-avatar": "^1.1.11",
2051
325
  "@radix-ui/react-collapsible": "^1.1.12",
2052
326
  "@radix-ui/react-dialog": "^1.1.15",
2053
327
  "@radix-ui/react-slot": "^1.2.4",
2054
328
  "@radix-ui/react-tooltip": "^1.2.8",
2055
- "ai": "^6.0.42",
329
+ "ai": "^6.0.69",
2056
330
  "class-variance-authority": "^0.7.1",
2057
331
  "clsx": "^2.1.1",
2058
- "lucide-react": "^0.562.0",
2059
- "next": "^16.1.4",
2060
- "react": "^19.2.3",
2061
- "react-dom": "^19.2.3",
2062
- "remark-gfm": "^4.0.1",
2063
- "tailwind-merge": "^3.4.0",
2064
- "zustand": "^5.0.10"
332
+ "lucide-react": "^0.563.0",
333
+ "next": "^16.1.6",
334
+ "react": "^19.2.4",
335
+ "react-dom": "^19.2.4",
336
+ "tailwind-merge": "^3.4.0"
2065
337
  },
2066
338
  "devDependencies": {
2067
339
  "@assistant-ui/x-buildutils": "workspace:*",
2068
340
  "@tailwindcss/postcss": "^4.1.18",
2069
- "@types/node": "^25.0.9",
2070
- "@types/react": "^19.2.9",
341
+ "@types/node": "^25.2.0",
342
+ "@types/react": "^19.2.10",
2071
343
  "@types/react-dom": "^19.2.3",
2072
344
  "postcss": "^8.5.6",
2073
345
  "tailwindcss": "^4.1.18",
@@ -2078,13 +350,67 @@ export default nextConfig;
2078
350
 
2079
351
  ```
2080
352
 
353
+ ## README.md
354
+
355
+ ```markdown
356
+ # Assistant Cloud Integration
357
+
358
+ This example demonstrates how to use assistant-ui with Assistant Cloud for persistent conversation storage and thread management.
359
+
360
+ ## Quick Start
361
+
362
+ ### Using CLI (Recommended)
363
+
364
+ ```bash
365
+ npx assistant-ui@latest create my-app --example with-cloud
366
+ cd my-app
367
+ ```
368
+
369
+ ### Environment Variables
370
+
371
+ Create `.env.local`:
372
+
373
+ ```
374
+ OPENAI_API_KEY=sk-...
375
+ JWT_SECRET=your-jwt-secret
376
+ ASSISTANT_API_KEY=sk-aui-proj-...
377
+ NEXT_PUBLIC_ASSISTANT_BASE_URL=https://proj-xxxx.assistant-api.com
378
+ ```
379
+
380
+ ### Run
381
+
382
+ ```bash
383
+ npm run dev
384
+ ```
385
+
386
+ ## Features
387
+
388
+ - Persistent conversation storage with Assistant Cloud
389
+ - Thread list for managing multiple conversations
390
+ - Vercel AI SDK integration
391
+ - Markdown message rendering
392
+
393
+ ## Related Documentation
394
+
395
+ - [assistant-ui Documentation](https://www.assistant-ui.com/docs)
396
+ - [Assistant Cloud Guide](https://www.assistant-ui.com/docs/cloud)
397
+
398
+ ```
399
+
2081
400
  ## tsconfig.json
2082
401
 
2083
402
  ```json
2084
403
  {
2085
404
  "extends": "@assistant-ui/x-buildutils/ts/next",
2086
405
  "compilerOptions": {
2087
- "paths": { "@/*": ["./*"] }
406
+ "paths": {
407
+ "@/*": ["./*"],
408
+ "@/components/assistant-ui/*": [
409
+ "../../packages/ui/src/components/assistant-ui/*"
410
+ ],
411
+ "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
412
+ "@assistant-ui/ui/*": ["../../packages/ui/src/*"]
413
+ }
2088
414
  },
2089
415
  "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2090
416
  "exclude": ["node_modules"]