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