@assistant-ui/mcp-docs-server 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +69 -1336
  2. package/.docs/organized/code-examples/with-ai-sdk-v6.md +429 -0
  3. package/.docs/organized/code-examples/with-assistant-transport.md +41 -1454
  4. package/.docs/organized/code-examples/with-cloud.md +73 -1442
  5. package/.docs/organized/code-examples/with-custom-thread-list.md +95 -1424
  6. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +674 -0
  7. package/.docs/organized/code-examples/with-external-store.md +68 -1334
  8. package/.docs/organized/code-examples/with-ffmpeg.md +73 -1341
  9. package/.docs/organized/code-examples/with-langgraph.md +52 -1545
  10. package/.docs/organized/code-examples/with-parent-id-grouping.md +58 -1364
  11. package/.docs/organized/code-examples/with-react-hook-form.md +73 -1726
  12. package/.docs/organized/code-examples/with-react-router.md +915 -0
  13. package/.docs/organized/code-examples/with-store.md +31 -31
  14. package/.docs/organized/code-examples/with-tanstack.md +78 -862
  15. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +0 -2
  16. package/.docs/raw/docs/{architecture.mdx → (docs)/architecture.mdx} +3 -2
  17. package/.docs/raw/docs/{cli.mdx → (docs)/cli.mdx} +66 -18
  18. package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-readable.mdx +1 -0
  19. package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-tool-ui.mdx +2 -1
  20. package/.docs/raw/docs/{copilots → (docs)/copilots}/make-assistant-tool.mdx +2 -1
  21. package/.docs/raw/docs/{copilots → (docs)/copilots}/model-context.mdx +5 -4
  22. package/.docs/raw/docs/{copilots → (docs)/copilots}/motivation.mdx +4 -3
  23. package/.docs/raw/docs/{copilots → (docs)/copilots}/use-assistant-instructions.mdx +1 -0
  24. package/.docs/raw/docs/{devtools.mdx → (docs)/devtools.mdx} +4 -4
  25. package/.docs/raw/docs/{guides/Attachments.mdx → (docs)/guides/attachments.mdx} +6 -7
  26. package/.docs/raw/docs/{guides/Branching.mdx → (docs)/guides/branching.mdx} +2 -1
  27. package/.docs/raw/docs/{guides → (docs)/guides}/context-api.mdx +118 -117
  28. package/.docs/raw/docs/(docs)/guides/dictation.mdx +370 -0
  29. package/.docs/raw/docs/{guides/Editing.mdx → (docs)/guides/editing.mdx} +1 -0
  30. package/.docs/raw/docs/{guides/Latex.mdx → (docs)/guides/latex.mdx} +1 -2
  31. package/.docs/raw/docs/{guides/Speech.mdx → (docs)/guides/speech.mdx} +9 -10
  32. package/.docs/raw/docs/(docs)/guides/suggestions.mdx +296 -0
  33. package/.docs/raw/docs/{guides/ToolUI.mdx → (docs)/guides/tool-ui.mdx} +15 -14
  34. package/.docs/raw/docs/(docs)/guides/tools.mdx +564 -0
  35. package/.docs/raw/docs/(docs)/index.mdx +74 -0
  36. package/.docs/raw/docs/{getting-started.mdx → (docs)/installation.mdx} +18 -23
  37. package/.docs/raw/docs/(docs)/llm.mdx +209 -0
  38. package/.docs/raw/docs/{api-reference/context-providers/AssistantRuntimeProvider.mdx → (reference)/api-reference/context-providers/assistant-runtime-provider.mdx} +2 -1
  39. package/.docs/raw/docs/{api-reference/context-providers/TextMessagePartProvider.mdx → (reference)/api-reference/context-providers/text-message-part-provider.mdx} +2 -1
  40. package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/react-data-stream.mdx +50 -3
  41. package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/react-hook-form.mdx +2 -1
  42. package/.docs/raw/docs/{api-reference → (reference)/api-reference}/integrations/vercel-ai-sdk.mdx +2 -2
  43. package/.docs/raw/docs/{api-reference → (reference)/api-reference}/overview.mdx +10 -4
  44. package/.docs/raw/docs/(reference)/api-reference/primitives/action-bar-more.mdx +327 -0
  45. package/.docs/raw/docs/{api-reference/primitives/ActionBar.mdx → (reference)/api-reference/primitives/action-bar.mdx} +7 -5
  46. package/.docs/raw/docs/{api-reference/primitives/AssistantIf.mdx → (reference)/api-reference/primitives/assistant-if.mdx} +51 -51
  47. package/.docs/raw/docs/{api-reference/primitives/AssistantModal.mdx → (reference)/api-reference/primitives/assistant-modal.mdx} +3 -1
  48. package/.docs/raw/docs/{api-reference/primitives/Attachment.mdx → (reference)/api-reference/primitives/attachment.mdx} +3 -2
  49. package/.docs/raw/docs/{api-reference/primitives/BranchPicker.mdx → (reference)/api-reference/primitives/branch-picker.mdx} +2 -1
  50. package/.docs/raw/docs/{api-reference/primitives/Composer.mdx → (reference)/api-reference/primitives/composer.mdx} +101 -2
  51. package/.docs/raw/docs/{api-reference → (reference)/api-reference}/primitives/composition.mdx +1 -0
  52. package/.docs/raw/docs/{api-reference/primitives/Error.mdx → (reference)/api-reference/primitives/error.mdx} +2 -1
  53. package/.docs/raw/docs/{api-reference/primitives/MessagePart.mdx → (reference)/api-reference/primitives/message-part.mdx} +2 -2
  54. package/.docs/raw/docs/{api-reference/primitives/Message.mdx → (reference)/api-reference/primitives/message.mdx} +2 -1
  55. package/.docs/raw/docs/(reference)/api-reference/primitives/suggestion.mdx +153 -0
  56. package/.docs/raw/docs/(reference)/api-reference/primitives/thread-list-item-more.mdx +221 -0
  57. package/.docs/raw/docs/{api-reference/primitives/ThreadListItem.mdx → (reference)/api-reference/primitives/thread-list-item.mdx} +2 -1
  58. package/.docs/raw/docs/{api-reference/primitives/ThreadList.mdx → (reference)/api-reference/primitives/thread-list.mdx} +2 -1
  59. package/.docs/raw/docs/{api-reference/primitives/Thread.mdx → (reference)/api-reference/primitives/thread.mdx} +30 -40
  60. package/.docs/raw/docs/{api-reference/runtimes/AssistantRuntime.mdx → (reference)/api-reference/runtimes/assistant-runtime.mdx} +2 -1
  61. package/.docs/raw/docs/{api-reference/runtimes/AttachmentRuntime.mdx → (reference)/api-reference/runtimes/attachment-runtime.mdx} +3 -2
  62. package/.docs/raw/docs/{api-reference/runtimes/ComposerRuntime.mdx → (reference)/api-reference/runtimes/composer-runtime.mdx} +2 -1
  63. package/.docs/raw/docs/{api-reference/runtimes/MessagePartRuntime.mdx → (reference)/api-reference/runtimes/message-part-runtime.mdx} +3 -2
  64. package/.docs/raw/docs/{api-reference/runtimes/MessageRuntime.mdx → (reference)/api-reference/runtimes/message-runtime.mdx} +3 -2
  65. package/.docs/raw/docs/{api-reference/runtimes/ThreadListItemRuntime.mdx → (reference)/api-reference/runtimes/thread-list-item-runtime.mdx} +2 -1
  66. package/.docs/raw/docs/{api-reference/runtimes/ThreadListRuntime.mdx → (reference)/api-reference/runtimes/thread-list-runtime.mdx} +2 -1
  67. package/.docs/raw/docs/{api-reference/runtimes/ThreadRuntime.mdx → (reference)/api-reference/runtimes/thread-runtime.mdx} +3 -5
  68. package/.docs/raw/docs/{legacy/styled/AssistantModal.mdx → (reference)/legacy/styled/assistant-modal.mdx} +2 -3
  69. package/.docs/raw/docs/{legacy/styled/Decomposition.mdx → (reference)/legacy/styled/decomposition.mdx} +6 -5
  70. package/.docs/raw/docs/{legacy/styled/Markdown.mdx → (reference)/legacy/styled/markdown.mdx} +2 -4
  71. package/.docs/raw/docs/{legacy/styled/Scrollbar.mdx → (reference)/legacy/styled/scrollbar.mdx} +2 -1
  72. package/.docs/raw/docs/{legacy/styled/ThreadWidth.mdx → (reference)/legacy/styled/thread-width.mdx} +1 -0
  73. package/.docs/raw/docs/{legacy/styled/Thread.mdx → (reference)/legacy/styled/thread.mdx} +2 -3
  74. package/.docs/raw/docs/{migrations → (reference)/migrations}/deprecation-policy.mdx +1 -0
  75. package/.docs/raw/docs/{migrations → (reference)/migrations}/react-langgraph-v0-7.mdx +1 -2
  76. package/.docs/raw/docs/{migrations → (reference)/migrations}/v0-11.mdx +1 -0
  77. package/.docs/raw/docs/(reference)/migrations/v0-12.mdx +300 -0
  78. package/.docs/raw/docs/{react-compatibility.mdx → (reference)/react-compatibility.mdx} +2 -3
  79. package/.docs/raw/docs/cloud/authorization.mdx +1 -0
  80. package/.docs/raw/docs/cloud/overview.mdx +1 -0
  81. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
  82. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +5 -7
  83. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +9 -8
  84. package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +2 -3
  85. package/.docs/raw/docs/runtimes/assistant-transport.mdx +10 -9
  86. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +7 -8
  87. package/.docs/raw/docs/runtimes/custom/external-store.mdx +6 -8
  88. package/.docs/raw/docs/runtimes/custom/local.mdx +55 -42
  89. package/.docs/raw/docs/runtimes/data-stream.mdx +67 -6
  90. package/.docs/raw/docs/runtimes/helicone.mdx +1 -0
  91. package/.docs/raw/docs/runtimes/langgraph/index.mdx +3 -3
  92. package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +1 -0
  93. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +1 -0
  94. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +1 -0
  95. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -0
  96. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -1
  97. package/.docs/raw/docs/runtimes/langserve.mdx +2 -2
  98. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +4 -5
  99. package/.docs/raw/docs/runtimes/mastra/overview.mdx +1 -0
  100. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +3 -4
  101. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +2 -4
  102. package/.docs/raw/docs/ui/accordion.mdx +261 -0
  103. package/.docs/raw/docs/ui/assistant-modal.mdx +163 -0
  104. package/.docs/raw/docs/ui/assistant-sidebar.mdx +90 -0
  105. package/.docs/raw/docs/ui/attachment.mdx +227 -0
  106. package/.docs/raw/docs/ui/badge.mdx +140 -0
  107. package/.docs/raw/docs/ui/file.mdx +153 -0
  108. package/.docs/raw/docs/ui/image.mdx +101 -0
  109. package/.docs/raw/docs/ui/{Markdown.mdx → markdown.mdx} +11 -6
  110. package/.docs/raw/docs/ui/{Mermaid.mdx → mermaid.mdx} +12 -5
  111. package/.docs/raw/docs/ui/model-selector.mdx +226 -0
  112. package/.docs/raw/docs/ui/{PartGrouping.mdx → part-grouping.mdx} +6 -8
  113. package/.docs/raw/docs/ui/reasoning.mdx +150 -0
  114. package/.docs/raw/docs/ui/{Scrollbar.mdx → scrollbar.mdx} +9 -1
  115. package/.docs/raw/docs/ui/select.mdx +247 -0
  116. package/.docs/raw/docs/ui/sources.mdx +89 -0
  117. package/.docs/raw/docs/ui/streamdown.mdx +348 -0
  118. package/.docs/raw/docs/ui/{SyntaxHighlighting.mdx → syntax-highlighting.mdx} +9 -5
  119. package/.docs/raw/docs/ui/tabs.mdx +261 -0
  120. package/.docs/raw/docs/ui/thread-list.mdx +275 -0
  121. package/.docs/raw/docs/ui/{Thread.mdx → thread.mdx} +61 -76
  122. package/.docs/raw/docs/ui/tool-fallback.mdx +112 -0
  123. package/.docs/raw/docs/ui/tool-group.mdx +214 -0
  124. package/README.md +3 -3
  125. package/dist/tools/docs.js +1 -1
  126. package/dist/tools/examples.js +1 -1
  127. package/dist/tools/examples.js.map +1 -1
  128. package/package.json +5 -5
  129. package/src/tools/docs.ts +1 -1
  130. package/src/tools/examples.ts +1 -1
  131. package/src/tools/tests/docs.test.ts +18 -16
  132. package/src/tools/tests/examples.test.ts +6 -6
  133. package/src/tools/tests/path-traversal.test.ts +3 -3
  134. package/src/utils/tests/security.test.ts +3 -3
  135. package/.docs/organized/code-examples/with-ai-sdk-v5.md +0 -1735
  136. package/.docs/raw/docs/about-assistantui.mdx +0 -53
  137. package/.docs/raw/docs/guides/Tools.mdx +0 -738
  138. package/.docs/raw/docs/index.mdx +0 -7
  139. package/.docs/raw/docs/mcp-docs-server.mdx +0 -322
  140. package/.docs/raw/docs/migrations/v0-12.mdx +0 -125
  141. package/.docs/raw/docs/ui/AssistantModal.mdx +0 -45
  142. package/.docs/raw/docs/ui/AssistantSidebar.mdx +0 -41
  143. package/.docs/raw/docs/ui/Attachment.mdx +0 -84
  144. package/.docs/raw/docs/ui/Reasoning.mdx +0 -152
  145. package/.docs/raw/docs/ui/ThreadList.mdx +0 -90
  146. package/.docs/raw/docs/ui/ToolFallback.mdx +0 -63
  147. package/.docs/raw/docs/ui/ToolGroup.mdx +0 -96
  148. /package/.docs/raw/docs/{copilots → (docs)/copilots}/assistant-frame.mdx +0 -0
