@assistant-ui/mcp-docs-server 0.1.12 → 0.1.14

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 (31) hide show
  1. package/.docs/organized/code-examples/with-ag-ui.md +1089 -0
  2. package/.docs/organized/code-examples/with-ai-sdk-v5.md +12 -21
  3. package/.docs/organized/code-examples/with-assistant-transport.md +10 -19
  4. package/.docs/organized/code-examples/with-cloud.md +7 -16
  5. package/.docs/organized/code-examples/with-external-store.md +6 -15
  6. package/.docs/organized/code-examples/with-ffmpeg.md +14 -21
  7. package/.docs/organized/code-examples/with-langgraph.md +5 -14
  8. package/.docs/organized/code-examples/with-parent-id-grouping.md +6 -15
  9. package/.docs/organized/code-examples/with-react-hook-form.md +10 -19
  10. package/.docs/raw/docs/api-reference/context-providers/AssistantRuntimeProvider.mdx +6 -1
  11. package/.docs/raw/docs/api-reference/integrations/vercel-ai-sdk.mdx +179 -70
  12. package/.docs/raw/docs/cloud/authorization.mdx +2 -2
  13. package/.docs/raw/docs/copilots/model-context.mdx +4 -5
  14. package/.docs/raw/docs/copilots/motivation.mdx +4 -4
  15. package/.docs/raw/docs/getting-started.mdx +8 -4
  16. package/.docs/raw/docs/guides/Attachments.mdx +2 -2
  17. package/.docs/raw/docs/guides/Tools.mdx +5 -5
  18. package/.docs/raw/docs/guides/context-api.mdx +5 -5
  19. package/.docs/raw/docs/migrations/v0-12.mdx +2 -2
  20. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +6 -2
  21. package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +9 -0
  22. package/.docs/raw/docs/runtimes/custom/local.mdx +77 -4
  23. package/.docs/raw/docs/runtimes/langgraph/index.mdx +7 -4
  24. package/.docs/raw/docs/runtimes/langserve.mdx +3 -8
  25. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +13 -11
  26. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +50 -31
  27. package/.docs/raw/docs/ui/PartGrouping.mdx +2 -2
  28. package/.docs/raw/docs/ui/Reasoning.mdx +174 -0
  29. package/dist/chunk-M2RKUM66.js +3 -3
  30. package/dist/chunk-NVNFQ5ZO.js +2 -2
  31. package/package.json +5 -6
