@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.
- package/.docs/organized/code-examples/with-ag-ui.md +172 -1633
- package/.docs/organized/code-examples/with-ai-sdk-v6.md +42 -1640
- package/.docs/organized/code-examples/with-assistant-transport.md +40 -1743
- package/.docs/organized/code-examples/with-cloud.md +71 -1745
- package/.docs/organized/code-examples/with-custom-thread-list.md +87 -1723
- package/.docs/organized/code-examples/with-elevenlabs-scribe.md +70 -1637
- package/.docs/organized/code-examples/with-external-store.md +67 -1624
- package/.docs/organized/code-examples/with-ffmpeg.md +71 -1629
- package/.docs/organized/code-examples/with-langgraph.md +95 -1893
- package/.docs/organized/code-examples/with-parent-id-grouping.md +57 -1654
- package/.docs/organized/code-examples/with-react-hook-form.md +220 -2163
- package/.docs/organized/code-examples/with-react-router.md +66 -1318
- package/.docs/organized/code-examples/with-store.md +31 -31
- package/.docs/organized/code-examples/with-tanstack.md +77 -861
- package/.docs/organized/code-examples/with-tap-runtime.md +812 -0
- package/.docs/raw/docs/(docs)/cli.mdx +66 -0
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool-ui.mdx +0 -1
- package/.docs/raw/docs/(docs)/copilots/make-assistant-tool.mdx +0 -1
- package/.docs/raw/docs/(docs)/copilots/model-context.mdx +4 -4
- package/.docs/raw/docs/(docs)/copilots/motivation.mdx +3 -3
- package/.docs/raw/docs/(docs)/devtools.mdx +0 -1
- package/.docs/raw/docs/(docs)/guides/attachments.mdx +2 -3
- package/.docs/raw/docs/(docs)/guides/context-api.mdx +117 -117
- package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
- package/.docs/raw/docs/(docs)/guides/tools.mdx +336 -513
- package/.docs/raw/docs/(docs)/index.mdx +33 -410
- package/.docs/raw/docs/(docs)/installation.mdx +450 -0
- package/.docs/raw/docs/(docs)/llm.mdx +209 -0
- package/.docs/raw/docs/(reference)/api-reference/context-providers/assistant-runtime-provider.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/context-providers/text-message-part-provider.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-data-stream.mdx +48 -3
- package/.docs/raw/docs/(reference)/api-reference/integrations/react-hook-form.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/integrations/vercel-ai-sdk.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/overview.mdx +9 -3
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +20 -52
- package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar.mdx +16 -39
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-if.mdx +49 -50
- package/.docs/raw/docs/(reference)/api-reference/primitives/assistant-modal.mdx +3 -11
- package/.docs/raw/docs/(reference)/api-reference/primitives/attachment.mdx +0 -3
- package/.docs/raw/docs/(reference)/api-reference/primitives/branch-picker.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/composer.mdx +5 -16
- package/.docs/raw/docs/(reference)/api-reference/primitives/composition.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/error.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/message.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +152 -0
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/primitives/thread.mdx +28 -40
- package/.docs/raw/docs/(reference)/api-reference/runtimes/assistant-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/attachment-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/composer-runtime.mdx +2 -3
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-part-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/message-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-item-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-list-runtime.mdx +0 -1
- package/.docs/raw/docs/(reference)/api-reference/runtimes/thread-runtime.mdx +1 -2
- package/.docs/raw/docs/(reference)/legacy/styled/assistant-modal.mdx +0 -1
- package/.docs/raw/docs/(reference)/legacy/styled/decomposition.mdx +5 -5
- package/.docs/raw/docs/(reference)/legacy/styled/markdown.mdx +0 -1
- package/.docs/raw/docs/(reference)/legacy/styled/thread.mdx +0 -1
- package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +207 -33
- package/.docs/raw/docs/(reference)/react-compatibility.mdx +0 -1
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +0 -1
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +0 -1
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +0 -1
- package/.docs/raw/docs/runtimes/ai-sdk/v5-legacy.mdx +118 -0
- package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +198 -0
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +3 -3
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +5 -6
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +9 -11
- package/.docs/raw/docs/runtimes/custom/local.mdx +43 -36
- package/.docs/raw/docs/runtimes/data-stream.mdx +35 -3
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +1 -2
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +0 -1
- package/.docs/raw/docs/runtimes/langserve.mdx +0 -1
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +0 -1
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +0 -1
- package/.docs/raw/docs/ui/accordion.mdx +259 -0
- package/.docs/raw/docs/ui/assistant-modal.mdx +1 -3
- package/.docs/raw/docs/ui/assistant-sidebar.mdx +1 -3
- package/.docs/raw/docs/ui/attachment.mdx +0 -2
- package/.docs/raw/docs/ui/badge.mdx +138 -0
- package/.docs/raw/docs/ui/diff-viewer.mdx +279 -0
- package/.docs/raw/docs/ui/file.mdx +152 -0
- package/.docs/raw/docs/ui/image.mdx +100 -0
- package/.docs/raw/docs/ui/markdown.mdx +0 -1
- package/.docs/raw/docs/ui/mermaid.mdx +0 -1
- package/.docs/raw/docs/ui/model-selector.mdx +224 -0
- package/.docs/raw/docs/ui/part-grouping.mdx +4 -5
- package/.docs/raw/docs/ui/reasoning.mdx +6 -5
- package/.docs/raw/docs/ui/scrollbar.mdx +26 -9
- package/.docs/raw/docs/ui/select.mdx +245 -0
- package/.docs/raw/docs/ui/sources.mdx +6 -5
- package/.docs/raw/docs/ui/streamdown.mdx +348 -0
- package/.docs/raw/docs/ui/syntax-highlighting.mdx +8 -63
- package/.docs/raw/docs/ui/tabs.mdx +259 -0
- package/.docs/raw/docs/ui/thread-list.mdx +98 -16
- package/.docs/raw/docs/ui/thread.mdx +57 -73
- package/.docs/raw/docs/ui/tool-fallback.mdx +0 -1
- package/.docs/raw/docs/ui/tool-group.mdx +1 -3
- package/README.md +3 -3
- package/package.json +4 -4
- package/src/tools/tests/examples.test.ts +1 -1
- package/.docs/raw/docs/(docs)/about-assistantui.mdx +0 -54
- package/.docs/raw/docs/(docs)/mcp-docs-server.mdx +0 -321
- 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/
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
import {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
404
|
-
|
|
341
|
+
export const SignupForm: FC = () => {
|
|
342
|
+
const form = useFormContext();
|
|
405
343
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
};
|
|
344
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
345
|
+
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
409
346
|
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
|
|
430
|
-
|
|
357
|
+
if (isSubmitting)
|
|
358
|
+
return <p className="my-4 font-bold text-green-600">Submitting...</p>;
|
|
431
359
|
|
|
432
|
-
if (
|
|
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
|
-
<
|
|
436
|
-
<
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
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
|
-
|
|
563
|
-
|
|
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
|
-
##
|
|
472
|
+
## lib/submitSignup.tsx
|
|
582
473
|
|
|
583
474
|
```tsx
|
|
584
|
-
"use
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
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
|
-
|
|
644
|
-
setIsCopied(true);
|
|
645
|
-
setTimeout(() => setIsCopied(false), copiedDuration);
|
|
646
|
-
});
|
|
647
|
-
};
|
|
490
|
+
## lib/utils.ts
|
|
648
491
|
|
|
649
|
-
|
|
650
|
-
};
|
|
492
|
+
```typescript
|
|
493
|
+
import { type ClassValue, clsx } from "clsx";
|
|
494
|
+
import { twMerge } from "tailwind-merge";
|
|
651
495
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
887
|
-
|
|
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
|
|
901
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
543
|
+
"ai": "^6.0.69",
|
|
2537
544
|
"class-variance-authority": "^0.7.1",
|
|
2538
545
|
"clsx": "^2.1.1",
|
|
2539
|
-
"lucide-react": "^0.
|
|
2540
|
-
"next": "^16.1.
|
|
2541
|
-
"react": "^19.2.
|
|
2542
|
-
"react-dom": "^19.2.
|
|
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.
|
|
2545
|
-
"remark-gfm": "^4.0.1",
|
|
551
|
+
"react-resizable-panels": "^4.5.9",
|
|
2546
552
|
"tailwind-merge": "^3.4.0",
|
|
2547
|
-
"zod": "^4.3.
|
|
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
|
|
2554
|
-
"@types/react": "^19.2.
|
|
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"]
|