@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
@@ -32,6 +32,8 @@ export async function POST(req: Request) {
32
32
  @import "tailwindcss";
33
33
  @import "tw-animate-css";
34
34
 
35
+ @source "../../../packages/ui/src";
36
+
35
37
  @custom-variant dark (&:is(.dark *));
36
38
 
37
39
  @theme inline {
@@ -310,2203 +312,207 @@ export default function Home() {
310
312
  "lib": "@/lib",
311
313
  "hooks": "@/hooks"
312
314
  },
313
- "iconLibrary": "lucide"
315
+ "iconLibrary": "lucide",
316
+ "registries": {
317
+ "@assistant-ui": "https://r.assistant-ui.com/{name}.json"
318
+ }
314
319
  }
315
320
 
316
321
  ```
317
322
 
318
- ## components/assistant-ui/assistant-sidebar.tsx
319
-
320
- ```tsx
321
- import { Separator, ResizablePanel, Group } from "@/components/ui/resizable";
322
- import type { FC, PropsWithChildren } from "react";
323
-
324
- import { Thread } from "@/components/assistant-ui/thread";
325
-
326
- export const AssistantSidebar: FC<PropsWithChildren> = ({ children }) => {
327
- return (
328
- <Group orientation="horizontal">
329
- <ResizablePanel>{children}</ResizablePanel>
330
- <Separator />
331
- <ResizablePanel>
332
- <Thread />
333
- </ResizablePanel>
334
- </Group>
335
- );
336
- };
337
-
338
- ```
339
-
340
- ## components/assistant-ui/attachment.tsx
323
+ ## components/SignupForm.tsx
341
324
 
342
325
  ```tsx
343
326
  "use client";
344
-
345
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
346
- import Image from "next/image";
347
- import { XIcon, PlusIcon, FileText } from "lucide-react";
348
- import {
349
- AttachmentPrimitive,
350
- ComposerPrimitive,
351
- MessagePrimitive,
352
- useAssistantState,
353
- useAssistantApi,
354
- } from "@assistant-ui/react";
355
- import { useShallow } from "zustand/shallow";
356
- import {
357
- Tooltip,
358
- TooltipContent,
359
- TooltipTrigger,
360
- } from "@/components/ui/tooltip";
327
+ import { Button } from "@/components/ui/button";
361
328
  import {
362
- Dialog,
363
- DialogTitle,
364
- DialogContent,
365
- DialogTrigger,
366
- } from "@/components/ui/dialog";
367
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
368
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
369
- import { cn } from "@/lib/utils";
370
-
371
- const useFileSrc = (file: File | undefined) => {
372
- const [src, setSrc] = useState<string | undefined>(undefined);
373
-
374
- useEffect(() => {
375
- if (!file) {
376
- setSrc(undefined);
377
- return;
378
- }
379
-
380
- const objectUrl = URL.createObjectURL(file);
381
- setSrc(objectUrl);
382
-
383
- return () => {
384
- URL.revokeObjectURL(objectUrl);
385
- };
386
- }, [file]);
387
-
388
- return src;
389
- };
390
-
391
- const useAttachmentSrc = () => {
392
- const { file, src } = useAssistantState(
393
- useShallow(({ attachment }): { file?: File; src?: string } => {
394
- if (attachment.type !== "image") return {};
395
- if (attachment.file) return { file: attachment.file };
396
- const src = attachment.content?.filter((c) => c.type === "image")[0]
397
- ?.image;
398
- if (!src) return {};
399
- return { src };
400
- }),
401
- );
329
+ FormControl,
330
+ FormDescription,
331
+ FormField,
332
+ FormItem,
333
+ FormLabel,
334
+ FormMessage,
335
+ } from "@/components/ui/form";
336
+ import { Input } from "@/components/ui/input";
337
+ import { type FC, useState } from "react";
338
+ import { useFormContext } from "react-hook-form";
339
+ import { submitSignup } from "../lib/submitSignup";
402
340
 
403
- return useFileSrc(file) ?? src;
404
- };
341
+ export const SignupForm: FC = () => {
342
+ const form = useFormContext();
405
343
 
406
- type AttachmentPreviewProps = {
407
- src: string;
408
- };
344
+ const [isSubmitting, setIsSubmitting] = useState(false);
345
+ const [isSubmitted, setIsSubmitted] = useState(false);
409
346
 
410
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
411
- const [isLoaded, setIsLoaded] = useState(false);
412
- return (
413
- <Image
414
- src={src}
415
- alt="Image Preview"
416
- width={1}
417
- height={1}
418
- className={
419
- isLoaded
420
- ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
421
- : "aui-attachment-preview-image-loading hidden"
422
- }
423
- onLoadingComplete={() => setIsLoaded(true)}
424
- priority={false}
425
- />
426
- );
427
- };
347
+ const onSubmit = async (values: object) => {
348
+ try {
349
+ setIsSubmitting(true);
350
+ await submitSignup(values);
351
+ setIsSubmitted(true);
352
+ } finally {
353
+ setIsSubmitting(false);
354
+ }
355
+ };
428
356
 
429
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
430
- const src = useAttachmentSrc();
357
+ if (isSubmitting)
358
+ return <p className="my-4 font-bold text-green-600">Submitting...</p>;
431
359
 
432
- if (!src) return children;
360
+ if (isSubmitted)
361
+ return (
362
+ <p className="my-4 font-bold text-green-600">
363
+ Thank you for signing up, you will hear from me soon!
364
+ </p>
365
+ );
433
366
 
434
367
  return (
435
- <Dialog>
436
- <DialogTrigger
437
- className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
438
- asChild
439
- >
440
- {children}
441
- </DialogTrigger>
442
- <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">
443
- <DialogTitle className="aui-sr-only sr-only">
444
- Image Attachment Preview
445
- </DialogTitle>
446
- <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
447
- <AttachmentPreview src={src} />
448
- </div>
449
- </DialogContent>
450
- </Dialog>
451
- );
452
- };
453
-
454
- const AttachmentThumb: FC = () => {
455
- const isImage = useAssistantState(
456
- ({ attachment }) => attachment.type === "image",
457
- );
458
- const src = useAttachmentSrc();
368
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
369
+ <input type="hidden" {...form.register("hidden")} />
459
370
 
460
- return (
461
- <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
462
- <AvatarImage
463
- src={src}
464
- alt="Attachment preview"
465
- className="aui-attachment-tile-image object-cover"
371
+ <FormField
372
+ control={form.control}
373
+ name="firstName"
374
+ render={({ field }) => (
375
+ <FormItem>
376
+ <FormLabel>First Name</FormLabel>
377
+ <FormDescription>Your first name.</FormDescription>
378
+ <FormControl>
379
+ <Input placeholder="First Name" {...field} />
380
+ </FormControl>
381
+ <FormMessage />
382
+ </FormItem>
383
+ )}
466
384
  />
467
- <AvatarFallback delayMs={isImage ? 200 : 0}>
468
- <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
469
- </AvatarFallback>
470
- </Avatar>
471
- );
472
- };
473
385
 