@@ -0,0 +1,1089 @@
1
+ # Example: with-ag-ui
2
+
3
+ ## app/globals.css
4
+
5
+ ```css
6
+ @import "tailwindcss";
7
+ @import "tw-animate-css";
8
+
9
+ @custom-variant dark (&:is(.dark *));
10
+
11
+ @theme inline {
12
+ --color-background: var(--background);
13
+ --color-foreground: var(--foreground);
14
+ --font-sans: var(--font-geist-sans);
15
+ --font-mono: var(--font-geist-mono);
16
+ --color-sidebar-ring: var(--sidebar-ring);
17
+ --color-sidebar-border: var(--sidebar-border);
18
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
19
+ --color-sidebar-accent: var(--sidebar-accent);
20
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
21
+ --color-sidebar-primary: var(--sidebar-primary);
22
+ --color-sidebar-foreground: var(--sidebar-foreground);
23
+ --color-sidebar: var(--sidebar);
24
+ --color-chart-5: var(--chart-5);
25
+ --color-chart-4: var(--chart-4);
26
+ --color-chart-3: var(--chart-3);
27
+ --color-chart-2: var(--chart-2);
28
+ --color-chart-1: var(--chart-1);
29
+ --color-ring: var(--ring);
30
+ --color-input: var(--input);
31
+ --color-border: var(--border);
32
+ --color-destructive: var(--destructive);
33
+ --color-accent-foreground: var(--accent-foreground);
34
+ --color-accent: var(--accent);
35
+ --color-muted-foreground: var(--muted-foreground);
36
+ --color-muted: var(--muted);
37
+ --color-secondary-foreground: var(--secondary-foreground);
38
+ --color-secondary: var(--secondary);
39
+ --color-primary-foreground: var(--primary-foreground);
40
+ --color-primary: var(--primary);
41
+ --color-popover-foreground: var(--popover-foreground);
42
+ --color-popover: var(--popover);
43
+ --color-card-foreground: var(--card-foreground);
44
+ --color-card: var(--card);
45
+ --radius-sm: calc(var(--radius) - 4px);
46
+ --radius-md: calc(var(--radius) - 2px);
47
+ --radius-lg: var(--radius);
48
+ --radius-xl: calc(var(--radius) + 4px);
49
+ }
50
+
51
+ :root {
52
+ --radius: 0.625rem;
53
+ --background: oklch(1 0 0);
54
+ --foreground: oklch(0.141 0.005 285.823);
55
+ --card: oklch(1 0 0);
56
+ --card-foreground: oklch(0.141 0.005 285.823);
57
+ --popover: oklch(1 0 0);
58
+ --popover-foreground: oklch(0.141 0.005 285.823);
59
+ --primary: oklch(0.21 0.006 285.885);
60
+ --primary-foreground: oklch(0.985 0 0);
61
+ --secondary: oklch(0.967 0.001 286.375);
62
+ --secondary-foreground: oklch(0.21 0.006 285.885);
63
+ --muted: oklch(0.967 0.001 286.375);
64
+ --muted-foreground: oklch(0.552 0.016 285.938);
65
+ --accent: oklch(0.967 0.001 286.375);
66
+ --accent-foreground: oklch(0.21 0.006 285.885);
67
+ --destructive: oklch(0.577 0.245 27.325);
68
+ --border: oklch(0.92 0.004 286.32);
69
+ --input: oklch(0.92 0.004 286.32);
70
+ --ring: oklch(0.705 0.015 286.067);
71
+ --chart-1: oklch(0.646 0.222 41.116);
72
+ --chart-2: oklch(0.6 0.118 184.704);
73
+ --chart-3: oklch(0.398 0.07 227.392);
74
+ --chart-4: oklch(0.828 0.189 84.429);
75
+ --chart-5: oklch(0.769 0.188 70.08);
76
+ --sidebar: oklch(0.985 0 0);
77
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
78
+ --sidebar-primary: oklch(0.21 0.006 285.885);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.967 0.001 286.375);
81
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
82
+ --sidebar-border: oklch(0.92 0.004 286.32);
83
+ --sidebar-ring: oklch(0.705 0.015 286.067);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.141 0.005 285.823);
88
+ --foreground: oklch(0.985 0 0);
89
+ --card: oklch(0.21 0.006 285.885);
90
+ --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.21 0.006 285.885);
92
+ --popover-foreground: oklch(0.985 0 0);
93
+ --primary: oklch(0.92 0.004 286.32);
94
+ --primary-foreground: oklch(0.21 0.006 285.885);
95
+ --secondary: oklch(0.274 0.006 286.033);
96
+ --secondary-foreground: oklch(0.985 0 0);
97
+ --muted: oklch(0.274 0.006 286.033);
98
+ --muted-foreground: oklch(0.705 0.015 286.067);
99
+ --accent: oklch(0.274 0.006 286.033);
100
+ --accent-foreground: oklch(0.985 0 0);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --border: oklch(1 0 0 / 10%);
103
+ --input: oklch(1 0 0 / 15%);
104
+ --ring: oklch(0.552 0.016 285.938);
105
+ --chart-1: oklch(0.488 0.243 264.376);
106
+ --chart-2: oklch(0.696 0.17 162.48);
107
+ --chart-3: oklch(0.769 0.188 70.08);
108
+ --chart-4: oklch(0.627 0.265 303.9);
109
+ --chart-5: oklch(0.645 0.246 16.439);
110
+ --sidebar: oklch(0.21 0.006 285.885);
111
+ --sidebar-foreground: oklch(0.985 0 0);
112
+ --sidebar-primary: oklch(0.488 0.243 264.376);
113
+ --sidebar-primary-foreground: oklch(0.985 0 0);
114
+ --sidebar-accent: oklch(0.274 0.006 286.033);
115
+ --sidebar-accent-foreground: oklch(0.985 0 0);
116
+ --sidebar-border: oklch(1 0 0 / 10%);
117
+ --sidebar-ring: oklch(0.552 0.016 285.938);
118
+ }
119
+
120
+ @layer base {
121
+ * {
122
+ @apply border-border outline-ring/50;
123
+ }
124
+ body {
125
+ @apply bg-background text-foreground;
126
+ }
127
+ }
128
+
129
+ ```
130
+
131
+ ## app/layout.tsx
132
+
133
+ ```tsx
134
+ import type { Metadata } from "next";
135
+ import { MyRuntimeProvider } from "@/app/MyRuntimeProvider";
136
+
137
+ import "./globals.css";
138
+
139
+ export const metadata: Metadata = {
140
+ title: "Create Next App",
141
+ description: "Generated by create next app",
142
+ };
143
+
144
+ export default function RootLayout({
145
+ children,
146
+ }: Readonly<{
147
+ children: React.ReactNode;
148
+ }>) {
149
+ return (
150
+ <html lang="en" className="h-dvh">
151
+ <body className="h-dvh font-sans">
152
+ <MyRuntimeProvider>{children}</MyRuntimeProvider>
153
+ </body>
154
+ </html>
155
+ );
156
+ }
157
+
158
+ ```
159
+
160
+ ## app/MyRuntimeProvider.tsx
161
+
162
+ ```tsx
163
+ "use client";
164
+
165
+ import React, { useMemo } from "react";
166
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
167
+ import { HttpAgent } from "@ag-ui/client";
168
+ import { useAgUiRuntime } from "@assistant-ui/react-ag-ui";
169
+
170
+ /**
171
+ * Minimal example: instantiate AG-UI runtime and provide to Assistant UI.
172
+ */
173
+ export function MyRuntimeProvider({
174
+ children,
175
+ }: Readonly<{ children: React.ReactNode }>) {
176
+ const agentUrl =
177
+ (process.env["NEXT_PUBLIC_AGUI_AGENT_URL"] as string | undefined) ??
178
+ "http://localhost:8000/agent";
179
+
180
+ const agent = useMemo(() => {
181
+ return new HttpAgent({
182
+ url: agentUrl,
183
+ headers: {
184
+ Accept: "text/event-stream",
185
+ },
186
+ });
187
+ }, [agentUrl]);
188
+
189
+ const runtime = useAgUiRuntime({
190
+ agent,
191
+ logger: {
192
+ debug: (...a: any[]) => console.debug("[agui]", ...a),
193
+ error: (...a: any[]) => console.error("[agui]", ...a),
194
+ },
195
+ });
196
+
197
+ return (
198
+ <AssistantRuntimeProvider runtime={runtime}>
199
+ {children}
200
+ </AssistantRuntimeProvider>
201
+ );
202
+ }
203
+
204
+ ```
205
+
206
+ ## app/page.tsx
207
+
208
+ ```tsx
209
+ "use client";
210
+
211
+ import { useAssistantTool } from "@assistant-ui/react";
212
+ import { Thread } from "@/components/assistant-ui/thread";
213
+
214
+ const BrowserAlertTool = () => {
215
+ useAssistantTool<{ message: string }, { status: string }>({
216
+ toolName: "browser_alert",
217
+ description: "Display a native browser alert dialog to the user.",
218
+ parameters: {
219
+ type: "object",
220
+ properties: {
221
+ message: {
222
+ type: "string",
223
+ description: "Text to display inside the alert dialog.",
224
+ },
225
+ },
226
+ required: ["message"],
227
+ },
228
+ execute: async ({ message }) => {
229
+ alert(message);
230
+ return { status: "shown" };
231
+ },
232
+ render: ({ args, result }) => (
233
+ <div className="mt-3 w-full max-w-[var(--thread-max-width)] rounded-lg border px-4 py-3 text-sm">
234
+ <p className="text-muted-foreground font-semibold">browser_alert</p>
235
+ <p className="mt-1">
236
+ Requested alert with message:
237
+ <span className="text-foreground ml-1 font-mono">
238
+ {JSON.stringify(args.message)}
239
+ </span>
240
+ </p>
241
+ {result?.status === "shown" && (
242
+ <p className="text-foreground/70 mt-2 text-xs">
243
+ Alert displayed in this tab.
244
+ </p>
245
+ )}
246
+ </div>
247
+ ),
248
+ });
249
+
250
+ return null;
251
+ };
252
+
253
+ export default function Home() {
254
+ return (
255
+ <main className="h-dvh">
256
+ <Thread />
257
+ <BrowserAlertTool />
258
+ </main>
259
+ );
260
+ }
261
+
262
+ ```
263
+
264
+ ## components.json
265
+
266
+ ```json
267
+ {
268
+ "$schema": "https://ui.shadcn.com/schema.json",
269
+ "style": "new-york",
270
+ "rsc": true,
271
+ "tsx": true,
272
+ "tailwind": {
273
+ "config": "",
274
+ "css": "app/globals.css",
275
+ "baseColor": "zinc",
276
+ "cssVariables": true,
277
+ "prefix": ""
278
+ },
279
+ "aliases": {
280
+ "components": "@/components",
281
+ "utils": "@/lib/utils",
282
+ "ui": "@/components/ui",
283
+ "lib": "@/lib",
284
+ "hooks": "@/hooks"
285
+ },
286
+ "iconLibrary": "lucide"
287
+ }
288
+
289
+ ```
290
+
291
+ ## components/assistant-ui/markdown-text.tsx
292
+
293
+ ```tsx
294
+ "use client";
295
+
296
+ import "@assistant-ui/react-markdown/styles/dot.css";
297
+
298
+ import {
299
+ CodeHeaderProps,
300
+ MarkdownTextPrimitive,
301
+ unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
302
+ useIsMarkdownCodeBlock,
303
+ } from "@assistant-ui/react-markdown";
304
+ import remarkGfm from "remark-gfm";
305
+ import { FC, memo, useState } from "react";
306
+ import { CheckIcon, CopyIcon } from "lucide-react";
307
+
308
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
309
+ import { cn } from "@/lib/utils";
310
+
311
+ const MarkdownTextImpl = () => {
312
+ return (
313
+ <MarkdownTextPrimitive
314
+ remarkPlugins={[remarkGfm]}
315
+ className="aui-md"
316
+ components={defaultComponents}
317
+ />
318
+ );
319
+ };
320
+
321
+ export const MarkdownText = memo(MarkdownTextImpl);
322
+
323
+ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
324
+ const { isCopied, copyToClipboard } = useCopyToClipboard();
325
+ const onCopy = () => {
326
+ if (!code || isCopied) return;
327
+ copyToClipboard(code);
328
+ };
329
+
330
+ return (
331
+ <div className="flex items-center justify-between gap-4 rounded-t-lg bg-zinc-900 px-4 py-2 text-sm font-semibold text-white">
332
+ <span className="lowercase [&>span]:text-xs">{language}</span>
333
+ <TooltipIconButton tooltip="Copy" onClick={onCopy}>
334
+ {!isCopied && <CopyIcon />}
335
+ {isCopied && <CheckIcon />}
336
+ </TooltipIconButton>
337
+ </div>
338
+ );
339
+ };
340
+
341
+ const useCopyToClipboard = ({
342
+ copiedDuration = 3000,
343
+ }: {
344
+ copiedDuration?: number;
345
+ } = {}) => {
346
+ const [isCopied, setIsCopied] = useState<boolean>(false);
347
+
348
+ const copyToClipboard = (value: string) => {
349
+ if (!value) return;
350
+
351
+ navigator.clipboard.writeText(value).then(() => {
352
+ setIsCopied(true);
353
+ setTimeout(() => setIsCopied(false), copiedDuration);
354
+ });
355
+ };
356
+
357
+ return { isCopied, copyToClipboard };
358
+ };
359
+
360
+ const defaultComponents = memoizeMarkdownComponents({
361
+ h1: ({ className, ...props }) => (
362
+ <h1
363
+ className={cn(
364
+ "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
365
+ className,
366
+ )}
367
+ {...props}
368
+ />
369
+ ),
370
+ h2: ({ className, ...props }) => (
371
+ <h2
372
+ className={cn(
373
+ "mt-8 mb-4 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
374
+ className,
375
+ )}
376
+ {...props}
377
+ />
378
+ ),
379
+ h3: ({ className, ...props }) => (
380
+ <h3
381
+ className={cn(
382
+ "mt-6 mb-4 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
383
+ className,
384
+ )}
385
+ {...props}
386
+ />
387
+ ),
388
+ h4: ({ className, ...props }) => (
389
+ <h4
390
+ className={cn(
391
+ "mt-6 mb-4 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
392
+ className,
393
+ )}
394
+ {...props}
395
+ />
396
+ ),
397
+ h5: ({ className, ...props }) => (
398
+ <h5
399
+ className={cn(
400
+ "my-4 text-lg font-semibold first:mt-0 last:mb-0",
401
+ className,
402
+ )}
403
+ {...props}
404
+ />
405
+ ),
406
+ h6: ({ className, ...props }) => (
407
+ <h6
408
+ className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
409
+ {...props}
410
+ />
411
+ ),
412
+ p: ({ className, ...props }) => (
413
+ <p
414
+ className={cn("mt-5 mb-5 leading-7 first:mt-0 last:mb-0", className)}
415
+ {...props}
416
+ />
417
+ ),
418
+ a: ({ className, ...props }) => (
419
+ <a
420
+ className={cn(
421
+ "text-primary font-medium underline underline-offset-4",
422
+ className,
423
+ )}
424
+ {...props}
425
+ />
426
+ ),
427
+ blockquote: ({ className, ...props }) => (
428
+ <blockquote
429
+ className={cn("border-l-2 pl-6 italic", className)}
430
+ {...props}
431
+ />
432
+ ),
433
+ ul: ({ className, ...props }) => (
434
+ <ul
435
+ className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
436
+ {...props}
437
+ />
438
+ ),
439
+ ol: ({ className, ...props }) => (
440
+ <ol
441
+ className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
442
+ {...props}
443
+ />
444
+ ),
445
+ hr: ({ className, ...props }) => (
446
+ <hr className={cn("my-5 border-b", className)} {...props} />
447
+ ),
448
+ table: ({ className, ...props }) => (
449
+ <table
450
+ className={cn(
451
+ "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
452
+ className,
453
+ )}
454
+ {...props}
455
+ />
456
+ ),
457
+ th: ({ className, ...props }) => (
458
+ <th
459
+ className={cn(
460
+ "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",
461
+ className,
462
+ )}
463
+ {...props}
464
+ />
465
+ ),
466
+ td: ({ className, ...props }) => (
467
+ <td
468
+ className={cn(
469
+ "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
470
+ className,
471
+ )}
472
+ {...props}
473
+ />
474
+ ),
475
+ tr: ({ className, ...props }) => (
476
+ <tr
477
+ className={cn(
478
+ "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",
479
+ className,
480
+ )}
481
+ {...props}
482
+ />
483
+ ),
484
+ sup: ({ className, ...props }) => (
485
+ <sup
486
+ className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
487
+ {...props}
488
+ />
489
+ ),
490
+ pre: ({ className, ...props }) => (
491
+ <pre
492
+ className={cn(
493
+ "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
494
+ className,
495
+ )}
496
+ {...props}
497
+ />
498
+ ),
499
+ code: function Code({ className, ...props }) {
500
+ const isCodeBlock = useIsMarkdownCodeBlock();
501
+ return (
502
+ <code
503
+ className={cn(
504
+ !isCodeBlock && "bg-muted rounded border font-semibold",
505
+ className,
506
+ )}
507
+ {...props}
508
+ />
509
+ );
510
+ },
511
+ CodeHeader,
512
+ });
513
+
514
+ ```
515
+
516
+ ## components/assistant-ui/thread.tsx
517
+
518
+ ```tsx
519
+ import {
520
+ ActionBarPrimitive,
521
+ BranchPickerPrimitive,
522
+ ComposerPrimitive,
523
+ MessagePrimitive,
524
+ ThreadPrimitive,
525
+ } from "@assistant-ui/react";
526
+ import type { FC } from "react";
527
+ import {
528
+ ArrowDownIcon,
529
+ CheckIcon,
530
+ ChevronLeftIcon,
531
+ ChevronRightIcon,
532
+ CopyIcon,
533
+ PencilIcon,
534
+ RefreshCwIcon,
535
+ SendHorizontalIcon,
536
+ } from "lucide-react";
537
+ import { cn } from "@/lib/utils";
538
+
539
+ import { Button } from "@/components/ui/button";
540
+ import { MarkdownText } from "@/components/assistant-ui/markdown-text";
541
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
542
+
543
+ export const Thread: FC = () => {
544
+ return (
545
+ <ThreadPrimitive.Root
546
+ className="bg-background box-border flex h-full flex-col overflow-hidden"
547
+ style={{
548
+ ["--thread-max-width" as string]: "42rem",
549
+ }}
550
+ >
551
+ <ThreadPrimitive.Viewport className="flex h-full flex-col items-center overflow-y-scroll scroll-smooth bg-inherit px-4 pt-8">
552
+ <ThreadWelcome />
553
+
554
+ <ThreadPrimitive.Messages
555
+ components={{
556
+ UserMessage: UserMessage,
557
+ EditComposer: EditComposer,
558
+ AssistantMessage: AssistantMessage,
559
+ }}
560
+ />
561
+
562
+ <ThreadPrimitive.If empty={false}>
563
+ <div className="min-h-8 flex-grow" />
564
+ </ThreadPrimitive.If>
565
+
566
+ <div className="sticky bottom-0 mt-3 flex w-full max-w-[var(--thread-max-width)] flex-col items-center justify-end rounded-t-lg bg-inherit pb-4">
567
+ <ThreadScrollToBottom />
568
+ <Composer />
569
+ </div>
570
+ </ThreadPrimitive.Viewport>
571
+ </ThreadPrimitive.Root>
572
+ );
573
+ };
574
+
575
+ const ThreadScrollToBottom: FC = () => {
576
+ return (
577
+ <ThreadPrimitive.ScrollToBottom asChild>
578
+ <TooltipIconButton
579
+ tooltip="Scroll to bottom"
580
+ variant="outline"
581
+ className="absolute -top-8 rounded-full disabled:invisible"
582
+ >
583
+ <ArrowDownIcon />
584
+ </TooltipIconButton>
585
+ </ThreadPrimitive.ScrollToBottom>
586
+ );
587
+ };
588
+
589
+ const ThreadWelcome: FC = () => {
590
+ return (
591
+ <ThreadPrimitive.Empty>
592
+ <div className="flex w-full max-w-[var(--thread-max-width)] flex-grow flex-col">
593
+ <div className="flex w-full flex-grow flex-col items-center justify-center">
594
+ <p className="mt-4 font-medium">How can I help you today?</p>
595
+ </div>
596
+ <ThreadWelcomeSuggestions />
597
+ </div>
598
+ </ThreadPrimitive.Empty>
599
+ );
600
+ };
601
+
602
+ const ThreadWelcomeSuggestions: FC = () => {
603
+ return (
604
+ <div className="mt-3 flex w-full items-stretch justify-center gap-4">
605
+ <ThreadPrimitive.Suggestion
606
+ className="hover:bg-muted/80 flex max-w-sm grow basis-0 flex-col items-center justify-center rounded-lg border p-3 transition-colors ease-in"
607
+ prompt="What is the weather in Tokyo?"
608
+ method="replace"
609
+ autoSend
610
+ >
611
+ <span className="line-clamp-2 text-sm font-semibold text-ellipsis">
612
+ What is the weather in Tokyo?
613
+ </span>
614
+ </ThreadPrimitive.Suggestion>
615
+ <ThreadPrimitive.Suggestion
616
+ className="hover:bg-muted/80 flex max-w-sm grow basis-0 flex-col items-center justify-center rounded-lg border p-3 transition-colors ease-in"
617
+ prompt="What is assistant-ui?"
618
+ method="replace"
619
+ autoSend
620
+ >
621
+ <span className="line-clamp-2 text-sm font-semibold text-ellipsis">
622
+ What is assistant-ui?
623
+ </span>
624
+ </ThreadPrimitive.Suggestion>
625
+ </div>
626
+ );
627
+ };
628
+
629
+ const Composer: FC = () => {
630
+ return (
631
+ <ComposerPrimitive.Root className="focus-within:border-ring/20 flex w-full flex-wrap items-end rounded-lg border bg-inherit px-2.5 shadow-sm transition-colors ease-in">
632
+ <ComposerPrimitive.Input
633
+ rows={1}
634
+ autoFocus
635
+ placeholder="Write a message..."
636
+ className="placeholder:text-muted-foreground max-h-40 flex-grow resize-none border-none bg-transparent px-2 py-4 text-sm outline-none focus:ring-0 disabled:cursor-not-allowed"
637
+ />
638
+ <ComposerAction />
639
+ </ComposerPrimitive.Root>
640
+ );
641
+ };
642
+
643
+ const ComposerAction: FC = () => {
644
+ return (
645
+ <>
646
+ <ThreadPrimitive.If running={false}>
647
+ <ComposerPrimitive.Send asChild>
648
+ <TooltipIconButton
649
+ tooltip="Send"
650
+ variant="default"
651
+ className="my-2.5 size-8 p-2 transition-opacity ease-in"
652
+ >
653
+ <SendHorizontalIcon />
654
+ </TooltipIconButton>
655
+ </ComposerPrimitive.Send>
656
+ </ThreadPrimitive.If>
657
+ <ThreadPrimitive.If running>
658
+ <ComposerPrimitive.Cancel asChild>
659
+ <TooltipIconButton
660
+ tooltip="Cancel"
661
+ variant="default"
662
+ className="my-2.5 size-8 p-2 transition-opacity ease-in"
663
+ >
664
+ <CircleStopIcon />
665
+ </TooltipIconButton>
666
+ </ComposerPrimitive.Cancel>
667
+ </ThreadPrimitive.If>
668
+ </>
669
+ );
670
+ };
671
+
672
+ const UserMessage: FC = () => {
673
+ return (
674
+ <MessagePrimitive.Root className="grid w-full max-w-[var(--thread-max-width)] auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] gap-y-2 py-4 [&:where(>*)]:col-start-2">
675
+ <UserActionBar />
676
+
677
+ <div className="bg-muted text-foreground col-start-2 row-start-2 max-w-[calc(var(--thread-max-width)*0.8)] rounded-3xl px-5 py-2.5 break-words">
678
+ <MessagePrimitive.Parts />
679
+ </div>
680
+
681
+ <BranchPicker className="col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
682
+ </MessagePrimitive.Root>
683
+ );
684
+ };
685
+
686
+ const UserActionBar: FC = () => {
687
+ return (
688
+ <ActionBarPrimitive.Root
689
+ hideWhenRunning
690
+ autohide="not-last"
691
+ className="col-start-1 row-start-2 mt-2.5 mr-3 flex flex-col items-end"
692
+ >
693
+ <ActionBarPrimitive.Edit asChild>
694
+ <TooltipIconButton tooltip="Edit">
695
+ <PencilIcon />
696
+ </TooltipIconButton>
697
+ </ActionBarPrimitive.Edit>
698
+ </ActionBarPrimitive.Root>
699
+ );
700
+ };
701
+
702
+ const EditComposer: FC = () => {
703
+ return (
704
+ <ComposerPrimitive.Root className="bg-muted my-4 flex w-full max-w-[var(--thread-max-width)] flex-col gap-2 rounded-xl">
705
+ <ComposerPrimitive.Input className="text-foreground flex h-8 w-full resize-none bg-transparent p-4 pb-0 outline-none" />
706
+
707
+ <div className="mx-3 mb-3 flex items-center justify-center gap-2 self-end">
708
+ <ComposerPrimitive.Cancel asChild>
709
+ <Button variant="ghost">Cancel</Button>
710
+ </ComposerPrimitive.Cancel>
711
+ <ComposerPrimitive.Send asChild>
712
+ <Button>Send</Button>
713
+ </ComposerPrimitive.Send>
714
+ </div>
715
+ </ComposerPrimitive.Root>
716
+ );
717
+ };
718
+
719
+ const AssistantMessage: FC = () => {
720
+ return (
721
+ <MessagePrimitive.Root className="relative grid w-full max-w-[var(--thread-max-width)] grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] py-4">
722
+ <div className="text-foreground col-span-2 col-start-2 row-start-1 my-1.5 max-w-[calc(var(--thread-max-width)*0.8)] leading-7 break-words">
723
+ <MessagePrimitive.Parts components={{ Text: MarkdownText }} />
724
+ </div>
725
+
726
+ <AssistantActionBar />
727
+
728
+ <BranchPicker className="col-start-2 row-start-2 mr-2 -ml-2" />
729
+ </MessagePrimitive.Root>
730
+ );
731
+ };
732
+
733
+ const AssistantActionBar: FC = () => {
734
+ return (
735
+ <ActionBarPrimitive.Root
736
+ hideWhenRunning
737
+ autohide="not-last"
738
+ autohideFloat="single-branch"
739
+ className="text-muted-foreground data-[floating]:bg-background col-start-3 row-start-2 -ml-1 flex gap-1 data-[floating]:absolute data-[floating]:rounded-md data-[floating]:border data-[floating]:p-1 data-[floating]:shadow-sm"
740
+ >
741
+ <ActionBarPrimitive.Copy asChild>
742
+ <TooltipIconButton tooltip="Copy">
743
+ <MessagePrimitive.If copied>
744
+ <CheckIcon />
745
+ </MessagePrimitive.If>
746
+ <MessagePrimitive.If copied={false}>
747
+ <CopyIcon />
748
+ </MessagePrimitive.If>
749
+ </TooltipIconButton>
750
+ </ActionBarPrimitive.Copy>
751
+ <ActionBarPrimitive.Reload asChild>
752
+ <TooltipIconButton tooltip="Refresh">
753
+ <RefreshCwIcon />
754
+ </TooltipIconButton>
755
+ </ActionBarPrimitive.Reload>
756
+ </ActionBarPrimitive.Root>
757
+ );
758
+ };
759
+
760
+ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
761
+ className,
762
+ ...rest
763
+ }) => {
764
+ return (
765
+ <BranchPickerPrimitive.Root
766
+ hideWhenSingleBranch
767
+ className={cn(
768
+ "text-muted-foreground inline-flex items-center text-xs",
769
+ className,
770
+ )}
771
+ {...rest}
772
+ >
773
+ <BranchPickerPrimitive.Previous asChild>
774
+ <TooltipIconButton tooltip="Previous">
775
+ <ChevronLeftIcon />
776
+ </TooltipIconButton>
777
+ </BranchPickerPrimitive.Previous>
778
+ <span className="font-medium">
779
+ <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
780
+ </span>
781
+ <BranchPickerPrimitive.Next asChild>
782
+ <TooltipIconButton tooltip="Next">
783
+ <ChevronRightIcon />
784
+ </TooltipIconButton>
785
+ </BranchPickerPrimitive.Next>
786
+ </BranchPickerPrimitive.Root>
787
+ );
788
+ };
789
+
790
+ const CircleStopIcon = () => {
791
+ return (
792
+ <svg
793
+ xmlns="http://www.w3.org/2000/svg"
794
+ viewBox="0 0 16 16"
795
+ fill="currentColor"
796
+ width="16"
797
+ height="16"
798
+ >
799
+ <rect width="10" height="10" x="3" y="3" rx="2" />
800
+ </svg>
801
+ );
802
+ };
803
+
804
+ ```
805
+
806
+ ## components/assistant-ui/tooltip-icon-button.tsx
807
+
808
+ ```tsx
809
+ "use client";
810
+
811
+ import { ComponentPropsWithoutRef, forwardRef } from "react";
812
+ import { Slottable } from "@radix-ui/react-slot";
813
+
814
+ import {
815
+ Tooltip,
816
+ TooltipContent,
817
+ TooltipTrigger,
818
+ } from "@/components/ui/tooltip";
819
+ import { Button } from "@/components/ui/button";
820
+ import { cn } from "@/lib/utils";
821
+
822
+ export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
823
+ tooltip: string;
824
+ side?: "top" | "bottom" | "left" | "right";
825
+ };
826
+
827
+ export const TooltipIconButton = forwardRef<
828
+ HTMLButtonElement,
829
+ TooltipIconButtonProps
830
+ >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
831
+ return (
832
+ <Tooltip>
833
+ <TooltipTrigger asChild>
834
+ <Button
835
+ variant="ghost"
836
+ size="icon"
837
+ {...rest}
838
+ className={cn("size-6 p-1", className)}
839
+ ref={ref}
840
+ >
841
+ <Slottable>{children}</Slottable>
842
+ <span className="sr-only">{tooltip}</span>
843
+ </Button>
844
+ </TooltipTrigger>
845
+ <TooltipContent side={side}>{tooltip}</TooltipContent>
846
+ </Tooltip>
847
+ );
848
+ });
849
+
850
+ TooltipIconButton.displayName = "TooltipIconButton";
851
+
852
+ ```
853
+
854
+ ## components/ui/button.tsx
855
+
856
+ ```tsx
857
+ "use client";
858
+
859
+ import * as React from "react";
860
+ import { Slot } from "@radix-ui/react-slot";
861
+ import { cva, type VariantProps } from "class-variance-authority";
862
+
863
+ import { cn } from "@/lib/utils";
864
+
865
+ const buttonVariants = cva(
866
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium 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",
867
+ {
868
+ variants: {
869
+ variant: {
870
+ default:
871
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
872
+ destructive:
873
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
874
+ outline:
875
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
876
+ secondary:
877
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
878
+ ghost: "hover:bg-accent hover:text-accent-foreground",
879
+ link: "text-primary underline-offset-4 hover:underline",
880
+ },
881
+ size: {
882
+ default: "h-9 px-4 py-2",
883
+ sm: "h-8 rounded-md px-3 text-xs",
884
+ lg: "h-10 rounded-md px-8",
885
+ icon: "h-9 w-9",
886
+ },
887
+ },
888
+ defaultVariants: {
889
+ variant: "default",
890
+ size: "default",
891
+ },
892
+ },
893
+ );
894
+
895
+ export interface ButtonProps
896
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
897
+ VariantProps<typeof buttonVariants> {
898
+ asChild?: boolean;
899
+ }
900
+
901
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
902
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
903
+ const Comp = asChild ? Slot : "button";
904
+ return (
905
+ <Comp
906
+ className={cn(buttonVariants({ variant, size, className }))}
907
+ ref={ref}
908
+ {...props}
909
+ />
910
+ );
911
+ },
912
+ );
913
+ Button.displayName = "Button";
914
+
915
+ export { Button, buttonVariants };
916
+
917
+ ```
918
+
919
+ ## components/ui/tooltip.tsx
920
+
921
+ ```tsx
922
+ "use client";
923
+
924
+ import * as React from "react";
925
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
926
+
927
+ import { cn } from "@/lib/utils";
928
+
929
+ function TooltipProvider({
930
+ delayDuration = 0,
931
+ ...props
932
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
933
+ return (
934
+ <TooltipPrimitive.Provider
935
+ data-slot="tooltip-provider"
936
+ delayDuration={delayDuration}
937
+ {...props}
938
+ />
939
+ );
940
+ }
941
+
942
+ function Tooltip({
943
+ ...props
944
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
945
+ return (
946
+ <TooltipProvider>
947
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
948
+ </TooltipProvider>
949
+ );
950
+ }
951
+
952
+ function TooltipTrigger({
953
+ ...props
954
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
955
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
956
+ }
957
+
958
+ function TooltipContent({
959
+ className,
960
+ sideOffset = 0,
961
+ children,
962
+ ...props
963
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
964
+ return (
965
+ <TooltipPrimitive.Portal>
966
+ <TooltipPrimitive.Content
967
+ data-slot="tooltip-content"
968
+ sideOffset={sideOffset}
969
+ className={cn(
970
+ "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out 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] rounded-md px-3 py-1.5 text-xs text-balance",
971
+ className,
972
+ )}
973
+ {...props}
974
+ >
975
+ {children}
976
+ <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
977
+ </TooltipPrimitive.Content>
978
+ </TooltipPrimitive.Portal>
979
+ );
980
+ }
981
+
982
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
983
+
984
+ ```
985
+
986
+ ## lib/utils.ts
987
+
988
+ ```typescript
989
+ import { clsx, type ClassValue } from "clsx";
990
+ import { twMerge } from "tailwind-merge";
991
+
992
+ export function cn(...inputs: ClassValue[]) {
993
+ return twMerge(clsx(inputs));
994
+ }
995
+
996
+ ```
997
+
998
+ ## next.config.ts
999
+
1000
+ ```typescript
1001
+ import type { NextConfig } from "next";
1002
+
1003
+ const nextConfig: NextConfig = {
1004
+ /* config options here */
1005
+ };
1006
+
1007
+ export default nextConfig;
1008
+
1009
+ ```
1010
+
1011
+ ## package.json
1012
+
1013
+ ```json
1014
+ {
1015
+ "name": "with-ag-ui",
1016
+ "version": "0.1.0",
1017
+ "private": true,
1018
+ "scripts": {
1019
+ "dev": "next dev --turbo",
1020
+ "build": "next build",
1021
+ "start": "next start",
1022
+ "lint": "eslint ."
1023
+ },
1024
+ "dependencies": {
1025
+ "@ai-sdk/openai": "^2.0.68",
1026
+ "@assistant-ui/react": "workspace:*",
1027
+ "@assistant-ui/react-markdown": "workspace:*",
1028
+ "@assistant-ui/react-ag-ui": "workspace:*",
1029
+ "@ag-ui/client": "^0.0.41",
1030
+ "@radix-ui/react-slot": "^1.2.4",
1031
+ "@radix-ui/react-tooltip": "^1.2.8",
1032
+ "class-variance-authority": "^0.7.1",
1033
+ "clsx": "^2.1.1",
1034
+ "lucide-react": "^0.554.0",
1035
+ "next": "16.0.3",
1036
+ "react": "19.2.0",
1037
+ "react-dom": "19.2.0",
1038
+ "remark-gfm": "^4.0.1",
1039
+ "tailwind-merge": "^3.4.0",
1040
+ "tw-animate-css": "^1.4.0"
1041
+ },
1042
+ "devDependencies": {
1043
+ "@assistant-ui/x-buildutils": "workspace:*",
1044
+ "@types/node": "^24",
1045
+ "@types/react": "^19",
1046
+ "@types/react-dom": "^19",
1047
+ "eslint": "^9",
1048
+ "eslint-config-next": "16.0.3",
1049
+ "postcss": "^8",
1050
+ "tailwindcss": "^4.1.17",
1051
+ "typescript": "^5"
1052
+ }
1053
+ }
1054
+
1055
+ ```
1056
+
1057
+ ## tsconfig.json
1058
+
1059
+ ```json
1060
+ {
1061
+ "extends": "@assistant-ui/x-buildutils/ts/base",
1062
+ "compilerOptions": {
1063
+ "target": "ES6",
1064
+ "module": "ESNext",
1065
+ "incremental": true,
1066
+ "plugins": [
1067
+ {
1068
+ "name": "next"
1069
+ }
1070
+ ],
1071
+ "allowJs": true,
1072
+ "strictNullChecks": true,
1073
+ "jsx": "preserve",
1074
+ "paths": {
1075
+ "@/*": ["./*"],
1076
+ "@assistant-ui/react-ag-ui": ["../../packages/react-ag-ui/src"],
1077
+ "@assistant-ui/*": ["../../packages/*/src"],
1078
+ "@assistant-ui/react/*": ["../../packages/react/src/*"],
1079
+ "@assistant-ui/tap/*": ["../../packages/tap/src/*"],
1080
+ "assistant-stream": ["../../packages/assistant-stream/src"],
1081
+ "assistant-stream/*": ["../../packages/assistant-stream/src/*"]
1082
+ }
1083
+ },
1084
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
1085
+ "exclude": ["node_modules"]
1086
+ }
1087
+
1088
+ ```
1089
+