@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 {
|
|
@@ -140,1307 +142,6 @@ body {
|
|
|
140
142
|
|
|
141
143
|
```
|
|
142
144
|
|
|
143
|
-
## app/components/assistant-ui/attachment.tsx
|
|
144
|
-
|
|
145
|
-
```tsx
|
|
146
|
-
"use client";
|
|
147
|
-
|
|
148
|
-
import { type PropsWithChildren, useEffect, useState, type FC } from "react";
|
|
149
|
-
import { XIcon, PlusIcon, FileText } from "lucide-react";
|
|
150
|
-
import {
|
|
151
|
-
AttachmentPrimitive,
|
|
152
|
-
ComposerPrimitive,
|
|
153
|
-
MessagePrimitive,
|
|
154
|
-
useAssistantState,
|
|
155
|
-
useAssistantApi,
|
|
156
|
-
} from "@assistant-ui/react";
|
|
157
|
-
import { useShallow } from "zustand/shallow";
|
|
158
|
-
import {
|
|
159
|
-
Tooltip,
|
|
160
|
-
TooltipContent,
|
|
161
|
-
TooltipTrigger,
|
|
162
|
-
} from "@/components/ui/tooltip";
|
|
163
|
-
import {
|
|
164
|
-
Dialog,
|
|
165
|
-
DialogTitle,
|
|
166
|
-
DialogContent,
|
|
167
|
-
DialogTrigger,
|
|
168
|
-
} from "@/components/ui/dialog";
|
|
169
|
-
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
|
170
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
171
|
-
import { cn } from "@/lib/utils";
|
|
172
|
-
|
|
173
|
-
const useFileSrc = (file: File | undefined) => {
|
|
174
|
-
const [src, setSrc] = useState<string | undefined>(undefined);
|
|
175
|
-
|
|
176
|
-
useEffect(() => {
|
|
177
|
-
if (!file) {
|
|
178
|
-
setSrc(undefined);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const objectUrl = URL.createObjectURL(file);
|
|
183
|
-
setSrc(objectUrl);
|
|
184
|
-
|
|
185
|
-
return () => {
|
|
186
|
-
URL.revokeObjectURL(objectUrl);
|
|
187
|
-
};
|
|
188
|
-
}, [file]);
|
|
189
|
-
|
|
190
|
-
return src;
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
const useAttachmentSrc = () => {
|
|
194
|
-
const { file, src } = useAssistantState(
|
|
195
|
-
useShallow(({ attachment }): { file?: File; src?: string } => {
|
|
196
|
-
if (attachment.type !== "image") return {};
|
|
197
|
-
if (attachment.file) return { file: attachment.file };
|
|
198
|
-
const src = attachment.content?.filter((c) => c.type === "image")[0]
|
|
199
|
-
?.image;
|
|
200
|
-
if (!src) return {};
|
|
201
|
-
return { src };
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
return useFileSrc(file) ?? src;
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
type AttachmentPreviewProps = {
|
|
209
|
-
src: string;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const AttachmentPreview: FC<AttachmentPreviewProps> = ({ src }) => {
|
|
213
|
-
const [isLoaded, setIsLoaded] = useState(false);
|
|
214
|
-
return (
|
|
215
|
-
<img
|
|
216
|
-
src={src}
|
|
217
|
-
alt="Attachment preview"
|
|
218
|
-
className={
|
|
219
|
-
isLoaded
|
|
220
|
-
? "aui-attachment-preview-image-loaded block h-auto max-h-[80vh] w-auto max-w-full object-contain"
|
|
221
|
-
: "aui-attachment-preview-image-loading hidden"
|
|
222
|
-
}
|
|
223
|
-
onLoad={() => setIsLoaded(true)}
|
|
224
|
-
/>
|
|
225
|
-
);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
|
|
229
|
-
const src = useAttachmentSrc();
|
|
230
|
-
|
|
231
|
-
if (!src) return children;
|
|
232
|
-
|
|
233
|
-
return (
|
|
234
|
-
<Dialog>
|
|
235
|
-
<DialogTrigger
|
|
236
|
-
className="aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50"
|
|
237
|
-
asChild
|
|
238
|
-
>
|
|
239
|
-
{children}
|
|
240
|
-
</DialogTrigger>
|
|
241
|
-
<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">
|
|
242
|
-
<DialogTitle className="aui-sr-only sr-only">
|
|
243
|
-
Image Attachment Preview
|
|
244
|
-
</DialogTitle>
|
|
245
|
-
<div className="aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background">
|
|
246
|
-
<AttachmentPreview src={src} />
|
|
247
|
-
</div>
|
|
248
|
-
</DialogContent>
|
|
249
|
-
</Dialog>
|
|
250
|
-
);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const AttachmentThumb: FC = () => {
|
|
254
|
-
const isImage = useAssistantState(
|
|
255
|
-
({ attachment }) => attachment.type === "image",
|
|
256
|
-
);
|
|
257
|
-
const src = useAttachmentSrc();
|
|
258
|
-
|
|
259
|
-
return (
|
|
260
|
-
<Avatar className="aui-attachment-tile-avatar h-full w-full rounded-none">
|
|
261
|
-
<AvatarImage
|
|
262
|
-
src={src}
|
|
263
|
-
alt="Attachment preview"
|
|
264
|
-
className="aui-attachment-tile-image object-cover"
|
|
265
|
-
/>
|
|
266
|
-
<AvatarFallback delayMs={isImage ? 200 : 0}>
|
|
267
|
-
<FileText className="aui-attachment-tile-fallback-icon size-8 text-muted-foreground" />
|
|
268
|
-
</AvatarFallback>
|
|
269
|
-
</Avatar>
|
|
270
|
-
);
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
const AttachmentUI: FC = () => {
|
|
274
|
-
const api = useAssistantApi();
|
|
275
|
-
const isComposer = api.attachment.source === "composer";
|
|
276
|
-
|
|
277
|
-
const isImage = useAssistantState(
|
|
278
|
-
({ attachment }) => attachment.type === "image",
|
|
279
|
-
);
|
|
280
|
-
const typeLabel = useAssistantState(({ attachment }) => {
|
|
281
|
-
const type = attachment.type;
|
|
282
|
-
switch (type) {
|
|
283
|
-
case "image":
|
|
284
|
-
return "Image";
|
|
285
|
-
case "document":
|
|
286
|
-
return "Document";
|
|
287
|
-
case "file":
|
|
288
|
-
return "File";
|
|
289
|
-
default:
|
|
290
|
-
const _exhaustiveCheck: never = type;
|
|
291
|
-
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
return (
|
|
296
|
-
<Tooltip>
|
|
297
|
-
<AttachmentPrimitive.Root
|
|
298
|
-
className={cn(
|
|
299
|
-
"aui-attachment-root relative",
|
|
300
|
-
isImage &&
|
|
301
|
-
"aui-attachment-root-composer only:[&>#attachment-tile]:size-24",
|
|
302
|
-
)}
|
|
303
|
-
>
|
|
304
|
-
<AttachmentPreviewDialog>
|
|
305
|
-
<TooltipTrigger asChild>
|
|
306
|
-
<div
|
|
307
|
-
className={cn(
|
|
308
|
-
"aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
|
|
309
|
-
isComposer &&
|
|
310
|
-
"aui-attachment-tile-composer border-foreground/20",
|
|
311
|
-
)}
|
|
312
|
-
role="button"
|
|
313
|
-
id="attachment-tile"
|
|
314
|
-
aria-label={`${typeLabel} attachment`}
|
|
315
|
-
>
|
|
316
|
-
<AttachmentThumb />
|
|
317
|
-
</div>
|
|
318
|
-
</TooltipTrigger>
|
|
319
|
-
</AttachmentPreviewDialog>
|
|
320
|
-
{isComposer && <AttachmentRemove />}
|
|
321
|
-
</AttachmentPrimitive.Root>
|
|
322
|
-
<TooltipContent side="top">
|
|
323
|
-
<AttachmentPrimitive.Name />
|
|
324
|
-
</TooltipContent>
|
|
325
|
-
</Tooltip>
|
|
326
|
-
);
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
const AttachmentRemove: FC = () => {
|
|
330
|
-
return (
|
|
331
|
-
<AttachmentPrimitive.Remove asChild>
|
|
332
|
-
<TooltipIconButton
|
|
333
|
-
tooltip="Remove file"
|
|
334
|
-
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"
|
|
335
|
-
side="top"
|
|
336
|
-
>
|
|
337
|
-
<XIcon className="aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" />
|
|
338
|
-
</TooltipIconButton>
|
|
339
|
-
</AttachmentPrimitive.Remove>
|
|
340
|
-
);
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
export const UserMessageAttachments: FC = () => {
|
|
344
|
-
return (
|
|
345
|
-
<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">
|
|
346
|
-
<MessagePrimitive.Attachments components={{ Attachment: AttachmentUI }} />
|
|
347
|
-
</div>
|
|
348
|
-
);
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
export const ComposerAttachments: FC = () => {
|
|
352
|
-
return (
|
|
353
|
-
<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">
|
|
354
|
-
<ComposerPrimitive.Attachments
|
|
355
|
-
components={{ Attachment: AttachmentUI }}
|
|
356
|
-
/>
|
|
357
|
-
</div>
|
|
358
|
-
);
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
export const ComposerAddAttachment: FC = () => {
|
|
362
|
-
return (
|
|
363
|
-
<ComposerPrimitive.AddAttachment asChild>
|
|
364
|
-
<TooltipIconButton
|
|
365
|
-
tooltip="Add Attachment"
|
|
366
|
-
side="bottom"
|
|
367
|
-
variant="ghost"
|
|
368
|
-
size="icon"
|
|
369
|
-
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"
|
|
370
|
-
aria-label="Add Attachment"
|
|
371
|
-
>
|
|
372
|
-
<PlusIcon className="aui-attachment-add-icon size-5 stroke-[1.5px]" />
|
|
373
|
-
</TooltipIconButton>
|
|
374
|
-
</ComposerPrimitive.AddAttachment>
|
|
375
|
-
);
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
## app/components/assistant-ui/markdown-text.tsx
|
|
381
|
-
|
|
382
|
-
```tsx
|
|
383
|
-
"use client";
|
|
384
|
-
|
|
385
|
-
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
386
|
-
|
|
387
|
-
import {
|
|
388
|
-
type CodeHeaderProps,
|
|
389
|
-
MarkdownTextPrimitive,
|
|
390
|
-
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
391
|
-
useIsMarkdownCodeBlock,
|
|
392
|
-
} from "@assistant-ui/react-markdown";
|
|
393
|
-
import remarkGfm from "remark-gfm";
|
|
394
|
-
import { type FC, memo, useState } from "react";
|
|
395
|
-
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
396
|
-
|
|
397
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
398
|
-
import { cn } from "@/lib/utils";
|
|
399
|
-
|
|
400
|
-
const MarkdownTextImpl = () => {
|
|
401
|
-
return (
|
|
402
|
-
<MarkdownTextPrimitive
|
|
403
|
-
remarkPlugins={[remarkGfm]}
|
|
404
|
-
className="aui-md"
|
|
405
|
-
components={defaultComponents}
|
|
406
|
-
/>
|
|
407
|
-
);
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
export const MarkdownText = memo(MarkdownTextImpl);
|
|
411
|
-
|
|
412
|
-
const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
|
|
413
|
-
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
414
|
-
const onCopy = () => {
|
|
415
|
-
if (!code || isCopied) return;
|
|
416
|
-
copyToClipboard(code);
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
return (
|
|
420
|
-
<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">
|
|
421
|
-
<span className="aui-code-header-language lowercase [&>span]:text-xs">
|
|
422
|
-
{language}
|
|
423
|
-
</span>
|
|
424
|
-
<TooltipIconButton tooltip="Copy" onClick={onCopy}>
|
|
425
|
-
{!isCopied && <CopyIcon />}
|
|
426
|
-
{isCopied && <CheckIcon />}
|
|
427
|
-
</TooltipIconButton>
|
|
428
|
-
</div>
|
|
429
|
-
);
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
const useCopyToClipboard = ({
|
|
433
|
-
copiedDuration = 3000,
|
|
434
|
-
}: {
|
|
435
|
-
copiedDuration?: number;
|
|
436
|
-
} = {}) => {
|
|
437
|
-
const [isCopied, setIsCopied] = useState<boolean>(false);
|
|
438
|
-
|
|
439
|
-
const copyToClipboard = (value: string) => {
|
|
440
|
-
if (!value) return;
|
|
441
|
-
|
|
442
|
-
navigator.clipboard.writeText(value).then(() => {
|
|
443
|
-
setIsCopied(true);
|
|
444
|
-
setTimeout(() => setIsCopied(false), copiedDuration);
|
|
445
|
-
});
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
return { isCopied, copyToClipboard };
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
const defaultComponents = memoizeMarkdownComponents({
|
|
452
|
-
h1: ({ className, ...props }) => (
|
|
453
|
-
<h1
|
|
454
|
-
className={cn(
|
|
455
|
-
"aui-md-h1 mb-8 scroll-m-20 font-extrabold text-4xl tracking-tight last:mb-0",
|
|
456
|
-
className,
|
|
457
|
-
)}
|
|
458
|
-
{...props}
|
|
459
|
-
/>
|
|
460
|
-
),
|
|
461
|
-
h2: ({ className, ...props }) => (
|
|
462
|
-
<h2
|
|
463
|
-
className={cn(
|
|
464
|
-
"aui-md-h2 mt-8 mb-4 scroll-m-20 font-semibold text-3xl tracking-tight first:mt-0 last:mb-0",
|
|
465
|
-
className,
|
|
466
|
-
)}
|
|
467
|
-
{...props}
|
|
468
|
-
/>
|
|
469
|
-
),
|
|
470
|
-
h3: ({ className, ...props }) => (
|
|
471
|
-
<h3
|
|
472
|
-
className={cn(
|
|
473
|
-
"aui-md-h3 mt-6 mb-4 scroll-m-20 font-semibold text-2xl tracking-tight first:mt-0 last:mb-0",
|
|
474
|
-
className,
|
|
475
|
-
)}
|
|
476
|
-
{...props}
|
|
477
|
-
/>
|
|
478
|
-
),
|
|
479
|
-
h4: ({ className, ...props }) => (
|
|
480
|
-
<h4
|
|
481
|
-
className={cn(
|
|
482
|
-
"aui-md-h4 mt-6 mb-4 scroll-m-20 font-semibold text-xl tracking-tight first:mt-0 last:mb-0",
|
|
483
|
-
className,
|
|
484
|
-
)}
|
|
485
|
-
{...props}
|
|
486
|
-
/>
|
|
487
|
-
),
|
|
488
|
-
h5: ({ className, ...props }) => (
|
|
489
|
-
<h5
|
|
490
|
-
className={cn(
|
|
491
|
-
"aui-md-h5 my-4 font-semibold text-lg first:mt-0 last:mb-0",
|
|
492
|
-
className,
|
|
493
|
-
)}
|
|
494
|
-
{...props}
|
|
495
|
-
/>
|
|
496
|
-
),
|
|
497
|
-
h6: ({ className, ...props }) => (
|
|
498
|
-
<h6
|
|
499
|
-
className={cn(
|
|
500
|
-
"aui-md-h6 my-4 font-semibold first:mt-0 last:mb-0",
|
|
501
|
-
className,
|
|
502
|
-
)}
|
|
503
|
-
{...props}
|
|
504
|
-
/>
|
|
505
|
-
),
|
|
506
|
-
p: ({ className, ...props }) => (
|
|
507
|
-
<p
|
|
508
|
-
className={cn(
|
|
509
|
-
"aui-md-p mt-5 mb-5 leading-7 first:mt-0 last:mb-0",
|
|
510
|
-
className,
|
|
511
|
-
)}
|
|
512
|
-
{...props}
|
|
513
|
-
/>
|
|
514
|
-
),
|
|
515
|
-
a: ({ className, ...props }) => (
|
|
516
|
-
<a
|
|
517
|
-
className={cn(
|
|
518
|
-
"aui-md-a font-medium text-primary underline underline-offset-4",
|
|
519
|
-
className,
|
|
520
|
-
)}
|
|
521
|
-
{...props}
|
|
522
|
-
/>
|
|
523
|
-
),
|
|
524
|
-
blockquote: ({ className, ...props }) => (
|
|
525
|
-
<blockquote
|
|
526
|
-
className={cn("aui-md-blockquote border-l-2 pl-6 italic", className)}
|
|
527
|
-
{...props}
|
|
528
|
-
/>
|
|
529
|
-
),
|
|
530
|
-
ul: ({ className, ...props }) => (
|
|
531
|
-
<ul
|
|
532
|
-
className={cn("aui-md-ul my-5 ml-6 list-disc [&>li]:mt-2", className)}
|
|
533
|
-
{...props}
|
|
534
|
-
/>
|
|
535
|
-
),
|
|
536
|
-
ol: ({ className, ...props }) => (
|
|
537
|
-
<ol
|
|
538
|
-
className={cn("aui-md-ol my-5 ml-6 list-decimal [&>li]:mt-2", className)}
|
|
539
|
-
{...props}
|
|
540
|
-
/>
|
|
541
|
-
),
|
|
542
|
-
hr: ({ className, ...props }) => (
|
|
543
|
-
<hr className={cn("aui-md-hr my-5 border-b", className)} {...props} />
|
|
544
|
-
),
|
|
545
|
-
table: ({ className, ...props }) => (
|
|
546
|
-
<table
|
|
547
|
-
className={cn(
|
|
548
|
-
"aui-md-table my-5 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
549
|
-
className,
|
|
550
|
-
)}
|
|
551
|
-
{...props}
|
|
552
|
-
/>
|
|
553
|
-
),
|
|
554
|
-
th: ({ className, ...props }) => (
|
|
555
|
-
<th
|
|
556
|
-
className={cn(
|
|
557
|
-
"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",
|
|
558
|
-
className,
|
|
559
|
-
)}
|
|
560
|
-
{...props}
|
|
561
|
-
/>
|
|
562
|
-
),
|
|
563
|
-
td: ({ className, ...props }) => (
|
|
564
|
-
<td
|
|
565
|
-
className={cn(
|
|
566
|
-
"aui-md-td border-b border-l px-4 py-2 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
|
|
567
|
-
className,
|
|
568
|
-
)}
|
|
569
|
-
{...props}
|
|
570
|
-
/>
|
|
571
|
-
),
|
|
572
|
-
tr: ({ className, ...props }) => (
|
|
573
|
-
<tr
|
|
574
|
-
className={cn(
|
|
575
|
-
"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",
|
|
576
|
-
className,
|
|
577
|
-
)}
|
|
578
|
-
{...props}
|
|
579
|
-
/>
|
|
580
|
-
),
|
|
581
|
-
sup: ({ className, ...props }) => (
|
|
582
|
-
<sup
|
|
583
|
-
className={cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className)}
|
|
584
|
-
{...props}
|
|
585
|
-
/>
|
|
586
|
-
),
|
|
587
|
-
pre: ({ className, ...props }) => (
|
|
588
|
-
<pre
|
|
589
|
-
className={cn(
|
|
590
|
-
"aui-md-pre overflow-x-auto rounded-t-none! rounded-b-lg bg-black p-4 text-white",
|
|
591
|
-
className,
|
|
592
|
-
)}
|
|
593
|
-
{...props}
|
|
594
|
-
/>
|
|
595
|
-
),
|
|
596
|
-
code: function Code({ className, ...props }) {
|
|
597
|
-
const isCodeBlock = useIsMarkdownCodeBlock();
|
|
598
|
-
return (
|
|
599
|
-
<code
|
|
600
|
-
className={cn(
|
|
601
|
-
!isCodeBlock &&
|
|
602
|
-
"aui-md-inline-code rounded border bg-muted font-semibold",
|
|
603
|
-
className,
|
|
604
|
-
)}
|
|
605
|
-
{...props}
|
|
606
|
-
/>
|
|
607
|
-
);
|
|
608
|
-
},
|
|
609
|
-
CodeHeader,
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
```
|
|
613
|
-
|
|
614
|
-
## app/components/assistant-ui/thread.tsx
|
|
615
|
-
|
|
616
|
-
```tsx
|
|
617
|
-
import {
|
|
618
|
-
ComposerAddAttachment,
|
|
619
|
-
ComposerAttachments,
|
|
620
|
-
UserMessageAttachments,
|
|
621
|
-
} from "@/components/assistant-ui/attachment";
|
|
622
|
-
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
|
|
623
|
-
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
|
|
624
|
-
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
|
|
625
|
-
import { Button } from "@/components/ui/button";
|
|
626
|
-
import { cn } from "@/lib/utils";
|
|
627
|
-
import {
|
|
628
|
-
ActionBarPrimitive,
|
|
629
|
-
AssistantIf,
|
|
630
|
-
BranchPickerPrimitive,
|
|
631
|
-
ComposerPrimitive,
|
|
632
|
-
ErrorPrimitive,
|
|
633
|
-
MessagePrimitive,
|
|
634
|
-
ThreadPrimitive,
|
|
635
|
-
} from "@assistant-ui/react";
|
|
636
|
-
import {
|
|
637
|
-
ArrowDownIcon,
|
|
638
|
-
ArrowUpIcon,
|
|
639
|
-
CheckIcon,
|
|
640
|
-
ChevronLeftIcon,
|
|
641
|
-
ChevronRightIcon,
|
|
642
|
-
CopyIcon,
|
|
643
|
-
DownloadIcon,
|
|
644
|
-
PencilIcon,
|
|
645
|
-
RefreshCwIcon,
|
|
646
|
-
SquareIcon,
|
|
647
|
-
} from "lucide-react";
|
|
648
|
-
import type { FC } from "react";
|
|
649
|
-
|
|
650
|
-
export const Thread: FC = () => {
|
|
651
|
-
return (
|
|
652
|
-
<ThreadPrimitive.Root
|
|
653
|
-
className="aui-root aui-thread-root @container flex h-full flex-col bg-background"
|
|
654
|
-
style={{
|
|
655
|
-
["--thread-max-width" as string]: "44rem",
|
|
656
|
-
}}
|
|
657
|
-
>
|
|
658
|
-
<ThreadPrimitive.Viewport
|
|
659
|
-
turnAnchor="top"
|
|
660
|
-
className="aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4"
|
|
661
|
-
>
|
|
662
|
-
<AssistantIf condition={({ thread }) => thread.isEmpty}>
|
|
663
|
-
<ThreadWelcome />
|
|
664
|
-
</AssistantIf>
|
|
665
|
-
|
|
666
|
-
<ThreadPrimitive.Messages
|
|
667
|
-
components={{
|
|
668
|
-
UserMessage,
|
|
669
|
-
EditComposer,
|
|
670
|
-
AssistantMessage,
|
|
671
|
-
}}
|
|
672
|
-
/>
|
|
673
|
-
|
|
674
|
-
<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">
|
|
675
|
-
<ThreadScrollToBottom />
|
|
676
|
-
<Composer />
|
|
677
|
-
</ThreadPrimitive.ViewportFooter>
|
|
678
|
-
</ThreadPrimitive.Viewport>
|
|
679
|
-
</ThreadPrimitive.Root>
|
|
680
|
-
);
|
|
681
|
-
};
|
|
682
|
-
|
|
683
|
-
const ThreadScrollToBottom: FC = () => {
|
|
684
|
-
return (
|
|
685
|
-
<ThreadPrimitive.ScrollToBottom asChild>
|
|
686
|
-
<TooltipIconButton
|
|
687
|
-
tooltip="Scroll to bottom"
|
|
688
|
-
variant="outline"
|
|
689
|
-
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"
|
|
690
|
-
>
|
|
691
|
-
<ArrowDownIcon />
|
|
692
|
-
</TooltipIconButton>
|
|
693
|
-
</ThreadPrimitive.ScrollToBottom>
|
|
694
|
-
);
|
|
695
|
-
};
|
|
696
|
-
|
|
697
|
-
const ThreadWelcome: FC = () => {
|
|
698
|
-
return (
|
|
699
|
-
<div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
|
|
700
|
-
<div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-center">
|
|
701
|
-
<div className="aui-thread-welcome-message flex size-full flex-col justify-center px-4">
|
|
702
|
-
<h1 className="aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in font-semibold text-2xl duration-200">
|
|
703
|
-
Hello there!
|
|
704
|
-
</h1>
|
|
705
|
-
<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">
|
|
706
|
-
How can I help you today?
|
|
707
|
-
</p>
|
|
708
|
-
</div>
|
|
709
|
-
</div>
|
|
710
|
-
<ThreadSuggestions />
|
|
711
|
-
</div>
|
|
712
|
-
);
|
|
713
|
-
};
|
|
714
|
-
|
|
715
|
-
const SUGGESTIONS = [
|
|
716
|
-
{
|
|
717
|
-
title: "What's the weather",
|
|
718
|
-
label: "in San Francisco?",
|
|
719
|
-
prompt: "What's the weather in San Francisco?",
|
|
720
|
-
},
|
|
721
|
-
{
|
|
722
|
-
title: "Explain React hooks",
|
|
723
|
-
label: "like useState and useEffect",
|
|
724
|
-
prompt: "Explain React hooks like useState and useEffect",
|
|
725
|
-
},
|
|
726
|
-
] as const;
|
|
727
|
-
|
|
728
|
-
const ThreadSuggestions: FC = () => {
|
|
729
|
-
return (
|
|
730
|
-
<div className="aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4">
|
|
731
|
-
{SUGGESTIONS.map((suggestion, index) => (
|
|
732
|
-
<div
|
|
733
|
-
key={suggestion.prompt}
|
|
734
|
-
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"
|
|
735
|
-
style={{ animationDelay: `${100 + index * 50}ms` }}
|
|
736
|
-
>
|
|
737
|
-
<ThreadPrimitive.Suggestion prompt={suggestion.prompt} send asChild>
|
|
738
|
-
<Button
|
|
739
|
-
variant="ghost"
|
|
740
|
-
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"
|
|
741
|
-
aria-label={suggestion.prompt}
|
|
742
|
-
>
|
|
743
|
-
<span className="aui-thread-welcome-suggestion-text-1 font-medium">
|
|
744
|
-
{suggestion.title}
|
|
745
|
-
</span>
|
|
746
|
-
<span className="aui-thread-welcome-suggestion-text-2 text-muted-foreground">
|
|
747
|
-
{suggestion.label}
|
|
748
|
-
</span>
|
|
749
|
-
</Button>
|
|
750
|
-
</ThreadPrimitive.Suggestion>
|
|
751
|
-
</div>
|
|
752
|
-
))}
|
|
753
|
-
</div>
|
|
754
|
-
);
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
const Composer: FC = () => {
|
|
758
|
-
return (
|
|
759
|
-
<ComposerPrimitive.Root className="aui-composer-root relative flex w-full flex-col">
|
|
760
|
-
<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">
|
|
761
|
-
<ComposerAttachments />
|
|
762
|
-
<ComposerPrimitive.Input
|
|
763
|
-
placeholder="Send a message..."
|
|
764
|
-
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"
|
|
765
|
-
rows={1}
|
|
766
|
-
autoFocus
|
|
767
|
-
aria-label="Message input"
|
|
768
|
-
/>
|
|
769
|
-
<ComposerAction />
|
|
770
|
-
</ComposerPrimitive.AttachmentDropzone>
|
|
771
|
-
</ComposerPrimitive.Root>
|
|
772
|
-
);
|
|
773
|
-
};
|
|
774
|
-
|
|
775
|
-
const ComposerAction: FC = () => {
|
|
776
|
-
return (
|
|
777
|
-
<div className="aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between">
|
|
778
|
-
<ComposerAddAttachment />
|
|
779
|
-
|
|
780
|
-
<AssistantIf condition={({ thread }) => !thread.isRunning}>
|
|
781
|
-
<ComposerPrimitive.Send asChild>
|
|
782
|
-
<TooltipIconButton
|
|
783
|
-
tooltip="Send message"
|
|
784
|
-
side="bottom"
|
|
785
|
-
type="submit"
|
|
786
|
-
variant="default"
|
|
787
|
-
size="icon"
|
|
788
|
-
className="aui-composer-send size-8 rounded-full"
|
|
789
|
-
aria-label="Send message"
|
|
790
|
-
>
|
|
791
|
-
<ArrowUpIcon className="aui-composer-send-icon size-4" />
|
|
792
|
-
</TooltipIconButton>
|
|
793
|
-
</ComposerPrimitive.Send>
|
|
794
|
-
</AssistantIf>
|
|
795
|
-
|
|
796
|
-
<AssistantIf condition={({ thread }) => thread.isRunning}>
|
|
797
|
-
<ComposerPrimitive.Cancel asChild>
|
|
798
|
-
<Button
|
|
799
|
-
type="button"
|
|
800
|
-
variant="default"
|
|
801
|
-
size="icon"
|
|
802
|
-
className="aui-composer-cancel size-8 rounded-full"
|
|
803
|
-
aria-label="Stop generating"
|
|
804
|
-
>
|
|
805
|
-
<SquareIcon className="aui-composer-cancel-icon size-3 fill-current" />
|
|
806
|
-
</Button>
|
|
807
|
-
</ComposerPrimitive.Cancel>
|
|
808
|
-
</AssistantIf>
|
|
809
|
-
</div>
|
|
810
|
-
);
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
const MessageError: FC = () => {
|
|
814
|
-
return (
|
|
815
|
-
<MessagePrimitive.Error>
|
|
816
|
-
<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">
|
|
817
|
-
<ErrorPrimitive.Message className="aui-message-error-message line-clamp-2" />
|
|
818
|
-
</ErrorPrimitive.Root>
|
|
819
|
-
</MessagePrimitive.Error>
|
|
820
|
-
);
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
const AssistantMessage: FC = () => {
|
|
824
|
-
return (
|
|
825
|
-
<MessagePrimitive.Root
|
|
826
|
-
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"
|
|
827
|
-
data-role="assistant"
|
|
828
|
-
>
|
|
829
|
-
<div className="aui-assistant-message-content wrap-break-word px-2 text-foreground leading-relaxed">
|
|
830
|
-
<MessagePrimitive.Parts
|
|
831
|
-
components={{
|
|
832
|
-
Text: MarkdownText,
|
|
833
|
-
tools: { Fallback: ToolFallback },
|
|
834
|
-
}}
|
|
835
|
-
/>
|
|
836
|
-
<MessageError />
|
|
837
|
-
</div>
|
|
838
|
-
|
|
839
|
-
<div className="aui-assistant-message-footer mt-1 ml-2 flex">
|
|
840
|
-
<BranchPicker />
|
|
841
|
-
<AssistantActionBar />
|
|
842
|
-
</div>
|
|
843
|
-
</MessagePrimitive.Root>
|
|
844
|
-
);
|
|
845
|
-
};
|
|
846
|
-
|
|
847
|
-
const AssistantActionBar: FC = () => {
|
|
848
|
-
return (
|
|
849
|
-
<ActionBarPrimitive.Root
|
|
850
|
-
hideWhenRunning
|
|
851
|
-
autohide="not-last"
|
|
852
|
-
autohideFloat="single-branch"
|
|
853
|
-
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"
|
|
854
|
-
>
|
|
855
|
-
<ActionBarPrimitive.Copy asChild>
|
|
856
|
-
<TooltipIconButton tooltip="Copy">
|
|
857
|
-
<AssistantIf condition={({ message }) => message.isCopied}>
|
|
858
|
-
<CheckIcon />
|
|
859
|
-
</AssistantIf>
|
|
860
|
-
<AssistantIf condition={({ message }) => !message.isCopied}>
|
|
861
|
-
<CopyIcon />
|
|
862
|
-
</AssistantIf>
|
|
863
|
-
</TooltipIconButton>
|
|
864
|
-
</ActionBarPrimitive.Copy>
|
|
865
|
-
<ActionBarPrimitive.ExportMarkdown asChild>
|
|
866
|
-
<TooltipIconButton tooltip="Export as Markdown">
|
|
867
|
-
<DownloadIcon />
|
|
868
|
-
</TooltipIconButton>
|
|
869
|
-
</ActionBarPrimitive.ExportMarkdown>
|
|
870
|
-
<ActionBarPrimitive.Reload asChild>
|
|
871
|
-
<TooltipIconButton tooltip="Refresh">
|
|
872
|
-
<RefreshCwIcon />
|
|
873
|
-
</TooltipIconButton>
|
|
874
|
-
</ActionBarPrimitive.Reload>
|
|
875
|
-
</ActionBarPrimitive.Root>
|
|
876
|
-
);
|
|
877
|
-
};
|
|
878
|
-
|
|
879
|
-
const UserMessage: FC = () => {
|
|
880
|
-
return (
|
|
881
|
-
<MessagePrimitive.Root
|
|
882
|
-
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"
|
|
883
|
-
data-role="user"
|
|
884
|
-
>
|
|
885
|
-
<UserMessageAttachments />
|
|
886
|
-
|
|
887
|
-
<div className="aui-user-message-content-wrapper relative col-start-2 min-w-0">
|
|
888
|
-
<div className="aui-user-message-content wrap-break-word rounded-2xl bg-muted px-4 py-2.5 text-foreground">
|
|
889
|
-
<MessagePrimitive.Parts />
|
|
890
|
-
</div>
|
|
891
|
-
<div className="aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2">
|
|
892
|
-
<UserActionBar />
|
|
893
|
-
</div>
|
|
894
|
-
</div>
|
|
895
|
-
|
|
896
|
-
<BranchPicker className="aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
|
|
897
|
-
</MessagePrimitive.Root>
|
|
898
|
-
);
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
const UserActionBar: FC = () => {
|
|
902
|
-
return (
|
|
903
|
-
<ActionBarPrimitive.Root
|
|
904
|
-
hideWhenRunning
|
|
905
|
-
autohide="not-last"
|
|
906
|
-
className="aui-user-action-bar-root flex flex-col items-end"
|
|
907
|
-
>
|
|
908
|
-
<ActionBarPrimitive.Edit asChild>
|
|
909
|
-
<TooltipIconButton tooltip="Edit" className="aui-user-action-edit p-4">
|
|
910
|
-
<PencilIcon />
|
|
911
|
-
</TooltipIconButton>
|
|
912
|
-
</ActionBarPrimitive.Edit>
|
|
913
|
-
</ActionBarPrimitive.Root>
|
|
914
|
-
);
|
|
915
|
-
};
|
|
916
|
-
|
|
917
|
-
const EditComposer: FC = () => {
|
|
918
|
-
return (
|
|
919
|
-
<MessagePrimitive.Root className="aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3">
|
|
920
|
-
<ComposerPrimitive.Root className="aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-2xl bg-muted">
|
|
921
|
-
<ComposerPrimitive.Input
|
|
922
|
-
className="aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none"
|
|
923
|
-
autoFocus
|
|
924
|
-
/>
|
|
925
|
-
<div className="aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end">
|
|
926
|
-
<ComposerPrimitive.Cancel asChild>
|
|
927
|
-
<Button variant="ghost" size="sm">
|
|
928
|
-
Cancel
|
|
929
|
-
</Button>
|
|
930
|
-
</ComposerPrimitive.Cancel>
|
|
931
|
-
<ComposerPrimitive.Send asChild>
|
|
932
|
-
<Button size="sm">Update</Button>
|
|
933
|
-
</ComposerPrimitive.Send>
|
|
934
|
-
</div>
|
|
935
|
-
</ComposerPrimitive.Root>
|
|
936
|
-
</MessagePrimitive.Root>
|
|
937
|
-
);
|
|
938
|
-
};
|
|
939
|
-
|
|
940
|
-
const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
|
|
941
|
-
className,
|
|
942
|
-
...rest
|
|
943
|
-
}) => {
|
|
944
|
-
return (
|
|
945
|
-
<BranchPickerPrimitive.Root
|
|
946
|
-
hideWhenSingleBranch
|
|
947
|
-
className={cn(
|
|
948
|
-
"aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
|
|
949
|
-
className,
|
|
950
|
-
)}
|
|
951
|
-
{...rest}
|
|
952
|
-
>
|
|
953
|
-
<BranchPickerPrimitive.Previous asChild>
|
|
954
|
-
<TooltipIconButton tooltip="Previous">
|
|
955
|
-
<ChevronLeftIcon />
|
|
956
|
-
</TooltipIconButton>
|
|
957
|
-
</BranchPickerPrimitive.Previous>
|
|
958
|
-
<span className="aui-branch-picker-state font-medium">
|
|
959
|
-
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
960
|
-
</span>
|
|
961
|
-
<BranchPickerPrimitive.Next asChild>
|
|
962
|
-
<TooltipIconButton tooltip="Next">
|
|
963
|
-
<ChevronRightIcon />
|
|
964
|
-
</TooltipIconButton>
|
|
965
|
-
</BranchPickerPrimitive.Next>
|
|
966
|
-
</BranchPickerPrimitive.Root>
|
|
967
|
-
);
|
|
968
|
-
};
|
|
969
|
-
|
|
970
|
-
```
|
|
971
|
-
|
|
972
|
-
## app/components/assistant-ui/tool-fallback.tsx
|
|
973
|
-
|
|
974
|
-
```tsx
|
|
975
|
-
import type { ToolCallMessagePartComponent } from "@assistant-ui/react";
|
|
976
|
-
import {
|
|
977
|
-
CheckIcon,
|
|
978
|
-
ChevronDownIcon,
|
|
979
|
-
ChevronUpIcon,
|
|
980
|
-
XCircleIcon,
|
|
981
|
-
} from "lucide-react";
|
|
982
|
-
import { useState } from "react";
|
|
983
|
-
import { Button } from "@/components/ui/button";
|
|
984
|
-
import { cn } from "@/lib/utils";
|
|
985
|
-
|
|
986
|
-
export const ToolFallback: ToolCallMessagePartComponent = ({
|
|
987
|
-
toolName,
|
|
988
|
-
argsText,
|
|
989
|
-
result,
|
|
990
|
-
status,
|
|
991
|
-
}) => {
|
|
992
|
-
const [isCollapsed, setIsCollapsed] = useState(true);
|
|
993
|
-
|
|
994
|
-
const isCancelled =
|
|
995
|
-
status?.type === "incomplete" && status.reason === "cancelled";
|
|
996
|
-
const cancelledReason =
|
|
997
|
-
isCancelled && status.error
|
|
998
|
-
? typeof status.error === "string"
|
|
999
|
-
? status.error
|
|
1000
|
-
: JSON.stringify(status.error)
|
|
1001
|
-
: null;
|
|
1002
|
-
|
|
1003
|
-
return (
|
|
1004
|
-
<div
|
|
1005
|
-
className={cn(
|
|
1006
|
-
"aui-tool-fallback-root mb-4 flex w-full flex-col gap-3 rounded-lg border py-3",
|
|
1007
|
-
isCancelled && "border-muted-foreground/30 bg-muted/30",
|
|
1008
|
-
)}
|
|
1009
|
-
>
|
|
1010
|
-
<div className="aui-tool-fallback-header flex items-center gap-2 px-4">
|
|
1011
|
-
{isCancelled ? (
|
|
1012
|
-
<XCircleIcon className="aui-tool-fallback-icon size-4 text-muted-foreground" />
|
|
1013
|
-
) : (
|
|
1014
|
-
<CheckIcon className="aui-tool-fallback-icon size-4" />
|
|
1015
|
-
)}
|
|
1016
|
-
<p
|
|
1017
|
-
className={cn(
|
|
1018
|
-
"aui-tool-fallback-title grow",
|
|
1019
|
-
isCancelled && "text-muted-foreground line-through",
|
|
1020
|
-
)}
|
|
1021
|
-
>
|
|
1022
|
-
{isCancelled ? "Cancelled tool: " : "Used tool: "}
|
|
1023
|
-
<b>{toolName}</b>
|
|
1024
|
-
</p>
|
|
1025
|
-
<Button onClick={() => setIsCollapsed(!isCollapsed)}>
|
|
1026
|
-
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
1027
|
-
</Button>
|
|
1028
|
-
</div>
|
|
1029
|
-
{!isCollapsed && (
|
|
1030
|
-
<div className="aui-tool-fallback-content flex flex-col gap-2 border-t pt-2">
|
|
1031
|
-
{cancelledReason && (
|
|
1032
|
-
<div className="aui-tool-fallback-cancelled-root px-4">
|
|
1033
|
-
<p className="aui-tool-fallback-cancelled-header font-semibold text-muted-foreground">
|
|
1034
|
-
Cancelled reason:
|
|
1035
|
-
</p>
|
|
1036
|
-
<p className="aui-tool-fallback-cancelled-reason text-muted-foreground">
|
|
1037
|
-
{cancelledReason}
|
|
1038
|
-
</p>
|
|
1039
|
-
</div>
|
|
1040
|
-
)}
|
|
1041
|
-
<div
|
|
1042
|
-
className={cn(
|
|
1043
|
-
"aui-tool-fallback-args-root px-4",
|
|
1044
|
-
isCancelled && "opacity-60",
|
|
1045
|
-
)}
|
|
1046
|
-
>
|
|
1047
|
-
<pre className="aui-tool-fallback-args-value whitespace-pre-wrap">
|
|
1048
|
-
{argsText}
|
|
1049
|
-
</pre>
|
|
1050
|
-
</div>
|
|
1051
|
-
{!isCancelled && result !== undefined && (
|
|
1052
|
-
<div className="aui-tool-fallback-result-root border-t border-dashed px-4 pt-2">
|
|
1053
|
-
<p className="aui-tool-fallback-result-header font-semibold">
|
|
1054
|
-
Result:
|
|
1055
|
-
</p>
|
|
1056
|
-
<pre className="aui-tool-fallback-result-content whitespace-pre-wrap">
|
|
1057
|
-
{typeof result === "string"
|
|
1058
|
-
? result
|
|
1059
|
-
: JSON.stringify(result, null, 2)}
|
|
1060
|
-
</pre>
|
|
1061
|
-
</div>
|
|
1062
|
-
)}
|
|
1063
|
-
</div>
|
|
1064
|
-
)}
|
|
1065
|
-
</div>
|
|
1066
|
-
);
|
|
1067
|
-
};
|
|
1068
|
-
|
|
1069
|
-
```
|
|
1070
|
-
|
|
1071
|
-
## app/components/assistant-ui/tooltip-icon-button.tsx
|
|
1072
|
-
|
|
1073
|
-
```tsx
|
|
1074
|
-
"use client";
|
|
1075
|
-
|
|
1076
|
-
import { type ComponentPropsWithRef, forwardRef } from "react";
|
|
1077
|
-
import { Slottable } from "@radix-ui/react-slot";
|
|
1078
|
-
|
|
1079
|
-
import {
|
|
1080
|
-
Tooltip,
|
|
1081
|
-
TooltipContent,
|
|
1082
|
-
TooltipTrigger,
|
|
1083
|
-
} from "@/components/ui/tooltip";
|
|
1084
|
-
import { Button } from "@/components/ui/button";
|
|
1085
|
-
import { cn } from "@/lib/utils";
|
|
1086
|
-
|
|
1087
|
-
export type TooltipIconButtonProps = ComponentPropsWithRef<typeof Button> & {
|
|
1088
|
-
tooltip: string;
|
|
1089
|
-
side?: "top" | "bottom" | "left" | "right";
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1092
|
-
export const TooltipIconButton = forwardRef<
|
|
1093
|
-
HTMLButtonElement,
|
|
1094
|
-
TooltipIconButtonProps
|
|
1095
|
-
>(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
|
|
1096
|
-
return (
|
|
1097
|
-
<Tooltip>
|
|
1098
|
-
<TooltipTrigger asChild>
|
|
1099
|
-
<Button
|
|
1100
|
-
variant="ghost"
|
|
1101
|
-
size="icon"
|
|
1102
|
-
{...rest}
|
|
1103
|
-
className={cn("aui-button-icon size-6 p-1", className)}
|
|
1104
|
-
ref={ref}
|
|
1105
|
-
>
|
|
1106
|
-
<Slottable>{children}</Slottable>
|
|
1107
|
-
<span className="aui-sr-only sr-only">{tooltip}</span>
|
|
1108
|
-
</Button>
|
|
1109
|
-
</TooltipTrigger>
|
|
1110
|
-
<TooltipContent side={side}>{tooltip}</TooltipContent>
|
|
1111
|
-
</Tooltip>
|
|
1112
|
-
);
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1115
|
-
TooltipIconButton.displayName = "TooltipIconButton";
|
|
1116
|
-
|
|
1117
|
-
```
|
|
1118
|
-
|
|
1119
|
-
## app/components/ui/avatar.tsx
|
|
1120
|
-
|
|
1121
|
-
```tsx
|
|
1122
|
-
"use client";
|
|
1123
|
-
|
|
1124
|
-
import * as React from "react";
|
|
1125
|
-
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
1126
|
-
|
|
1127
|
-
import { cn } from "@/lib/utils";
|
|
1128
|
-
|
|
1129
|
-
const Avatar = React.forwardRef<
|
|
1130
|
-
React.ElementRef<typeof AvatarPrimitive.Root>,
|
|
1131
|
-
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
|
1132
|
-
>(({ className, ...props }, ref) => (
|
|
1133
|
-
<AvatarPrimitive.Root
|
|
1134
|
-
ref={ref}
|
|
1135
|
-
className={cn(
|
|
1136
|
-
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
|
1137
|
-
className,
|
|
1138
|
-
)}
|
|
1139
|
-
{...props}
|
|
1140
|
-
/>
|
|
1141
|
-
));
|
|
1142
|
-
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
1143
|
-
|
|
1144
|
-
const AvatarImage = React.forwardRef<
|
|
1145
|
-
React.ElementRef<typeof AvatarPrimitive.Image>,
|
|
1146
|
-
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
|
1147
|
-
>(({ className, ...props }, ref) => (
|
|
1148
|
-
<AvatarPrimitive.Image
|
|
1149
|
-
ref={ref}
|
|
1150
|
-
className={cn("aspect-square h-full w-full", className)}
|
|
1151
|
-
{...props}
|
|
1152
|
-
/>
|
|
1153
|
-
));
|
|
1154
|
-
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
1155
|
-
|
|
1156
|
-
const AvatarFallback = React.forwardRef<
|
|
1157
|
-
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
|
1158
|
-
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
|
1159
|
-
>(({ className, ...props }, ref) => (
|
|
1160
|
-
<AvatarPrimitive.Fallback
|
|
1161
|
-
ref={ref}
|
|
1162
|
-
className={cn(
|
|
1163
|
-
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
|
1164
|
-
className,
|
|
1165
|
-
)}
|
|
1166
|
-
{...props}
|
|
1167
|
-
/>
|
|
1168
|
-
));
|
|
1169
|
-
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
1170
|
-
|
|
1171
|
-
export { Avatar, AvatarImage, AvatarFallback };
|
|
1172
|
-
|
|
1173
|
-
```
|
|
1174
|
-
|
|
1175
|
-
## app/components/ui/button.tsx
|
|
1176
|
-
|
|
1177
|
-
```tsx
|
|
1178
|
-
import * as React from "react";
|
|
1179
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
1180
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
1181
|
-
|
|
1182
|
-
import { cn } from "@/lib/utils";
|
|
1183
|
-
|
|
1184
|
-
const buttonVariants = cva(
|
|
1185
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
1186
|
-
{
|
|
1187
|
-
variants: {
|
|
1188
|
-
variant: {
|
|
1189
|
-
default:
|
|
1190
|
-
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
1191
|
-
destructive:
|
|
1192
|
-
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
1193
|
-
outline:
|
|
1194
|
-
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
1195
|
-
secondary:
|
|
1196
|
-
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
1197
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
1198
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
1199
|
-
},
|
|
1200
|
-
size: {
|
|
1201
|
-
default: "h-9 px-4 py-2",
|
|
1202
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
1203
|
-
lg: "h-10 rounded-md px-8",
|
|
1204
|
-
icon: "h-9 w-9",
|
|
1205
|
-
},
|
|
1206
|
-
},
|
|
1207
|
-
defaultVariants: {
|
|
1208
|
-
variant: "default",
|
|
1209
|
-
size: "default",
|
|
1210
|
-
},
|
|
1211
|
-
},
|
|
1212
|
-
);
|
|
1213
|
-
|
|
1214
|
-
export interface ButtonProps
|
|
1215
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
1216
|
-
VariantProps<typeof buttonVariants> {
|
|
1217
|
-
asChild?: boolean;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
1221
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
1222
|
-
const Comp = asChild ? Slot : "button";
|
|
1223
|
-
return (
|
|
1224
|
-
<Comp
|
|
1225
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
1226
|
-
ref={ref}
|
|
1227
|
-
{...props}
|
|
1228
|
-
/>
|
|
1229
|
-
);
|
|
1230
|
-
},
|
|
1231
|
-
);
|
|
1232
|
-
Button.displayName = "Button";
|
|
1233
|
-
|
|
1234
|
-
export { Button, buttonVariants };
|
|
1235
|
-
|
|
1236
|
-
```
|
|
1237
|
-
|
|
1238
|
-
## app/components/ui/dialog.tsx
|
|
1239
|
-
|
|
1240
|
-
```tsx
|
|
1241
|
-
"use client";
|
|
1242
|
-
|
|
1243
|
-
import * as React from "react";
|
|
1244
|
-
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
1245
|
-
import { XIcon } from "lucide-react";
|
|
1246
|
-
|
|
1247
|
-
import { cn } from "@/lib/utils";
|
|
1248
|
-
|
|
1249
|
-
function Dialog({
|
|
1250
|
-
...props
|
|
1251
|
-
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
1252
|
-
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
function DialogTrigger({
|
|
1256
|
-
...props
|
|
1257
|
-
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
1258
|
-
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
function DialogPortal({
|
|
1262
|
-
...props
|
|
1263
|
-
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
1264
|
-
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
function DialogClose({
|
|
1268
|
-
...props
|
|
1269
|
-
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
1270
|
-
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
|
-
function DialogOverlay({
|
|
1274
|
-
className,
|
|
1275
|
-
...props
|
|
1276
|
-
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
1277
|
-
return (
|
|
1278
|
-
<DialogPrimitive.Overlay
|
|
1279
|
-
data-slot="dialog-overlay"
|
|
1280
|
-
className={cn(
|
|
1281
|
-
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
1282
|
-
className,
|
|
1283
|
-
)}
|
|
1284
|
-
{...props}
|
|
1285
|
-
/>
|
|
1286
|
-
);
|
|
1287
|
-
}
|
|
1288
|
-
|
|
1289
|
-
function DialogContent({
|
|
1290
|
-
className,
|
|
1291
|
-
children,
|
|
1292
|
-
...props
|
|
1293
|
-
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
|
1294
|
-
return (
|
|
1295
|
-
<DialogPortal data-slot="dialog-portal">
|
|
1296
|
-
<DialogOverlay />
|
|
1297
|
-
<DialogPrimitive.Content
|
|
1298
|
-
data-slot="dialog-content"
|
|
1299
|
-
className={cn(
|
|
1300
|
-
"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg",
|
|
1301
|
-
className,
|
|
1302
|
-
)}
|
|
1303
|
-
{...props}
|
|
1304
|
-
>
|
|
1305
|
-
{children}
|
|
1306
|
-
<DialogPrimitive.Close className="absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0">
|
|
1307
|
-
<XIcon />
|
|
1308
|
-
<span className="sr-only">Close</span>
|
|
1309
|
-
</DialogPrimitive.Close>
|
|
1310
|
-
</DialogPrimitive.Content>
|
|
1311
|
-
</DialogPortal>
|
|
1312
|
-
);
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
1316
|
-
return (
|
|
1317
|
-
<div
|
|
1318
|
-
data-slot="dialog-header"
|
|
1319
|
-
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
1320
|
-
{...props}
|
|
1321
|
-
/>
|
|
1322
|
-
);
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
1326
|
-
return (
|
|
1327
|
-
<div
|
|
1328
|
-
data-slot="dialog-footer"
|
|
1329
|
-
className={cn(
|
|
1330
|
-
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1331
|
-
className,
|
|
1332
|
-
)}
|
|
1333
|
-
{...props}
|
|
1334
|
-
/>
|
|
1335
|
-
);
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
function DialogTitle({
|
|
1339
|
-
className,
|
|
1340
|
-
...props
|
|
1341
|
-
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
1342
|
-
return (
|
|
1343
|
-
<DialogPrimitive.Title
|
|
1344
|
-
data-slot="dialog-title"
|
|
1345
|
-
className={cn("font-semibold text-lg leading-none", className)}
|
|
1346
|
-
{...props}
|
|
1347
|
-
/>
|
|
1348
|
-
);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
function DialogDescription({
|
|
1352
|
-
className,
|
|
1353
|
-
...props
|
|
1354
|
-
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
1355
|
-
return (
|
|
1356
|
-
<DialogPrimitive.Description
|
|
1357
|
-
data-slot="dialog-description"
|
|
1358
|
-
className={cn("text-muted-foreground text-sm", className)}
|
|
1359
|
-
{...props}
|
|
1360
|
-
/>
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
|
|
1364
|
-
export {
|
|
1365
|
-
Dialog,
|
|
1366
|
-
DialogClose,
|
|
1367
|
-
DialogContent,
|
|
1368
|
-
DialogDescription,
|
|
1369
|
-
DialogFooter,
|
|
1370
|
-
DialogHeader,
|
|
1371
|
-
DialogOverlay,
|
|
1372
|
-
DialogPortal,
|
|
1373
|
-
DialogTitle,
|
|
1374
|
-
DialogTrigger,
|
|
1375
|
-
};
|
|
1376
|
-
|
|
1377
|
-
```
|
|
1378
|
-
|
|
1379
|
-
## app/components/ui/tooltip.tsx
|
|
1380
|
-
|
|
1381
|
-
```tsx
|
|
1382
|
-
import * as React from "react";
|
|
1383
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
1384
|
-
|
|
1385
|
-
import { cn } from "@/lib/utils";
|
|
1386
|
-
|
|
1387
|
-
function TooltipProvider({
|
|
1388
|
-
delayDuration = 0,
|
|
1389
|
-
...props
|
|
1390
|
-
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
1391
|
-
return (
|
|
1392
|
-
<TooltipPrimitive.Provider
|
|
1393
|
-
data-slot="tooltip-provider"
|
|
1394
|
-
delayDuration={delayDuration}
|
|
1395
|
-
{...props}
|
|
1396
|
-
/>
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
function Tooltip({
|
|
1401
|
-
...props
|
|
1402
|
-
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
1403
|
-
return (
|
|
1404
|
-
<TooltipProvider>
|
|
1405
|
-
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
1406
|
-
</TooltipProvider>
|
|
1407
|
-
);
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
function TooltipTrigger({
|
|
1411
|
-
...props
|
|
1412
|
-
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
1413
|
-
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
function TooltipContent({
|
|
1417
|
-
className,
|
|
1418
|
-
sideOffset = 0,
|
|
1419
|
-
children,
|
|
1420
|
-
...props
|
|
1421
|
-
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
1422
|
-
return (
|
|
1423
|
-
<TooltipPrimitive.Portal>
|
|
1424
|
-
<TooltipPrimitive.Content
|
|
1425
|
-
data-slot="tooltip-content"
|
|
1426
|
-
sideOffset={sideOffset}
|
|
1427
|
-
className={cn(
|
|
1428
|
-
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-[--radix-tooltip-content-transform-origin] animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
|
|
1429
|
-
className,
|
|
1430
|
-
)}
|
|
1431
|
-
{...props}
|
|
1432
|
-
>
|
|
1433
|
-
{children}
|
|
1434
|
-
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-primary fill-primary" />
|
|
1435
|
-
</TooltipPrimitive.Content>
|
|
1436
|
-
</TooltipPrimitive.Portal>
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
1441
|
-
|
|
1442
|
-
```
|
|
1443
|
-
|
|
1444
145
|
## app/lib/utils.ts
|
|
1445
146
|
|
|
1446
147
|
```typescript
|
|
@@ -2037,7 +738,9 @@ export default function Home() {
|
|
|
2037
738
|
"lib": "@/lib",
|
|
2038
739
|
"hooks": "@/hooks"
|
|
2039
740
|
},
|
|
2040
|
-
"registries": {
|
|
741
|
+
"registries": {
|
|
742
|
+
"@assistant-ui": "https://r.assistant-ui.com/{name}.json"
|
|
743
|
+
}
|
|
2041
744
|
}
|
|
2042
745
|
|
|
2043
746
|
```
|
|
@@ -2046,7 +749,7 @@ export default function Home() {
|
|
|
2046
749
|
|
|
2047
750
|
```json
|
|
2048
751
|
{
|
|
2049
|
-
"name": "
|
|
752
|
+
"name": "with-react-router",
|
|
2050
753
|
"private": true,
|
|
2051
754
|
"type": "module",
|
|
2052
755
|
"scripts": {
|
|
@@ -2062,32 +765,32 @@ export default function Home() {
|
|
|
2062
765
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
2063
766
|
"@radix-ui/react-slot": "^1.2.4",
|
|
2064
767
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
2065
|
-
"@react-router/node": "7.
|
|
2066
|
-
"@react-router/serve": "7.
|
|
768
|
+
"@react-router/node": "7.13.0",
|
|
769
|
+
"@react-router/serve": "7.13.0",
|
|
2067
770
|
"class-variance-authority": "^0.7.1",
|
|
2068
771
|
"clsx": "^2.1.1",
|
|
2069
772
|
"eventsource-parser": "^3.0.6",
|
|
2070
|
-
"isbot": "^5.1.
|
|
2071
|
-
"lucide-react": "^0.
|
|
2072
|
-
"openai": "^6.
|
|
2073
|
-
"react": "^19.2.
|
|
2074
|
-
"react-dom": "^19.2.
|
|
2075
|
-
"react-router": "7.
|
|
773
|
+
"isbot": "^5.1.34",
|
|
774
|
+
"lucide-react": "^0.563.0",
|
|
775
|
+
"openai": "^6.17.0",
|
|
776
|
+
"react": "^19.2.4",
|
|
777
|
+
"react-dom": "^19.2.4",
|
|
778
|
+
"react-router": "7.13.0",
|
|
2076
779
|
"remark-gfm": "^4.0.1",
|
|
2077
780
|
"tailwind-merge": "^3.4.0",
|
|
2078
|
-
"zustand": "^5.0.
|
|
781
|
+
"zustand": "^5.0.11"
|
|
2079
782
|
},
|
|
2080
783
|
"devDependencies": {
|
|
2081
|
-
"@react-router/dev": "7.
|
|
784
|
+
"@react-router/dev": "7.13.0",
|
|
2082
785
|
"@tailwindcss/vite": "^4.1.18",
|
|
2083
|
-
"@types/node": "^25.0
|
|
2084
|
-
"@types/react": "^19.2.
|
|
786
|
+
"@types/node": "^25.2.0",
|
|
787
|
+
"@types/react": "^19.2.10",
|
|
2085
788
|
"@types/react-dom": "^19.2.3",
|
|
2086
789
|
"tailwindcss": "^4.1.18",
|
|
2087
790
|
"tw-animate-css": "^1.4.0",
|
|
2088
791
|
"typescript": "^5.9.3",
|
|
2089
792
|
"vite": "^7.3.1",
|
|
2090
|
-
"vite-tsconfig-paths": "^6.0.
|
|
793
|
+
"vite-tsconfig-paths": "^6.0.5"
|
|
2091
794
|
}
|
|
2092
795
|
}
|
|
2093
796
|
|
|
@@ -2109,7 +812,47 @@ export default {
|
|
|
2109
812
|
## README.md
|
|
2110
813
|
|
|
2111
814
|
```markdown
|
|
2112
|
-
#
|
|
815
|
+
# React Router Integration
|
|
816
|
+
|
|
817
|
+
This example demonstrates how to use assistant-ui with React Router (v7) and Vite.
|
|
818
|
+
|
|
819
|
+
## Quick Start
|
|
820
|
+
|
|
821
|
+
### Using CLI (Recommended)
|
|
822
|
+
|
|
823
|
+
```bash
|
|
824
|
+
npx assistant-ui@latest create my-app --example with-react-router
|
|
825
|
+
cd my-app
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### Environment Variables
|
|
829
|
+
|
|
830
|
+
Create `.env`:
|
|
831
|
+
|
|
832
|
+
```
|
|
833
|
+
OPENAI_API_KEY=sk-...
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Run
|
|
837
|
+
|
|
838
|
+
```bash
|
|
839
|
+
npm install
|
|
840
|
+
npm run dev
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
## Features
|
|
844
|
+
|
|
845
|
+
- React Router v7 integration
|
|
846
|
+
- Vite-based development server
|
|
847
|
+
- OpenAI streaming chat
|
|
848
|
+
- Zustand for state management
|
|
849
|
+
- Markdown message rendering
|
|
850
|
+
|
|
851
|
+
## Related Documentation
|
|
852
|
+
|
|
853
|
+
- [assistant-ui Documentation](https://www.assistant-ui.com/docs)
|
|
854
|
+
- [React Router Documentation](https://reactrouter.com/)
|
|
855
|
+
|
|
2113
856
|
```
|
|
2114
857
|
|
|
2115
858
|
## tsconfig.json
|
|
@@ -2132,7 +875,12 @@ export default {
|
|
|
2132
875
|
"rootDirs": [".", "./.react-router/types"],
|
|
2133
876
|
"baseUrl": ".",
|
|
2134
877
|
"paths": {
|
|
2135
|
-
"@/*": ["./app/*"]
|
|
878
|
+
"@/*": ["./app/*"],
|
|
879
|
+
"@/components/assistant-ui/*": [
|
|
880
|
+
"../../packages/ui/src/components/assistant-ui/*"
|
|
881
|
+
],
|
|
882
|
+
"@/components/ui/*": ["../../packages/ui/src/components/ui/*"],
|
|
883
|
+
"@assistant-ui/ui/*": ["../../packages/ui/src/*"]
|
|
2136
884
|
},
|
|
2137
885
|
"esModuleInterop": true,
|
|
2138
886
|
"verbatimModuleSyntax": true,
|