474
- const AttachmentUI: FC = () => {
475
- const api = useAssistantApi();
476
- const isComposer = api.attachment.source === "composer";
477
-
478
- const isImage = useAssistantState(
479
- ({ attachment }) => attachment.type === "image",
480
- );
481
- const typeLabel = useAssistantState(({ attachment }) => {
482
- const type = attachment.type;
483
- switch (type) {
484
- case "image":
485
- return "Image";
486
- case "document":
487
- return "Document";
488
- case "file":
489
- return "File";
490
- default:
491
- const _exhaustiveCheck: never = type;
492
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
493
- }
494
- });
386
+ <FormField
387
+ control={form.control}
388
+ name="lastName"
389
+ render={({ field }) => (
390
+ <FormItem>
391
+ <FormLabel>Last Name</FormLabel>
392
+ <FormDescription>Your last name.</FormDescription>
393
+ <FormControl>
394
+ <Input placeholder="Last Name" {...field} />
395
+ </FormControl>
396
+ <FormMessage />
397
+ </FormItem>
398
+ )}
399
+ />
495
400
 
496
- return (
497
- <Tooltip>
498
- <AttachmentPrimitive.Root
499
- className={cn(
500
- "aui-attachment-root relative",
501
- isImage &&
502
- "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
401
+ <FormField
402
+ control={form.control}
403
+ name="email"
404
+ render={({ field }) => (
405
+ <FormItem>
406
+ <FormLabel>Email</FormLabel>
407
+ <FormDescription>Your email.</FormDescription>
408
+ <FormControl>
409
+ <Input placeholder="Email" {...field} />
410
+ </FormControl>
411
+ <FormMessage />
412
+ </FormItem>
503
413
  )}
504
- >
505
- <AttachmentPreviewDialog>
506
- <TooltipTrigger asChild>
507
- <div
508
- className={cn(
509
- "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
510
- isComposer &&
511
- "aui-attachment-tile-composer border-foreground/20",
512
- )}
513
- role="button"
514
- id="attachment-tile"
515
- aria-label={`${typeLabel} attachment`}
516
- >
517
- <AttachmentThumb />
518
- </div>
519
- </TooltipTrigger>
520
- </AttachmentPreviewDialog>
521
- {isComposer && <AttachmentRemove />}
522
- </AttachmentPrimitive.Root>
523
- <TooltipContent side="top">
524
- <AttachmentPrimitive.Name />
525
- </TooltipContent>
526
- </Tooltip>
527
- );
528
- };
414
+ />
529
415
 
530
- const AttachmentRemove: FC = () => {
531
- return (
532
- <AttachmentPrimitive.Remove asChild>
533
- <TooltipIconButton
534
- tooltip="Remove file"
535
- 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"
536
- side="top"
537
- >
538
- <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
539
- </TooltipIconButton>
540
- </AttachmentPrimitive.Remove>
541
- );
542
- };
416
+ <FormField
417
+ control={form.control}
418
+ name="cityAndCountry"
419
+ render={({ field }) => (
420
+ <FormItem>
421
+ <FormLabel>City</FormLabel>
422
+ <FormDescription>The city and country you live in.</FormDescription>
423
+ <FormControl>
424
+ <Input placeholder="City" {...field} />
425
+ </FormControl>
426
+ <FormMessage />
427
+ </FormItem>
428
+ )}
429
+ />
543
430
 
544
- export const UserMessageAttachments: FC = () => {
545
- return (
546
- <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">
547
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
548
- </div>
549
- );
550
- };
431
+ <FormField
432
+ control={form.control}
433
+ name="projectIdea"
434
+ render={({ field }) => (
435
+ <FormItem>
436
+ <FormLabel>Idea</FormLabel>
437
+ <FormDescription>
438
+ Do you have an idea for a project?
439
+ </FormDescription>
440
+ <FormControl>
441
+ <Input placeholder="Idea" {...field} />
442
+ </FormControl>
443
+ <FormMessage />
444
+ </FormItem>
445
+ )}
446
+ />
551
447
 
552
- export const ComposerAttachments: FC = () => {
553
- return (
554
- <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">
555
- <ComposerPrimitive.Attachments
556
- components={{ Attachment: AttachmentUI }}
448
+ <FormField
449
+ control={form.control}
450
+ name="proficientTechnologies"
451
+ render={({ field }) => (
452
+ <FormItem>
453
+ <FormLabel>Technologies</FormLabel>
454
+ <FormDescription>
455
+ What technologies are you most comfortable with?
456
+ </FormDescription>
457
+ <FormControl>
458
+ <Input placeholder="Next.js, Tailwind CSS" {...field} />
459
+ </FormControl>
460
+ <FormMessage />
461
+ </FormItem>
462
+ )}
557
463
  />
558
- </div>
559
- );
560
- };
561
464
 
562
- export const ComposerAddAttachment: FC = () => {
563
- return (
564
- <ComposerPrimitive.AddAttachment asChild>
565
- <TooltipIconButton
566
- tooltip="Add Attachment"
567
- side="bottom"
568
- variant="ghost"
569
- size="icon"
570
- 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"
571
- aria-label="Add Attachment"
572
- >
573
- <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
574
- </TooltipIconButton>
575
- </ComposerPrimitive.AddAttachment>
465
+ <Button type="submit">Submit</Button>
466
+ </form>
576
467
  );
577
468
  };
578
469
 
579
470
  ```
580
471
 
581
- ## components/assistant-ui/markdown-text.tsx
472
+ ## lib/submitSignup.tsx
582
473
 
583
474
  ```tsx
584
- "use client";
585
-
586
- import "@assistant-ui/react-markdown/styles/dot.css";
587
-
588
- import {
589
- type CodeHeaderProps,
590
- MarkdownTextPrimitive,
591
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
592
- useIsMarkdownCodeBlock,
593
- } from "@assistant-ui/react-markdown";
594
- import remarkGfm from "remark-gfm";
595
- import { type FC, memo, useState } from "react";
596
- import { CheckIcon, CopyIcon } from "lucide-react";
597
-
598
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
599
- import { cn } from "@/lib/utils";
600
-
601
- const MarkdownTextImpl = () => {
602
- return (
603
- <MarkdownTextPrimitive
604
- remarkPlugins={[remarkGfm]}
605
- className="aui-md"
606
- components={defaultComponents}
607
- />
608
- );
609
- };
610
-
611
- export const MarkdownText = memo(MarkdownTextImpl);
612
-
613
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
614
- const { isCopied, copyToClipboard } = useCopyToClipboard();
615
- const onCopy = () => {
616
- if (!code || isCopied) return;
617
- copyToClipboard(code);
618
- };
475
+ "use server";
619
476
 
620
- return (
621
- <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">
622
- <span className="aui-code-header-language lowercase [&>span]:text-xs">
623
- {language}
624
- </span>
625
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
626
- {!isCopied && <CopyIcon />}
627
- {isCopied && <CheckIcon />}
628
- </TooltipIconButton>
629
- </div>
630
- );
477
+ export const submitSignup = async (data: object) => {
478
+ const res = await fetch(process.env["ASSISTANT_UI_SUBMIT_SIGNUP_ENDPOINT"]!, {
479
+ method: "POST",
480
+ headers: {
481
+ "Content-Type": "application/json",
482
+ },
483
+ body: JSON.stringify(data),
484
+ });
485
+ return res.json();
631
486
  };
632
487
 
633
- const useCopyToClipboard = ({
634
- copiedDuration = 3000,
635
- }: {
636
- copiedDuration?: number;
637
- } = {}) => {
638
- const [isCopied, setIsCopied] = useState<boolean>(false);
639
-
640
- const copyToClipboard = (value: string) => {
641
- if (!value) return;
488
+ ```
642
489
 
643
- navigator.clipboard.writeText(value).then(() => {
644
- setIsCopied(true);
645
- setTimeout(() => setIsCopied(false), copiedDuration);
646
- });
647
- };
490
+ ## lib/utils.ts
648
491
 
649
- return { isCopied, copyToClipboard };
650
- };
492
+ ```typescript
493
+ import { type ClassValue, clsx } from "clsx";
494
+ import { twMerge } from "tailwind-merge";
651
495
 
652
- const defaultComponents = memoizeMarkdownComponents({
653
- h1: ({ className, ...props }) => (
654
- <h1
655
- className={cn(
656
- "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
657
- className,
658
- )}
659
- {...props}
660
- />
661
- ),
662
- h2: ({ className, ...props }) => (
663
- <h2
664
- className={cn(
665
- "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
666
- className,
667
- )}
668
- {...props}
669
- />
670
- ),
671
- h3: ({ className, ...props }) => (
672
- <h3
673
- className={cn(
674
- "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
675
- className,
676
- )}
677
- {...props}
678
- />
679
- ),
680
- h4: ({ className, ...props }) => (
681
- <h4
682
- className={cn(
683
- "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
684
- className,
685
- )}
686
- {...props}
687
- />
688
- ),
689
- h5: ({ className, ...props }) => (
690
- <h5
691
- className={cn(
692
- "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
693
- className,
694
- )}
695
- {...props}
696
- />
697
- ),
698
- h6: ({ className, ...props }) => (
699
- <h6
700
- className={cn(
701
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
702
- className,
703
- )}
704
- {...props}
705
- />
706
- ),
707
- p: ({ className, ...props }) => (
708
- <p
709
- className={cn(
710
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
711
- className,
712
- )}
713
- {...props}
714
- />
715
- ),
716
- a: ({ className, ...props }) => (
717
- <a
718
- className={cn(
719
- "aui-md-a font-medium text-primary underline underline-offset-4",
720
- className,
721
- )}
722
- {...props}
723
- />
724
- ),
725
- blockquote: ({ className, ...props }) => (
726
- <blockquote
727
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
728
- {...props}
729
- />
730
- ),
731
- ul: ({ className, ...props }) => (
732
- <ul
733
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
734
- {...props}
735
- />
736
- ),
737
- ol: ({ className, ...props }) => (
738
- <ol
739
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
740
- {...props}
741
- />
742
- ),
743
- hr: ({ className, ...props }) => (
744
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
745
- ),
746
- table: ({ className, ...props }) => (
747
- <table
748
- className={cn(
749
- "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
750
- className,
751
- )}
752
- {...props}
753
- />
754
- ),
755
- th: ({ className, ...props }) => (
756
- <th
757
- className={cn(
758
- "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",
759
- className,
760
- )}
761
- {...props}
762
- />
763
- ),
764
- td: ({ className, ...props }) => (
765
- <td
766
- className={cn(
767
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
768
- className,
769
- )}
770
- {...props}
771
- />
772
- ),
773
- tr: ({ className, ...props }) => (
774
- <tr
775
- className={cn(
776
- "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",
777
- className,
778
- )}
779
- {...props}
780
- />
781
- ),
782
- sup: ({ className, ...props }) => (
783
- <sup
784
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
785
- {...props}
786
- />
787
- ),
788
- pre: ({ className, ...props }) => (
789
- <pre
790
- className={cn(
791
- "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
792
- className,
793
- )}
794
- {...props}
795
- />
796
- ),
797
- code: function Code({ className, ...props }) {
798
- const isCodeBlock = useIsMarkdownCodeBlock();
799
- return (
800
- <code
801
- className={cn(
802
- !isCodeBlock &&
803
- "aui-md-inline-code rounded border bg-muted font-semibold",
804
- className,
805
- )}
806
- {...props}
807
- />
808
- );
809
- },
810
- CodeHeader,
811
- });
496
+ export function cn(...inputs: ClassValue[]) {
497
+ return twMerge(clsx(inputs));
498
+ }
812
499
 
813
500
  ```
814
501
 
815
- ## components/assistant-ui/thread.tsx
816
-
817
- ```tsx
818
- import {
819
- ComposerAddAttachment,
820
- ComposerAttachments,
821
- UserMessageAttachments,
822
- } from "@/components/assistant-ui/attachment";
823
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
824
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
825
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
826
- import { Button } from "@/components/ui/button";
827
- import { cn } from "@/lib/utils";
828
- import {
829
- ActionBarMorePrimitive,
830
- ActionBarPrimitive,
831
- AssistantIf,
832
- BranchPickerPrimitive,
833
- ComposerPrimitive,
834
- ErrorPrimitive,
835
- MessagePrimitive,
836
- ThreadPrimitive,
837
- } from "@assistant-ui/react";
838
- import {
839
- ArrowDownIcon,
840
- ArrowUpIcon,
841
- CheckIcon,
842
- ChevronLeftIcon,
843
- ChevronRightIcon,
844
- CopyIcon,
845
- DownloadIcon,
846
- MoreHorizontalIcon,
847
- PencilIcon,
848
- RefreshCwIcon,
849
- SquareIcon,
850
- } from "lucide-react";
851
- import type { FC } from "react";
852
-
853
- export const Thread: FC = () => {
854
- return (
855
- <ThreadPrimitive.Root
856
- className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
857
- style={{
858
- ["--thread-max-width" as string]: "44rem",
859
- }}
860
- >
861
- <ThreadPrimitive.Viewport
862
- turnAnchor="top"
863
- className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
864
- >
865
- <AssistantIf condition={({ thread }) => thread.isEmpty}>
866
- <ThreadWelcome />
867
- </AssistantIf>
868
-
869
- <ThreadPrimitive.Messages
870
- components={{
871
- UserMessage,
872
- EditComposer,
873
- AssistantMessage,
874
- }}
875
- />
876
-
877
- <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">
878
- <ThreadScrollToBottom />
879
- <Composer />
880
- </ThreadPrimitive.ViewportFooter>
881
- </ThreadPrimitive.Viewport>
882
- </ThreadPrimitive.Root>
883
- );
884
- };
502
+ ## next.config.ts
885
503
 
886
- const ThreadScrollToBottom: FC = () => {
887
- return (
888
- <ThreadPrimitive.ScrollToBottom asChild>
889
- <TooltipIconButton
890
- tooltip="Scroll to bottom"
891
- variant="outline"
892
- 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"
893
- >
894
- <ArrowDownIcon />
895
- </TooltipIconButton>
896
- </ThreadPrimitive.ScrollToBottom>
897
- );
898
- };
504
+ ```typescript
505
+ import type { NextConfig } from "next";
899
506
 
900
- const ThreadWelcome: FC = () => {
901
- return (
902
- <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
903
- <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
904
- <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
905
- <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
906
- Hello there!
907
- </h1>
908
- <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">
909
- How can I help you today?
910
- </p>
911
- </div>
912
- </div>
913
- <ThreadSuggestions />
914
- </div>
915
- );
507
+ const nextConfig: NextConfig = {
508
+ /* config options here */
916
509
  };
917
510
 
918
- const SUGGESTIONS = [
919
- {
920
- title: "What's the weather",
921
- label: "in San Francisco?",
922
- prompt: "What's the weather in San Francisco?",
923
- },
924
- {
925
- title: "Explain React hooks",
926
- label: "like useState and useEffect",
927
- prompt: "Explain React hooks like useState and useEffect",
928
- },
929
- ] as const;
511
+ export default nextConfig;
930
512
 
931
- const ThreadSuggestions: FC = () => {
932
- return (
933
- <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
934
- {SUGGESTIONS.map((suggestion, index) => (
935
- <div
936
- key={suggestion.prompt}
937
- 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"
938
- style={{ animationDelay: `${100 + index * 50}ms` }}
939
- >
940
- <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
941
- <Button
942
- variant="ghost"
943
- 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"
944
- aria-label={suggestion.prompt}
945
- >
946
- <span className="aui-thread-welcome-suggestion-text-1 font-medium">
947
- {suggestion.title}
948
- </span>
949
- <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
950
- {suggestion.label}
951
- </span>
952
- </Button>
953
- </ThreadPrimitive.Suggestion>
954
- </div>
955
- ))}
956
- </div>
957
- );
958
- };
513
+ ```
959
514
 
960
- const Composer: FC = () => {
961
- return (
962
- <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
963
- <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">
964
- <ComposerAttachments />
965
- <ComposerPrimitive.Input
966
- placeholder="Send a message..."
967
- 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"
968
- rows={1}
969
- autoFocus
970
- aria-label="Message input"
971
- />
972
- <ComposerAction />
973
- </ComposerPrimitive.AttachmentDropzone>
974
- </ComposerPrimitive.Root>
975
- );
976
- };
977
-
978
- const ComposerAction: FC = () => {
979
- return (
980
- <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
981
- <ComposerAddAttachment />
982
-
983
- <AssistantIf condition={({ thread }) => !thread.isRunning}>
984
- <ComposerPrimitive.Send asChild>
985
- <TooltipIconButton
986
- tooltip="Send message"
987
- side="bottom"
988
- type="submit"
989
- variant="default"
990
- size="icon"
991
- className="aui-composer-send size-8 rounded-full"
992
- aria-label="Send message"
993
- >
994
- <ArrowUpIcon className="aui-composer-send-icon size-4" />
995
- </TooltipIconButton>
996
- </ComposerPrimitive.Send>
997
- </AssistantIf>
998
-
999
- <AssistantIf condition={({ thread }) => thread.isRunning}>
1000
- <ComposerPrimitive.Cancel asChild>
1001
- <Button
1002
- type="button"
1003
- variant="default"
1004
- size="icon"
1005
- className="aui-composer-cancel size-8 rounded-full"
1006
- aria-label="Stop generating"
1007
- >
1008
- <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
1009
- </Button>
1010
- </ComposerPrimitive.Cancel>
1011
- </AssistantIf>
1012
- </div>
1013
- );
1014
- };
1015
-
1016
- const MessageError: FC = () => {
1017
- return (
1018
- <MessagePrimitive.Error>
1019
- <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">
1020
- <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
1021
- </ErrorPrimitive.Root>
1022
- </MessagePrimitive.Error>
1023
- );
1024
- };
1025
-
1026
- const AssistantMessage: FC = () => {
1027
- return (
1028
- <MessagePrimitive.Root
1029
- 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"
1030
- data-role="assistant"
1031
- >
1032
- <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
1033
- <MessagePrimitive.Parts
1034
- components={{
1035
- Text: MarkdownText,
1036
- tools: { Fallback: ToolFallback },
1037
- }}
1038
- />
1039
- <MessageError />
1040
- </div>
1041
-
1042
- <div className="aui-assistant-message-footer mt-1 ml-2 flex">
1043
- <BranchPicker />
1044
- <AssistantActionBar />
1045
- </div>
1046
- </MessagePrimitive.Root>
1047
- );
1048
- };
1049
-
1050
- const AssistantActionBar: FC = () => {
1051
- return (
1052
- <ActionBarPrimitive.Root
1053
- hideWhenRunning
1054
- autohide="not-last"
1055
- autohideFloat="single-branch"
1056
- 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"
1057
- >
1058
- <ActionBarPrimitive.Copy asChild>
1059
- <TooltipIconButton tooltip="Copy">
1060
- <AssistantIf condition={({ message }) => message.isCopied}>
1061
- <CheckIcon />
1062
- </AssistantIf>
1063
- <AssistantIf condition={({ message }) => !message.isCopied}>
1064
- <CopyIcon />
1065
- </AssistantIf>
1066
- </TooltipIconButton>
1067
- </ActionBarPrimitive.Copy>
1068
- <ActionBarPrimitive.Reload asChild>
1069
- <TooltipIconButton tooltip="Refresh">
1070
- <RefreshCwIcon />
1071
- </TooltipIconButton>
1072
- </ActionBarPrimitive.Reload>
1073
- <ActionBarMorePrimitive.Root>
1074
- <ActionBarMorePrimitive.Trigger asChild>
1075
- <TooltipIconButton
1076
- tooltip="More"
1077
- className="data-[state=open]:bg-accent"
1078
- >
1079
- <MoreHorizontalIcon />
1080
- </TooltipIconButton>
1081
- </ActionBarMorePrimitive.Trigger>
1082
- <ActionBarMorePrimitive.Content
1083
- side="bottom"
1084
- align="start"
1085
- 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"
1086
- >
1087
- <ActionBarPrimitive.ExportMarkdown asChild>
1088
- <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">
1089
- <DownloadIcon className="size-4" />
1090
- Export as Markdown
1091
- </ActionBarMorePrimitive.Item>
1092
- </ActionBarPrimitive.ExportMarkdown>
1093
- </ActionBarMorePrimitive.Content>
1094
- </ActionBarMorePrimitive.Root>
1095
- </ActionBarPrimitive.Root>
1096
- );
1097
- };
1098
-
1099
- const UserMessage: FC = () => {
1100
- return (
1101
- <MessagePrimitive.Root
1102
- 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"
1103
- data-role="user"
1104
- >
1105
- <UserMessageAttachments />
1106
-
1107
- <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1108
- <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1109
- <MessagePrimitive.Parts />
1110
- </div>
1111
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1112
- <UserActionBar />
1113
- </div>
1114
- </div>
1115
-
1116
- <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1117
- </MessagePrimitive.Root>
1118
- );
1119
- };
1120
-
1121
- const UserActionBar: FC = () => {
1122
- return (
1123
- <ActionBarPrimitive.Root
1124
- hideWhenRunning
1125
- autohide="not-last"
1126
- className="aui-user-action-bar-root flex flex-col items-end"
1127
- >
1128
- <ActionBarPrimitive.Edit asChild>
1129
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1130
- <PencilIcon />
1131
- </TooltipIconButton>
1132
- </ActionBarPrimitive.Edit>
1133
- </ActionBarPrimitive.Root>
1134
- );
1135
- };
1136
-
1137
- const EditComposer: FC = () => {
1138
- return (
1139
- <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1140
- <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1141
- <ComposerPrimitive.Input
1142
- className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1143
- autoFocus
1144
- />
1145
- <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1146
- <ComposerPrimitive.Cancel asChild>
1147
- <Button variant="ghost" size="sm">
1148
- Cancel
1149
- </Button>
1150
- </ComposerPrimitive.Cancel>
1151
- <ComposerPrimitive.Send asChild>
1152
- <Button size="sm">Update</Button>
1153
- </ComposerPrimitive.Send>
1154
- </div>
1155
- </ComposerPrimitive.Root>
1156
- </MessagePrimitive.Root>
1157
- );
1158
- };
1159
-
1160
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1161
- className,
1162
- ...rest
1163
- }) => {
1164
- return (
1165
- <BranchPickerPrimitive.Root
1166
- hideWhenSingleBranch
1167
- className={cn(
1168
- "aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
1169
- className,
1170
- )}
1171
- {...rest}
1172
- >
1173
- <BranchPickerPrimitive.Previous asChild>
1174
- <TooltipIconButton tooltip="Previous">
1175
- <ChevronLeftIcon />
1176
- </TooltipIconButton>
1177
- </BranchPickerPrimitive.Previous>
1178
- <span className="aui-branch-picker-state font-medium">
1179
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1180
- </span>
1181
- <BranchPickerPrimitive.Next asChild>
1182
- <TooltipIconButton tooltip="Next">
1183
- <ChevronRightIcon />
1184
- </TooltipIconButton>
1185
- </BranchPickerPrimitive.Next>
1186
- </BranchPickerPrimitive.Root>
1187
- );
1188
- };
1189
-
1190
- ```
1191
-
1192
- ## components/assistant-ui/tool-fallback.tsx
1193
-
1194
- ```tsx
1195
- "use client";
1196
-
1197
- import { memo, useCallback, useRef, useState } from "react";
1198
- import {
1199
- AlertCircleIcon,
1200
- CheckIcon,
1201
- ChevronDownIcon,
1202
- LoaderIcon,
1203
- XCircleIcon,
1204
- } from "lucide-react";
1205
- import {
1206
- useScrollLock,
1207
- type ToolCallMessagePartStatus,
1208
- type ToolCallMessagePartComponent,
1209
- } from "@assistant-ui/react";
1210
- import {
1211
- Collapsible,
1212
- CollapsibleContent,
1213
- CollapsibleTrigger,
1214
- } from "@/components/ui/collapsible";
1215
- import { cn } from "@/lib/utils";
1216
-
1217
- const ANIMATION_DURATION = 200;
1218
-
1219
- export type ToolFallbackRootProps = Omit<
1220
- React.ComponentProps<typeof Collapsible>,
1221
- "open" | "onOpenChange"
1222
- > & {
1223
- open?: boolean;
1224
- onOpenChange?: (open: boolean) => void;
1225
- defaultOpen?: boolean;
1226
- };
1227
-
1228
- function ToolFallbackRoot({
1229
- className,
1230
- open: controlledOpen,
1231
- onOpenChange: controlledOnOpenChange,
1232
- defaultOpen = false,
1233
- children,
1234
- ...props
1235
- }: ToolFallbackRootProps) {
1236
- const collapsibleRef = useRef<HTMLDivElement>(null);
1237
- const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
1238
- const lockScroll = useScrollLock(collapsibleRef, ANIMATION_DURATION);
1239
-
1240
- const isControlled = controlledOpen !== undefined;
1241
- const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
1242
-
1243
- const handleOpenChange = useCallback(
1244
- (open: boolean) => {
1245
- if (!open) {
1246
- lockScroll();
1247
- }
1248
- if (!isControlled) {
1249
- setUncontrolledOpen(open);
1250
- }
1251
- controlledOnOpenChange?.(open);
1252
- },
1253
- [lockScroll, isControlled, controlledOnOpenChange],
1254
- );
1255
-
1256
- return (
1257
- <Collapsible
1258
- ref={collapsibleRef}
1259
- data-slot="tool-fallback-root"
1260
- open={isOpen}
1261
- onOpenChange={handleOpenChange}
1262
- className={cn(
1263
- "aui-tool-fallback-root group/tool-fallback-root w-full rounded-lg border py-3",
1264
- className,
1265
- )}
1266
- style={
1267
- {
1268
- "--animation-duration": `${ANIMATION_DURATION}ms`,
1269
- } as React.CSSProperties
1270
- }
1271
- {...props}
1272
- >
1273
- {children}
1274
- </Collapsible>
1275
- );
1276
- }
1277
-
1278
- type ToolStatus = ToolCallMessagePartStatus["type"];
1279
-
1280
- const statusIconMap: Record<ToolStatus, React.ElementType> = {
1281
- running: LoaderIcon,
1282
- complete: CheckIcon,
1283
- incomplete: XCircleIcon,
1284
- "requires-action": AlertCircleIcon,
1285
- };
1286
-
1287
- function ToolFallbackTrigger({
1288
- toolName,
1289
- status,
1290
- className,
1291
- ...props
1292
- }: React.ComponentProps<typeof CollapsibleTrigger> & {
1293
- toolName: string;
1294
- status?: ToolCallMessagePartStatus;
1295
- }) {
1296
- const statusType = status?.type ?? "complete";
1297
- const isRunning = statusType === "running";
1298
- const isCancelled =
1299
- status?.type === "incomplete" && status.reason === "cancelled";
1300
-
1301
- const Icon = statusIconMap[statusType];
1302
- const label = isCancelled ? "Cancelled tool" : "Used tool";
1303
-
1304
- return (
1305
- <CollapsibleTrigger
1306
- data-slot="tool-fallback-trigger"
1307
- className={cn(
1308
- "aui-tool-fallback-trigger group/trigger flex w-full items-center gap-2 px-4 text-sm transition-colors",
1309
- className,
1310
- )}
1311
- {...props}
1312
- >
1313
- <Icon
1314
- data-slot="tool-fallback-trigger-icon"
1315
- className={cn(
1316
- "aui-tool-fallback-trigger-icon size-4 shrink-0",
1317
- isCancelled && "text-muted-foreground",
1318
- isRunning && "animate-spin",
1319
- )}
1320
- />
1321
- <span
1322
- data-slot="tool-fallback-trigger-label"
1323
- className={cn(
1324
- "aui-tool-fallback-trigger-label-wrapper relative inline-block grow text-left leading-none",
1325
- isCancelled && "text-muted-foreground line-through",
1326
- )}
1327
- >
1328
- <span>
1329
- {label}: <b>{toolName}</b>
1330
- </span>
1331
- {isRunning && (
1332
- <span
1333
- aria-hidden
1334
- data-slot="tool-fallback-trigger-shimmer"
1335
- className="aui-tool-fallback-trigger-shimmer shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
1336
- >
1337
- {label}: <b>{toolName}</b>
1338
- </span>
1339
- )}
1340
- </span>
1341
- <ChevronDownIcon
1342
- data-slot="tool-fallback-trigger-chevron"
1343
- className={cn(
1344
- "aui-tool-fallback-trigger-chevron size-4 shrink-0",
1345
- "transition-transform duration-(--animation-duration) ease-out",
1346
- "group-data-[state=closed]/trigger:-rotate-90",
1347
- "group-data-[state=open]/trigger:rotate-0",
1348
- )}
1349
- />
1350
- </CollapsibleTrigger>
1351
- );
1352
- }
1353
-
1354
- function ToolFallbackContent({
1355
- className,
1356
- children,
1357
- ...props
1358
- }: React.ComponentProps<typeof CollapsibleContent>) {
1359
- return (
1360
- <CollapsibleContent
1361
- data-slot="tool-fallback-content"
1362
- className={cn(
1363
- "aui-tool-fallback-content relative overflow-hidden text-sm outline-none",
1364
- "group/collapsible-content ease-out",
1365
- "data-[state=closed]:animate-collapsible-up",
1366
- "data-[state=open]:animate-collapsible-down",
1367
- "data-[state=closed]:fill-mode-forwards",
1368
- "data-[state=closed]:pointer-events-none",
1369
- "data-[state=open]:duration-(--animation-duration)",
1370
- "data-[state=closed]:duration-(--animation-duration)",
1371
- className,
1372
- )}
1373
- {...props}
1374
- >
1375
- <div className="mt-3 flex flex-col gap-2 border-t pt-2">{children}</div>
1376
- </CollapsibleContent>
1377
- );
1378
- }
1379
-
1380
- function ToolFallbackArgs({
1381
- argsText,
1382
- className,
1383
- ...props
1384
- }: React.ComponentProps<"div"> & {
1385
- argsText?: string;
1386
- }) {
1387
- if (!argsText) return null;
1388
-
1389
- return (
1390
- <div
1391
- data-slot="tool-fallback-args"
1392
- className={cn("aui-tool-fallback-args px-4", className)}
1393
- {...props}
1394
- >
1395
- <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1396
- {argsText}
1397
- </pre>
1398
- </div>
1399
- );
1400
- }
1401
-
1402
- function ToolFallbackResult({
1403
- result,
1404
- className,
1405
- ...props
1406
- }: React.ComponentProps<"div"> & {
1407
- result?: unknown;
1408
- }) {
1409
- if (result === undefined) return null;
1410
-
1411
- return (
1412
- <div
1413
- data-slot="tool-fallback-result"
1414
- className={cn(
1415
- "aui-tool-fallback-result border-t border-dashed px-4 pt-2",
1416
- className,
1417
- )}
1418
- {...props}
1419
- >
1420
- <p className="aui-tool-fallback-result-header font-semibold">Result:</p>
1421
- <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1422
- {typeof result === "string" ? result : JSON.stringify(result, null, 2)}
1423
- </pre>
1424
- </div>
1425
- );
1426
- }
1427
-
1428
- function ToolFallbackError({
1429
- status,
1430
- className,
1431
- ...props
1432
- }: React.ComponentProps<"div"> & {
1433
- status?: ToolCallMessagePartStatus;
1434
- }) {
1435
- if (status?.type !== "incomplete") return null;
1436
-
1437
- const error = status.error;
1438
- const errorText = error
1439
- ? typeof error === "string"
1440
- ? error
1441
- : JSON.stringify(error)
1442
- : null;
1443
-
1444
- if (!errorText) return null;
1445
-
1446
- const isCancelled = status.reason === "cancelled";
1447
- const headerText = isCancelled ? "Cancelled reason:" : "Error:";
1448
-
1449
- return (
1450
- <div
1451
- data-slot="tool-fallback-error"
1452
- className={cn("aui-tool-fallback-error px-4", className)}
1453
- {...props}
1454
- >
1455
- <p className="aui-tool-fallback-error-header font-semibold text-muted-foreground">
1456
- {headerText}
1457
- </p>
1458
- <p className="aui-tool-fallback-error-reason text-muted-foreground">
1459
- {errorText}
1460
- </p>
1461
- </div>
1462
- );
1463
- }
1464
-
1465
- const ToolFallbackImpl: ToolCallMessagePartComponent = ({
1466
- toolName,
1467
- argsText,
1468
- result,
1469
- status,
1470
- }) => {
1471
- const isCancelled =
1472
- status?.type === "incomplete" && status.reason === "cancelled";
1473
-
1474
- return (
1475
- <ToolFallbackRoot
1476
- className={cn(isCancelled && "border-muted-foreground/30 bg-muted/30")}
1477
- >
1478
- <ToolFallbackTrigger toolName={toolName} status={status} />
1479
- <ToolFallbackContent>
1480
- <ToolFallbackError status={status} />
1481
- <ToolFallbackArgs
1482
- argsText={argsText}
1483
- className={cn(isCancelled && "opacity-60")}
1484
- />
1485
- {!isCancelled && <ToolFallbackResult result={result} />}
1486
- </ToolFallbackContent>
1487
- </ToolFallbackRoot>
1488
- );
1489
- };
1490
-
1491
- const ToolFallback = memo(
1492
- ToolFallbackImpl,
1493
- ) as unknown as ToolCallMessagePartComponent & {
1494
- Root: typeof ToolFallbackRoot;
1495
- Trigger: typeof ToolFallbackTrigger;
1496
- Content: typeof ToolFallbackContent;
1497
- Args: typeof ToolFallbackArgs;
1498
- Result: typeof ToolFallbackResult;
1499
- Error: typeof ToolFallbackError;
1500
- };
1501
-
1502
- ToolFallback.displayName = "ToolFallback";
1503
- ToolFallback.Root = ToolFallbackRoot;
1504
- ToolFallback.Trigger = ToolFallbackTrigger;
1505
- ToolFallback.Content = ToolFallbackContent;
1506
- ToolFallback.Args = ToolFallbackArgs;
1507
- ToolFallback.Result = ToolFallbackResult;
1508
- ToolFallback.Error = ToolFallbackError;
1509
-
1510
- export {
1511
- ToolFallback,
1512
- ToolFallbackRoot,
1513
- ToolFallbackTrigger,
1514
- ToolFallbackContent,
1515
- ToolFallbackArgs,
1516
- ToolFallbackResult,
1517
- ToolFallbackError,
1518
- };
1519
-
1520
- ```
1521
-
1522
- ## components/assistant-ui/tooltip-icon-button.tsx
1523
-
1524
- ```tsx
1525
- "use client";
1526
-
1527
- import { ComponentPropsWithRef, forwardRef } from "react";
1528
- import { Slottable } from "@radix-ui/react-slot";
1529
-
1530
- import {
1531
- Tooltip,
1532
- TooltipContent,
1533
- TooltipTrigger,
1534
- } from "@/components/ui/tooltip";
1535
- import { Button } from "@/components/ui/button";
1536
- import { cn } from "@/lib/utils";
1537
-
1538
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1539
- tooltip: string;
1540
- side?: "top" | "bottom" | "left" | "right";
1541
- };
1542
-
1543
- export const TooltipIconButton = forwardRef<
1544
- HTMLButtonElement,
1545
- TooltipIconButtonProps
1546
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1547
- return (
1548
- <Tooltip>
1549
- <TooltipTrigger asChild>
1550
- <Button
1551
- variant="ghost"
1552
- size="icon"
1553
- {...rest}
1554
- className={cn("aui-button-icon size-6 p-1", className)}
1555
- ref={ref}
1556
- >
1557
- <Slottable>{children}</Slottable>
1558
- <span className="aui-sr-only sr-only">{tooltip}</span>
1559
- </Button>
1560
- </TooltipTrigger>
1561
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1562
- </Tooltip>
1563
- );
1564
- });
1565
-
1566
- TooltipIconButton.displayName = "TooltipIconButton";
1567
-
1568
- ```
1569
-
1570
- ## components/SignupForm.tsx
1571
-
1572
- ```tsx
1573
- "use client";
1574
- import { Button } from "@/components/ui/button";
1575
- import {
1576
- FormControl,
1577
- FormDescription,
1578
- FormField,
1579
- FormItem,
1580
- FormLabel,
1581
- FormMessage,
1582
- } from "@/components/ui/form";
1583
- import { Input } from "@/components/ui/input";
1584
- import { type FC, useState } from "react";
1585
- import { useFormContext } from "react-hook-form";
1586
- import { submitSignup } from "../lib/submitSignup";
1587
-
1588
- export const SignupForm: FC = () => {
1589
- const form = useFormContext();
1590
-
1591
- const [isSubmitting, setIsSubmitting] = useState(false);
1592
- const [isSubmitted, setIsSubmitted] = useState(false);
1593
-
1594
- const onSubmit = async (values: object) => {
1595
- try {
1596
- setIsSubmitting(true);
1597
- await submitSignup(values);
1598
- setIsSubmitted(true);
1599
- } finally {
1600
- setIsSubmitting(false);
1601
- }
1602
- };
1603
-
1604
- if (isSubmitting)
1605
- return <p className="my-4 font-bold text-green-600">Submitting...</p>;
1606
-
1607
- if (isSubmitted)
1608
- return (
1609
- <p className="my-4 font-bold text-green-600">
1610
- Thank you for signing up, you will hear from me soon!
1611
- </p>
1612
- );
1613
-
1614
- return (
1615
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
1616
- <input type="hidden" {...form.register("hidden")} />
1617
-
1618
- <FormField
1619
- control={form.control}
1620
- name="firstName"
1621
- render={({ field }) => (
1622
- <FormItem>
1623
- <FormLabel>First Name</FormLabel>
1624
- <FormDescription>Your first name.</FormDescription>
1625
- <FormControl>
1626
- <Input placeholder="First Name" {...field} />
1627
- </FormControl>
1628
- <FormMessage />
1629
- </FormItem>
1630
- )}
1631
- />
1632
-
1633
- <FormField
1634
- control={form.control}
1635
- name="lastName"
1636
- render={({ field }) => (
1637
- <FormItem>
1638
- <FormLabel>Last Name</FormLabel>
1639
- <FormDescription>Your last name.</FormDescription>
1640
- <FormControl>
1641
- <Input placeholder="Last Name" {...field} />
1642
- </FormControl>
1643
- <FormMessage />
1644
- </FormItem>
1645
- )}
1646
- />
1647
-
1648
- <FormField
1649
- control={form.control}
1650
- name="email"
1651
- render={({ field }) => (
1652
- <FormItem>
1653
- <FormLabel>Email</FormLabel>
1654
- <FormDescription>Your email.</FormDescription>
1655
- <FormControl>
1656
- <Input placeholder="Email" {...field} />
1657
- </FormControl>
1658
- <FormMessage />
1659
- </FormItem>
1660
- )}
1661
- />
1662
-
1663
- <FormField
1664
- control={form.control}
1665
- name="cityAndCountry"
1666
- render={({ field }) => (
1667
- <FormItem>
1668
- <FormLabel>City</FormLabel>
1669
- <FormDescription>The city and country you live in.</FormDescription>
1670
- <FormControl>
1671
- <Input placeholder="City" {...field} />
1672
- </FormControl>
1673
- <FormMessage />
1674
- </FormItem>
1675
- )}
1676
- />
1677
-
1678
- <FormField
1679
- control={form.control}
1680
- name="projectIdea"
1681
- render={({ field }) => (
1682
- <FormItem>
1683
- <FormLabel>Idea</FormLabel>
1684
- <FormDescription>
1685
- Do you have an idea for a project?
1686
- </FormDescription>
1687
- <FormControl>
1688
- <Input placeholder="Idea" {...field} />
1689
- </FormControl>
1690
- <FormMessage />
1691
- </FormItem>
1692
- )}
1693
- />
1694
-
1695
- <FormField
1696
- control={form.control}
1697
- name="proficientTechnologies"
1698
- render={({ field }) => (
1699
- <FormItem>
1700
- <FormLabel>Technologies</FormLabel>
1701
- <FormDescription>
1702
- What technologies are you most comfortable with?
1703
- </FormDescription>
1704
- <FormControl>
1705
- <Input placeholder="Next.js, Tailwind CSS" {...field} />
1706
- </FormControl>
1707
- <FormMessage />
1708
- </FormItem>
1709
- )}
1710
- />
1711
-
1712
- <Button type="submit">Submit</Button>
1713
- </form>
1714
- );
1715
- };
1716
-
1717
- ```
1718
-
1719
- ## components/ui/avatar.tsx
1720
-
1721
- ```tsx
1722
- "use client";
1723
-
1724
- import * as React from "react";
1725
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1726
-
1727
- import { cn } from "@/lib/utils";
1728
-
1729
- function Avatar({
1730
- className,
1731
- ...props
1732
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
1733
- return (
1734
- <AvatarPrimitive.Root
1735
- data-slot="avatar"
1736
- className={cn(
1737
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1738
- className,
1739
- )}
1740
- {...props}
1741
- />
1742
- );
1743
- }
1744
-
1745
- function AvatarImage({
1746
- className,
1747
- ...props
1748
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
1749
- return (
1750
- <AvatarPrimitive.Image
1751
- data-slot="avatar-image"
1752
- className={cn("aspect-square size-full", className)}
1753
- {...props}
1754
- />
1755
- );
1756
- }
1757
-
1758
- function AvatarFallback({
1759
- className,
1760
- ...props
1761
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
1762
- return (
1763
- <AvatarPrimitive.Fallback
1764
- data-slot="avatar-fallback"
1765
- className={cn(
1766
- "flex size-full items-center justify-center rounded-full bg-muted",
1767
- className,
1768
- )}
1769
- {...props}
1770
- />
1771
- );
1772
- }
1773
-
1774
- export { Avatar, AvatarImage, AvatarFallback };
1775
-
1776
- ```
1777
-
1778
- ## components/ui/button.tsx
1779
-
1780
- ```tsx
1781
- import * as React from "react";
1782
- import { Slot } from "@radix-ui/react-slot";
1783
- import { cva, type VariantProps } from "class-variance-authority";
1784
-
1785
- import { cn } from "@/lib/utils";
1786
-
1787
- const buttonVariants = cva(
1788
- "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",
1789
- {
1790
- variants: {
1791
- variant: {
1792
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1793
- destructive:
1794
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1795
- outline:
1796
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1797
- secondary:
1798
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1799
- ghost:
1800
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1801
- link: "text-primary underline-offset-4 hover:underline",
1802
- },
1803
- size: {
1804
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1805
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1806
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1807
- icon: "size-9",
1808
- "icon-sm": "size-8",
1809
- "icon-lg": "size-10",
1810
- },
1811
- },
1812
- defaultVariants: {
1813
- variant: "default",
1814
- size: "default",
1815
- },
1816
- },
1817
- );
1818
-
1819
- function Button({
1820
- className,
1821
- variant = "default",
1822
- size = "default",
1823
- asChild = false,
1824
- ...props
1825
- }: React.ComponentProps<"button"> &
1826
- VariantProps<typeof buttonVariants> & {
1827
- asChild?: boolean;
1828
- }) {
1829
- const Comp = asChild ? Slot : "button";
1830
-
1831
- return (
1832
- <Comp
1833
- data-slot="button"
1834
- data-variant={variant}
1835
- data-size={size}
1836
- className={cn(buttonVariants({ variant, size, className }))}
1837
- {...props}
1838
- />
1839
- );
1840
- }
1841
-
1842
- export { Button, buttonVariants };
1843
-
1844
- ```
1845
-
1846
- ## components/ui/collapsible.tsx
1847
-
1848
- ```tsx
1849
- "use client";
1850
-
1851
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
1852
-
1853
- function Collapsible({
1854
- ...props
1855
- }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
1856
- return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
1857
- }
1858
-
1859
- function CollapsibleTrigger({
1860
- ...props
1861
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
1862
- return (
1863
- <CollapsiblePrimitive.CollapsibleTrigger
1864
- data-slot="collapsible-trigger"
1865
- {...props}
1866
- />
1867
- );
1868
- }
1869
-
1870
- function CollapsibleContent({
1871
- ...props
1872
- }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
1873
- return (
1874
- <CollapsiblePrimitive.CollapsibleContent
1875
- data-slot="collapsible-content"
1876
- {...props}
1877
- />
1878
- );
1879
- }
1880
-
1881
- export { Collapsible, CollapsibleTrigger, CollapsibleContent };
1882
-
1883
- ```
1884
-
1885
- ## components/ui/dialog.tsx
1886
-
1887
- ```tsx
1888
- "use client";
1889
-
1890
- import * as React from "react";
1891
- import * as DialogPrimitive from "@radix-ui/react-dialog";
1892
- import { XIcon } from "lucide-react";
1893
-
1894
- import { cn } from "@/lib/utils";
1895
-
1896
- function Dialog({
1897
- ...props
1898
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1899
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1900
- }
1901
-
1902
- function DialogTrigger({
1903
- ...props
1904
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1905
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1906
- }
1907
-
1908
- function DialogPortal({
1909
- ...props
1910
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1911
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1912
- }
1913
-
1914
- function DialogClose({
1915
- ...props
1916
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1917
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1918
- }
1919
-
1920
- function DialogOverlay({
1921
- className,
1922
- ...props
1923
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1924
- return (
1925
- <DialogPrimitive.Overlay
1926
- data-slot="dialog-overlay"
1927
- className={cn(
1928
- "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",
1929
- className,
1930
- )}
1931
- {...props}
1932
- />
1933
- );
1934
- }
1935
-
1936
- function DialogContent({
1937
- className,
1938
- children,
1939
- showCloseButton = true,
1940
- ...props
1941
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
1942
- showCloseButton?: boolean;
1943
- }) {
1944
- return (
1945
- <DialogPortal data-slot="dialog-portal">
1946
- <DialogOverlay />
1947
- <DialogPrimitive.Content
1948
- data-slot="dialog-content"
1949
- className={cn(
1950
- "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",
1951
- className,
1952
- )}
1953
- {...props}
1954
- >
1955
- {children}
1956
- {showCloseButton && (
1957
- <DialogPrimitive.Close
1958
- data-slot="dialog-close"
1959
- 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"
1960
- >
1961
- <XIcon />
1962
- <span className="sr-only">Close</span>
1963
- </DialogPrimitive.Close>
1964
- )}
1965
- </DialogPrimitive.Content>
1966
- </DialogPortal>
1967
- );
1968
- }
1969
-
1970
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1971
- return (
1972
- <div
1973
- data-slot="dialog-header"
1974
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1975
- {...props}
1976
- />
1977
- );
1978
- }
1979
-
1980
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1981
- return (
1982
- <div
1983
- data-slot="dialog-footer"
1984
- className={cn(
1985
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1986
- className,
1987
- )}
1988
- {...props}
1989
- />
1990
- );
1991
- }
1992
-
1993
- function DialogTitle({
1994
- className,
1995
- ...props
1996
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1997
- return (
1998
- <DialogPrimitive.Title
1999
- data-slot="dialog-title"
2000
- className={cn("font-semibold text-lg leading-none", className)}
2001
- {...props}
2002
- />
2003
- );
2004
- }
2005
-
2006
- function DialogDescription({
2007
- className,
2008
- ...props
2009
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
2010
- return (
2011
- <DialogPrimitive.Description
2012
- data-slot="dialog-description"
2013
- className={cn("text-muted-foreground text-sm", className)}
2014
- {...props}
2015
- />
2016
- );
2017
- }
2018
-
2019
- export {
2020
- Dialog,
2021
- DialogClose,
2022
- DialogContent,
2023
- DialogDescription,
2024
- DialogFooter,
2025
- DialogHeader,
2026
- DialogOverlay,
2027
- DialogPortal,
2028
- DialogTitle,
2029
- DialogTrigger,
2030
- };
2031
-
2032
- ```
2033
-
2034
- ## components/ui/form.tsx
2035
-
2036
- ```tsx
2037
- "use client";
2038
-
2039
- import * as React from "react";
2040
- import type * as LabelPrimitive from "@radix-ui/react-label";
2041
- import { Slot } from "@radix-ui/react-slot";
2042
- import {
2043
- Controller,
2044
- FormProvider,
2045
- useFormContext,
2046
- useFormState,
2047
- type ControllerProps,
2048
- type FieldPath,
2049
- type FieldValues,
2050
- } from "react-hook-form";
2051
-
2052
- import { cn } from "@/lib/utils";
2053
- import { Label } from "@/components/ui/label";
2054
-
2055
- const Form = FormProvider;
2056
-
2057
- type FormFieldContextValue<
2058
- TFieldValues extends FieldValues = FieldValues,
2059
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
2060
- > = {
2061
- name: TName;
2062
- };
2063
-
2064
- const FormFieldContext = React.createContext<FormFieldContextValue>(
2065
- {} as FormFieldContextValue,
2066
- );
2067
-
2068
- const FormField = <
2069
- TFieldValues extends FieldValues = FieldValues,
2070
- TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
2071
- >({
2072
- ...props
2073
- }: ControllerProps<TFieldValues, TName>) => {
2074
- return (
2075
- <FormFieldContext.Provider value={{ name: props.name }}>
2076
- <Controller {...props} />
2077
- </FormFieldContext.Provider>
2078
- );
2079
- };
2080
-
2081
- const useFormField = () => {
2082
- const fieldContext = React.useContext(FormFieldContext);
2083
- const itemContext = React.useContext(FormItemContext);
2084
- const { getFieldState } = useFormContext();
2085
- const formState = useFormState({ name: fieldContext.name });
2086
- const fieldState = getFieldState(fieldContext.name, formState);
2087
-
2088
- if (!fieldContext) {
2089
- throw new Error("useFormField should be used within <FormField>");
2090
- }
2091
-
2092
- const { id } = itemContext;
2093
-
2094
- return {
2095
- id,
2096
- name: fieldContext.name,
2097
- formItemId: `${id}-form-item`,
2098
- formDescriptionId: `${id}-form-item-description`,
2099
- formMessageId: `${id}-form-item-message`,
2100
- ...fieldState,
2101
- };
2102
- };
2103
-
2104
- type FormItemContextValue = {
2105
- id: string;
2106
- };
2107
-
2108
- const FormItemContext = React.createContext<FormItemContextValue>(
2109
- {} as FormItemContextValue,
2110
- );
2111
-
2112
- function FormItem({ className, ...props }: React.ComponentProps<"div">) {
2113
- const id = React.useId();
2114
-
2115
- return (
2116
- <FormItemContext.Provider value={{ id }}>
2117
- <div
2118
- data-slot="form-item"
2119
- className={cn("grid gap-2", className)}
2120
- {...props}
2121
- />
2122
- </FormItemContext.Provider>
2123
- );
2124
- }
2125
-
2126
- function FormLabel({
2127
- className,
2128
- ...props
2129
- }: React.ComponentProps<typeof LabelPrimitive.Root>) {
2130
- const { error, formItemId } = useFormField();
2131
-
2132
- return (
2133
- <Label
2134
- data-slot="form-label"
2135
- data-error={!!error}
2136
- className={cn("data-[error=true]:text-destructive", className)}
2137
- htmlFor={formItemId}
2138
- {...props}
2139
- />
2140
- );
2141
- }
2142
-
2143
- function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
2144
- const { error, formItemId, formDescriptionId, formMessageId } =
2145
- useFormField();
2146
-
2147
- return (
2148
- <Slot
2149
- data-slot="form-control"
2150
- id={formItemId}
2151
- aria-describedby={
2152
- !error
2153
- ? `${formDescriptionId}`
2154
- : `${formDescriptionId} ${formMessageId}`
2155
- }
2156
- aria-invalid={!!error}
2157
- {...props}
2158
- />
2159
- );
2160
- }
2161
-
2162
- function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
2163
- const { formDescriptionId } = useFormField();
2164
-
2165
- return (
2166
- <p
2167
- data-slot="form-description"
2168
- id={formDescriptionId}
2169
- className={cn("text-muted-foreground text-sm", className)}
2170
- {...props}
2171
- />
2172
- );
2173
- }
2174
-
2175
- function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
2176
- const { error, formMessageId } = useFormField();
2177
- const body = error ? String(error?.message ?? "") : props.children;
2178
-
2179
- if (!body) {
2180
- return null;
2181
- }
2182
-
2183
- return (
2184
- <p
2185
- data-slot="form-message"
2186
- id={formMessageId}
2187
- className={cn("text-destructive text-sm", className)}
2188
- {...props}
2189
- >
2190
- {body}
2191
- </p>
2192
- );
2193
- }
2194
-
2195
- export {
2196
- useFormField,
2197
- Form,
2198
- FormItem,
2199
- FormLabel,
2200
- FormControl,
2201
- FormDescription,
2202
- FormMessage,
2203
- FormField,
2204
- };
2205
-
2206
- ```
2207
-
2208
- ## components/ui/input.tsx
2209
-
2210
- ```tsx
2211
- import * as React from "react";
2212
-
2213
- import { cn } from "@/lib/utils";
2214
-
2215
- function Input({ className, type, ...props }: React.ComponentProps<"input">) {
2216
- return (
2217
- <input
2218
- type={type}
2219
- data-slot="input"
2220
- className={cn(
2221
- "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:font-medium file:text-foreground file:text-sm placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
2222
- "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
2223
- "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
2224
- className,
2225
- )}
2226
- {...props}
2227
- />
2228
- );
2229
- }
2230
-
2231
- export { Input };
2232
-
2233
- ```
2234
-
2235
- ## components/ui/label.tsx
2236
-
2237
- ```tsx
2238
- "use client";
2239
-
2240
- import * as React from "react";
2241
- import * as LabelPrimitive from "@radix-ui/react-label";
2242
-
2243
- import { cn } from "@/lib/utils";
2244
-
2245
- function Label({
2246
- className,
2247
- ...props
2248
- }: React.ComponentProps<typeof LabelPrimitive.Root>) {
2249
- return (
2250
- <LabelPrimitive.Root
2251
- data-slot="label"
2252
- className={cn(
2253
- "flex select-none items-center gap-2 font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
2254
- className,
2255
- )}
2256
- {...props}
2257
- />
2258
- );
2259
- }
2260
-
2261
- export { Label };
2262
-
2263
- ```
2264
-
2265
- ## components/ui/resizable.tsx
2266
-
2267
- ```tsx
2268
- "use client";
2269
-
2270
- import * as React from "react";
2271
- import { GripVerticalIcon } from "lucide-react";
2272
- import * as ResizablePrimitive from "react-resizable-panels";
2273
-
2274
- import { cn } from "@/lib/utils";
2275
-
2276
- function Group({
2277
- className,
2278
- ...props
2279
- }: React.ComponentProps<typeof ResizablePrimitive.Group>) {
2280
- return (
2281
- <ResizablePrimitive.Group
2282
- data-slot="resizable-panel-group"
2283
- className={cn(
2284
- "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
2285
- className,
2286
- )}
2287
- {...props}
2288
- />
2289
- );
2290
- }
2291
-
2292
- function ResizablePanel({
2293
- ...props
2294
- }: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
2295
- return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />;
2296
- }
2297
-
2298
- function Separator({
2299
- withHandle,
2300
- className,
2301
- ...props
2302
- }: React.ComponentProps<typeof ResizablePrimitive.Separator> & {
2303
- withHandle?: boolean;
2304
- }) {
2305
- return (
2306
- <ResizablePrimitive.Separator
2307
- data-slot="resizable-handle"
2308
- className={cn(
2309
- "relative flex w-px items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:translate-x-0 data-[panel-group-direction=vertical]:after:-translate-y-1/2 [&[data-panel-group-direction=vertical]>div]:rotate-90",
2310
- className,
2311
- )}
2312
- {...props}
2313
- >
2314
- {withHandle && (
2315
- <div className="z-10 flex h-4 w-3 items-center justify-center rounded-xs border bg-border">
2316
- <GripVerticalIcon className="size-2.5" />
2317
- </div>
2318
- )}
2319
- </ResizablePrimitive.Separator>
2320
- );
2321
- }
2322
-
2323
- export { Group, ResizablePanel, Separator };
2324
-
2325
- ```
2326
-
2327
- ## components/ui/tabs.tsx
2328
-
2329
- ```tsx
2330
- "use client";
2331
-
2332
- import * as React from "react";
2333
- import * as TabsPrimitive from "@radix-ui/react-tabs";
2334
-
2335
- import { cn } from "@/lib/utils";
2336
-
2337
- function Tabs({
2338
- className,
2339
- ...props
2340
- }: React.ComponentProps<typeof TabsPrimitive.Root>) {
2341
- return (
2342
- <TabsPrimitive.Root
2343
- data-slot="tabs"
2344
- className={cn("flex flex-col gap-2", className)}
2345
- {...props}
2346
- />
2347
- );
2348
- }
2349
-
2350
- function TabsList({
2351
- className,
2352
- ...props
2353
- }: React.ComponentProps<typeof TabsPrimitive.List>) {
2354
- return (
2355
- <TabsPrimitive.List
2356
- data-slot="tabs-list"
2357
- className={cn(
2358
- "inline-flex h-9 w-fit items-center justify-center rounded-lg bg-muted p-[3px] text-muted-foreground",
2359
- className,
2360
- )}
2361
- {...props}
2362
- />
2363
- );
2364
- }
2365
-
2366
- function TabsTrigger({
2367
- className,
2368
- ...props
2369
- }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
2370
- return (
2371
- <TabsPrimitive.Trigger
2372
- data-slot="tabs-trigger"
2373
- className={cn(
2374
- "inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 font-medium text-foreground text-sm transition-[color,box-shadow] focus-visible:border-ring focus-visible:outline-1 focus-visible:outline-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:shadow-sm dark:text-muted-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
2375
- className,
2376
- )}
2377
- {...props}
2378
- />
2379
- );
2380
- }
2381
-
2382
- function TabsContent({
2383
- className,
2384
- ...props
2385
- }: React.ComponentProps<typeof TabsPrimitive.Content>) {
2386
- return (
2387
- <TabsPrimitive.Content
2388
- data-slot="tabs-content"
2389
- className={cn("flex-1 outline-none", className)}
2390
- {...props}
2391
- />
2392
- );
2393
- }
2394
-
2395
- export { Tabs, TabsList, TabsTrigger, TabsContent };
2396
-
2397
- ```
2398
-
2399
- ## components/ui/tooltip.tsx
2400
-
2401
- ```tsx
2402
- "use client";
2403
-
2404
- import * as React from "react";
2405
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2406
-
2407
- import { cn } from "@/lib/utils";
2408
-
2409
- function TooltipProvider({
2410
- delayDuration = 0,
2411
- ...props
2412
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
2413
- return (
2414
- <TooltipPrimitive.Provider
2415
- data-slot="tooltip-provider"
2416
- delayDuration={delayDuration}
2417
- {...props}
2418
- />
2419
- );
2420
- }
2421
-
2422
- function Tooltip({
2423
- ...props
2424
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
2425
- return (
2426
- <TooltipProvider>
2427
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
2428
- </TooltipProvider>
2429
- );
2430
- }
2431
-
2432
- function TooltipTrigger({
2433
- ...props
2434
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
2435
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
2436
- }
2437
-
2438
- function TooltipContent({
2439
- className,
2440
- sideOffset = 0,
2441
- children,
2442
- ...props
2443
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
2444
- return (
2445
- <TooltipPrimitive.Portal>
2446
- <TooltipPrimitive.Content
2447
- data-slot="tooltip-content"
2448
- sideOffset={sideOffset}
2449
- className={cn(
2450
- "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",
2451
- className,
2452
- )}
2453
- {...props}
2454
- >
2455
- {children}
2456
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
2457
- </TooltipPrimitive.Content>
2458
- </TooltipPrimitive.Portal>
2459
- );
2460
- }
2461
-
2462
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
2463
-
2464
- ```
2465
-
2466
- ## lib/submitSignup.tsx
2467
-
2468
- ```tsx
2469
- "use server";
2470
-
2471
- export const submitSignup = async (data: object) => {
2472
- const res = await fetch(process.env["ASSISTANT_UI_SUBMIT_SIGNUP_ENDPOINT"]!, {
2473
- method: "POST",
2474
- headers: {
2475
- "Content-Type": "application/json",
2476
- },
2477
- body: JSON.stringify(data),
2478
- });
2479
- return res.json();
2480
- };
2481
-
2482
- ```
2483
-
2484
- ## lib/utils.ts
2485
-
2486
- ```typescript
2487
- import { type ClassValue, clsx } from "clsx";
2488
- import { twMerge } from "tailwind-merge";
2489
-
2490
- export function cn(...inputs: ClassValue[]) {
2491
- return twMerge(clsx(inputs));
2492
- }
2493
-
2494
- ```
2495
-
2496
- ## next.config.ts
2497
-
2498
- ```typescript
2499
- import type { NextConfig } from "next";
2500
-
2501
- const nextConfig: NextConfig = {
2502
- /* config options here */
2503
- };
2504
-
2505
- export default nextConfig;
2506
-
2507
- ```
2508
-
2509
- ## package.json
515
+ ## package.json
2510
516
 
2511
517
  ```json
2512
518
  {
@@ -2520,11 +526,12 @@ export default nextConfig;
2520
526
  "start": "next start"
2521
527
  },
2522
528
  "dependencies": {
2523
- "@ai-sdk/openai": "^3.0.13",
529
+ "@ai-sdk/openai": "^3.0.25",
2524
530
  "@assistant-ui/react": "workspace:*",
2525
531
  "@assistant-ui/react-ai-sdk": "workspace:*",
2526
532
  "@assistant-ui/react-hook-form": "workspace:*",
2527
533
  "@assistant-ui/react-markdown": "workspace:*",
534
+ "@assistant-ui/ui": "workspace:*",
2528
535
  "@hookform/resolvers": "^5.2.2",
2529
536
  "@radix-ui/react-avatar": "^1.1.11",
2530
537
  "@radix-ui/react-collapsible": "^1.1.12",
@@ -2533,25 +540,23 @@ export default nextConfig;
2533
540
  "@radix-ui/react-slot": "^1.2.4",
2534
541
  "@radix-ui/react-tabs": "^1.1.13",
2535
542
  "@radix-ui/react-tooltip": "^1.2.8",
2536
- "ai": "^6.0.42",
543
+ "ai": "^6.0.69",
2537
544
  "class-variance-authority": "^0.7.1",
2538
545
  "clsx": "^2.1.1",
2539
- "lucide-react": "^0.562.0",
2540
- "next": "^16.1.4",
2541
- "react": "^19.2.3",
2542
- "react-dom": "^19.2.3",
546
+ "lucide-react": "^0.563.0",
547
+ "next": "^16.1.6",
548
+ "react": "^19.2.4",
549
+ "react-dom": "^19.2.4",
2543
550
  "react-hook-form": "^7.71.1",
2544
- "react-resizable-panels": "^4.4.1",
2545
- "remark-gfm": "^4.0.1",
551
+ "react-resizable-panels": "^4.5.9",
2546
552
  "tailwind-merge": "^3.4.0",
2547
- "zod": "^4.3.5",
2548
- "zustand": "^5.0.10"
553
+ "zod": "^4.3.6"
2549
554
  },
2550
555
  "devDependencies": {
2551
556
  "@assistant-ui/x-buildutils": "workspace:*",
2552
557
  "@tailwindcss/postcss": "^4.1.18",
2553
- "@types/node": "^25.0.9",
2554
- "@types/react": "^19.2.9",
558
+ "@types/node": "^25.2.0",
559
+ "@types/react": "^19.2.10",
2555
560
  "@types/react-dom": "^19.2.3",
2556
561
  "postcss": "^8.5.6",
2557
562
  "tailwindcss": "^4.1.18",
@@ -2562,13 +567,65 @@ export default nextConfig;
2562
567
 
2563
568
  ```
2564
569
 
570
+ ## README.md
571
+
572
+ ```markdown
573
+ # React Hook Form Integration
574
+
575
+ This example demonstrates how to integrate assistant-ui with React Hook Form, allowing the AI assistant to fill out and submit forms.
576
+
577
+ ## Quick Start
578
+
579
+ ### Using CLI (Recommended)
580
+
581
+ ```bash
582
+ npx assistant-ui@latest create my-app --example with-react-hook-form
583
+ cd my-app
584
+ ```
585
+
586
+ ### Environment Variables
587
+
588
+ Create `.env.local`:
589
+
590
+ ```
591
+ OPENAI_API_KEY=sk-...
592
+ ```
593
+
594
+ ### Run
595
+
596
+ ```bash
597
+ npm run dev
598
+ ```
599
+
600
+ ## Features
601
+
602
+ - React Hook Form integration via `@assistant-ui/react-hook-form`
603
+ - AI-powered form filling with `useAssistantForm`
604
+ - Custom tool rendering for form actions
605
+ - Sidebar assistant layout
606
+ - Form validation with Zod
607
+
608
+ ## Related Documentation
609
+
610
+ - [assistant-ui Documentation](https://www.assistant-ui.com/docs)
611
+ - [React Hook Form Integration](https://www.assistant-ui.com/docs/integrations/react-hook-form)
612
+
613
+ ```
614
+
2565
615
  ## tsconfig.json
2566
616
 
2567
617
  ```json
2568
618
  {
2569
619
  "extends": "@assistant-ui/x-buildutils/ts/next",
2570
620
  "compilerOptions": {
2571
- "paths": { "@/*": ["./*"] }
621
+ "paths": {
622
+ "@/*": ["./*"],
623
+ "@/components/assistant-ui/*": [
624
+ "../../packages/ui/src/components/assistant-ui/*"
625
+ ],
626
+ "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
627
+ "@assistant-ui/ui/*": ["../../packages/ui/src/*"]
628
+ }
2572
629
  },
2573
630
  "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
2574
631
  "exclude": ["node_modules"]