@assistant-ui/mcp-docs-server 0.1.14 → 0.1.16
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/store-example.md +628 -0
- package/.docs/organized/code-examples/with-ag-ui.md +792 -178
- package/.docs/organized/code-examples/with-ai-sdk-v5.md +762 -209
- package/.docs/organized/code-examples/with-assistant-transport.md +707 -254
- package/.docs/organized/code-examples/with-cloud.md +848 -202
- package/.docs/organized/code-examples/with-custom-thread-list.md +1855 -0
- package/.docs/organized/code-examples/with-external-store.md +788 -172
- package/.docs/organized/code-examples/with-ffmpeg.md +796 -196
- package/.docs/organized/code-examples/with-langgraph.md +864 -230
- package/.docs/organized/code-examples/with-parent-id-grouping.md +785 -255
- package/.docs/organized/code-examples/with-react-hook-form.md +804 -226
- package/.docs/organized/code-examples/with-tanstack.md +1574 -0
- package/.docs/raw/blog/2024-07-29-hello/index.mdx +2 -3
- package/.docs/raw/docs/api-reference/overview.mdx +6 -6
- package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +85 -4
- package/.docs/raw/docs/api-reference/primitives/AssistantIf.mdx +200 -0
- package/.docs/raw/docs/api-reference/primitives/Composer.mdx +0 -20
- package/.docs/raw/docs/api-reference/primitives/Message.mdx +0 -45
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +0 -50
- package/.docs/raw/docs/cli.mdx +396 -0
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +2 -3
- package/.docs/raw/docs/devtools.mdx +2 -3
- package/.docs/raw/docs/getting-started.mdx +37 -1109
- package/.docs/raw/docs/guides/Attachments.mdx +3 -25
- package/.docs/raw/docs/guides/Branching.mdx +1 -1
- package/.docs/raw/docs/guides/Speech.mdx +1 -1
- package/.docs/raw/docs/guides/ToolUI.mdx +1 -1
- package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +2 -3
- package/.docs/raw/docs/legacy/styled/Decomposition.mdx +6 -5
- package/.docs/raw/docs/legacy/styled/Markdown.mdx +2 -3
- package/.docs/raw/docs/legacy/styled/Thread.mdx +2 -3
- package/.docs/raw/docs/react-compatibility.mdx +2 -5
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +3 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +3 -6
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -3
- package/.docs/raw/docs/runtimes/custom/local.mdx +11 -41
- package/.docs/raw/docs/runtimes/data-stream.mdx +15 -11
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +4 -4
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -3
- package/.docs/raw/docs/runtimes/langserve.mdx +2 -3
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +2 -3
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +2 -3
- package/.docs/raw/docs/ui/AssistantModal.mdx +3 -25
- package/.docs/raw/docs/ui/AssistantSidebar.mdx +2 -24
- package/.docs/raw/docs/ui/Attachment.mdx +3 -25
- package/.docs/raw/docs/ui/Markdown.mdx +2 -24
- package/.docs/raw/docs/ui/Mermaid.mdx +2 -24
- package/.docs/raw/docs/ui/Reasoning.mdx +2 -24
- package/.docs/raw/docs/ui/Scrollbar.mdx +4 -6
- package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +3 -47
- package/.docs/raw/docs/ui/Thread.mdx +38 -53
- package/.docs/raw/docs/ui/ThreadList.mdx +4 -47
- package/.docs/raw/docs/ui/ToolFallback.mdx +2 -24
- package/package.json +15 -8
|
@@ -238,7 +238,7 @@ import { ThreadList } from "@/components/assistant-ui/thread-list";
|
|
|
238
238
|
|
|
239
239
|
export default function Home() {
|
|
240
240
|
return (
|
|
241
|
-
<main className="grid h-dvh grid-cols-[
|
|
241
|
+
<main className="grid h-dvh grid-cols-[200px_1fr] gap-4 p-4">
|
|
242
242
|
<ThreadList />
|
|
243
243
|
<Thread />
|
|
244
244
|
</main>
|
|
@@ -274,6 +274,247 @@ export default function Home() {
|
|
|
274
274
|
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
+
## components/assistant-ui/attachment.tsx
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
"use client";
|
|
281
|
+
|
|
282
|
+
import { PropsWithChildren, useEffect, useState, type FC } from "react";
|
|
283
|
+
import Image from "next/image";
|
|
284
|
+
import { XIcon, PlusIcon, FileText } from "lucide-react";
|
|
285
|
+
import {
|
|
286
|
+
AttachmentPrimitive,
|
|
287
|
+
ComposerPrimitive,
|
|
288
|
+
MessagePrimitive,
|
|
289
|
+
useAssistantState,
|
|
290
|
+
useAssistantApi,
|
|
291
|
+
} from "@assistant-ui/react";
|
|
292
|
+
import { useShallow } from "zustand/shallow";
|
|
293
|
+
import {
|
|
294
|
+
Tooltip,
|
|
295
|
+
TooltipContent,
|
|
296
|
+
TooltipTrigger,
|
|
297
|
+
} from "@/components/ui/tooltip";
|
|
298
|
+
import {
|
|
299
|
+
Dialog,
|
|
300
|
+
DialogTitle,
|
|
301
|
+
DialogContent,
|
|
302
|
+
DialogTrigger,
|
|
303
|
+
} from "@/components/ui/dialog";
|
|
304
|
+
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
|
305
|
+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
306
|
+
import { cn } from "@/lib/utils";
|
|
307
|
+
|
|
308
|
+
const useFileSrc = (file: File | undefined) => {
|
|
309
|
+
const [src, setSrc] = useState<string | undefined>(undefined);
|
|
310
|
+
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
if (!file) {
|
|
313
|
+
setSrc(undefined);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const objectUrl = URL.createObjectURL(file);
|
|
318
|
+
setSrc(objectUrl);
|
|
319
|
+
|
|
320
|
+
return () => {
|
|
321
|
+
URL.revokeObjectURL(objectUrl);
|
|
322
|
+
};
|
|
323
|
+
}, [file]);
|
|
324
|
+
|
|
325
|
+
return src;
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const useAttachmentSrc = () => {
|
|
329
|
+
const { file, src } = useAssistantState(
|
|
330
|
+
useShallow(({ attachment }): { file?: File; src?: string } => {
|
|
331
|
+
if (attachment.type !== "image") return {};
|
|
332
|
+
if (attachment.file) return { file: attachment.file };
|
|
333
|
+
const src = attachment.content?.filter((c) => c.type === "image")[0]
|
|
334
|
+
?.image;
|
|
335
|
+
if (!src) return {};
|
|
336
|
+
return { src };
|
|
337
|
+
}),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
return useFileSrc(file) ?? src;
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
type AttachmentPreviewProps = {
|
|
344
|
+
src: string;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
|
|
348
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
349
|
+
return (
|
|
350
|
+
<Image
|
|
351
|
+
src={src}
|
|
352
|
+
alt="Image Preview"
|
|
353
|
+
width={1}
|
|
354
|
+
height={1}
|
|
355
|
+
className={
|
|
356
|
+
isLoaded
|
|
357
|
+
? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
|
|
358
|
+
: "aui-attachment-preview-image-loading hidden"
|
|
359
|
+
}
|
|
360
|
+
onLoadingComplete={() => setIsLoaded(true)}
|
|
361
|
+
priority={false}
|
|
362
|
+
/>
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
|
|
367
|
+
const src = useAttachmentSrc();
|
|
368
|
+
|
|
369
|
+
if (!src) return children;
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<Dialog>
|
|
373
|
+
<DialogTrigger
|
|
374
|
+
className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
|
|
375
|
+
asChild
|
|
376
|
+
>
|
|
377
|
+
{children}
|
|
378
|
+
</DialogTrigger>
|
|
379
|
+
<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">
|
|
380
|
+
<DialogTitle className="aui-sr-only sr-only">
|
|
381
|
+
Image Attachment Preview
|
|
382
|
+
</DialogTitle>
|
|
383
|
+
<div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
|
|
384
|
+
<AttachmentPreview src={src} />
|
|
385
|
+
</div>
|
|
386
|
+
</DialogContent>
|
|
387
|
+
</Dialog>
|
|
388
|
+
);
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const AttachmentThumb: FC = () => {
|
|
392
|
+
const isImage = useAssistantState(
|
|
393
|
+
({ attachment }) => attachment.type === "image",
|
|
394
|
+
);
|
|
395
|
+
const src = useAttachmentSrc();
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
|
|
399
|
+
<AvatarImage
|
|
400
|
+
src={src}
|
|
401
|
+
alt="Attachment preview"
|
|
402
|
+
className="aui-attachment-tile-image object-cover"
|
|
403
|
+
/>
|
|
404
|
+
<AvatarFallback delayMs={isImage ? 200 : 0}>
|
|
405
|
+
<FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
|
|
406
|
+
</AvatarFallback>
|
|
407
|
+
</Avatar>
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const AttachmentUI: FC = () => {
|
|
412
|
+
const api = useAssistantApi();
|
|
413
|
+
const isComposer = api.attachment.source === "composer";
|
|
414
|
+
|
|
415
|
+
const isImage = useAssistantState(
|
|
416
|
+
({ attachment }) => attachment.type === "image",
|
|
417
|
+
);
|
|
418
|
+
const typeLabel = useAssistantState(({ attachment }) => {
|
|
419
|
+
const type = attachment.type;
|
|
420
|
+
switch (type) {
|
|
421
|
+
case "image":
|
|
422
|
+
return "Image";
|
|
423
|
+
case "document":
|
|
424
|
+
return "Document";
|
|
425
|
+
case "file":
|
|
426
|
+
return "File";
|
|
427
|
+
default:
|
|
428
|
+
const _exhaustiveCheck: never = type;
|
|
429
|
+
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
return (
|
|
434
|
+
<Tooltip>
|
|
435
|
+
<AttachmentPrimitive.Root
|
|
436
|
+
className={cn(
|
|
437
|
+
"aui-attachment-root relative",
|
|
438
|
+
isImage &&
|
|
439
|
+
"aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
|
|
440
|
+
)}
|
|
441
|
+
>
|
|
442
|
+
<AttachmentPreviewDialog>
|
|
443
|
+
<TooltipTrigger asChild>
|
|
444
|
+
<div
|
|
445
|
+
className={cn(
|
|
446
|
+
"aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
|
|
447
|
+
isComposer &&
|
|
448
|
+
"aui-attachment-tile-composer border-foreground/20",
|
|
449
|
+
)}
|
|
450
|
+
role="button"
|
|
451
|
+
id="attachment-tile"
|
|
452
|
+
aria-label={`${typeLabel} attachment`}
|
|
453
|
+
>
|
|
454
|
+
<AttachmentThumb />
|
|
455
|
+
</div>
|
|
456
|
+
</TooltipTrigger>
|
|
457
|
+
</AttachmentPreviewDialog>
|
|
458
|
+
{isComposer && <AttachmentRemove />}
|
|
459
|
+
</AttachmentPrimitive.Root>
|
|
460
|
+
<TooltipContent side="top">
|
|
461
|
+
<AttachmentPrimitive.Name />
|
|
462
|
+
</TooltipContent>
|
|
463
|
+
</Tooltip>
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const AttachmentRemove: FC = () => {
|
|
468
|
+
return (
|
|
469
|
+
<AttachmentPrimitive.Remove asChild>
|
|
470
|
+
<TooltipIconButton
|
|
471
|
+
tooltip="Remove file"
|
|
472
|
+
className="aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive"
|
|
473
|
+
side="top"
|
|
474
|
+
>
|
|
475
|
+
<XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
|
|
476
|
+
</TooltipIconButton>
|
|
477
|
+
</AttachmentPrimitive.Remove>
|
|
478
|
+
);
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
export const UserMessageAttachments: FC = () => {
|
|
482
|
+
return (
|
|
483
|
+
<div className="aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2">
|
|
484
|
+
<MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
|
|
485
|
+
</div>
|
|
486
|
+
);
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
export const ComposerAttachments: FC = () => {
|
|
490
|
+
return (
|
|
491
|
+
<div className="aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden">
|
|
492
|
+
<ComposerPrimitive.Attachments
|
|
493
|
+
components={{ Attachment: AttachmentUI }}
|
|
494
|
+
/>
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
export const ComposerAddAttachment: FC = () => {
|
|
500
|
+
return (
|
|
501
|
+
<ComposerPrimitive.AddAttachment asChild>
|
|
502
|
+
<TooltipIconButton
|
|
503
|
+
tooltip="Add Attachment"
|
|
504
|
+
side="bottom"
|
|
505
|
+
variant="ghost"
|
|
506
|
+
size="icon"
|
|
507
|
+
className="aui-composer-add-attachment size-[34px] rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30"
|
|
508
|
+
aria-label="Add Attachment"
|
|
509
|
+
>
|
|
510
|
+
<PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
|
|
511
|
+
</TooltipIconButton>
|
|
512
|
+
</ComposerPrimitive.AddAttachment>
|
|
513
|
+
);
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
|
|
277
518
|
## components/assistant-ui/markdown-text.tsx
|
|
278
519
|
|
|
279
520
|
```tsx
|
|
@@ -282,13 +523,13 @@ export default function Home() {
|
|
|
282
523
|
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
283
524
|
|
|
284
525
|
import {
|
|
285
|
-
CodeHeaderProps,
|
|
526
|
+
type CodeHeaderProps,
|
|
286
527
|
MarkdownTextPrimitive,
|
|
287
528
|
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
288
529
|
useIsMarkdownCodeBlock,
|
|
289
530
|
} from "@assistant-ui/react-markdown";
|
|
290
531
|
import remarkGfm from "remark-gfm";
|
|
291
|
-
import { FC, memo, useState } from "react";
|
|
532
|
+
import { type FC, memo, useState } from "react";
|
|
292
533
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
293
534
|
|
|
294
535
|
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
@@ -314,8 +555,10 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
|
|
|
314
555
|
};
|
|
315
556
|
|
|
316
557
|
return (
|
|
317
|
-
<div className="flex items-center justify-between gap-4 rounded-t-lg bg-
|
|
318
|
-
<span className="lowercase [&>span]:text-xs">
|
|
558
|
+
<div className="aui-code-header-root mt-4 flex items-center justify-between gap-4 rounded-t-lg bg-muted-foreground/15 px-4 py-2 font-semibold text-foreground text-sm dark:bg-muted-foreground/20">
|
|
559
|
+
<span className="aui-code-header-language lowercase [&>span]:text-xs">
|
|
560
|
+
{language}
|
|
561
|
+
</span>
|
|
319
562
|
<TooltipIconButton tooltip="Copy" onClick={onCopy}>
|
|
320
563
|
{!isCopied && <CopyIcon />}
|
|
321
564
|
{isCopied && <CheckIcon />}
|
|
@@ -347,7 +590,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
347
590
|
h1: ({ className, ...props }) => (
|
|
348
591
|
<h1
|
|
349
592
|
className={cn(
|
|
350
|
-
"mb-8 scroll-m-20 text-4xl
|
|
593
|
+
"aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
|
|
351
594
|
className,
|
|
352
595
|
)}
|
|
353
596
|
{...props}
|
|
@@ -356,7 +599,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
356
599
|
h2: ({ className, ...props }) => (
|
|
357
600
|
<h2
|
|
358
601
|
className={cn(
|
|
359
|
-
"mt-8 mb-4 scroll-m-20 text-3xl
|
|
602
|
+
"aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
|
|
360
603
|
className,
|
|
361
604
|
)}
|
|
362
605
|
{...props}
|
|
@@ -365,7 +608,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
365
608
|
h3: ({ className, ...props }) => (
|
|
366
609
|
<h3
|
|
367
610
|
className={cn(
|
|
368
|
-
"mt-6 mb-4 scroll-m-20 text-2xl
|
|
611
|
+
"aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
|
|
369
612
|
className,
|
|
370
613
|
)}
|
|
371
614
|
{...props}
|
|
@@ -374,7 +617,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
374
617
|
h4: ({ className, ...props }) => (
|
|
375
618
|
<h4
|
|
376
619
|
className={cn(
|
|
377
|
-
"mt-6 mb-4 scroll-m-20 text-xl
|
|
620
|
+
"aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
|
|
378
621
|
className,
|
|
379
622
|
)}
|
|
380
623
|
{...props}
|
|
@@ -383,7 +626,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
383
626
|
h5: ({ className, ...props }) => (
|
|
384
627
|
<h5
|
|
385
628
|
className={cn(
|
|
386
|
-
"my-4 text-lg
|
|
629
|
+
"aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
|
|
387
630
|
className,
|
|
388
631
|
)}
|
|
389
632
|
{...props}
|
|
@@ -391,20 +634,26 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
391
634
|
),
|
|
392
635
|
h6: ({ className, ...props }) => (
|
|
393
636
|
<h6
|
|
394
|
-
className={cn(
|
|
637
|
+
className={cn(
|
|
638
|
+
"aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
|
|
639
|
+
className,
|
|
640
|
+
)}
|
|
395
641
|
{...props}
|
|
396
642
|
/>
|
|
397
643
|
),
|
|
398
644
|
p: ({ className, ...props }) => (
|
|
399
645
|
<p
|
|
400
|
-
className={cn(
|
|
646
|
+
className={cn(
|
|
647
|
+
"aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
|
|
648
|
+
className,
|
|
649
|
+
)}
|
|
401
650
|
{...props}
|
|
402
651
|
/>
|
|
403
652
|
),
|
|
404
653
|
a: ({ className, ...props }) => (
|
|
405
654
|
<a
|
|
406
655
|
className={cn(
|
|
407
|
-
"
|
|
656
|
+
"aui-md-a font-medium text-primary underline underline-offset-4",
|
|
408
657
|
className,
|
|
409
658
|
)}
|
|
410
659
|
{...props}
|
|
@@ -412,29 +661,29 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
412
661
|
),
|
|
413
662
|
blockquote: ({ className, ...props }) => (
|
|
414
663
|
<blockquote
|
|
415
|
-
className={cn("border-l-2 pl-6 italic", className)}
|
|
664
|
+
className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
|
|
416
665
|
{...props}
|
|
417
666
|
/>
|
|
418
667
|
),
|
|
419
668
|
ul: ({ className, ...props }) => (
|
|
420
669
|
<ul
|
|
421
|
-
className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
|
|
670
|
+
className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
|
|
422
671
|
{...props}
|
|
423
672
|
/>
|
|
424
673
|
),
|
|
425
674
|
ol: ({ className, ...props }) => (
|
|
426
675
|
<ol
|
|
427
|
-
className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
|
|
676
|
+
className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
|
|
428
677
|
{...props}
|
|
429
678
|
/>
|
|
430
679
|
),
|
|
431
680
|
hr: ({ className, ...props }) => (
|
|
432
|
-
<hr className={cn("my-5 border-b", className)} {...props} />
|
|
681
|
+
<hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
|
|
433
682
|
),
|
|
434
683
|
table: ({ className, ...props }) => (
|
|
435
684
|
<table
|
|
436
685
|
className={cn(
|
|
437
|
-
"my-5 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
686
|
+
"aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
438
687
|
className,
|
|
439
688
|
)}
|
|
440
689
|
{...props}
|
|
@@ -443,7 +692,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
443
692
|
th: ({ className, ...props }) => (
|
|
444
693
|
<th
|
|
445
694
|
className={cn(
|
|
446
|
-
"bg-muted px-4 py-2 text-left font-bold first:rounded-tl-lg last:rounded-tr-lg [
|
|
695
|
+
"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",
|
|
447
696
|
className,
|
|
448
697
|
)}
|
|
449
698
|
{...props}
|
|
@@ -452,7 +701,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
452
701
|
td: ({ className, ...props }) => (
|
|
453
702
|
<td
|
|
454
703
|
className={cn(
|
|
455
|
-
"border-b border-l px-4 py-2 text-left last:border-r [
|
|
704
|
+
"aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
|
|
456
705
|
className,
|
|
457
706
|
)}
|
|
458
707
|
{...props}
|
|
@@ -461,7 +710,7 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
461
710
|
tr: ({ className, ...props }) => (
|
|
462
711
|
<tr
|
|
463
712
|
className={cn(
|
|
464
|
-
"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",
|
|
713
|
+
"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",
|
|
465
714
|
className,
|
|
466
715
|
)}
|
|
467
716
|
{...props}
|
|
@@ -469,14 +718,14 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
469
718
|
),
|
|
470
719
|
sup: ({ className, ...props }) => (
|
|
471
720
|
<sup
|
|
472
|
-
className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
|
|
721
|
+
className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
|
|
473
722
|
{...props}
|
|
474
723
|
/>
|
|
475
724
|
),
|
|
476
725
|
pre: ({ className, ...props }) => (
|
|
477
726
|
<pre
|
|
478
727
|
className={cn(
|
|
479
|
-
"overflow-x-auto rounded-b-lg bg-black p-4 text-white",
|
|
728
|
+
"aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
|
|
480
729
|
className,
|
|
481
730
|
)}
|
|
482
731
|
{...props}
|
|
@@ -487,7 +736,8 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
487
736
|
return (
|
|
488
737
|
<code
|
|
489
738
|
className={cn(
|
|
490
|
-
!isCodeBlock &&
|
|
739
|
+
!isCodeBlock &&
|
|
740
|
+
"aui-md-inline-code rounded border bg-muted font-semibold",
|
|
491
741
|
className,
|
|
492
742
|
)}
|
|
493
743
|
{...props}
|
|
@@ -502,21 +752,27 @@ const defaultComponents = memoizeMarkdownComponents({
|
|
|
502
752
|
## components/assistant-ui/thread-list.tsx
|
|
503
753
|
|
|
504
754
|
```tsx
|
|
505
|
-
import
|
|
755
|
+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
756
|
+
import { Button } from "@/components/ui/button";
|
|
757
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
506
758
|
import {
|
|
759
|
+
AssistantIf,
|
|
507
760
|
ThreadListItemPrimitive,
|
|
508
761
|
ThreadListPrimitive,
|
|
509
762
|
} from "@assistant-ui/react";
|
|
510
763
|
import { ArchiveIcon, PlusIcon } from "lucide-react";
|
|
511
|
-
|
|
512
|
-
import { Button } from "@/components/ui/button";
|
|
513
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
764
|
+
import type { FC } from "react";
|
|
514
765
|
|
|
515
766
|
export const ThreadList: FC = () => {
|
|
516
767
|
return (
|
|
517
|
-
<ThreadListPrimitive.Root className="flex flex-col
|
|
768
|
+
<ThreadListPrimitive.Root className="aui-root aui-thread-list-root flex flex-col gap-1">
|
|
518
769
|
<ThreadListNew />
|
|
519
|
-
<
|
|
770
|
+
<AssistantIf condition={({ threads }) => threads.isLoading}>
|
|
771
|
+
<ThreadListSkeleton />
|
|
772
|
+
</AssistantIf>
|
|
773
|
+
<AssistantIf condition={({ threads }) => !threads.isLoading}>
|
|
774
|
+
<ThreadListPrimitive.Items components={{ ThreadListItem }} />
|
|
775
|
+
</AssistantIf>
|
|
520
776
|
</ThreadListPrimitive.Root>
|
|
521
777
|
);
|
|
522
778
|
};
|
|
@@ -525,48 +781,53 @@ const ThreadListNew: FC = () => {
|
|
|
525
781
|
return (
|
|
526
782
|
<ThreadListPrimitive.New asChild>
|
|
527
783
|
<Button
|
|
528
|
-
|
|
529
|
-
|
|
784
|
+
variant="outline"
|
|
785
|
+
className="aui-thread-list-new h-9 justify-start gap-2 rounded-lg px-3 text-sm hover:bg-muted data-active:bg-muted"
|
|
530
786
|
>
|
|
531
|
-
<PlusIcon />
|
|
787
|
+
<PlusIcon className="size-4" />
|
|
532
788
|
New Thread
|
|
533
789
|
</Button>
|
|
534
790
|
</ThreadListPrimitive.New>
|
|
535
791
|
);
|
|
536
792
|
};
|
|
537
793
|
|
|
538
|
-
const
|
|
539
|
-
return
|
|
794
|
+
const ThreadListSkeleton: FC = () => {
|
|
795
|
+
return (
|
|
796
|
+
<div className="flex flex-col gap-1">
|
|
797
|
+
{Array.from({ length: 5 }, (_, i) => (
|
|
798
|
+
<div
|
|
799
|
+
key={i}
|
|
800
|
+
role="status"
|
|
801
|
+
aria-label="Loading threads"
|
|
802
|
+
className="aui-thread-list-skeleton-wrapper flex h-9 items-center px-3"
|
|
803
|
+
>
|
|
804
|
+
<Skeleton className="aui-thread-list-skeleton h-4 w-full" />
|
|
805
|
+
</div>
|
|
806
|
+
))}
|
|
807
|
+
</div>
|
|
808
|
+
);
|
|
540
809
|
};
|
|
541
810
|
|
|
542
811
|
const ThreadListItem: FC = () => {
|
|
543
812
|
return (
|
|
544
|
-
<ThreadListItemPrimitive.Root className="
|
|
545
|
-
<ThreadListItemPrimitive.Trigger className="flex-
|
|
546
|
-
<
|
|
813
|
+
<ThreadListItemPrimitive.Root className="aui-thread-list-item group flex h-9 items-center rounded-lg transition-colors hover:bg-muted focus-visible:bg-muted focus-visible:outline-none data-active:bg-muted">
|
|
814
|
+
<ThreadListItemPrimitive.Trigger className="aui-thread-list-item-trigger flex h-full flex-1 items-center truncate px-3 text-start text-sm">
|
|
815
|
+
<ThreadListItemPrimitive.Title fallback="New Chat" />
|
|
547
816
|
</ThreadListItemPrimitive.Trigger>
|
|
548
817
|
<ThreadListItemArchive />
|
|
549
818
|
</ThreadListItemPrimitive.Root>
|
|
550
819
|
);
|
|
551
820
|
};
|
|
552
821
|
|
|
553
|
-
const ThreadListItemTitle: FC = () => {
|
|
554
|
-
return (
|
|
555
|
-
<p className="text-sm">
|
|
556
|
-
<ThreadListItemPrimitive.Title fallback="New Chat" />
|
|
557
|
-
</p>
|
|
558
|
-
);
|
|
559
|
-
};
|
|
560
|
-
|
|
561
822
|
const ThreadListItemArchive: FC = () => {
|
|
562
823
|
return (
|
|
563
824
|
<ThreadListItemPrimitive.Archive asChild>
|
|
564
825
|
<TooltipIconButton
|
|
565
|
-
className="hover:text-primary text-foreground mr-3 ml-auto size-4 p-0"
|
|
566
826
|
variant="ghost"
|
|
567
827
|
tooltip="Archive thread"
|
|
828
|
+
className="aui-thread-list-item-archive mr-2 size-7 p-0 opacity-0 transition-opacity group-hover:opacity-100"
|
|
568
829
|
>
|
|
569
|
-
<ArchiveIcon />
|
|
830
|
+
<ArchiveIcon className="size-4" />
|
|
570
831
|
</TooltipIconButton>
|
|
571
832
|
</ThreadListItemPrimitive.Archive>
|
|
572
833
|
);
|
|
@@ -577,57 +838,67 @@ const ThreadListItemArchive: FC = () => {
|
|
|
577
838
|
## components/assistant-ui/thread.tsx
|
|
578
839
|
|
|
579
840
|
```tsx
|
|
841
|
+
import {
|
|
842
|
+
ComposerAddAttachment,
|
|
843
|
+
ComposerAttachments,
|
|
844
|
+
UserMessageAttachments,
|
|
845
|
+
} from "@/components/assistant-ui/attachment";
|
|
846
|
+
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
847
|
+
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
|
|
848
|
+
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
849
|
+
import { Button } from "@/components/ui/button";
|
|
850
|
+
import { cn } from "@/lib/utils";
|
|
580
851
|
import {
|
|
581
852
|
ActionBarPrimitive,
|
|
853
|
+
AssistantIf,
|
|
582
854
|
BranchPickerPrimitive,
|
|
583
855
|
ComposerPrimitive,
|
|
856
|
+
ErrorPrimitive,
|
|
584
857
|
MessagePrimitive,
|
|
585
858
|
ThreadPrimitive,
|
|
586
859
|
} from "@assistant-ui/react";
|
|
587
|
-
import type { FC } from "react";
|
|
588
860
|
import {
|
|
589
861
|
ArrowDownIcon,
|
|
862
|
+
ArrowUpIcon,
|
|
590
863
|
CheckIcon,
|
|
591
864
|
ChevronLeftIcon,
|
|
592
865
|
ChevronRightIcon,
|
|
593
866
|
CopyIcon,
|
|
867
|
+
DownloadIcon,
|
|
594
868
|
PencilIcon,
|
|
595
869
|
RefreshCwIcon,
|
|
596
|
-
|
|
870
|
+
SquareIcon,
|
|
597
871
|
} from "lucide-react";
|
|
598
|
-
import {
|
|
599
|
-
|
|
600
|
-
import { Button } from "@/components/ui/button";
|
|
601
|
-
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
602
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
872
|
+
import type { FC } from "react";
|
|
603
873
|
|
|
604
874
|
export const Thread: FC = () => {
|
|
605
875
|
return (
|
|
606
876
|
<ThreadPrimitive.Root
|
|
607
|
-
className="
|
|
877
|
+
className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
|
|
608
878
|
style={{
|
|
609
|
-
["--thread-max-width" as string]: "
|
|
879
|
+
["--thread-max-width" as string]: "44rem",
|
|
610
880
|
}}
|
|
611
881
|
>
|
|
612
|
-
<ThreadPrimitive.Viewport
|
|
613
|
-
|
|
882
|
+
<ThreadPrimitive.Viewport
|
|
883
|
+
turnAnchor="top"
|
|
884
|
+
className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
|
|
885
|
+
>
|
|
886
|
+
<AssistantIf condition={({ thread }) => thread.isEmpty}>
|
|
887
|
+
<ThreadWelcome />
|
|
888
|
+
</AssistantIf>
|
|
614
889
|
|
|
615
890
|
<ThreadPrimitive.Messages
|
|
616
891
|
components={{
|
|
617
|
-
UserMessage
|
|
618
|
-
EditComposer
|
|
619
|
-
AssistantMessage
|
|
892
|
+
UserMessage,
|
|
893
|
+
EditComposer,
|
|
894
|
+
AssistantMessage,
|
|
620
895
|
}}
|
|
621
896
|
/>
|
|
622
897
|
|
|
623
|
-
<ThreadPrimitive.
|
|
624
|
-
<div className="min-h-8 flex-grow" />
|
|
625
|
-
</ThreadPrimitive.If>
|
|
626
|
-
|
|
627
|
-
<div className="sticky bottom-0 mt-3 flex w-full max-w-[var(--thread-max-width)] flex-col items-center justify-end rounded-t-lg bg-inherit pb-4">
|
|
898
|
+
<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">
|
|
628
899
|
<ThreadScrollToBottom />
|
|
629
900
|
<Composer />
|
|
630
|
-
</
|
|
901
|
+
</ThreadPrimitive.ViewportFooter>
|
|
631
902
|
</ThreadPrimitive.Viewport>
|
|
632
903
|
</ThreadPrimitive.Root>
|
|
633
904
|
);
|
|
@@ -639,7 +910,7 @@ const ThreadScrollToBottom: FC = () => {
|
|
|
639
910
|
<TooltipIconButton
|
|
640
911
|
tooltip="Scroll to bottom"
|
|
641
912
|
variant="outline"
|
|
642
|
-
className="
|
|
913
|
+
className="aui-thread-scroll-to-bottom -top-12 absolute z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent"
|
|
643
914
|
>
|
|
644
915
|
<ArrowDownIcon />
|
|
645
916
|
</TooltipIconButton>
|
|
@@ -649,175 +920,247 @@ const ThreadScrollToBottom: FC = () => {
|
|
|
649
920
|
|
|
650
921
|
const ThreadWelcome: FC = () => {
|
|
651
922
|
return (
|
|
652
|
-
<
|
|
653
|
-
<div className="
|
|
654
|
-
<div className="flex
|
|
655
|
-
<
|
|
923
|
+
<div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
|
|
924
|
+
<div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
|
|
925
|
+
<div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
|
|
926
|
+
<h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
|
|
927
|
+
Hello there!
|
|
928
|
+
</h1>
|
|
929
|
+
<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">
|
|
930
|
+
How can I help you today?
|
|
931
|
+
</p>
|
|
656
932
|
</div>
|
|
657
|
-
<ThreadWelcomeSuggestions />
|
|
658
933
|
</div>
|
|
659
|
-
|
|
934
|
+
<ThreadSuggestions />
|
|
935
|
+
</div>
|
|
660
936
|
);
|
|
661
937
|
};
|
|
662
938
|
|
|
663
|
-
const
|
|
939
|
+
const SUGGESTIONS = [
|
|
940
|
+
{
|
|
941
|
+
title: "What's the weather",
|
|
942
|
+
label: "in San Francisco?",
|
|
943
|
+
prompt: "What's the weather in San Francisco?",
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
title: "Explain React hooks",
|
|
947
|
+
label: "like useState and useEffect",
|
|
948
|
+
prompt: "Explain React hooks like useState and useEffect",
|
|
949
|
+
},
|
|
950
|
+
] as const;
|
|
951
|
+
|
|
952
|
+
const ThreadSuggestions: FC = () => {
|
|
664
953
|
return (
|
|
665
|
-
<div className="
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
954
|
+
<div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
|
|
955
|
+
{SUGGESTIONS.map((suggestion, index) => (
|
|
956
|
+
<div
|
|
957
|
+
key={suggestion.prompt}
|
|
958
|
+
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"
|
|
959
|
+
style={{ animationDelay: `${100 + index * 50}ms` }}
|
|
960
|
+
>
|
|
961
|
+
<ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
|
|
962
|
+
<Button
|
|
963
|
+
variant="ghost"
|
|
964
|
+
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"
|
|
965
|
+
aria-label={suggestion.prompt}
|
|
966
|
+
>
|
|
967
|
+
<span className="aui-thread-welcome-suggestion-text-1 font-medium">
|
|
968
|
+
{suggestion.title}
|
|
969
|
+
</span>
|
|
970
|
+
<span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
|
|
971
|
+
{suggestion.label}
|
|
972
|
+
</span>
|
|
973
|
+
</Button>
|
|
974
|
+
</ThreadPrimitive.Suggestion>
|
|
975
|
+
</div>
|
|
976
|
+
))}
|
|
686
977
|
</div>
|
|
687
978
|
);
|
|
688
979
|
};
|
|
689
980
|
|
|
690
981
|
const Composer: FC = () => {
|
|
691
982
|
return (
|
|
692
|
-
<ComposerPrimitive.Root className="
|
|
693
|
-
<ComposerPrimitive.
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
983
|
+
<ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
|
|
984
|
+
<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">
|
|
985
|
+
<ComposerAttachments />
|
|
986
|
+
<ComposerPrimitive.Input
|
|
987
|
+
placeholder="Send a message..."
|
|
988
|
+
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"
|
|
989
|
+
rows={1}
|
|
990
|
+
autoFocus
|
|
991
|
+
aria-label="Message input"
|
|
992
|
+
/>
|
|
993
|
+
<ComposerAction />
|
|
994
|
+
</ComposerPrimitive.AttachmentDropzone>
|
|
700
995
|
</ComposerPrimitive.Root>
|
|
701
996
|
);
|
|
702
997
|
};
|
|
703
998
|
|
|
704
999
|
const ComposerAction: FC = () => {
|
|
705
1000
|
return (
|
|
706
|
-
|
|
707
|
-
<
|
|
1001
|
+
<div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
|
|
1002
|
+
<ComposerAddAttachment />
|
|
1003
|
+
|
|
1004
|
+
<AssistantIf condition={({ thread }) => !thread.isRunning}>
|
|
708
1005
|
<ComposerPrimitive.Send asChild>
|
|
709
1006
|
<TooltipIconButton
|
|
710
|
-
tooltip="Send"
|
|
1007
|
+
tooltip="Send message"
|
|
1008
|
+
side="bottom"
|
|
1009
|
+
type="submit"
|
|
711
1010
|
variant="default"
|
|
712
|
-
|
|
1011
|
+
size="icon"
|
|
1012
|
+
className="aui-composer-send size-8 rounded-full"
|
|
1013
|
+
aria-label="Send message"
|
|
713
1014
|
>
|
|
714
|
-
<
|
|
1015
|
+
<ArrowUpIcon className="aui-composer-send-icon size-4" />
|
|
715
1016
|
</TooltipIconButton>
|
|
716
1017
|
</ComposerPrimitive.Send>
|
|
717
|
-
</
|
|
718
|
-
|
|
1018
|
+
</AssistantIf>
|
|
1019
|
+
|
|
1020
|
+
<AssistantIf condition={({ thread }) => thread.isRunning}>
|
|
719
1021
|
<ComposerPrimitive.Cancel asChild>
|
|
720
|
-
<
|
|
721
|
-
|
|
1022
|
+
<Button
|
|
1023
|
+
type="button"
|
|
722
1024
|
variant="default"
|
|
723
|
-
|
|
1025
|
+
size="icon"
|
|
1026
|
+
className="aui-composer-cancel size-8 rounded-full"
|
|
1027
|
+
aria-label="Stop generating"
|
|
724
1028
|
>
|
|
725
|
-
<
|
|
726
|
-
</
|
|
1029
|
+
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
|
|
1030
|
+
</Button>
|
|
727
1031
|
</ComposerPrimitive.Cancel>
|
|
728
|
-
</
|
|
729
|
-
|
|
1032
|
+
</AssistantIf>
|
|
1033
|
+
</div>
|
|
730
1034
|
);
|
|
731
1035
|
};
|
|
732
1036
|
|
|
733
|
-
const
|
|
1037
|
+
const MessageError: FC = () => {
|
|
734
1038
|
return (
|
|
735
|
-
<MessagePrimitive.
|
|
736
|
-
<
|
|
1039
|
+
<MessagePrimitive.Error>
|
|
1040
|
+
<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">
|
|
1041
|
+
<ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
|
|
1042
|
+
</ErrorPrimitive.Root>
|
|
1043
|
+
</MessagePrimitive.Error>
|
|
1044
|
+
);
|
|
1045
|
+
};
|
|
737
1046
|
|
|
738
|
-
|
|
739
|
-
|
|
1047
|
+
const AssistantMessage: FC = () => {
|
|
1048
|
+
return (
|
|
1049
|
+
<MessagePrimitive.Root
|
|
1050
|
+
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"
|
|
1051
|
+
data-role="assistant"
|
|
1052
|
+
>
|
|
1053
|
+
<div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
|
|
1054
|
+
<MessagePrimitive.Parts
|
|
1055
|
+
components={{
|
|
1056
|
+
Text: MarkdownText,
|
|
1057
|
+
tools: { Fallback: ToolFallback },
|
|
1058
|
+
}}
|
|
1059
|
+
/>
|
|
1060
|
+
<MessageError />
|
|
740
1061
|
</div>
|
|
741
1062
|
|
|
742
|
-
<
|
|
1063
|
+
<div className="aui-assistant-message-footer mt-1 ml-2 flex">
|
|
1064
|
+
<BranchPicker />
|
|
1065
|
+
<AssistantActionBar />
|
|
1066
|
+
</div>
|
|
743
1067
|
</MessagePrimitive.Root>
|
|
744
1068
|
);
|
|
745
1069
|
};
|
|
746
1070
|
|
|
747
|
-
const
|
|
1071
|
+
const AssistantActionBar: FC = () => {
|
|
748
1072
|
return (
|
|
749
1073
|
<ActionBarPrimitive.Root
|
|
750
1074
|
hideWhenRunning
|
|
751
1075
|
autohide="not-last"
|
|
752
|
-
|
|
1076
|
+
autohideFloat="single-branch"
|
|
1077
|
+
className="aui-assistant-action-bar-root -ml-1 col-start-3 row-start-2 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"
|
|
753
1078
|
>
|
|
754
|
-
<ActionBarPrimitive.
|
|
755
|
-
<TooltipIconButton tooltip="
|
|
756
|
-
<
|
|
1079
|
+
<ActionBarPrimitive.Copy asChild>
|
|
1080
|
+
<TooltipIconButton tooltip="Copy">
|
|
1081
|
+
<AssistantIf condition={({ message }) => message.isCopied}>
|
|
1082
|
+
<CheckIcon />
|
|
1083
|
+
</AssistantIf>
|
|
1084
|
+
<AssistantIf condition={({ message }) => !message.isCopied}>
|
|
1085
|
+
<CopyIcon />
|
|
1086
|
+
</AssistantIf>
|
|
757
1087
|
</TooltipIconButton>
|
|
758
|
-
</ActionBarPrimitive.
|
|
1088
|
+
</ActionBarPrimitive.Copy>
|
|
1089
|
+
<ActionBarPrimitive.ExportMarkdown asChild>
|
|
1090
|
+
<TooltipIconButton tooltip="Export as Markdown">
|
|
1091
|
+
<DownloadIcon />
|
|
1092
|
+
</TooltipIconButton>
|
|
1093
|
+
</ActionBarPrimitive.ExportMarkdown>
|
|
1094
|
+
<ActionBarPrimitive.Reload asChild>
|
|
1095
|
+
<TooltipIconButton tooltip="Refresh">
|
|
1096
|
+
<RefreshCwIcon />
|
|
1097
|
+
</TooltipIconButton>
|
|
1098
|
+
</ActionBarPrimitive.Reload>
|
|
759
1099
|
</ActionBarPrimitive.Root>
|
|
760
1100
|
);
|
|
761
1101
|
};
|
|
762
1102
|
|
|
763
|
-
const
|
|
1103
|
+
const UserMessage: FC = () => {
|
|
764
1104
|
return (
|
|
765
|
-
<
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
<Button variant="ghost">Cancel</Button>
|
|
771
|
-
</ComposerPrimitive.Cancel>
|
|
772
|
-
<ComposerPrimitive.Send asChild>
|
|
773
|
-
<Button>Send</Button>
|
|
774
|
-
</ComposerPrimitive.Send>
|
|
775
|
-
</div>
|
|
776
|
-
</ComposerPrimitive.Root>
|
|
777
|
-
);
|
|
778
|
-
};
|
|
1105
|
+
<MessagePrimitive.Root
|
|
1106
|
+
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"
|
|
1107
|
+
data-role="user"
|
|
1108
|
+
>
|
|
1109
|
+
<UserMessageAttachments />
|
|
779
1110
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
<
|
|
1111
|
+
<div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
|
|
1112
|
+
<div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
|
|
1113
|
+
<MessagePrimitive.Parts />
|
|
1114
|
+
</div>
|
|
1115
|
+
<div className="aui-user-action-bar-wrapper -translate-x-full -translate-y-1/2 absolute top-1/2 left-0 pr-2">
|
|
1116
|
+
<UserActionBar />
|
|
1117
|
+
</div>
|
|
785
1118
|
</div>
|
|
786
1119
|
|
|
787
|
-
<
|
|
788
|
-
|
|
789
|
-
<BranchPicker className="col-start-2 row-start-2 mr-2 -ml-2" />
|
|
1120
|
+
<BranchPicker className="aui-user-branch-picker -mr-1 col-span-full col-start-1 row-start-3 justify-end" />
|
|
790
1121
|
</MessagePrimitive.Root>
|
|
791
1122
|
);
|
|
792
1123
|
};
|
|
793
1124
|
|
|
794
|
-
const
|
|
1125
|
+
const UserActionBar: FC = () => {
|
|
795
1126
|
return (
|
|
796
1127
|
<ActionBarPrimitive.Root
|
|
797
1128
|
hideWhenRunning
|
|
798
1129
|
autohide="not-last"
|
|
799
|
-
|
|
800
|
-
className="text-muted-foreground data-[floating]:bg-background col-start-3 row-start-2 -ml-1 flex gap-1 data-[floating]:absolute data-[floating]:rounded-md data-[floating]:border data-[floating]:p-1 data-[floating]:shadow-sm"
|
|
1130
|
+
className="aui-user-action-bar-root flex flex-col items-end"
|
|
801
1131
|
>
|
|
802
|
-
<ActionBarPrimitive.
|
|
803
|
-
<TooltipIconButton tooltip="
|
|
804
|
-
<
|
|
805
|
-
<CheckIcon />
|
|
806
|
-
</MessagePrimitive.If>
|
|
807
|
-
<MessagePrimitive.If copied={false}>
|
|
808
|
-
<CopyIcon />
|
|
809
|
-
</MessagePrimitive.If>
|
|
810
|
-
</TooltipIconButton>
|
|
811
|
-
</ActionBarPrimitive.Copy>
|
|
812
|
-
<ActionBarPrimitive.Reload asChild>
|
|
813
|
-
<TooltipIconButton tooltip="Refresh">
|
|
814
|
-
<RefreshCwIcon />
|
|
1132
|
+
<ActionBarPrimitive.Edit asChild>
|
|
1133
|
+
<TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
|
|
1134
|
+
<PencilIcon />
|
|
815
1135
|
</TooltipIconButton>
|
|
816
|
-
</ActionBarPrimitive.
|
|
1136
|
+
</ActionBarPrimitive.Edit>
|
|
817
1137
|
</ActionBarPrimitive.Root>
|
|
818
1138
|
);
|
|
819
1139
|
};
|
|
820
1140
|
|
|
1141
|
+
const EditComposer: FC = () => {
|
|
1142
|
+
return (
|
|
1143
|
+
<MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
|
|
1144
|
+
<ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
|
|
1145
|
+
<ComposerPrimitive.Input
|
|
1146
|
+
className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
|
|
1147
|
+
autoFocus
|
|
1148
|
+
/>
|
|
1149
|
+
<div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
|
|
1150
|
+
<ComposerPrimitive.Cancel asChild>
|
|
1151
|
+
<Button variant="ghost" size="sm">
|
|
1152
|
+
Cancel
|
|
1153
|
+
</Button>
|
|
1154
|
+
</ComposerPrimitive.Cancel>
|
|
1155
|
+
<ComposerPrimitive.Send asChild>
|
|
1156
|
+
<Button size="sm">Update</Button>
|
|
1157
|
+
</ComposerPrimitive.Send>
|
|
1158
|
+
</div>
|
|
1159
|
+
</ComposerPrimitive.Root>
|
|
1160
|
+
</MessagePrimitive.Root>
|
|
1161
|
+
);
|
|
1162
|
+
};
|
|
1163
|
+
|
|
821
1164
|
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
822
1165
|
className,
|
|
823
1166
|
...rest
|
|
@@ -826,7 +1169,7 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
|
826
1169
|
<BranchPickerPrimitive.Root
|
|
827
1170
|
hideWhenSingleBranch
|
|
828
1171
|
className={cn(
|
|
829
|
-
"
|
|
1172
|
+
"aui-branch-picker-root -ml-2 mr-2 inline-flex items-center text-muted-foreground text-xs",
|
|
830
1173
|
className,
|
|
831
1174
|
)}
|
|
832
1175
|
{...rest}
|
|
@@ -836,7 +1179,7 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
|
836
1179
|
<ChevronLeftIcon />
|
|
837
1180
|
</TooltipIconButton>
|
|
838
1181
|
</BranchPickerPrimitive.Previous>
|
|
839
|
-
<span className="font-medium">
|
|
1182
|
+
<span className="aui-branch-picker-state font-medium">
|
|
840
1183
|
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
841
1184
|
</span>
|
|
842
1185
|
<BranchPickerPrimitive.Next asChild>
|
|
@@ -848,17 +1191,102 @@ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
|
848
1191
|
);
|
|
849
1192
|
};
|
|
850
1193
|
|
|
851
|
-
|
|
1194
|
+
```
|
|
1195
|
+
|
|
1196
|
+
## components/assistant-ui/tool-fallback.tsx
|
|
1197
|
+
|
|
1198
|
+
```tsx
|
|
1199
|
+
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
1200
|
+
import {
|
|
1201
|
+
CheckIcon,
|
|
1202
|
+
ChevronDownIcon,
|
|
1203
|
+
ChevronUpIcon,
|
|
1204
|
+
XCircleIcon,
|
|
1205
|
+
} from "lucide-react";
|
|
1206
|
+
import { useState } from "react";
|
|
1207
|
+
import { Button } from "@/components/ui/button";
|
|
1208
|
+
import { cn } from "@/lib/utils";
|
|
1209
|
+
|
|
1210
|
+
export const ToolFallback: ToolCallMessagePartComponent = ({
|
|
1211
|
+
toolName,
|
|
1212
|
+
argsText,
|
|
1213
|
+
result,
|
|
1214
|
+
status,
|
|
1215
|
+
}) => {
|
|
1216
|
+
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
1217
|
+
|
|
1218
|
+
const isCancelled =
|
|
1219
|
+
status?.type === "incomplete" && status.reason === "cancelled";
|
|
1220
|
+
const cancelledReason =
|
|
1221
|
+
isCancelled && status.error
|
|
1222
|
+
? typeof status.error === "string"
|
|
1223
|
+
? status.error
|
|
1224
|
+
: JSON.stringify(status.error)
|
|
1225
|
+
: null;
|
|
1226
|
+
|
|
852
1227
|
return (
|
|
853
|
-
<
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
height="16"
|
|
1228
|
+
<div
|
|
1229
|
+
className={cn(
|
|
1230
|
+
"aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
|
|
1231
|
+
isCancelled && "border-muted-foreground/30 bg-muted/30",
|
|
1232
|
+
)}
|
|
859
1233
|
>
|
|
860
|
-
<
|
|
861
|
-
|
|
1234
|
+
<div className="aui-tool-fallback-header flex items-center gap-2 px-4">
|
|
1235
|
+
{isCancelled ? (
|
|
1236
|
+
<XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" />
|
|
1237
|
+
) : (
|
|
1238
|
+
<CheckIcon className="aui-tool-fallback-icon size-4" />
|
|
1239
|
+
)}
|
|
1240
|
+
<p
|
|
1241
|
+
className={cn(
|
|
1242
|
+
"aui-tool-fallback-title grow",
|
|
1243
|
+
isCancelled && "text-muted-foreground line-through",
|
|
1244
|
+
)}
|
|
1245
|
+
>
|
|
1246
|
+
{isCancelled ? "Cancelled tool: " : "Used tool: "}
|
|
1247
|
+
<b>{toolName}</b>
|
|
1248
|
+
</p>
|
|
1249
|
+
<Button onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
1250
|
+
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
1251
|
+
</Button>
|
|
1252
|
+
</div>
|
|
1253
|
+
{!isCollapsed && (
|
|
1254
|
+
<div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
|
|
1255
|
+
{cancelledReason && (
|
|
1256
|
+
<div className="aui-tool-fallback-cancelled-root px-4">
|
|
1257
|
+
<p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground">
|
|
1258
|
+
Cancelled reason:
|
|
1259
|
+
</p>
|
|
1260
|
+
<p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
|
|
1261
|
+
{cancelledReason}
|
|
1262
|
+
</p>
|
|
1263
|
+
</div>
|
|
1264
|
+
)}
|
|
1265
|
+
<div
|
|
1266
|
+
className={cn(
|
|
1267
|
+
"aui-tool-fallback-args-root px-4",
|
|
1268
|
+
isCancelled && "opacity-60",
|
|
1269
|
+
)}
|
|
1270
|
+
>
|
|
1271
|
+
<pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
|
|
1272
|
+
{argsText}
|
|
1273
|
+
</pre>
|
|
1274
|
+
</div>
|
|
1275
|
+
{!isCancelled && result !== undefined && (
|
|
1276
|
+
<div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
|
|
1277
|
+
<p className="aui-tool-fallback-result-header font-semibold">
|
|
1278
|
+
Result:
|
|
1279
|
+
</p>
|
|
1280
|
+
<pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
|
|
1281
|
+
{typeof result === "string"
|
|
1282
|
+
? result
|
|
1283
|
+
: JSON.stringify(result, null, 2)}
|
|
1284
|
+
</pre>
|
|
1285
|
+
</div>
|
|
1286
|
+
)}
|
|
1287
|
+
</div>
|
|
1288
|
+
)}
|
|
1289
|
+
</div>
|
|
862
1290
|
);
|
|
863
1291
|
};
|
|
864
1292
|
|
|
@@ -869,7 +1297,7 @@ const CircleStopIcon = () => {
|
|
|
869
1297
|
```tsx
|
|
870
1298
|
"use client";
|
|
871
1299
|
|
|
872
|
-
import {
|
|
1300
|
+
import { ComponentPropsWithRef, forwardRef } from "react";
|
|
873
1301
|
import { Slottable } from "@radix-ui/react-slot";
|
|
874
1302
|
|
|
875
1303
|
import {
|
|
@@ -880,7 +1308,7 @@ import {
|
|
|
880
1308
|
import { Button } from "@/components/ui/button";
|
|
881
1309
|
import { cn } from "@/lib/utils";
|
|
882
1310
|
|
|
883
|
-
export type TooltipIconButtonProps =
|
|
1311
|
+
export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
|
|
884
1312
|
tooltip: string;
|
|
885
1313
|
side?: "top" | "bottom" | "left" | "right";
|
|
886
1314
|
};
|
|
@@ -896,11 +1324,11 @@ export const TooltipIconButton = forwardRef<
|
|
|
896
1324
|
variant="ghost"
|
|
897
1325
|
size="icon"
|
|
898
1326
|
{...rest}
|
|
899
|
-
className={cn("size-6 p-1", className)}
|
|
1327
|
+
className={cn("aui-button-icon size-6 p-1", className)}
|
|
900
1328
|
ref={ref}
|
|
901
1329
|
>
|
|
902
1330
|
<Slottable>{children}</Slottable>
|
|
903
|
-
<span className="sr-only">{tooltip}</span>
|
|
1331
|
+
<span className="aui-sr-only sr-only">{tooltip}</span>
|
|
904
1332
|
</Button>
|
|
905
1333
|
</TooltipTrigger>
|
|
906
1334
|
<TooltipContent side={side}>{tooltip}</TooltipContent>
|
|
@@ -912,6 +1340,62 @@ TooltipIconButton.displayName = "TooltipIconButton";
|
|
|
912
1340
|
|
|
913
1341
|
```
|
|
914
1342
|
|
|
1343
|
+
## components/ui/avatar.tsx
|
|
1344
|
+
|
|
1345
|
+
```tsx
|
|
1346
|
+
"use client";
|
|
1347
|
+
|
|
1348
|
+
import * as React from "react";
|
|
1349
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
1350
|
+
|
|
1351
|
+
import { cn } from "@/lib/utils";
|
|
1352
|
+
|
|
1353
|
+
const Avatar = React.forwardRef<
|
|
1354
|
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
1355
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
1356
|
+
>(({ className, ...props }, ref) => (
|
|
1357
|
+
<AvatarPrimitive.Root
|
|
1358
|
+
ref={ref}
|
|
1359
|
+
className={cn(
|
|
1360
|
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
1361
|
+
className,
|
|
1362
|
+
)}
|
|
1363
|
+
{...props}
|
|
1364
|
+
/>
|
|
1365
|
+
));
|
|
1366
|
+
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
1367
|
+
|
|
1368
|
+
const AvatarImage = React.forwardRef<
|
|
1369
|
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
1370
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
1371
|
+
>(({ className, ...props }, ref) => (
|
|
1372
|
+
<AvatarPrimitive.Image
|
|
1373
|
+
ref={ref}
|
|
1374
|
+
className={cn("aspect-square h-full w-full", className)}
|
|
1375
|
+
{...props}
|
|
1376
|
+
/>
|
|
1377
|
+
));
|
|
1378
|
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
1379
|
+
|
|
1380
|
+
const AvatarFallback = React.forwardRef<
|
|
1381
|
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
1382
|
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
1383
|
+
>(({ className, ...props }, ref) => (
|
|
1384
|
+
<AvatarPrimitive.Fallback
|
|
1385
|
+
ref={ref}
|
|
1386
|
+
className={cn(
|
|
1387
|
+
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
|
1388
|
+
className,
|
|
1389
|
+
)}
|
|
1390
|
+
{...props}
|
|
1391
|
+
/>
|
|
1392
|
+
));
|
|
1393
|
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
1394
|
+
|
|
1395
|
+
export { Avatar, AvatarImage, AvatarFallback };
|
|
1396
|
+
|
|
1397
|
+
```
|
|
1398
|
+
|
|
915
1399
|
## components/ui/button.tsx
|
|
916
1400
|
|
|
917
1401
|
```tsx
|
|
@@ -922,16 +1406,16 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
922
1406
|
import { cn } from "@/lib/utils";
|
|
923
1407
|
|
|
924
1408
|
const buttonVariants = cva(
|
|
925
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm
|
|
1409
|
+
"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",
|
|
926
1410
|
{
|
|
927
1411
|
variants: {
|
|
928
1412
|
variant: {
|
|
929
1413
|
default:
|
|
930
1414
|
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
931
1415
|
destructive:
|
|
932
|
-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40
|
|
1416
|
+
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
933
1417
|
outline:
|
|
934
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:
|
|
1418
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
935
1419
|
secondary:
|
|
936
1420
|
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
937
1421
|
ghost:
|
|
@@ -940,7 +1424,7 @@ const buttonVariants = cva(
|
|
|
940
1424
|
},
|
|
941
1425
|
size: {
|
|
942
1426
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
943
|
-
sm: "h-8
|
|
1427
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
944
1428
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
945
1429
|
icon: "size-9",
|
|
946
1430
|
},
|
|
@@ -977,6 +1461,166 @@ export { Button, buttonVariants };
|
|
|
977
1461
|
|
|
978
1462
|
```
|
|
979
1463
|
|
|
1464
|
+
## components/ui/dialog.tsx
|
|
1465
|
+
|
|
1466
|
+
```tsx
|
|
1467
|
+
"use client";
|
|
1468
|
+
|
|
1469
|
+
import * as React from "react";
|
|
1470
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1471
|
+
import { XIcon } from "lucide-react";
|
|
1472
|
+
|
|
1473
|
+
import { cn } from "@/lib/utils";
|
|
1474
|
+
|
|
1475
|
+
function Dialog({
|
|
1476
|
+
...props
|
|
1477
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
1478
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function DialogTrigger({
|
|
1482
|
+
...props
|
|
1483
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
1484
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
function DialogPortal({
|
|
1488
|
+
...props
|
|
1489
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
1490
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function DialogClose({
|
|
1494
|
+
...props
|
|
1495
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
1496
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function DialogOverlay({
|
|
1500
|
+
className,
|
|
1501
|
+
...props
|
|
1502
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
1503
|
+
return (
|
|
1504
|
+
<DialogPrimitive.Overlay
|
|
1505
|
+
data-slot="dialog-overlay"
|
|
1506
|
+
className={cn(
|
|
1507
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1508
|
+
className,
|
|
1509
|
+
)}
|
|
1510
|
+
{...props}
|
|
1511
|
+
/>
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
function DialogContent({
|
|
1516
|
+
className,
|
|
1517
|
+
children,
|
|
1518
|
+
...props
|
|
1519
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
|
1520
|
+
return (
|
|
1521
|
+
<DialogPortal data-slot="dialog-portal">
|
|
1522
|
+
<DialogOverlay />
|
|
1523
|
+
<DialogPrimitive.Content
|
|
1524
|
+
data-slot="dialog-content"
|
|
1525
|
+
className={cn(
|
|
1526
|
+
"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
|
|
1527
|
+
className,
|
|
1528
|
+
)}
|
|
1529
|
+
{...props}
|
|
1530
|
+
>
|
|
1531
|
+
{children}
|
|
1532
|
+
<DialogPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0">
|
|
1533
|
+
<XIcon />
|
|
1534
|
+
<span className="sr-only">Close</span>
|
|
1535
|
+
</DialogPrimitive.Close>
|
|
1536
|
+
</DialogPrimitive.Content>
|
|
1537
|
+
</DialogPortal>
|
|
1538
|
+
);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
1542
|
+
return (
|
|
1543
|
+
<div
|
|
1544
|
+
data-slot="dialog-header"
|
|
1545
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
1546
|
+
{...props}
|
|
1547
|
+
/>
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
1552
|
+
return (
|
|
1553
|
+
<div
|
|
1554
|
+
data-slot="dialog-footer"
|
|
1555
|
+
className={cn(
|
|
1556
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1557
|
+
className,
|
|
1558
|
+
)}
|
|
1559
|
+
{...props}
|
|
1560
|
+
/>
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
function DialogTitle({
|
|
1565
|
+
className,
|
|
1566
|
+
...props
|
|
1567
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
1568
|
+
return (
|
|
1569
|
+
<DialogPrimitive.Title
|
|
1570
|
+
data-slot="dialog-title"
|
|
1571
|
+
className={cn("font-semibold text-lg leading-none", className)}
|
|
1572
|
+
{...props}
|
|
1573
|
+
/>
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
function DialogDescription({
|
|
1578
|
+
className,
|
|
1579
|
+
...props
|
|
1580
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
1581
|
+
return (
|
|
1582
|
+
<DialogPrimitive.Description
|
|
1583
|
+
data-slot="dialog-description"
|
|
1584
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
1585
|
+
{...props}
|
|
1586
|
+
/>
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
export {
|
|
1591
|
+
Dialog,
|
|
1592
|
+
DialogClose,
|
|
1593
|
+
DialogContent,
|
|
1594
|
+
DialogDescription,
|
|
1595
|
+
DialogFooter,
|
|
1596
|
+
DialogHeader,
|
|
1597
|
+
DialogOverlay,
|
|
1598
|
+
DialogPortal,
|
|
1599
|
+
DialogTitle,
|
|
1600
|
+
DialogTrigger,
|
|
1601
|
+
};
|
|
1602
|
+
|
|
1603
|
+
```
|
|
1604
|
+
|
|
1605
|
+
## components/ui/skeleton.tsx
|
|
1606
|
+
|
|
1607
|
+
```tsx
|
|
1608
|
+
import { cn } from "@/lib/utils";
|
|
1609
|
+
|
|
1610
|
+
function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
|
|
1611
|
+
return (
|
|
1612
|
+
<div
|
|
1613
|
+
data-slot="skeleton"
|
|
1614
|
+
className={cn("animate-pulse rounded-md bg-accent", className)}
|
|
1615
|
+
{...props}
|
|
1616
|
+
/>
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
export { Skeleton };
|
|
1621
|
+
|
|
1622
|
+
```
|
|
1623
|
+
|
|
980
1624
|
## components/ui/tooltip.tsx
|
|
981
1625
|
|
|
982
1626
|
```tsx
|
|
@@ -1028,13 +1672,13 @@ function TooltipContent({
|
|
|
1028
1672
|
data-slot="tooltip-content"
|
|
1029
1673
|
sideOffset={sideOffset}
|
|
1030
1674
|
className={cn(
|
|
1031
|
-
"
|
|
1675
|
+
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-[--radix-tooltip-content-transform-origin] animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
|
|
1032
1676
|
className,
|
|
1033
1677
|
)}
|
|
1034
1678
|
{...props}
|
|
1035
1679
|
>
|
|
1036
1680
|
{children}
|
|
1037
|
-
<TooltipPrimitive.Arrow className="
|
|
1681
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-primary fill-primary" />
|
|
1038
1682
|
</TooltipPrimitive.Content>
|
|
1039
1683
|
</TooltipPrimitive.Portal>
|
|
1040
1684
|
);
|
|
@@ -1079,28 +1723,30 @@ export default nextConfig;
|
|
|
1079
1723
|
"scripts": {
|
|
1080
1724
|
"dev": "next dev --turbo",
|
|
1081
1725
|
"build": "next build",
|
|
1082
|
-
"start": "next start"
|
|
1083
|
-
"lint": "eslint ."
|
|
1726
|
+
"start": "next start"
|
|
1084
1727
|
},
|
|
1085
1728
|
"dependencies": {
|
|
1086
|
-
"@ai-sdk/openai": "^2.0.
|
|
1729
|
+
"@ai-sdk/openai": "^2.0.77",
|
|
1087
1730
|
"@assistant-ui/react": "workspace:*",
|
|
1088
1731
|
"@assistant-ui/react-ai-sdk": "workspace:*",
|
|
1089
1732
|
"@assistant-ui/react-markdown": "workspace:*",
|
|
1733
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
1734
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
1090
1735
|
"@radix-ui/react-slot": "^1.2.4",
|
|
1091
1736
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
1092
|
-
"ai": "^5.0.
|
|
1737
|
+
"ai": "^5.0.107",
|
|
1093
1738
|
"class-variance-authority": "^0.7.1",
|
|
1094
1739
|
"clsx": "^2.1.1",
|
|
1095
|
-
"jsonwebtoken": "^9.0.
|
|
1096
|
-
"lucide-react": "^0.
|
|
1740
|
+
"jsonwebtoken": "^9.0.3",
|
|
1741
|
+
"lucide-react": "^0.556.0",
|
|
1097
1742
|
"nanoid": "5.1.6",
|
|
1098
|
-
"next": "16.0.
|
|
1099
|
-
"react": "19.2.
|
|
1100
|
-
"react-dom": "19.2.
|
|
1743
|
+
"next": "16.0.7",
|
|
1744
|
+
"react": "19.2.1",
|
|
1745
|
+
"react-dom": "19.2.1",
|
|
1101
1746
|
"remark-gfm": "^4.0.1",
|
|
1102
1747
|
"tailwind-merge": "^3.4.0",
|
|
1103
|
-
"tw-animate-css": "^1.4.0"
|
|
1748
|
+
"tw-animate-css": "^1.4.0",
|
|
1749
|
+
"zustand": "^5.0.9"
|
|
1104
1750
|
},
|
|
1105
1751
|
"devDependencies": {
|
|
1106
1752
|
"@assistant-ui/x-buildutils": "workspace:*",
|