@assistant-ui/mcp-docs-server 0.1.1

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 (109) hide show
  1. package/.docs/organized/code-examples/local-ollama.md +1135 -0
  2. package/.docs/organized/code-examples/search-agent-for-e-commerce.md +1721 -0
  3. package/.docs/organized/code-examples/with-ai-sdk.md +1081 -0
  4. package/.docs/organized/code-examples/with-cloud.md +1164 -0
  5. package/.docs/organized/code-examples/with-external-store.md +1064 -0
  6. package/.docs/organized/code-examples/with-ffmpeg.md +1305 -0
  7. package/.docs/organized/code-examples/with-langgraph.md +1819 -0
  8. package/.docs/organized/code-examples/with-openai-assistants.md +1175 -0
  9. package/.docs/organized/code-examples/with-react-hook-form.md +1727 -0
  10. package/.docs/organized/code-examples/with-vercel-ai-rsc.md +1157 -0
  11. package/.docs/raw/blog/2024-07-29-hello/index.mdx +65 -0
  12. package/.docs/raw/blog/2024-09-11/index.mdx +10 -0
  13. package/.docs/raw/blog/2024-12-15/index.mdx +10 -0
  14. package/.docs/raw/blog/2025-01-31-changelog/index.mdx +129 -0
  15. package/.docs/raw/docs/about-assistantui.mdx +44 -0
  16. package/.docs/raw/docs/api-reference/context-providers/AssistantRuntimeProvider.mdx +30 -0
  17. package/.docs/raw/docs/api-reference/context-providers/TextContentPartProvider.mdx +26 -0
  18. package/.docs/raw/docs/api-reference/integrations/react-hook-form.mdx +103 -0
  19. package/.docs/raw/docs/api-reference/integrations/vercel-ai-sdk.mdx +145 -0
  20. package/.docs/raw/docs/api-reference/overview.mdx +583 -0
  21. package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +264 -0
  22. package/.docs/raw/docs/api-reference/primitives/AssistantModal.mdx +129 -0
  23. package/.docs/raw/docs/api-reference/primitives/Attachment.mdx +96 -0
  24. package/.docs/raw/docs/api-reference/primitives/BranchPicker.mdx +87 -0
  25. package/.docs/raw/docs/api-reference/primitives/Composer.mdx +204 -0
  26. package/.docs/raw/docs/api-reference/primitives/ContentPart.mdx +173 -0
  27. package/.docs/raw/docs/api-reference/primitives/Error.mdx +70 -0
  28. package/.docs/raw/docs/api-reference/primitives/Message.mdx +181 -0
  29. package/.docs/raw/docs/api-reference/primitives/Thread.mdx +197 -0
  30. package/.docs/raw/docs/api-reference/primitives/composition.mdx +21 -0
  31. package/.docs/raw/docs/api-reference/runtimes/AssistantRuntime.mdx +33 -0
  32. package/.docs/raw/docs/api-reference/runtimes/AttachmentRuntime.mdx +46 -0
  33. package/.docs/raw/docs/api-reference/runtimes/ComposerRuntime.mdx +69 -0
  34. package/.docs/raw/docs/api-reference/runtimes/ContentPartRuntime.mdx +22 -0
  35. package/.docs/raw/docs/api-reference/runtimes/MessageRuntime.mdx +49 -0
  36. package/.docs/raw/docs/api-reference/runtimes/ThreadListItemRuntime.mdx +32 -0
  37. package/.docs/raw/docs/api-reference/runtimes/ThreadListRuntime.mdx +31 -0
  38. package/.docs/raw/docs/api-reference/runtimes/ThreadRuntime.mdx +48 -0
  39. package/.docs/raw/docs/architecture.mdx +92 -0
  40. package/.docs/raw/docs/cloud/authorization.mdx +152 -0
  41. package/.docs/raw/docs/cloud/overview.mdx +55 -0
  42. package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +54 -0
  43. package/.docs/raw/docs/cloud/persistence/langgraph.mdx +123 -0
  44. package/.docs/raw/docs/concepts/architecture.mdx +19 -0
  45. package/.docs/raw/docs/concepts/runtime-layer.mdx +163 -0
  46. package/.docs/raw/docs/concepts/why.mdx +9 -0
  47. package/.docs/raw/docs/copilots/make-assistant-readable.mdx +71 -0
  48. package/.docs/raw/docs/copilots/make-assistant-tool-ui.mdx +76 -0
  49. package/.docs/raw/docs/copilots/make-assistant-tool.mdx +117 -0
  50. package/.docs/raw/docs/copilots/model-context.mdx +135 -0
  51. package/.docs/raw/docs/copilots/motivation.mdx +191 -0
  52. package/.docs/raw/docs/copilots/use-assistant-instructions.mdx +62 -0
  53. package/.docs/raw/docs/getting-started.mdx +1133 -0
  54. package/.docs/raw/docs/guides/Attachments.mdx +640 -0
  55. package/.docs/raw/docs/guides/Branching.mdx +59 -0
  56. package/.docs/raw/docs/guides/Editing.mdx +56 -0
  57. package/.docs/raw/docs/guides/Speech.mdx +43 -0
  58. package/.docs/raw/docs/guides/ToolUI.mdx +663 -0
  59. package/.docs/raw/docs/guides/Tools.mdx +496 -0
  60. package/.docs/raw/docs/index.mdx +7 -0
  61. package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +85 -0
  62. package/.docs/raw/docs/legacy/styled/Decomposition.mdx +633 -0
  63. package/.docs/raw/docs/legacy/styled/Markdown.mdx +86 -0
  64. package/.docs/raw/docs/legacy/styled/Scrollbar.mdx +71 -0
  65. package/.docs/raw/docs/legacy/styled/Thread.mdx +84 -0
  66. package/.docs/raw/docs/legacy/styled/ThreadWidth.mdx +21 -0
  67. package/.docs/raw/docs/mcp-docs-server.mdx +324 -0
  68. package/.docs/raw/docs/migrations/deprecation-policy.mdx +41 -0
  69. package/.docs/raw/docs/migrations/v0-7.mdx +188 -0
  70. package/.docs/raw/docs/migrations/v0-8.mdx +160 -0
  71. package/.docs/raw/docs/migrations/v0-9.mdx +75 -0
  72. package/.docs/raw/docs/react-compatibility.mdx +208 -0
  73. package/.docs/raw/docs/runtimes/ai-sdk/rsc.mdx +226 -0
  74. package/.docs/raw/docs/runtimes/ai-sdk/use-assistant-hook.mdx +195 -0
  75. package/.docs/raw/docs/runtimes/ai-sdk/use-chat-hook.mdx +138 -0
  76. package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +136 -0
  77. package/.docs/raw/docs/runtimes/custom/external-store.mdx +1624 -0
  78. package/.docs/raw/docs/runtimes/custom/local.mdx +1185 -0
  79. package/.docs/raw/docs/runtimes/helicone.mdx +60 -0
  80. package/.docs/raw/docs/runtimes/langgraph/index.mdx +320 -0
  81. package/.docs/raw/docs/runtimes/langgraph/tutorial/index.mdx +11 -0
  82. package/.docs/raw/docs/runtimes/langgraph/tutorial/introduction.mdx +28 -0
  83. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-1.mdx +120 -0
  84. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +336 -0
  85. package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +385 -0
  86. package/.docs/raw/docs/runtimes/langserve.mdx +126 -0
  87. package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +218 -0
  88. package/.docs/raw/docs/runtimes/mastra/overview.mdx +17 -0
  89. package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +196 -0
  90. package/.docs/raw/docs/runtimes/pick-a-runtime.mdx +222 -0
  91. package/.docs/raw/docs/ui/AssistantModal.mdx +46 -0
  92. package/.docs/raw/docs/ui/AssistantSidebar.mdx +42 -0
  93. package/.docs/raw/docs/ui/Attachment.mdx +82 -0
  94. package/.docs/raw/docs/ui/Markdown.mdx +72 -0
  95. package/.docs/raw/docs/ui/Mermaid.mdx +79 -0
  96. package/.docs/raw/docs/ui/Scrollbar.mdx +59 -0
  97. package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +253 -0
  98. package/.docs/raw/docs/ui/Thread.mdx +47 -0
  99. package/.docs/raw/docs/ui/ThreadList.mdx +49 -0
  100. package/.docs/raw/docs/ui/ToolFallback.mdx +64 -0
  101. package/.docs/raw/docs/ui/primitives/Thread.mdx +197 -0
  102. package/LICENSE +21 -0
  103. package/README.md +128 -0
  104. package/dist/chunk-C7O7EFKU.js +38 -0
  105. package/dist/chunk-CZCDQ3YH.js +420 -0
  106. package/dist/index.js +1 -0
  107. package/dist/prepare-docs/prepare.js +199 -0
  108. package/dist/stdio.js +8 -0
  109. package/package.json +43 -0