@@ -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 {
@@ -254,1332 +256,12 @@ export default function Home() {
254
256
  "lib": "@/lib",
255
257
  "hooks": "@/hooks"
256
258
  },
257
- "iconLibrary": "lucide"
258
- }
259
-
260
- ```
261
-
262
- ## components/assistant-ui/attachment.tsx
263
-
264
- ```tsx
265
- "use client";
266
-
267
- import { PropsWithChildren, useEffect, useState, type FC } from "react";
268
- import Image from "next/image";
269
- import { XIcon, PlusIcon, FileText } from "lucide-react";
270
- import {
271
- AttachmentPrimitive,
272
- ComposerPrimitive,
273
- MessagePrimitive,
274
- useAssistantState,
275
- useAssistantApi,
276
- } from "@assistant-ui/react";
277
- import { useShallow } from "zustand/shallow";
278
- import {
279
- Tooltip,
280
- TooltipContent,
281
- TooltipTrigger,
282
- } from "@/components/ui/tooltip";
283
- import {
284
- Dialog,
285
- DialogTitle,
286
- DialogContent,
287
- DialogTrigger,
288
- } from "@/components/ui/dialog";
289
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
290
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
291
- import { cn } from "@/lib/utils";
292
-
293
- const useFileSrc = (file: File | undefined) => {
294
- const [src, setSrc] = useState<string | undefined>(undefined);
295
-
296
- useEffect(() => {
297
- if (!file) {
298
- setSrc(undefined);
299
- return;
300
- }
301
-
302
- const objectUrl = URL.createObjectURL(file);
303
- setSrc(objectUrl);
304
-
305
- return () => {
306
- URL.revokeObjectURL(objectUrl);
307
- };
308
- }, [file]);
309
-
310
- return src;
311
- };
312
-
313
- const useAttachmentSrc = () => {
314
- const { file, src } = useAssistantState(
315
- useShallow(({ attachment }): { file?: File; src?: string } => {
316
- if (attachment.type !== "image") return {};
317
- if (attachment.file) return { file: attachment.file };
318
- const src = attachment.content?.filter((c) => c.type === "image")[0]
319
- ?.image;
320
- if (!src) return {};
321
- return { src };
322
- }),
323
- );
324
-
325
- return useFileSrc(file) ?? src;
326
- };
327
-
328
- type AttachmentPreviewProps = {
329
- src: string;
330
- };
331
-
332
- const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
333
- const [isLoaded, setIsLoaded] = useState(false);
334
- return (
335
- <Image
336
- src={src}
337
- alt="Image Preview"
338
- width={1}
339
- height={1}
340
- className={
341
- isLoaded
342
- ? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
343
- : "aui-attachment-preview-image-loading hidden"
344
- }
345
- onLoadingComplete={() => setIsLoaded(true)}
346
- priority={false}
347
- />
348
- );
349
- };
350
-
351
- const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
352
- const src = useAttachmentSrc();
353
-
354
- if (!src) return children;
355
-
356
- return (
357
- <Dialog>
358
- <DialogTrigger
359
- className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
360
- asChild
361
- >
362
- {children}
363
- </DialogTrigger>
364
- <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">
365
- <DialogTitle className="aui-sr-only sr-only">
366
- Image Attachment Preview
367
- </DialogTitle>
368
- <div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
369
- <AttachmentPreview src={src} />
370
- </div>
371
- </DialogContent>
372
- </Dialog>
373
- );
374
- };
375
-
376
- const AttachmentThumb: FC = () => {
377
- const isImage = useAssistantState(
378
- ({ attachment }) => attachment.type === "image",
379
- );
380
- const src = useAttachmentSrc();
381
-
382
- return (
383
- <Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
384
- <AvatarImage
385
- src={src}
386
- alt="Attachment preview"
387
- className="aui-attachment-tile-image object-cover"
388
- />
389
- <AvatarFallback delayMs={isImage ? 200 : 0}>
390
- <FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
391
- </AvatarFallback>
392
- </Avatar>
393
- );
394
- };
395
-
396
- const AttachmentUI: FC = () => {
397
- const api = useAssistantApi();
398
- const isComposer = api.attachment.source === "composer";
399
-
400
- const isImage = useAssistantState(
401
- ({ attachment }) => attachment.type === "image",
402
- );
403
- const typeLabel = useAssistantState(({ attachment }) => {
404
- const type = attachment.type;
405
- switch (type) {
406
- case "image":
407
- return "Image";
408
- case "document":
409
- return "Document";
410
- case "file":
411
- return "File";
412
- default:
413
- const _exhaustiveCheck: never = type;
414
- throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
415
- }
416
- });
417
-
418
- return (
419
- <Tooltip>
420
- <AttachmentPrimitive.Root
421
- className={cn(
422
- "aui-attachment-root relative",
423
- isImage &&
424
- "aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
425
- )}
426
- >
427
- <AttachmentPreviewDialog>
428
- <TooltipTrigger asChild>
429
- <div
430
- className={cn(
431
- "aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
432
- isComposer &&
433
- "aui-attachment-tile-composer border-foreground/20",
434
- )}
435
- role="button"
436
- id="attachment-tile"
437
- aria-label={`${typeLabel} attachment`}
438
- >
439
- <AttachmentThumb />
440
- </div>
441
- </TooltipTrigger>
442
- </AttachmentPreviewDialog>
443
- {isComposer && <AttachmentRemove />}
444
- </AttachmentPrimitive.Root>
445
- <TooltipContent side="top">
446
- <AttachmentPrimitive.Name />
447
- </TooltipContent>
448
- </Tooltip>
449
- );
450
- };
451
-
452
- const AttachmentRemove: FC = () => {
453
- return (
454
- <AttachmentPrimitive.Remove asChild>
455
- <TooltipIconButton
456
- tooltip="Remove file"
457
- 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"
458
- side="top"
459
- >
460
- <XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
461
- </TooltipIconButton>
462
- </AttachmentPrimitive.Remove>
463
- );
464
- };
465
-
466
- export const UserMessageAttachments: FC = () => {
467
- return (
468
- <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">
469
- <MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
470
- </div>
471
- );
472
- };
473
-
474
- export const ComposerAttachments: FC = () => {
475
- return (
476
- <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">
477
- <ComposerPrimitive.Attachments
478
- components={{ Attachment: AttachmentUI }}
479
- />
480
- </div>
481
- );
482
- };
483
-
484
- export const ComposerAddAttachment: FC = () => {
485
- return (
486
- <ComposerPrimitive.AddAttachment asChild>
487
- <TooltipIconButton
488
- tooltip="Add Attachment"
489
- side="bottom"
490
- variant="ghost"
491
- size="icon"
492
- 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"
493
- aria-label="Add Attachment"
494
- >
495
- <PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
496
- </TooltipIconButton>
497
- </ComposerPrimitive.AddAttachment>
498
- );
499
- };
500
-
501
- ```
502
-
503
- ## components/assistant-ui/markdown-text.tsx
504
-
505
- ```tsx
506
- "use client";
507
-
508
- import "@assistant-ui/react-markdown/styles/dot.css";
509
-
510
- import {
511
- type CodeHeaderProps,
512
- MarkdownTextPrimitive,
513
- unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
514
- useIsMarkdownCodeBlock,
515
- } from "@assistant-ui/react-markdown";
516
- import remarkGfm from "remark-gfm";
517
- import { type FC, memo, useState } from "react";
518
- import { CheckIcon, CopyIcon } from "lucide-react";
519
-
520
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
521
- import { cn } from "@/lib/utils";
522
-
523
- const MarkdownTextImpl = () => {
524
- return (
525
- <MarkdownTextPrimitive
526
- remarkPlugins={[remarkGfm]}
527
- className="aui-md"
528
- components={defaultComponents}
529
- />
530
- );
531
- };
532
-
533
- export const MarkdownText = memo(MarkdownTextImpl);
534
-
535
- const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
536
- const { isCopied, copyToClipboard } = useCopyToClipboard();
537
- const onCopy = () => {
538
- if (!code || isCopied) return;
539
- copyToClipboard(code);
540
- };
541
-
542
- return (
543
- <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">
544
- <span className="aui-code-header-language lowercase [&>span]:text-xs">
545
- {language}
546
- </span>
547
- <TooltipIconButton tooltip="Copy" onClick={onCopy}>
548
- {!isCopied && <CopyIcon />}
549
- {isCopied && <CheckIcon />}
550
- </TooltipIconButton>
551
- </div>
552
- );
553
- };
554
-
555
- const useCopyToClipboard = ({
556
- copiedDuration = 3000,
557
- }: {
558
- copiedDuration?: number;
559
- } = {}) => {
560
- const [isCopied, setIsCopied] = useState<boolean>(false);
561
-
562
- const copyToClipboard = (value: string) => {
563
- if (!value) return;
564
-
565
- navigator.clipboard.writeText(value).then(() => {
566
- setIsCopied(true);
567
- setTimeout(() => setIsCopied(false), copiedDuration);
568
- });
569
- };
570
-
571
- return { isCopied, copyToClipboard };
572
- };
573
-
574
- const defaultComponents = memoizeMarkdownComponents({
575
- h1: ({ className, ...props }) => (
576
- <h1
577
- className={cn(
578
- "aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
579
- className,
580
- )}
581
- {...props}
582
- />
583
- ),
584
- h2: ({ className, ...props }) => (
585
- <h2
586
- className={cn(
587
- "aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
588
- className,
589
- )}
590
- {...props}
591
- />
592
- ),
593
- h3: ({ className, ...props }) => (
594
- <h3
595
- className={cn(
596
- "aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
597
- className,
598
- )}
599
- {...props}
600
- />
601
- ),
602
- h4: ({ className, ...props }) => (
603
- <h4
604
- className={cn(
605
- "aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
606
- className,
607
- )}
608
- {...props}
609
- />
610
- ),
611
- h5: ({ className, ...props }) => (
612
- <h5
613
- className={cn(
614
- "aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
615
- className,
616
- )}
617
- {...props}
618
- />
619
- ),
620
- h6: ({ className, ...props }) => (
621
- <h6
622
- className={cn(
623
- "aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
624
- className,
625
- )}
626
- {...props}
627
- />
628
- ),
629
- p: ({ className, ...props }) => (
630
- <p
631
- className={cn(
632
- "aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
633
- className,
634
- )}
635
- {...props}
636
- />
637
- ),
638
- a: ({ className, ...props }) => (
639
- <a
640
- className={cn(
641
- "aui-md-a font-medium text-primary underline underline-offset-4",
642
- className,
643
- )}
644
- {...props}
645
- />
646
- ),
647
- blockquote: ({ className, ...props }) => (
648
- <blockquote
649
- className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
650
- {...props}
651
- />
652
- ),
653
- ul: ({ className, ...props }) => (
654
- <ul
655
- className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
656
- {...props}
657
- />
658
- ),
659
- ol: ({ className, ...props }) => (
660
- <ol
661
- className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
662
- {...props}
663
- />
664
- ),
665
- hr: ({ className, ...props }) => (
666
- <hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
667
- ),
668
- table: ({ className, ...props }) => (
669
- <table
670
- className={cn(
671
- "aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
672
- className,
673
- )}
674
- {...props}
675
- />
676
- ),
677
- th: ({ className, ...props }) => (
678
- <th
679
- className={cn(
680
- "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",
681
- className,
682
- )}
683
- {...props}
684
- />
685
- ),
686
- td: ({ className, ...props }) => (
687
- <td
688
- className={cn(
689
- "aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
690
- className,
691
- )}
692
- {...props}
693
- />
694
- ),
695
- tr: ({ className, ...props }) => (
696
- <tr
697
- className={cn(
698
- "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",
699
- className,
700
- )}
701
- {...props}
702
- />
703
- ),
704
- sup: ({ className, ...props }) => (
705
- <sup
706
- className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
707
- {...props}
708
- />
709
- ),
710
- pre: ({ className, ...props }) => (
711
- <pre
712
- className={cn(
713
- "aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
714
- className,
715
- )}
716
- {...props}
717
- />
718
- ),
719
- code: function Code({ className, ...props }) {
720
- const isCodeBlock = useIsMarkdownCodeBlock();
721
- return (
722
- <code
723
- className={cn(
724
- !isCodeBlock &&
725
- "aui-md-inline-code rounded border bg-muted font-semibold",
726
- className,
727
- )}
728
- {...props}
729
- />
730
- );
731
- },
732
- CodeHeader,
733
- });
734
-
735
- ```
736
-
737
- ## components/assistant-ui/thread.tsx
738
-
739
- ```tsx
740
- import {
741
- ComposerAddAttachment,
742
- ComposerAttachments,
743
- UserMessageAttachments,
744
- } from "@/components/assistant-ui/attachment";
745
- import { MarkdownText } from "@/components/assistant-ui/markdown-text";
746
- import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
747
- import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
748
- import { Button } from "@/components/ui/button";
749
- import { cn } from "@/lib/utils";
750
- import {
751
- ActionBarPrimitive,
752
- AssistantIf,
753
- BranchPickerPrimitive,
754
- ComposerPrimitive,
755
- ErrorPrimitive,
756
- MessagePrimitive,
757
- ThreadPrimitive,
758
- } from "@assistant-ui/react";
759
- import {
760
- ArrowDownIcon,
761
- ArrowUpIcon,
762
- CheckIcon,
763
- ChevronLeftIcon,
764
- ChevronRightIcon,
765
- CopyIcon,
766
- DownloadIcon,
767
- PencilIcon,
768
- RefreshCwIcon,
769
- SquareIcon,
770
- } from "lucide-react";
771
- import type { FC } from "react";
772
-
773
- export const Thread: FC = () => {
774
- return (
775
- <ThreadPrimitive.Root
776
- className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
777
- style={{
778
- ["--thread-max-width" as string]: "44rem",
779
- }}
780
- >
781
- <ThreadPrimitive.Viewport
782
- turnAnchor="top"
783
- className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
784
- >
785
- <AssistantIf condition={({ thread }) => thread.isEmpty}>
786
- <ThreadWelcome />
787
- </AssistantIf>
788
-
789
- <ThreadPrimitive.Messages
790
- components={{
791
- UserMessage,
792
- EditComposer,
793
- AssistantMessage,
794
- }}
795
- />
796
-
797
- <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">
798
- <ThreadScrollToBottom />
799
- <Composer />
800
- </ThreadPrimitive.ViewportFooter>
801
- </ThreadPrimitive.Viewport>
802
- </ThreadPrimitive.Root>
803
- );
804
- };
805
-
806
- const ThreadScrollToBottom: FC = () => {
807
- return (
808
- <ThreadPrimitive.ScrollToBottom asChild>
809
- <TooltipIconButton
810
- tooltip="Scroll to bottom"
811
- variant="outline"
812
- 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"
813
- >
814
- <ArrowDownIcon />
815
- </TooltipIconButton>
816
- </ThreadPrimitive.ScrollToBottom>
817
- );
818
- };
819
-
820
- const ThreadWelcome: FC = () => {
821
- return (
822
- <div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
823
- <div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
824
- <div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
825
- <h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
826
- Hello there!
827
- </h1>
828
- <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">
829
- How can I help you today?
830
- </p>
831
- </div>
832
- </div>
833
- <ThreadSuggestions />
834
- </div>
835
- );
836
- };
837
-
838
- const SUGGESTIONS = [
839
- {
840
- title: "What's the weather",
841
- label: "in San Francisco?",
842
- prompt: "What's the weather in San Francisco?",
843
- },
844
- {
845
- title: "Explain React hooks",
846
- label: "like useState and useEffect",
847
- prompt: "Explain React hooks like useState and useEffect",
848
- },
849
- ] as const;
850
-
851
- const ThreadSuggestions: FC = () => {
852
- return (
853
- <div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
854
- {SUGGESTIONS.map((suggestion, index) => (
855
- <div
856
- key={suggestion.prompt}
857
- 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"
858
- style={{ animationDelay: `${100 + index * 50}ms` }}
859
- >
860
- <ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
861
- <Button
862
- variant="ghost"
863
- 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"
864
- aria-label={suggestion.prompt}
865
- >
866
- <span className="aui-thread-welcome-suggestion-text-1 font-medium">
867
- {suggestion.title}
868
- </span>
869
- <span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
870
- {suggestion.label}
871
- </span>
872
- </Button>
873
- </ThreadPrimitive.Suggestion>
874
- </div>
875
- ))}
876
- </div>
877
- );
878
- };
879
-
880
- const Composer: FC = () => {
881
- return (
882
- <ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
883
- <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">
884
- <ComposerAttachments />
885
- <ComposerPrimitive.Input
886
- placeholder="Send a message..."
887
- 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"
888
- rows={1}
889
- autoFocus
890
- aria-label="Message input"
891
- />
892
- <ComposerAction />
893
- </ComposerPrimitive.AttachmentDropzone>
894
- </ComposerPrimitive.Root>
895
- );
896
- };
897
-
898
- const ComposerAction: FC = () => {
899
- return (
900
- <div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
901
- <ComposerAddAttachment />
902
-
903
- <AssistantIf condition={({ thread }) => !thread.isRunning}>
904
- <ComposerPrimitive.Send asChild>
905
- <TooltipIconButton
906
- tooltip="Send message"
907
- side="bottom"
908
- type="submit"
909
- variant="default"
910
- size="icon"
911
- className="aui-composer-send size-8 rounded-full"
912
- aria-label="Send message"
913
- >
914
- <ArrowUpIcon className="aui-composer-send-icon size-4" />
915
- </TooltipIconButton>
916
- </ComposerPrimitive.Send>
917
- </AssistantIf>
918
-
919
- <AssistantIf condition={({ thread }) => thread.isRunning}>
920
- <ComposerPrimitive.Cancel asChild>
921
- <Button
922
- type="button"
923
- variant="default"
924
- size="icon"
925
- className="aui-composer-cancel size-8 rounded-full"
926
- aria-label="Stop generating"
927
- >
928
- <SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
929
- </Button>
930
- </ComposerPrimitive.Cancel>
931
- </AssistantIf>
932
- </div>
933
- );
934
- };
935
-
936
- const MessageError: FC = () => {
937
- return (
938
- <MessagePrimitive.Error>
939
- <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">
940
- <ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
941
- </ErrorPrimitive.Root>
942
- </MessagePrimitive.Error>
943
- );
944
- };
945
-
946
- const AssistantMessage: FC = () => {
947
- return (
948
- <MessagePrimitive.Root
949
- 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"
950
- data-role="assistant"
951
- >
952
- <div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
953
- <MessagePrimitive.Parts
954
- components={{
955
- Text: MarkdownText,
956
- tools: { Fallback: ToolFallback },
957
- }}
958
- />
959
- <MessageError />
960
- </div>
961
-
962
- <div className="aui-assistant-message-footer mt-1 ml-2 flex">
963
- <BranchPicker />
964
- <AssistantActionBar />
965
- </div>
966
- </MessagePrimitive.Root>
967
- );
968
- };
969
-
970
- const AssistantActionBar: FC = () => {
971
- return (
972
- <ActionBarPrimitive.Root
973
- hideWhenRunning
974
- autohide="not-last"
975
- autohideFloat="single-branch"
976
- 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"
977
- >
978
- <ActionBarPrimitive.Copy asChild>
979
- <TooltipIconButton tooltip="Copy">
980
- <AssistantIf condition={({ message }) => message.isCopied}>
981
- <CheckIcon />
982
- </AssistantIf>
983
- <AssistantIf condition={({ message }) => !message.isCopied}>
984
- <CopyIcon />
985
- </AssistantIf>
986
- </TooltipIconButton>
987
- </ActionBarPrimitive.Copy>
988
- <ActionBarPrimitive.ExportMarkdown asChild>
989
- <TooltipIconButton tooltip="Export as Markdown">
990
- <DownloadIcon />
991
- </TooltipIconButton>
992
- </ActionBarPrimitive.ExportMarkdown>
993
- <ActionBarPrimitive.Reload asChild>
994
- <TooltipIconButton tooltip="Refresh">
995
- <RefreshCwIcon />
996
- </TooltipIconButton>
997
- </ActionBarPrimitive.Reload>
998
- </ActionBarPrimitive.Root>
999
- );
1000
- };
1001
-
1002
- const UserMessage: FC = () => {
1003
- return (
1004
- <MessagePrimitive.Root
1005
- 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"
1006
- data-role="user"
1007
- >
1008
- <UserMessageAttachments />
1009
-
1010
- <div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
1011
- <div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
1012
- <MessagePrimitive.Parts />
1013
- </div>
1014
- <div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
1015
- <UserActionBar />
1016
- </div>
1017
- </div>
1018
-
1019
- <BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
1020
- </MessagePrimitive.Root>
1021
- );
1022
- };
1023
-
1024
- const UserActionBar: FC = () => {
1025
- return (
1026
- <ActionBarPrimitive.Root
1027
- hideWhenRunning
1028
- autohide="not-last"
1029
- className="aui-user-action-bar-root flex flex-col items-end"
1030
- >
1031
- <ActionBarPrimitive.Edit asChild>
1032
- <TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
1033
- <PencilIcon />
1034
- </TooltipIconButton>
1035
- </ActionBarPrimitive.Edit>
1036
- </ActionBarPrimitive.Root>
1037
- );
1038
- };
1039
-
1040
- const EditComposer: FC = () => {
1041
- return (
1042
- <MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
1043
- <ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
1044
- <ComposerPrimitive.Input
1045
- className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
1046
- autoFocus
1047
- />
1048
- <div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
1049
- <ComposerPrimitive.Cancel asChild>
1050
- <Button variant="ghost" size="sm">
1051
- Cancel
1052
- </Button>
1053
- </ComposerPrimitive.Cancel>
1054
- <ComposerPrimitive.Send asChild>
1055
- <Button size="sm">Update</Button>
1056
- </ComposerPrimitive.Send>
1057
- </div>
1058
- </ComposerPrimitive.Root>
1059
- </MessagePrimitive.Root>
1060
- );
1061
- };
1062
-
1063
- const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
1064
- className,
1065
- ...rest
1066
- }) => {
1067
- return (
1068
- <BranchPickerPrimitive.Root
1069
- hideWhenSingleBranch
1070
- className={cn(
1071
- "aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
1072
- className,
1073
- )}
1074
- {...rest}
1075
- >
1076
- <BranchPickerPrimitive.Previous asChild>
1077
- <TooltipIconButton tooltip="Previous">
1078
- <ChevronLeftIcon />
1079
- </TooltipIconButton>
1080
- </BranchPickerPrimitive.Previous>
1081
- <span className="aui-branch-picker-state font-medium">
1082
- <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
1083
- </span>
1084
- <BranchPickerPrimitive.Next asChild>
1085
- <TooltipIconButton tooltip="Next">
1086
- <ChevronRightIcon />
1087
- </TooltipIconButton>
1088
- </BranchPickerPrimitive.Next>
1089
- </BranchPickerPrimitive.Root>
1090
- );
1091
- };
1092
-
1093
- ```
1094
-
1095
- ## components/assistant-ui/tool-fallback.tsx
1096
-
1097
- ```tsx
1098
- import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
1099
- import {
1100
- CheckIcon,
1101
- ChevronDownIcon,
1102
- ChevronUpIcon,
1103
- XCircleIcon,
1104
- } from "lucide-react";
1105
- import { useState } from "react";
1106
- import { Button } from "@/components/ui/button";
1107
- import { cn } from "@/lib/utils";
1108
-
1109
- export const ToolFallback: ToolCallMessagePartComponent = ({
1110
- toolName,
1111
- argsText,
1112
- result,
1113
- status,
1114
- }) => {
1115
- const [isCollapsed, setIsCollapsed] = useState(true);
1116
-
1117
- const isCancelled =
1118
- status?.type === "incomplete" && status.reason === "cancelled";
1119
- const cancelledReason =
1120
- isCancelled && status.error
1121
- ? typeof status.error === "string"
1122
- ? status.error
1123
- : JSON.stringify(status.error)
1124
- : null;
1125
-
1126
- return (
1127
- <div
1128
- className={cn(
1129
- "aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
1130
- isCancelled && "border-muted-foreground/30 bg-muted/30",
1131
- )}
1132
- >
1133
- <div className="aui-tool-fallback-header flex items-center gap-2 px-4">
1134
- {isCancelled ? (
1135
- <XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" />
1136
- ) : (
1137
- <CheckIcon className="aui-tool-fallback-icon size-4" />
1138
- )}
1139
- <p
1140
- className={cn(
1141
- "aui-tool-fallback-title grow",
1142
- isCancelled && "text-muted-foreground line-through",
1143
- )}
1144
- >
1145
- {isCancelled ? "Cancelled tool: " : "Used tool: "}
1146
- <b>{toolName}</b>
1147
- </p>
1148
- <Button onClick={() => setIsCollapsed(!isCollapsed)}>
1149
- {isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
1150
- </Button>
1151
- </div>
1152
- {!isCollapsed && (
1153
- <div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
1154
- {cancelledReason && (
1155
- <div className="aui-tool-fallback-cancelled-root px-4">
1156
- <p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground">
1157
- Cancelled reason:
1158
- </p>
1159
- <p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
1160
- {cancelledReason}
1161
- </p>
1162
- </div>
1163
- )}
1164
- <div
1165
- className={cn(
1166
- "aui-tool-fallback-args-root px-4",
1167
- isCancelled && "opacity-60",
1168
- )}
1169
- >
1170
- <pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
1171
- {argsText}
1172
- </pre>
1173
- </div>
1174
- {!isCancelled && result !== undefined && (
1175
- <div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
1176
- <p className="aui-tool-fallback-result-header font-semibold">
1177
- Result:
1178
- </p>
1179
- <pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
1180
- {typeof result === "string"
1181
- ? result
1182
- : JSON.stringify(result, null, 2)}
1183
- </pre>
1184
- </div>
1185
- )}
1186
- </div>
1187
- )}
1188
- </div>
1189
- );
1190
- };
1191
-
1192
- ```
1193
-
1194
- ## components/assistant-ui/tooltip-icon-button.tsx
1195
-
1196
- ```tsx
1197
- "use client";
1198
-
1199
- import { ComponentPropsWithRef, forwardRef } from "react";
1200
- import { Slottable } from "@radix-ui/react-slot";
1201
-
1202
- import {
1203
- Tooltip,
1204
- TooltipContent,
1205
- TooltipTrigger,
1206
- } from "@/components/ui/tooltip";
1207
- import { Button } from "@/components/ui/button";
1208
- import { cn } from "@/lib/utils";
1209
-
1210
- export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
1211
- tooltip: string;
1212
- side?: "top" | "bottom" | "left" | "right";
1213
- };
1214
-
1215
- export const TooltipIconButton = forwardRef<
1216
- HTMLButtonElement,
1217
- TooltipIconButtonProps
1218
- >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
1219
- return (
1220
- <Tooltip>
1221
- <TooltipTrigger asChild>
1222
- <Button
1223
- variant="ghost"
1224
- size="icon"
1225
- {...rest}
1226
- className={cn("aui-button-icon size-6 p-1", className)}
1227
- ref={ref}
1228
- >
1229
- <Slottable>{children}</Slottable>
1230
- <span className="aui-sr-only sr-only">{tooltip}</span>
1231
- </Button>
1232
- </TooltipTrigger>
1233
- <TooltipContent side={side}>{tooltip}</TooltipContent>
1234
- </Tooltip>
1235
- );
1236
- });
1237
-
1238
- TooltipIconButton.displayName = "TooltipIconButton";
1239
-
1240
- ```
1241
-
1242
- ## components/ui/avatar.tsx
1243
-
1244
- ```tsx
1245
- "use client";
1246
-
1247
- import * as React from "react";
1248
- import * as AvatarPrimitive from "@radix-ui/react-avatar";
1249
-
1250
- import { cn } from "@/lib/utils";
1251
-
1252
- function Avatar({
1253
- className,
1254
- ...props
1255
- }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
1256
- return (
1257
- <AvatarPrimitive.Root
1258
- data-slot="avatar"
1259
- className={cn(
1260
- "relative flex size-8 shrink-0 overflow-hidden rounded-full",
1261
- className,
1262
- )}
1263
- {...props}
1264
- />
1265
- );
1266
- }
1267
-
1268
- function AvatarImage({
1269
- className,
1270
- ...props
1271
- }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
1272
- return (
1273
- <AvatarPrimitive.Image
1274
- data-slot="avatar-image"
1275
- className={cn("aspect-square size-full", className)}
1276
- {...props}
1277
- />
1278
- );
1279
- }
1280
-
1281
- function AvatarFallback({
1282
- className,
1283
- ...props
1284
- }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
1285
- return (
1286
- <AvatarPrimitive.Fallback
1287
- data-slot="avatar-fallback"
1288
- className={cn(
1289
- "flex size-full items-center justify-center rounded-full bg-muted",
1290
- className,
1291
- )}
1292
- {...props}
1293
- />
1294
- );
1295
- }
1296
-
1297
- export { Avatar, AvatarImage, AvatarFallback };
1298
-
1299
- ```
1300
-
1301
- ## components/ui/button.tsx
1302
-
1303
- ```tsx
1304
- import * as React from "react";
1305
- import { Slot } from "@radix-ui/react-slot";
1306
- import { cva, type VariantProps } from "class-variance-authority";
1307
-
1308
- import { cn } from "@/lib/utils";
1309
-
1310
- const buttonVariants = cva(
1311
- "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",
1312
- {
1313
- variants: {
1314
- variant: {
1315
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
1316
- destructive:
1317
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
1318
- outline:
1319
- "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
1320
- secondary:
1321
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
1322
- ghost:
1323
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1324
- link: "text-primary underline-offset-4 hover:underline",
1325
- },
1326
- size: {
1327
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
1328
- sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
1329
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1330
- icon: "size-9",
1331
- "icon-sm": "size-8",
1332
- "icon-lg": "size-10",
1333
- },
1334
- },
1335
- defaultVariants: {
1336
- variant: "default",
1337
- size: "default",
1338
- },
1339
- },
1340
- );
1341
-
1342
- function Button({
1343
- className,
1344
- variant = "default",
1345
- size = "default",
1346
- asChild = false,
1347
- ...props
1348
- }: React.ComponentProps<"button"> &
1349
- VariantProps<typeof buttonVariants> & {
1350
- asChild?: boolean;
1351
- }) {
1352
- const Comp = asChild ? Slot : "button";
1353
-
1354
- return (
1355
- <Comp
1356
- data-slot="button"
1357
- data-variant={variant}
1358
- data-size={size}
1359
- className={cn(buttonVariants({ variant, size, className }))}
1360
- {...props}
1361
- />
1362
- );
1363
- }
1364
-
1365
- export { Button, buttonVariants };
1366
-
1367
- ```
1368
-
1369
- ## components/ui/dialog.tsx
1370
-
1371
- ```tsx
1372
- "use client";
1373
-
1374
- import * as React from "react";
1375
- import * as DialogPrimitive from "@radix-ui/react-dialog";
1376
- import { XIcon } from "lucide-react";
1377
-
1378
- import { cn } from "@/lib/utils";
1379
-
1380
- function Dialog({
1381
- ...props
1382
- }: React.ComponentProps<typeof DialogPrimitive.Root>) {
1383
- return <DialogPrimitive.Root data-slot="dialog" {...props} />;
1384
- }
1385
-
1386
- function DialogTrigger({
1387
- ...props
1388
- }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
1389
- return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
1390
- }
1391
-
1392
- function DialogPortal({
1393
- ...props
1394
- }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
1395
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
1396
- }
1397
-
1398
- function DialogClose({
1399
- ...props
1400
- }: React.ComponentProps<typeof DialogPrimitive.Close>) {
1401
- return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
1402
- }
1403
-
1404
- function DialogOverlay({
1405
- className,
1406
- ...props
1407
- }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
1408
- return (
1409
- <DialogPrimitive.Overlay
1410
- data-slot="dialog-overlay"
1411
- className={cn(
1412
- "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",
1413
- className,
1414
- )}
1415
- {...props}
1416
- />
1417
- );
1418
- }
1419
-
1420
- function DialogContent({
1421
- className,
1422
- children,
1423
- showCloseButton = true,
1424
- ...props
1425
- }: React.ComponentProps<typeof DialogPrimitive.Content> & {
1426
- showCloseButton?: boolean;
1427
- }) {
1428
- return (
1429
- <DialogPortal data-slot="dialog-portal">
1430
- <DialogOverlay />
1431
- <DialogPrimitive.Content
1432
- data-slot="dialog-content"
1433
- className={cn(
1434
- "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",
1435
- className,
1436
- )}
1437
- {...props}
1438
- >
1439
- {children}
1440
- {showCloseButton && (
1441
- <DialogPrimitive.Close
1442
- data-slot="dialog-close"
1443
- 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"
1444
- >
1445
- <XIcon />
1446
- <span className="sr-only">Close</span>
1447
- </DialogPrimitive.Close>
1448
- )}
1449
- </DialogPrimitive.Content>
1450
- </DialogPortal>
1451
- );
1452
- }
1453
-
1454
- function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
1455
- return (
1456
- <div
1457
- data-slot="dialog-header"
1458
- className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
1459
- {...props}
1460
- />
1461
- );
1462
- }
1463
-
1464
- function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
1465
- return (
1466
- <div
1467
- data-slot="dialog-footer"
1468
- className={cn(
1469
- "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
1470
- className,
1471
- )}
1472
- {...props}
1473
- />
1474
- );
1475
- }
1476
-
1477
- function DialogTitle({
1478
- className,
1479
- ...props
1480
- }: React.ComponentProps<typeof DialogPrimitive.Title>) {
1481
- return (
1482
- <DialogPrimitive.Title
1483
- data-slot="dialog-title"
1484
- className={cn("font-semibold text-lg leading-none", className)}
1485
- {...props}
1486
- />
1487
- );
1488
- }
1489
-
1490
- function DialogDescription({
1491
- className,
1492
- ...props
1493
- }: React.ComponentProps<typeof DialogPrimitive.Description>) {
1494
- return (
1495
- <DialogPrimitive.Description
1496
- data-slot="dialog-description"
1497
- className={cn("text-muted-foreground text-sm", className)}
1498
- {...props}
1499
- />
1500
- );
1501
- }
1502
-
1503
- export {
1504
- Dialog,
1505
- DialogClose,
1506
- DialogContent,
1507
- DialogDescription,
1508
- DialogFooter,
1509
- DialogHeader,
1510
- DialogOverlay,
1511
- DialogPortal,
1512
- DialogTitle,
1513
- DialogTrigger,
1514
- };
1515
-
1516
- ```
1517
-
1518
- ## components/ui/tooltip.tsx
1519
-
1520
- ```tsx
1521
- "use client";
1522
-
1523
- import * as React from "react";
1524
- import * as TooltipPrimitive from "@radix-ui/react-tooltip";
1525
-
1526
- import { cn } from "@/lib/utils";
1527
-
1528
- function TooltipProvider({
1529
- delayDuration = 0,
1530
- ...props
1531
- }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
1532
- return (
1533
- <TooltipPrimitive.Provider
1534
- data-slot="tooltip-provider"
1535
- delayDuration={delayDuration}
1536
- {...props}
1537
- />
1538
- );
1539
- }
1540
-
1541
- function Tooltip({
1542
- ...props
1543
- }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
1544
- return (
1545
- <TooltipProvider>
1546
- <TooltipPrimitive.Root data-slot="tooltip" {...props} />
1547
- </TooltipProvider>
1548
- );
1549
- }
1550
-
1551
- function TooltipTrigger({
1552
- ...props
1553
- }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
1554
- return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
1555
- }
1556
-
1557
- function TooltipContent({
1558
- className,
1559
- sideOffset = 0,
1560
- children,
1561
- ...props
1562
- }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
1563
- return (
1564
- <TooltipPrimitive.Portal>
1565
- <TooltipPrimitive.Content
1566
- data-slot="tooltip-content"
1567
- sideOffset={sideOffset}
1568
- className={cn(
1569
- "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",
1570
- className,
1571
- )}
1572
- {...props}
1573
- >
1574
- {children}
1575
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
1576
- </TooltipPrimitive.Content>
1577
- </TooltipPrimitive.Portal>
1578
- );
259
+ "iconLibrary": "lucide",
260
+ "registries": {
261
+ "@assistant-ui": "https://r.assistant-ui.com/{name}.json"
262
+ }
1579
263
  }
