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