@@ -0,0 +1,1727 @@
1
+ # Example: with-react-hook-form
2
+
3
+ ## app/api/chat/route.ts
4
+
5
+ ```typescript
6
+ import { openai } from "@ai-sdk/openai";
7
+ import { frontendTools } from "@assistant-ui/react-ai-sdk";
8
+ import { streamText } from "ai";
9
+
10
+ export const runtime = "edge";
11
+ export const maxDuration = 30;
12
+
13
+ export async function POST(req: Request) {
14
+ const { messages, system, tools } = await req.json();
15
+
16
+ const result = streamText({
17
+ model: openai("gpt-4o"),
18
+ messages,
19
+ toolCallStreaming: true,
20
+ system,
21
+ tools: {
22
+ ...frontendTools(tools),
23
+ },
24
+ });
25
+
26
+ return result.toDataStreamResponse();
27
+ }
28
+
29
+ ```
30
+
31
+ ## app/globals.css
32
+
33
+ ```css
34
+ @import "tailwindcss";
35
+ @import "tw-animate-css";
36
+
37
+ @custom-variant dark (&:is(.dark *));
38
+
39
+ @theme inline {
40
+ --color-background: var(--background);
41
+ --color-foreground: var(--foreground);
42
+ --font-sans: var(--font-geist-sans);
43
+ --font-mono: var(--font-geist-mono);
44
+ --color-sidebar-ring: var(--sidebar-ring);
45
+ --color-sidebar-border: var(--sidebar-border);
46
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
47
+ --color-sidebar-accent: var(--sidebar-accent);
48
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
49
+ --color-sidebar-primary: var(--sidebar-primary);
50
+ --color-sidebar-foreground: var(--sidebar-foreground);
51
+ --color-sidebar: var(--sidebar);
52
+ --color-chart-5: var(--chart-5);
53
+ --color-chart-4: var(--chart-4);
54
+ --color-chart-3: var(--chart-3);
55
+ --color-chart-2: var(--chart-2);
56
+ --color-chart-1: var(--chart-1);
57
+ --color-ring: var(--ring);
58
+ --color-input: var(--input);
59
+ --color-border: var(--border);
60
+ --color-destructive: var(--destructive);
61
+ --color-accent-foreground: var(--accent-foreground);
62
+ --color-accent: var(--accent);
63
+ --color-muted-foreground: var(--muted-foreground);
64
+ --color-muted: var(--muted);
65
+ --color-secondary-foreground: var(--secondary-foreground);
66
+ --color-secondary: var(--secondary);
67
+ --color-primary-foreground: var(--primary-foreground);
68
+ --color-primary: var(--primary);
69
+ --color-popover-foreground: var(--popover-foreground);
70
+ --color-popover: var(--popover);
71
+ --color-card-foreground: var(--card-foreground);
72
+ --color-card: var(--card);
73
+ --radius-sm: calc(var(--radius) - 4px);
74
+ --radius-md: calc(var(--radius) - 2px);
75
+ --radius-lg: var(--radius);
76
+ --radius-xl: calc(var(--radius) + 4px);
77
+ }
78
+
79
+ :root {
80
+ --radius: 0.625rem;
81
+ --background: oklch(1 0 0);
82
+ --foreground: oklch(0.141 0.005 285.823);
83
+ --card: oklch(1 0 0);
84
+ --card-foreground: oklch(0.141 0.005 285.823);
85
+ --popover: oklch(1 0 0);
86
+ --popover-foreground: oklch(0.141 0.005 285.823);
87
+ --primary: oklch(0.21 0.006 285.885);
88
+ --primary-foreground: oklch(0.985 0 0);
89
+ --secondary: oklch(0.967 0.001 286.375);
90
+ --secondary-foreground: oklch(0.21 0.006 285.885);
91
+ --muted: oklch(0.967 0.001 286.375);
92
+ --muted-foreground: oklch(0.552 0.016 285.938);
93
+ --accent: oklch(0.967 0.001 286.375);
94
+ --accent-foreground: oklch(0.21 0.006 285.885);
95
+ --destructive: oklch(0.577 0.245 27.325);
96
+ --border: oklch(0.92 0.004 286.32);
97
+ --input: oklch(0.92 0.004 286.32);
98
+ --ring: oklch(0.705 0.015 286.067);
99
+ --chart-1: oklch(0.646 0.222 41.116);
100
+ --chart-2: oklch(0.6 0.118 184.704);
101
+ --chart-3: oklch(0.398 0.07 227.392);
102
+ --chart-4: oklch(0.828 0.189 84.429);
103
+ --chart-5: oklch(0.769 0.188 70.08);
104
+ --sidebar: oklch(0.985 0 0);
105
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
106
+ --sidebar-primary: oklch(0.21 0.006 285.885);
107
+ --sidebar-primary-foreground: oklch(0.985 0 0);
108
+ --sidebar-accent: oklch(0.967 0.001 286.375);
109
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
110
+ --sidebar-border: oklch(0.92 0.004 286.32);
111
+ --sidebar-ring: oklch(0.705 0.015 286.067);
112
+ }
113
+
114
+ .dark {
115
+ --background: oklch(0.141 0.005 285.823);
116
+ --foreground: oklch(0.985 0 0);
117
+ --card: oklch(0.21 0.006 285.885);
118
+ --card-foreground: oklch(0.985 0 0);
119
+ --popover: oklch(0.21 0.006 285.885);
120
+ --popover-foreground: oklch(0.985 0 0);
121
+ --primary: oklch(0.92 0.004 286.32);
122
+ --primary-foreground: oklch(0.21 0.006 285.885);
123
+ --secondary: oklch(0.274 0.006 286.033);
124
+ --secondary-foreground: oklch(0.985 0 0);
125
+ --muted: oklch(0.274 0.006 286.033);
126
+ --muted-foreground: oklch(0.705 0.015 286.067);
127
+ --accent: oklch(0.274 0.006 286.033);
128
+ --accent-foreground: oklch(0.985 0 0);
129
+ --destructive: oklch(0.704 0.191 22.216);
130
+ --border: oklch(1 0 0 / 10%);
131
+ --input: oklch(1 0 0 / 15%);
132
+ --ring: oklch(0.552 0.016 285.938);
133
+ --chart-1: oklch(0.488 0.243 264.376);
134
+ --chart-2: oklch(0.696 0.17 162.48);
135
+ --chart-3: oklch(0.769 0.188 70.08);
136
+ --chart-4: oklch(0.627 0.265 303.9);
137
+ --chart-5: oklch(0.645 0.246 16.439);
138
+ --sidebar: oklch(0.21 0.006 285.885);
139
+ --sidebar-foreground: oklch(0.985 0 0);
140
+ --sidebar-primary: oklch(0.488 0.243 264.376);
141
+ --sidebar-primary-foreground: oklch(0.985 0 0);
142
+ --sidebar-accent: oklch(0.274 0.006 286.033);
143
+ --sidebar-accent-foreground: oklch(0.985 0 0);
144
+ --sidebar-border: oklch(1 0 0 / 10%);
145
+ --sidebar-ring: oklch(0.552 0.016 285.938);
146
+ }
147
+
148
+ @layer base {
149
+ * {
150
+ @apply border-border outline-ring/50;
151
+ }
152
+ body {
153
+ @apply bg-background text-foreground;
154
+ }
155
+ }
156
+
157
+ ```
158
+
159
+ ## app/layout.tsx
160
+
161
+ ```tsx
162
+ import "./globals.css";
163
+
164
+ import { cn } from "@/lib/utils";
165
+ import { Montserrat } from "next/font/google";
166
+ import { MyRuntimeProvider } from "./MyRuntimeProvider";
167
+
168
+ const montserrat = Montserrat({ subsets: ["latin"] });
169
+
170
+ export default function RootLayout({
171
+ children,
172
+ }: Readonly<{
173
+ children: React.ReactNode;
174
+ }>) {
175
+ return (
176
+ <MyRuntimeProvider>
177
+ <html lang="en">
178
+ <body className={cn(montserrat.className, "h-dvh")}>{children}</body>
179
+ </html>
180
+ </MyRuntimeProvider>
181
+ );
182
+ }
183
+
184
+ ```
185
+
186
+ ## app/MyRuntimeProvider.tsx
187
+
188
+ ```tsx
189
+ "use client";
190
+
191
+ import { AssistantRuntimeProvider } from "@assistant-ui/react";
192
+ import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
193
+
194
+ export function MyRuntimeProvider({
195
+ children,
196
+ }: Readonly<{
197
+ children: React.ReactNode;
198
+ }>) {
199
+ const runtime = useChatRuntime({
200
+ api: "/api/chat",
201
+ });
202
+
203
+ return (
204
+ <AssistantRuntimeProvider runtime={runtime}>
205
+ {children}
206
+ </AssistantRuntimeProvider>
207
+ );
208
+ }
209
+
210
+ ```
211
+
212
+ ## app/page.tsx
213
+
214
+ ```tsx
215
+ "use client";
216
+
217
+ import { SignupForm } from "@/components/SignupForm";
218
+ import { AssistantSidebar } from "@/components/assistant-ui/assistant-sidebar";
219
+ import { Form } from "@/components/ui/form";
220
+ import { useAssistantForm } from "@assistant-ui/react-hook-form";
221
+ import { useAssistantInstructions } from "@assistant-ui/react";
222
+ import Link from "next/link";
223
+
224
+ const SetFormFieldTool = () => {
225
+ return (
226
+ <p className="text-center font-mono text-sm font-bold text-blue-500">
227
+ set_form_field(...)
228
+ </p>
229
+ );
230
+ };
231
+
232
+ const SubmitFormTool = () => {
233
+ return (
234
+ <p className="text-center font-mono text-sm font-bold text-blue-500">
235
+ submit_form(...)
236
+ </p>
237
+ );
238
+ };
239
+
240
+ export default function Home() {
241
+ useAssistantInstructions("Help users sign up for Simon's hackathon.");
242
+ const form = useAssistantForm({
243
+ defaultValues: {
244
+ firstName: "",
245
+ lastName: "",
246
+ email: "",
247
+ cityAndCountry: "",
248
+ projectIdea: "",
249
+ proficientTechnologies: "",
250
+ },
251
+ assistant: {
252
+ tools: {
253
+ set_form_field: {
254
+ render: SetFormFieldTool,
255
+ },
256
+ submit_form: {
257
+ render: SubmitFormTool,
258
+ },
259
+ },
260
+ },
261
+ });
262
+
263
+ return (
264
+ <AssistantSidebar>
265
+ <div className="h-full overflow-y-scroll">
266
+ <main className="container py-8">
267
+ <h1 className="mb-2 text-2xl font-semibold">
268
+ Simon&apos;s Hackathon
269
+ </h1>
270
+ <p>
271
+ I&apos;m hosting a Hackathon on AI UX. Be the first to get an
272
+ invite!
273
+ </p>
274
+
275
+ <div className="my-4 font-bold">
276
+ Built with{" "}
277
+ <Link
278
+ href="https://github.com/assistant-ui/assistant-ui"
279
+ className="text-blue-600 underline"
280
+ >
281
+ assistant-ui
282
+ </Link>
283
+ .
284
+ </div>
285
+
286
+ <Form {...form}>
287
+ <SignupForm />
288
+ </Form>
289
+ </main>
290
+ </div>
291
+ </AssistantSidebar>
292
+ );
293
+ }
294
+
295
+ ```
296
+
297
+ ## components.json
298
+
299
+ ```json
300
+ {
301
+ "$schema": "https://ui.shadcn.com/schema.json",
302
+ "style": "new-york",
303
+ "rsc": true,
304
+ "tsx": true,
305
+ "tailwind": {
306
+ "config": "",
307
+ "css": "app/globals.css",
308
+ "baseColor": "zinc",
309
+ "cssVariables": true,
310
+ "prefix": ""
311
+ },
312
+ "aliases": {
313
+ "components": "@/components",
314
+ "utils": "@/lib/utils"
315
+ }
316
+ }
317
+
318
+ ```
319
+
320
+ ## components/assistant-ui/assistant-sidebar.tsx
321
+
322
+ ```tsx
323
+ "use client";
324
+
325
+ import {
326
+ ResizableHandle,
327
+ ResizablePanel,
328
+ ResizablePanelGroup,
329
+ } from "@/components/ui/resizable";
330
+ import { useMediaQuery } from "@react-hook/media-query";
331
+ import type { FC, PropsWithChildren } from "react";
332
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
333
+ import { Thread } from "@/components/assistant-ui/thread";
334
+
335
+ export const AssistantSidebar: FC<PropsWithChildren> = ({ children }) => {
336
+ const isSmall = useMediaQuery("(max-width: 768px)");
337
+
338
+ if (isSmall) {
339
+ return (
340
+ <Tabs
341
+ defaultValue="app"
342
+ className="mx-auto flex h-full max-w-[480px] flex-col px-4 pt-4"
343
+ >
344
+ <TabsList className="grid w-full grid-cols-2">
345
+ <TabsTrigger value="app">Form</TabsTrigger>
346
+ <TabsTrigger value="thread">Chat</TabsTrigger>
347
+ </TabsList>
348
+ <TabsContent value="app">{children}</TabsContent>
349
+ <TabsContent value="thread">
350
+ <Thread />
351
+ </TabsContent>
352
+ </Tabs>
353
+ );
354
+ }
355
+
356
+ return (
357
+ <ResizablePanelGroup direction="horizontal">
358
+ <ResizablePanel>{children}</ResizablePanel>
359
+ <ResizableHandle />
360
+ <ResizablePanel>
361
+ <Thread />
362
+ </ResizablePanel>
363
+ </ResizablePanelGroup>
364
+ );
365
+ };
366
+
367
+ ```
368
+
369
+ ## components/assistant-ui/markdown-text.tsx
370
+
371
+ ```tsx
372
+ "use client";
373
+
374
+ import "@assistant-ui/react-markdown/styles/dot.css";
375
+
376
+ import {
377
+ CodeHeaderProps,
378
+ MarkdownTextPrimitive,
379
+ unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
380
+ useIsMarkdownCodeBlock,
381
+ } from "@assistant-ui/react-markdown";
382
+ import remarkGfm from "remark-gfm";
383
+ import { FC, memo, useState } from "react";
384
+ import { CheckIcon, CopyIcon } from "lucide-react";
385
+
386
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
387
+ import { cn } from "@/lib/utils";
388
+
389
+ const MarkdownTextImpl = () => {
390
+ return (
391
+ <MarkdownTextPrimitive
392
+ remarkPlugins={[remarkGfm]}
393
+ className="aui-md"
394
+ components={defaultComponents}
395
+ />
396
+ );
397
+ };
398
+
399
+ export const MarkdownText = memo(MarkdownTextImpl);
400
+
401
+ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
402
+ const { isCopied, copyToClipboard } = useCopyToClipboard();
403
+ const onCopy = () => {
404
+ if (!code || isCopied) return;
405
+ copyToClipboard(code);
406
+ };
407
+
408
+ return (
409
+ <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">
410
+ <span className="lowercase [&>span]:text-xs">{language}</span>
411
+ <TooltipIconButton tooltip="Copy" onClick={onCopy}>
412
+ {!isCopied && <CopyIcon />}
413
+ {isCopied && <CheckIcon />}
414
+ </TooltipIconButton>
415
+ </div>
416
+ );
417
+ };
418
+
419
+ const useCopyToClipboard = ({
420
+ copiedDuration = 3000,
421
+ }: {
422
+ copiedDuration?: number;
423
+ } = {}) => {
424
+ const [isCopied, setIsCopied] = useState<boolean>(false);
425
+
426
+ const copyToClipboard = (value: string) => {
427
+ if (!value) return;
428
+
429
+ navigator.clipboard.writeText(value).then(() => {
430
+ setIsCopied(true);
431
+ setTimeout(() => setIsCopied(false), copiedDuration);
432
+ });
433
+ };
434
+
435
+ return { isCopied, copyToClipboard };
436
+ };
437
+
438
+ const defaultComponents = memoizeMarkdownComponents({
439
+ h1: ({ className, ...props }) => (
440
+ <h1
441
+ className={cn(
442
+ "mb-8 scroll-m-20 text-4xl font-extrabold tracking-tight last:mb-0",
443
+ className,
444
+ )}
445
+ {...props}
446
+ />
447
+ ),
448
+ h2: ({ className, ...props }) => (
449
+ <h2
450
+ className={cn(
451
+ "mb-4 mt-8 scroll-m-20 text-3xl font-semibold tracking-tight first:mt-0 last:mb-0",
452
+ className,
453
+ )}
454
+ {...props}
455
+ />
456
+ ),
457
+ h3: ({ className, ...props }) => (
458
+ <h3
459
+ className={cn(
460
+ "mb-4 mt-6 scroll-m-20 text-2xl font-semibold tracking-tight first:mt-0 last:mb-0",
461
+ className,
462
+ )}
463
+ {...props}
464
+ />
465
+ ),
466
+ h4: ({ className, ...props }) => (
467
+ <h4
468
+ className={cn(
469
+ "mb-4 mt-6 scroll-m-20 text-xl font-semibold tracking-tight first:mt-0 last:mb-0",
470
+ className,
471
+ )}
472
+ {...props}
473
+ />
474
+ ),
475
+ h5: ({ className, ...props }) => (
476
+ <h5
477
+ className={cn(
478
+ "my-4 text-lg font-semibold first:mt-0 last:mb-0",
479
+ className,
480
+ )}
481
+ {...props}
482
+ />
483
+ ),
484
+ h6: ({ className, ...props }) => (
485
+ <h6
486
+ className={cn("my-4 font-semibold first:mt-0 last:mb-0", className)}
487
+ {...props}
488
+ />
489
+ ),
490
+ p: ({ className, ...props }) => (
491
+ <p
492
+ className={cn("mb-5 mt-5 leading-7 first:mt-0 last:mb-0", className)}
493
+ {...props}
494
+ />
495
+ ),
496
+ a: ({ className, ...props }) => (
497
+ <a
498
+ className={cn(
499
+ "text-primary font-medium underline underline-offset-4",
500
+ className,
501
+ )}
502
+ {...props}
503
+ />
504
+ ),
505
+ blockquote: ({ className, ...props }) => (
506
+ <blockquote
507
+ className={cn("border-l-2 pl-6 italic", className)}
508
+ {...props}
509
+ />
510
+ ),
511
+ ul: ({ className, ...props }) => (
512
+ <ul
513
+ className={cn("my-5 ml-6 list-disc [&>li]:mt-2", className)}
514
+ {...props}
515
+ />
516
+ ),
517
+ ol: ({ className, ...props }) => (
518
+ <ol
519
+ className={cn("my-5 ml-6 list-decimal [&>li]:mt-2", className)}
520
+ {...props}
521
+ />
522
+ ),
523
+ hr: ({ className, ...props }) => (
524
+ <hr className={cn("my-5 border-b", className)} {...props} />
525
+ ),
526
+ table: ({ className, ...props }) => (
527
+ <table
528
+ className={cn(
529
+ "my-5 w-full border-separate border-spacing-0 overflow-y-auto",
530
+ className,
531
+ )}
532
+ {...props}
533
+ />
534
+ ),
535
+ th: ({ className, ...props }) => (
536
+ <th
537
+ className={cn(
538
+ "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",
539
+ className,
540
+ )}
541
+ {...props}
542
+ />
543
+ ),
544
+ td: ({ className, ...props }) => (
545
+ <td
546
+ className={cn(
547
+ "border-b border-l px-4 py-2 text-left last:border-r [&[align=center]]:text-center [&[align=right]]:text-right",
548
+ className,
549
+ )}
550
+ {...props}
551
+ />
552
+ ),
553
+ tr: ({ className, ...props }) => (
554
+ <tr
555
+ className={cn(
556
+ "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",
557
+ className,
558
+ )}
559
+ {...props}
560
+ />
561
+ ),
562
+ sup: ({ className, ...props }) => (
563
+ <sup
564
+ className={cn("[&>a]:text-xs [&>a]:no-underline", className)}
565
+ {...props}
566
+ />
567
+ ),
568
+ pre: ({ className, ...props }) => (
569
+ <pre
570
+ className={cn(
571
+ "overflow-x-auto rounded-b-lg bg-black p-4 text-white",
572
+ className,
573
+ )}
574
+ {...props}
575
+ />
576
+ ),
577
+ code: function Code({ className, ...props }) {
578
+ const isCodeBlock = useIsMarkdownCodeBlock();
579
+ return (
580
+ <code
581
+ className={cn(
582
+ !isCodeBlock && "bg-muted rounded border font-semibold",
583
+ className,
584
+ )}
585
+ {...props}
586
+ />
587
+ );
588
+ },
589
+ CodeHeader,
590
+ });
591
+
592
+ ```
593
+
594
+ ## components/assistant-ui/thread.tsx
595
+
596
+ ```tsx
597
+ import {
598
+ ActionBarPrimitive,
599
+ BranchPickerPrimitive,
600
+ ComposerPrimitive,
601
+ MessagePrimitive,
602
+ ThreadPrimitive,
603
+ } from "@assistant-ui/react";
604
+ import type { FC } from "react";
605
+ import {
606
+ ArrowDownIcon,
607
+ CheckIcon,
608
+ ChevronLeftIcon,
609
+ ChevronRightIcon,
610
+ CopyIcon,
611
+ PencilIcon,
612
+ RefreshCwIcon,
613
+ SendHorizontalIcon,
614
+ } from "lucide-react";
615
+ import { cn } from "@/lib/utils";
616
+
617
+ import { Button } from "@/components/ui/button";
618
+ import { MarkdownText } from "@/components/assistant-ui/markdown-text";
619
+ import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
620
+
621
+ export const Thread: FC = () => {
622
+ return (
623
+ <ThreadPrimitive.Root
624
+ className="bg-background box-border flex h-full flex-col overflow-hidden"
625
+ style={{
626
+ ["--thread-max-width" as string]: "42rem",
627
+ }}
628
+ >
629
+ <ThreadPrimitive.Viewport className="flex h-full flex-col items-center overflow-y-scroll scroll-smooth bg-inherit px-4 pt-8">
630
+ <ThreadWelcome />
631
+
632
+ <ThreadPrimitive.Messages
633
+ components={{
634
+ UserMessage: UserMessage,
635
+ EditComposer: EditComposer,
636
+ AssistantMessage: AssistantMessage,
637
+ }}
638
+ />
639
+
640
+ <ThreadPrimitive.If empty={false}>
641
+ <div className="min-h-8 flex-grow" />
642
+ </ThreadPrimitive.If>
643
+
644
+ <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">
645
+ <ThreadScrollToBottom />
646
+ <Composer />
647
+ </div>
648
+ </ThreadPrimitive.Viewport>
649
+ </ThreadPrimitive.Root>
650
+ );
651
+ };
652
+
653
+ const ThreadScrollToBottom: FC = () => {
654
+ return (
655
+ <ThreadPrimitive.ScrollToBottom asChild>
656
+ <TooltipIconButton
657
+ tooltip="Scroll to bottom"
658
+ variant="outline"
659
+ className="absolute -top-8 rounded-full disabled:invisible"
660
+ >
661
+ <ArrowDownIcon />
662
+ </TooltipIconButton>
663
+ </ThreadPrimitive.ScrollToBottom>
664
+ );
665
+ };
666
+
667
+ const ThreadWelcome: FC = () => {
668
+ return (
669
+ <ThreadPrimitive.Empty>
670
+ <div className="flex w-full max-w-[var(--thread-max-width)] flex-grow flex-col">
671
+ <div className="flex w-full flex-grow flex-col items-center justify-center">
672
+ <p className="mt-4 font-medium">How can I help you today?</p>
673
+ </div>
674
+ <ThreadWelcomeSuggestions />
675
+ </div>
676
+ </ThreadPrimitive.Empty>
677
+ );
678
+ };
679
+
680
+ const ThreadWelcomeSuggestions: FC = () => {
681
+ return (
682
+ <div className="mt-3 flex w-full items-stretch justify-center gap-4">
683
+ <ThreadPrimitive.Suggestion
684
+ 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"
685
+ prompt="What is the weather in Tokyo?"
686
+ method="replace"
687
+ autoSend
688
+ >
689
+ <span className="line-clamp-2 text-ellipsis text-sm font-semibold">
690
+ What is the weather in Tokyo?
691
+ </span>
692
+ </ThreadPrimitive.Suggestion>
693
+ <ThreadPrimitive.Suggestion
694
+ 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"
695
+ prompt="What is assistant-ui?"
696
+ method="replace"
697
+ autoSend
698
+ >
699
+ <span className="line-clamp-2 text-ellipsis text-sm font-semibold">
700
+ What is assistant-ui?
701
+ </span>
702
+ </ThreadPrimitive.Suggestion>
703
+ </div>
704
+ );
705
+ };
706
+
707
+ const Composer: FC = () => {
708
+ return (
709
+ <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">
710
+ <ComposerPrimitive.Input
711
+ rows={1}
712
+ autoFocus
713
+ placeholder="Write a message..."
714
+ 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"
715
+ />
716
+ <ComposerAction />
717
+ </ComposerPrimitive.Root>
718
+ );
719
+ };
720
+
721
+ const ComposerAction: FC = () => {
722
+ return (
723
+ <>
724
+ <ThreadPrimitive.If running={false}>
725
+ <ComposerPrimitive.Send asChild>
726
+ <TooltipIconButton
727
+ tooltip="Send"
728
+ variant="default"
729
+ className="my-2.5 size-8 p-2 transition-opacity ease-in"
730
+ >
731
+ <SendHorizontalIcon />
732
+ </TooltipIconButton>
733
+ </ComposerPrimitive.Send>
734
+ </ThreadPrimitive.If>
735
+ <ThreadPrimitive.If running>
736
+ <ComposerPrimitive.Cancel asChild>
737
+ <TooltipIconButton
738
+ tooltip="Cancel"
739
+ variant="default"
740
+ className="my-2.5 size-8 p-2 transition-opacity ease-in"
741
+ >
742
+ <CircleStopIcon />
743
+ </TooltipIconButton>
744
+ </ComposerPrimitive.Cancel>
745
+ </ThreadPrimitive.If>
746
+ </>
747
+ );
748
+ };
749
+
750
+ const UserMessage: FC = () => {
751
+ return (
752
+ <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">
753
+ <UserActionBar />
754
+
755
+ <div className="bg-muted text-foreground col-start-2 row-start-2 max-w-[calc(var(--thread-max-width)*0.8)] break-words rounded-3xl px-5 py-2.5">
756
+ <MessagePrimitive.Content />
757
+ </div>
758
+
759
+ <BranchPicker className="col-span-full col-start-1 row-start-3 -mr-1 justify-end" />
760
+ </MessagePrimitive.Root>
761
+ );
762
+ };
763
+
764
+ const UserActionBar: FC = () => {
765
+ return (
766
+ <ActionBarPrimitive.Root
767
+ hideWhenRunning
768
+ autohide="not-last"
769
+ className="col-start-1 row-start-2 mr-3 mt-2.5 flex flex-col items-end"
770
+ >
771
+ <ActionBarPrimitive.Edit asChild>
772
+ <TooltipIconButton tooltip="Edit">
773
+ <PencilIcon />
774
+ </TooltipIconButton>
775
+ </ActionBarPrimitive.Edit>
776
+ </ActionBarPrimitive.Root>
777
+ );
778
+ };
779
+
780
+ const EditComposer: FC = () => {
781
+ return (
782
+ <ComposerPrimitive.Root className="bg-muted my-4 flex w-full max-w-[var(--thread-max-width)] flex-col gap-2 rounded-xl">
783
+ <ComposerPrimitive.Input className="text-foreground flex h-8 w-full resize-none bg-transparent p-4 pb-0 outline-none" />
784
+
785
+ <div className="mx-3 mb-3 flex items-center justify-center gap-2 self-end">
786
+ <ComposerPrimitive.Cancel asChild>
787
+ <Button variant="ghost">Cancel</Button>
788
+ </ComposerPrimitive.Cancel>
789
+ <ComposerPrimitive.Send asChild>
790
+ <Button>Send</Button>
791
+ </ComposerPrimitive.Send>
792
+ </div>
793
+ </ComposerPrimitive.Root>
794
+ );
795
+ };
796
+
797
+ const AssistantMessage: FC = () => {
798
+ return (
799
+ <MessagePrimitive.Root className="relative grid w-full max-w-[var(--thread-max-width)] grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] py-4">
800
+ <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)] break-words leading-7">
801
+ <MessagePrimitive.Content components={{ Text: MarkdownText }} />
802
+ </div>
803
+
804
+ <AssistantActionBar />
805
+
806
+ <BranchPicker className="col-start-2 row-start-2 -ml-2 mr-2" />
807
+ </MessagePrimitive.Root>
808
+ );
809
+ };
810
+
811
+ const AssistantActionBar: FC = () => {
812
+ return (
813
+ <ActionBarPrimitive.Root
814
+ hideWhenRunning
815
+ autohide="not-last"
816
+ autohideFloat="single-branch"
817
+ 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"
818
+ >
819
+ <ActionBarPrimitive.Copy asChild>
820
+ <TooltipIconButton tooltip="Copy">
821
+ <MessagePrimitive.If copied>
822
+ <CheckIcon />
823
+ </MessagePrimitive.If>
824
+ <MessagePrimitive.If copied={false}>
825
+ <CopyIcon />
826
+ </MessagePrimitive.If>
827
+ </TooltipIconButton>
828
+ </ActionBarPrimitive.Copy>
829
+ <ActionBarPrimitive.Reload asChild>
830
+ <TooltipIconButton tooltip="Refresh">
831
+ <RefreshCwIcon />
832
+ </TooltipIconButton>
833
+ </ActionBarPrimitive.Reload>
834
+ </ActionBarPrimitive.Root>
835
+ );
836
+ };
837
+
838
+ const BranchPicker: FC<BranchPickerPrimitive.Root.Props> = ({
839
+ className,
840
+ ...rest
841
+ }) => {
842
+ return (
843
+ <BranchPickerPrimitive.Root
844
+ hideWhenSingleBranch
845
+ className={cn(
846
+ "text-muted-foreground inline-flex items-center text-xs",
847
+ className,
848
+ )}
849
+ {...rest}
850
+ >
851
+ <BranchPickerPrimitive.Previous asChild>
852
+ <TooltipIconButton tooltip="Previous">
853
+ <ChevronLeftIcon />
854
+ </TooltipIconButton>
855
+ </BranchPickerPrimitive.Previous>
856
+ <span className="font-medium">
857
+ <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
858
+ </span>
859
+ <BranchPickerPrimitive.Next asChild>
860
+ <TooltipIconButton tooltip="Next">
861
+ <ChevronRightIcon />
862
+ </TooltipIconButton>
863
+ </BranchPickerPrimitive.Next>
864
+ </BranchPickerPrimitive.Root>
865
+ );
866
+ };
867
+
868
+ const CircleStopIcon = () => {
869
+ return (
870
+ <svg
871
+ xmlns="http://www.w3.org/2000/svg"
872
+ viewBox="0 0 16 16"
873
+ fill="currentColor"
874
+ width="16"
875
+ height="16"
876
+ >
877
+ <rect width="10" height="10" x="3" y="3" rx="2" />
878
+ </svg>
879
+ );
880
+ };
881
+
882
+ ```
883
+
884
+ ## components/assistant-ui/tooltip-icon-button.tsx
885
+
886
+ ```tsx
887
+ "use client";
888
+
889
+ import { ComponentPropsWithoutRef, forwardRef } from "react";
890
+
891
+ import {
892
+ Tooltip,
893
+ TooltipContent,
894
+ TooltipTrigger,
895
+ } from "@/components/ui/tooltip";
896
+ import { Button } from "@/components/ui/button";
897
+ import { cn } from "@/lib/utils";
898
+
899
+ export type TooltipIconButtonProps = ComponentPropsWithoutRef<typeof Button> & {
900
+ tooltip: string;
901
+ side?: "top" | "bottom" | "left" | "right";
902
+ };
903
+
904
+ export const TooltipIconButton = forwardRef<
905
+ HTMLButtonElement,
906
+ TooltipIconButtonProps
907
+ >(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
908
+ return (
909
+ <Tooltip>
910
+ <TooltipTrigger asChild>
911
+ <Button
912
+ variant="ghost"
913
+ size="icon"
914
+ {...rest}
915
+ className={cn("size-6 p-1", className)}
916
+ ref={ref}
917
+ >
918
+ {children}
919
+ <span className="sr-only">{tooltip}</span>
920
+ </Button>
921
+ </TooltipTrigger>
922
+ <TooltipContent side={side}>{tooltip}</TooltipContent>
923
+ </Tooltip>
924
+ );
925
+ });
926
+
927
+ TooltipIconButton.displayName = "TooltipIconButton";
928
+
929
+ ```
930
+
931
+ ## components/SignupForm.tsx
932
+
933
+ ```tsx
934
+ "use client";
935
+ import { Button } from "@/components/ui/button";
936
+ import {
937
+ FormControl,
938
+ FormDescription,
939
+ FormField,
940
+ FormItem,
941
+ FormLabel,
942
+ FormMessage,
943
+ } from "@/components/ui/form";
944
+ import { Input } from "@/components/ui/input";
945
+ import { type FC, useState } from "react";
946
+ import { useFormContext } from "react-hook-form";
947
+ import { submitSignup } from "../lib/submitSignup";
948
+
949
+ export const SignupForm: FC = () => {
950
+ const form = useFormContext();
951
+
952
+ const [isSubmitting, setIsSubmitting] = useState(false);
953
+ const [isSubmitted, setIsSubmitted] = useState(false);
954
+
955
+ const onSubmit = async (values: object) => {
956
+ try {
957
+ setIsSubmitting(true);
958
+ await submitSignup(values);
959
+ setIsSubmitted(true);
960
+ } finally {
961
+ setIsSubmitting(false);
962
+ }
963
+ };
964
+
965
+ if (isSubmitting)
966
+ return <p className="my-4 font-bold text-green-600">Submitting...</p>;
967
+
968
+ if (isSubmitted)
969
+ return (
970
+ <p className="my-4 font-bold text-green-600">
971
+ Thank you for signing up, you will hear from me soon!
972
+ </p>
973
+ );
974
+
975
+ return (
976
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
977
+ <input type="hidden" {...form.register("hidden")} />
978
+
979
+ <FormField
980
+ control={form.control}
981
+ name="firstName"
982
+ render={({ field }) => (
983
+ <FormItem>
984
+ <FormLabel>First Name</FormLabel>
985
+ <FormDescription>Your first name.</FormDescription>
986
+ <FormControl>
987
+ <Input placeholder="First Name" {...field} />
988
+ </FormControl>
989
+ <FormMessage />
990
+ </FormItem>
991
+ )}
992
+ />
993
+
994
+ <FormField
995
+ control={form.control}
996
+ name="lastName"
997
+ render={({ field }) => (
998
+ <FormItem>
999
+ <FormLabel>Last Name</FormLabel>
1000
+ <FormDescription>Your last name.</FormDescription>
1001
+ <FormControl>
1002
+ <Input placeholder="Last Name" {...field} />
1003
+ </FormControl>
1004
+ <FormMessage />
1005
+ </FormItem>
1006
+ )}
1007
+ />
1008
+
1009
+ <FormField
1010
+ control={form.control}
1011
+ name="email"
1012
+ render={({ field }) => (
1013
+ <FormItem>
1014
+ <FormLabel>Email</FormLabel>
1015
+ <FormDescription>Your email.</FormDescription>
1016
+ <FormControl>
1017
+ <Input placeholder="Email" {...field} />
1018
+ </FormControl>
1019
+ <FormMessage />
1020
+ </FormItem>
1021
+ )}
1022
+ />
1023
+
1024
+ <FormField
1025
+ control={form.control}
1026
+ name="cityAndCountry"
1027
+ render={({ field }) => (
1028
+ <FormItem>
1029
+ <FormLabel>City</FormLabel>
1030
+ <FormDescription>The city and country you live in.</FormDescription>
1031
+ <FormControl>
1032
+ <Input placeholder="City" {...field} />
1033
+ </FormControl>
1034
+ <FormMessage />
1035
+ </FormItem>
1036
+ )}
1037
+ />
1038
+
1039
+ <FormField
1040
+ control={form.control}
1041
+ name="projectIdea"
1042
+ render={({ field }) => (
1043
+ <FormItem>
1044
+ <FormLabel>Idea</FormLabel>
1045
+ <FormDescription>
1046
+ Do you have an idea for a project?
1047
+ </FormDescription>
1048
+ <FormControl>
1049
+ <Input placeholder="Idea" {...field} />
1050
+ </FormControl>
1051
+ <FormMessage />
1052
+ </FormItem>
1053
+ )}
1054
+ />
1055
+
1056
+ <FormField
1057
+ control={form.control}
1058
+ name="proficientTechnologies"
1059
+ render={({ field }) => (
1060
+ <FormItem>
1061
+ <FormLabel>Technologies</FormLabel>
1062
+ <FormDescription>
1063
+ What technologies are you most comfortable with?
1064
+ </FormDescription>
1065
+ <FormControl>
1066
+ <Input placeholder="Next.js, Tailwind CSS" {...field} />
1067
+ </FormControl>
1068
+ <FormMessage />
1069
+ </FormItem>
1070
+ )}
1071
+ />
1072
+
1073
+ <Button type="submit">Submit</Button>
1074
+ </form>
1075
+ );
1076
+ };
1077
+
1078
+ ```
1079
+
1080
+ ## components/ui/button.tsx
1081
+
1082
+ ```tsx
1083
+ import * as React from "react";
1084
+ import { Slot } from "@radix-ui/react-slot";
1085
+ import { cva, type VariantProps } from "class-variance-authority";
1086
+
1087
+ import { cn } from "@/lib/utils";
1088
+
1089
+ const buttonVariants = cva(
1090
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1091
+ {
1092
+ variants: {
1093
+ variant: {
1094
+ default:
1095
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
1096
+ destructive:
1097
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
1098
+ outline:
1099
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
1100
+ secondary:
1101
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
1102
+ ghost:
1103
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
1104
+ link: "text-primary underline-offset-4 hover:underline",
1105
+ },
1106
+ size: {
1107
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
1108
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
1109
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
1110
+ icon: "size-9",
1111
+ },
1112
+ },
1113
+ defaultVariants: {
1114
+ variant: "default",
1115
+ size: "default",
1116
+ },
1117
+ },
1118
+ );
1119
+
1120
+ function Button({
1121
+ className,
1122
+ variant,
1123
+ size,
1124
+ asChild = false,
1125
+ ...props
1126
+ }: React.ComponentProps<"button"> &
1127
+ VariantProps<typeof buttonVariants> & {
1128
+ asChild?: boolean;
1129
+ }) {
1130
+ const Comp = asChild ? Slot : "button";
1131
+
1132
+ return (
1133
+ <Comp
1134
+ data-slot="button"
1135
+ className={cn(buttonVariants({ variant, size, className }))}
1136
+ {...props}
1137
+ />
1138
+ );
1139
+ }
1140
+
1141
+ export { Button, buttonVariants };
1142
+
1143
+ ```
1144
+
1145
+ ## components/ui/form.tsx
1146
+
1147
+ ```tsx
1148
+ "use client";
1149
+
1150
+ import * as React from "react";
1151
+ import * as LabelPrimitive from "@radix-ui/react-label";
1152
+ import { Slot } from "@radix-ui/react-slot";
1153
+ import {
1154
+ Controller,
1155
+ FormProvider,
1156
+ useFormContext,
1157
+ useFormState,
1158
+ type ControllerProps,
1159
+ type FieldPath,
1160
+ type FieldValues,
1161
+ } from "react-hook-form";
1162
+
1163
+ import { cn } from "@/lib/utils";
1164
+ import { Label } from "@/components/ui/label";
1165
+
1166
+ const Form = FormProvider;
1167
+
1168
+ type FormFieldContextValue<
1169
+ TFieldValues extends FieldValues = FieldValues,
1170
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
1171
+ > = {
1172
+ name: TName;
1173
+ };
1174
+
1175
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
1176
+ {} as FormFieldContextValue,
1177
+ );
1178
+
1179
+ const FormField = <
1180
+ TFieldValues extends FieldValues = FieldValues,
1181
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
1182
+ >({
1183
+ ...props
1184
+ }: ControllerProps<TFieldValues, TName>) => {
1185
+ return (
1186
+ <FormFieldContext.Provider value={{ name: props.name }}>
1187
+ <Controller {...props} />
1188
+ </FormFieldContext.Provider>
1189
+ );
1190
+ };
1191
+
1192
+ const useFormField = () => {
1193
+ const fieldContext = React.useContext(FormFieldContext);
1194
+ const itemContext = React.useContext(FormItemContext);
1195
+ const { getFieldState } = useFormContext();
1196
+ const formState = useFormState({ name: fieldContext.name });
1197
+ const fieldState = getFieldState(fieldContext.name, formState);
1198
+
1199
+ if (!fieldContext) {
1200
+ throw new Error("useFormField should be used within <FormField>");
1201
+ }
1202
+
1203
+ const { id } = itemContext;
1204
+
1205
+ return {
1206
+ id,
1207
+ name: fieldContext.name,
1208
+ formItemId: `${id}-form-item`,
1209
+ formDescriptionId: `${id}-form-item-description`,
1210
+ formMessageId: `${id}-form-item-message`,
1211
+ ...fieldState,
1212
+ };
1213
+ };
1214
+
1215
+ type FormItemContextValue = {
1216
+ id: string;
1217
+ };
1218
+
1219
+ const FormItemContext = React.createContext<FormItemContextValue>(
1220
+ {} as FormItemContextValue,
1221
+ );
1222
+
1223
+ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
1224
+ const id = React.useId();
1225
+
1226
+ return (
1227
+ <FormItemContext.Provider value={{ id }}>
1228
+ <div
1229
+ data-slot="form-item"
1230
+ className={cn("grid gap-2", className)}
1231
+ {...props}
1232
+ />
1233
+ </FormItemContext.Provider>
1234
+ );
1235
+ }
1236
+
1237
+ function FormLabel({
1238
+ className,
1239
+ ...props
1240
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
1241
+ const { error, formItemId } = useFormField();
1242
+
1243
+ return (
1244
+ <Label
1245
+ data-slot="form-label"
1246
+ data-error={!!error}
1247
+ className={cn("data-[error=true]:text-destructive", className)}
1248
+ htmlFor={formItemId}
1249
+ {...props}
1250
+ />
1251
+ );
1252
+ }
1253
+
1254
+ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
1255
+ const { error, formItemId, formDescriptionId, formMessageId } =
1256
+ useFormField();
1257
+
1258
+ return (
1259
+ <Slot
1260
+ data-slot="form-control"
1261
+ id={formItemId}
1262
+ aria-describedby={
1263
+ !error
1264
+ ? `${formDescriptionId}`
1265
+ : `${formDescriptionId} ${formMessageId}`
1266
+ }
1267
+ aria-invalid={!!error}
1268
+ {...props}
1269
+ />
1270
+ );
1271
+ }
1272
+
1273
+ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
1274
+ const { formDescriptionId } = useFormField();
1275
+
1276
+ return (
1277
+ <p
1278
+ data-slot="form-description"
1279
+ id={formDescriptionId}
1280
+ className={cn("text-muted-foreground text-sm", className)}
1281
+ {...props}
1282
+ />
1283
+ );
1284
+ }
1285
+
1286
+ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
1287
+ const { error, formMessageId } = useFormField();
1288
+ const body = error ? String(error?.message ?? "") : props.children;
1289
+
1290
+ if (!body) {
1291
+ return null;
1292
+ }
1293
+
1294
+ return (
1295
+ <p
1296
+ data-slot="form-message"
1297
+ id={formMessageId}
1298
+ className={cn("text-destructive text-sm", className)}
1299
+ {...props}
1300
+ >
1301
+ {body}
1302
+ </p>
1303
+ );
1304
+ }
1305
+
1306
+ export {
1307
+ useFormField,
1308
+ Form,
1309
+ FormItem,
1310
+ FormLabel,
1311
+ FormControl,
1312
+ FormDescription,
1313
+ FormMessage,
1314
+ FormField,
1315
+ };
1316
+
1317
+ ```
1318
+
1319
+ ## components/ui/input.tsx
1320
+
1321
+ ```tsx
1322
+ import * as React from "react";
1323
+
1324
+ import { cn } from "@/lib/utils";
1325
+
1326
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
1327
+ return (
1328
+ <input
1329
+ type={type}
1330
+ data-slot="input"
1331
+ className={cn(
1332
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base outline-none transition-[color,box-shadow] file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
1333
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
1334
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
1335
+ className,
1336
+ )}
1337
+ {...props}
1338
+ />
1339
+ );
1340
+ }
1341
+
1342
+ export { Input };
1343
+
1344
+ ```
1345
+
1346
+ ## components/ui/label.tsx
1347
+
1348
+ ```tsx
1349
+ "use client";
1350
+
1351
+ import * as React from "react";
1352
+ import * as LabelPrimitive from "@radix-ui/react-label";
1353
+
1354
+ import { cn } from "@/lib/utils";
1355
+
1356
+ function Label({
1357
+ className,
1358
+ ...props
1359
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
1360
+ return (
1361
+ <LabelPrimitive.Root
1362
+ data-slot="label"
1363
+ className={cn(
1364
+ "flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50",
1365
+ className,
1366
+ )}
1367
+ {...props}
1368
+ />
1369
+ );
1370
+ }
1371
+
1372
+ export { Label };
1373
+
1374
+ ```
1375
+
1376
+ ## components/ui/resizable.tsx
1377
+
1378
+ ```tsx
1379
+ "use client";
1380
+
1381
+ import * as React from "react";
1382
+ import { GripVerticalIcon } from "lucide-react";
1383
+ import * as ResizablePrimitive from "react-resizable-panels";
1384
+
1385
+ import { cn } from "@/lib/utils";
1386
+
1387
+ function ResizablePanelGroup({
1388
+ className,
1389
+ ...props
1390
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) {
1391
+ return (
1392
+ <ResizablePrimitive.PanelGroup
1393
+ data-slot="resizable-panel-group"
1394
+ className={cn(
1395
+ "flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
1396
+ className,
1397
+ )}
1398
+ {...props}
1399
+ />
1400
+ );
1401
+ }
1402
+
1403
+ function ResizablePanel({
1404
+ ...props
1405
+ }: React.ComponentProps<typeof ResizablePrimitive.Panel>) {
1406
+ return <ResizablePrimitive.Panel data-slot="resizable-panel" {...props} />;
1407
+ }
1408
+
1409
+ function ResizableHandle({
1410
+ withHandle,
1411
+ className,
1412
+ ...props
1413
+ }: React.ComponentProps<typeof ResizablePrimitive.PanelResizeHandle> & {
1414
+ withHandle?: boolean;
1415
+ }) {
1416
+ return (
1417
+ <ResizablePrimitive.PanelResizeHandle
1418
+ data-slot="resizable-handle"
1419
+ className={cn(
1420
+ "bg-border focus-visible:ring-ring focus-visible:outline-hidden relative flex w-px items-center justify-center after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:ring-1 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90",
1421
+ className,
1422
+ )}
1423
+ {...props}
1424
+ >
1425
+ {withHandle && (
1426
+ <div className="bg-border rounded-xs z-10 flex h-4 w-3 items-center justify-center border">
1427
+ <GripVerticalIcon className="size-2.5" />
1428
+ </div>
1429
+ )}
1430
+ </ResizablePrimitive.PanelResizeHandle>
1431
+ );
1432
+ }
1433
+
1434
+ export { ResizablePanelGroup, ResizablePanel, ResizableHandle };
1435
+
1436
+ ```
1437
+
1438
+ ## components/ui/tabs.tsx
1439
+
1440
+ ```tsx
1441
+ "use client";
1442
+
1443
+ import * as React from "react";
1444
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
1445
+
1446
+ import { cn } from "@/lib/utils";
1447
+
1448
+ function Tabs({
1449
+ className,
1450
+ ...props
1451
+ }: React.ComponentProps<typeof TabsPrimitive.Root>) {
1452
+ return (
1453
+ <TabsPrimitive.Root
1454
+ data-slot="tabs"
1455
+ className={cn("flex flex-col gap-2", className)}
1456
+ {...props}
1457
+ />
1458
+ );
1459
+ }
1460
+
1461
+ function TabsList({
1462
+ className,
1463
+ ...props
1464
+ }: React.ComponentProps<typeof TabsPrimitive.List>) {
1465
+ return (
1466
+ <TabsPrimitive.List
1467
+ data-slot="tabs-list"
1468
+ className={cn(
1469
+ "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
1470
+ className,
1471
+ )}
1472
+ {...props}
1473
+ />
1474
+ );
1475
+ }
1476
+
1477
+ function TabsTrigger({
1478
+ className,
1479
+ ...props
1480
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
1481
+ return (
1482
+ <TabsPrimitive.Trigger
1483
+ data-slot="tabs-trigger"
1484
+ className={cn(
1485
+ "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 whitespace-nowrap rounded-md border border-transparent px-2 py-1 text-sm font-medium transition-[color,box-shadow] focus-visible:outline-1 focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
1486
+ className,
1487
+ )}
1488
+ {...props}
1489
+ />
1490
+ );
1491
+ }
1492
+
1493
+ function TabsContent({
1494
+ className,
1495
+ ...props
1496
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
1497
+ return (
1498
+ <TabsPrimitive.Content
1499
+ data-slot="tabs-content"
1500
+ className={cn("flex-1 outline-none", className)}
1501
+ {...props}
1502
+ />
1503
+ );
1504
+ }
1505
+
1506
+ export { Tabs, TabsList, TabsTrigger, TabsContent };
1507
+
1508
+ ```
1509
+
1510
+ ## components/ui/tooltip.tsx
1511
+
1512
+ ```tsx
1513
+ "use client";
1514
+
1515
+ import * as React from "react";
1516
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
1517
+
1518
+ import { cn } from "@/lib/utils";
1519
+
1520
+ function TooltipProvider({
1521
+ delayDuration = 0,
1522
+ ...props
1523
+ }: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
1524
+ return (
1525
+ <TooltipPrimitive.Provider
1526
+ data-slot="tooltip-provider"
1527
+ delayDuration={delayDuration}
1528
+ {...props}
1529
+ />
1530
+ );
1531
+ }
1532
+
1533
+ function Tooltip({
1534
+ ...props
1535
+ }: React.ComponentProps<typeof TooltipPrimitive.Root>) {
1536
+ return (
1537
+ <TooltipProvider>
1538
+ <TooltipPrimitive.Root data-slot="tooltip" {...props} />
1539
+ </TooltipProvider>
1540
+ );
1541
+ }
1542
+
1543
+ function TooltipTrigger({
1544
+ ...props
1545
+ }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
1546
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
1547
+ }
1548
+
1549
+ function TooltipContent({
1550
+ className,
1551
+ sideOffset = 0,
1552
+ children,
1553
+ ...props
1554
+ }: React.ComponentProps<typeof TooltipPrimitive.Content>) {
1555
+ return (
1556
+ <TooltipPrimitive.Portal>
1557
+ <TooltipPrimitive.Content
1558
+ data-slot="tooltip-content"
1559
+ sideOffset={sideOffset}
1560
+ className={cn(
1561
+ "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] text-balance rounded-md px-3 py-1.5 text-xs",
1562
+ className,
1563
+ )}
1564
+ {...props}
1565
+ >
1566
+ {children}
1567
+ <TooltipPrimitive.Arrow className="bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
1568
+ </TooltipPrimitive.Content>
1569
+ </TooltipPrimitive.Portal>
1570
+ );
1571
+ }
1572
+
1573
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
1574
+
1575
+ ```
1576
+
1577
+ ## eslint.config.ts
1578
+
1579
+ ```typescript
1580
+ export { default } from "@assistant-ui/x-buildutils/eslint";
1581
+
1582
+ ```
1583
+
1584
+ ## lib/submitSignup.tsx
1585
+
1586
+ ```tsx
1587
+ "use server";
1588
+
1589
+ export const submitSignup = async (data: object) => {
1590
+ const res = await fetch(process.env["ASSISTANT_UI_SUBMIT_SIGNUP_ENDPOINT"]!, {
1591
+ method: "POST",
1592
+ headers: {
1593
+ "Content-Type": "application/json",
1594
+ },
1595
+ body: JSON.stringify(data),
1596
+ });
1597
+ return res.json();
1598
+ };
1599
+
1600
+ ```
1601
+
1602
+ ## lib/utils.ts
1603
+
1604
+ ```typescript
1605
+ import { type ClassValue, clsx } from "clsx";
1606
+ import { twMerge } from "tailwind-merge";
1607
+
1608
+ export function cn(...inputs: ClassValue[]) {
1609
+ return twMerge(clsx(inputs));
1610
+ }
1611
+
1612
+ ```
1613
+
1614
+ ## next-env.d.ts
1615
+
1616
+ ```typescript
1617
+ /// <reference types="next" />
1618
+ /// <reference types="next/image-types/global" />
1619
+
1620
+ // NOTE: This file should not be edited
1621
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
1622
+
1623
+ ```
1624
+
1625
+ ## next.config.ts
1626
+
1627
+ ```typescript
1628
+ import type { NextConfig } from "next";
1629
+
1630
+ const nextConfig: NextConfig = {
1631
+ /* config options here */
1632
+ };
1633
+
1634
+ export default nextConfig;
1635
+
1636
+ ```
1637
+
1638
+ ## package.json
1639
+
1640
+ ```json
1641
+ {
1642
+ "name": "with-react-hook-form",
1643
+ "version": "0.1.0",
1644
+ "private": true,
1645
+ "scripts": {
1646
+ "dev": "next dev --turbo",
1647
+ "build": "next build",
1648
+ "start": "next start",
1649
+ "lint": "next lint"
1650
+ },
1651
+ "dependencies": {
1652
+ "@ai-sdk/openai": "^1.3.22",
1653
+ "@assistant-ui/react": "workspace:*",
1654
+ "@assistant-ui/react-ai-sdk": "workspace:*",
1655
+ "@assistant-ui/react-hook-form": "workspace:*",
1656
+ "@assistant-ui/react-markdown": "workspace:*",
1657
+ "@hookform/resolvers": "^5.0.1",
1658
+ "@radix-ui/react-avatar": "^1.1.10",
1659
+ "@radix-ui/react-icons": "^1.3.2",
1660
+ "@radix-ui/react-label": "^2.1.7",
1661
+ "@radix-ui/react-slot": "^1.2.3",
1662
+ "@radix-ui/react-tabs": "^1.1.12",
1663
+ "@radix-ui/react-tooltip": "^1.2.7",
1664
+ "@react-hook/media-query": "^1.1.1",
1665
+ "ai": "^4.3.16",
1666
+ "class-variance-authority": "^0.7.1",
1667
+ "clsx": "^2.1.1",
1668
+ "json-schema-to-zod": "^2.6.1",
1669
+ "lucide-react": "^0.511.0",
1670
+ "next": "15.3.3",
1671
+ "react": "19.1.0",
1672
+ "react-dom": "19.1.0",
1673
+ "react-hook-form": "^7.57.0",
1674
+ "react-resizable-panels": "^3.0.2",
1675
+ "remark-gfm": "^4.0.1",
1676
+ "tailwind-merge": "^3.3.0",
1677
+ "tw-animate-css": "^1.3.3",
1678
+ "zod": "^3.25.49",
1679
+ "zod-to-json-schema": "^3.24.5",
1680
+ "zustand": "^5.0.5"
1681
+ },
1682
+ "devDependencies": {
1683
+ "@assistant-ui/x-buildutils": "workspace:*",
1684
+ "@types/node": "^22",
1685
+ "@types/react": "^19",
1686
+ "@types/react-dom": "^19",
1687
+ "eslint": "^9",
1688
+ "eslint-config-next": "15.3.3",
1689
+ "postcss": "^8",
1690
+ "tailwindcss": "^4.1.8",
1691
+ "typescript": "^5.8.3"
1692
+ }
1693
+ }
1694
+
1695
+ ```
1696
+
1697
+ ## tsconfig.json
1698
+
1699
+ ```json
1700
+ {
1701
+ "extends": "@assistant-ui/x-buildutils/ts/base",
1702
+ "compilerOptions": {
1703
+ "target": "ES6",
1704
+ "module": "ESNext",
1705
+ "incremental": true,
1706
+ "plugins": [
1707
+ {
1708
+ "name": "next"
1709
+ }
1710
+ ],
1711
+ "allowJs": true,
1712
+ "strictNullChecks": true,
1713
+ "jsx": "preserve",
1714
+ "paths": {
1715
+ "@/*": ["./*"],
1716
+ "@assistant-ui/*": ["../../packages/*/src"],
1717
+ "@assistant-ui/react/*": ["../../packages/react/src/*"],
1718
+ "assistant-stream": ["../../packages/assistant-stream/src"],
1719
+ "assistant-stream/*": ["../../packages/assistant-stream/src/*"]
1720
+ }
1721
+ },
1722
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
1723
+ "exclude": ["node_modules"]
1724
+ }
1725
+
1726
+ ```
1727
+