1580
264
 
1581
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
1582
-
1583
265
  ```
1584
266
 
1585
267
  ## lib/utils.ts
@@ -1621,27 +303,28 @@ export default nextConfig;
1621
303
  "start": "next start"
1622
304
  },
1623
305
  "dependencies": {
306
+ "@ai-sdk/openai": "^3.0.19",
1624
307
  "@assistant-ui/react": "workspace:*",
1625
308
  "@assistant-ui/react-markdown": "workspace:*",
309
+ "@assistant-ui/ui": "workspace:*",
1626
310
  "@radix-ui/react-avatar": "^1.1.11",
311
+ "@radix-ui/react-collapsible": "^1.1.12",
1627
312
  "@radix-ui/react-dialog": "^1.1.15",
1628
313
  "@radix-ui/react-slot": "^1.2.4",
1629
314
  "@radix-ui/react-tooltip": "^1.2.8",
1630
315
  "class-variance-authority": "^0.7.1",
1631
316
  "clsx": "^2.1.1",
1632
- "lucide-react": "^0.562.0",
1633
- "next": "16.1.0",
1634
- "react": "19.2.3",
1635
- "react-dom": "19.2.3",
1636
- "remark-gfm": "^4.0.1",
1637
- "tailwind-merge": "^3.4.0",
1638
- "zustand": "^5.0.9"
317
+ "lucide-react": "^0.563.0",
318
+ "next": "^16.1.5",
319
+ "react": "^19.2.4",
320
+ "react-dom": "^19.2.4",
321
+ "tailwind-merge": "^3.4.0"
1639
322
  },
1640
323
  "devDependencies": {
1641
324
  "@assistant-ui/x-buildutils": "workspace:*",
1642
325
  "@tailwindcss/postcss": "^4.1.18",
1643
- "@types/node": "^25.0.3",
1644
- "@types/react": "^19.2.7",
326
+ "@types/node": "^25.0.10",
327
+ "@types/react": "^19.2.9",
1645
328
  "@types/react-dom": "^19.2.3",
1646
329
  "postcss": "^8.5.6",
1647
330
  "tailwindcss": "^4.1.18",
@@ -1652,13 +335,64 @@ export default nextConfig;
1652
335
 
1653
336
  ```
1654
337
 
338
+ ## README.md
339
+
340
+ ```markdown
341
+ # External Store Integration
342
+
343
+ This example demonstrates how to use assistant-ui with an external message store using `useExternalStoreRuntime`.
344
+
345
+ ## Quick Start
346
+
347
+ ### Using CLI (Recommended)
348
+
349
+ ```bash
350
+ npx assistant-ui@latest create my-app --example with-external-store
351
+ cd my-app
352
+ ```
353
+
354
+ ### Environment Variables
355
+
356
+ Create `.env.local`:
357
+
358
+ ```
359
+ OPENAI_API_KEY=sk-...
360
+ ```
361
+
362
+ ### Run
363
+
364
+ ```bash
365
+ npm run dev
366
+ ```
367
+
368
+ ## Features
369
+
370
+ - External store runtime via `useExternalStoreRuntime`
371
+ - Custom message state management
372
+ - React state-based message storage
373
+ - Message conversion utilities
374
+
375
+ ## Related Documentation
376
+
377
+ - [assistant-ui Documentation](https://www.assistant-ui.com/docs)
378
+ - [External Store Runtime Guide](https://www.assistant-ui.com/docs/runtimes/external-store)
379
+
380
+ ```
381
+
1655
382
  ## tsconfig.json
1656
383
 
1657
384
  ```json
1658
385
  {
1659
386
  "extends": "@assistant-ui/x-buildutils/ts/next",
1660
387
  "compilerOptions": {
1661
- "paths": { "@/*": ["./*"] }
388
+ "paths": {
389
+ "@/*": ["./*"],
390
+ "@/components/assistant-ui/*": [
391
+ "../../packages/ui/src/components/assistant-ui/*"
392
+ ],
393
+ "@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
394
+ "@assistant-ui/ui/*": ["../../packages/ui/src/*"]
395
+ }
1662
396
  },
1663
397
  "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
1664
398
  "exclude": ["node_modules"]