@assistant-ui/mcp-docs-server 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/organized/code-examples/store-example.md +628 -0
- package/.docs/organized/code-examples/with-ag-ui.md +792 -178
- package/.docs/organized/code-examples/with-ai-sdk-v5.md +762 -209
- package/.docs/organized/code-examples/with-assistant-transport.md +707 -254
- package/.docs/organized/code-examples/with-cloud.md +848 -202
- package/.docs/organized/code-examples/with-custom-thread-list.md +1855 -0
- package/.docs/organized/code-examples/with-external-store.md +788 -172
- package/.docs/organized/code-examples/with-ffmpeg.md +796 -196
- package/.docs/organized/code-examples/with-langgraph.md +864 -230
- package/.docs/organized/code-examples/with-parent-id-grouping.md +785 -255
- package/.docs/organized/code-examples/with-react-hook-form.md +804 -226
- package/.docs/organized/code-examples/with-tanstack.md +1574 -0
- package/.docs/raw/blog/2024-07-29-hello/index.mdx +2 -3
- package/.docs/raw/docs/api-reference/overview.mdx +6 -6
- package/.docs/raw/docs/api-reference/primitives/ActionBar.mdx +85 -4
- package/.docs/raw/docs/api-reference/primitives/AssistantIf.mdx +200 -0
- package/.docs/raw/docs/api-reference/primitives/Composer.mdx +0 -20
- package/.docs/raw/docs/api-reference/primitives/Message.mdx +0 -45
- package/.docs/raw/docs/api-reference/primitives/Thread.mdx +0 -50
- package/.docs/raw/docs/cli.mdx +396 -0
- package/.docs/raw/docs/cloud/persistence/ai-sdk.mdx +2 -3
- package/.docs/raw/docs/cloud/persistence/langgraph.mdx +2 -3
- package/.docs/raw/docs/devtools.mdx +2 -3
- package/.docs/raw/docs/getting-started.mdx +37 -1109
- package/.docs/raw/docs/guides/Attachments.mdx +3 -25
- package/.docs/raw/docs/guides/Branching.mdx +1 -1
- package/.docs/raw/docs/guides/Speech.mdx +1 -1
- package/.docs/raw/docs/guides/ToolUI.mdx +1 -1
- package/.docs/raw/docs/legacy/styled/AssistantModal.mdx +2 -3
- package/.docs/raw/docs/legacy/styled/Decomposition.mdx +6 -5
- package/.docs/raw/docs/legacy/styled/Markdown.mdx +2 -3
- package/.docs/raw/docs/legacy/styled/Thread.mdx +2 -3
- package/.docs/raw/docs/react-compatibility.mdx +2 -5
- package/.docs/raw/docs/runtimes/ai-sdk/use-chat.mdx +3 -4
- package/.docs/raw/docs/runtimes/ai-sdk/v4-legacy.mdx +3 -6
- package/.docs/raw/docs/runtimes/assistant-transport.mdx +891 -0
- package/.docs/raw/docs/runtimes/custom/external-store.mdx +2 -3
- package/.docs/raw/docs/runtimes/custom/local.mdx +11 -41
- package/.docs/raw/docs/runtimes/data-stream.mdx +15 -11
- package/.docs/raw/docs/runtimes/langgraph/index.mdx +4 -4
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-2.mdx +1 -1
- package/.docs/raw/docs/runtimes/langgraph/tutorial/part-3.mdx +2 -3
- package/.docs/raw/docs/runtimes/langserve.mdx +2 -3
- package/.docs/raw/docs/runtimes/mastra/full-stack-integration.mdx +2 -3
- package/.docs/raw/docs/runtimes/mastra/separate-server-integration.mdx +2 -3
- package/.docs/raw/docs/ui/AssistantModal.mdx +3 -25
- package/.docs/raw/docs/ui/AssistantSidebar.mdx +2 -24
- package/.docs/raw/docs/ui/Attachment.mdx +3 -25
- package/.docs/raw/docs/ui/Markdown.mdx +2 -24
- package/.docs/raw/docs/ui/Mermaid.mdx +2 -24
- package/.docs/raw/docs/ui/Reasoning.mdx +2 -24
- package/.docs/raw/docs/ui/Scrollbar.mdx +4 -6
- package/.docs/raw/docs/ui/SyntaxHighlighting.mdx +3 -47
- package/.docs/raw/docs/ui/Thread.mdx +38 -53
- package/.docs/raw/docs/ui/ThreadList.mdx +4 -47
- package/.docs/raw/docs/ui/ToolFallback.mdx +2 -24
- package/package.json +15 -8
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
# Example: store-example
|
|
2
|
+
|
|
3
|
+
## app/globals.css
|
|
4
|
+
|
|
5
|
+
```css
|
|
6
|
+
@import "tailwindcss";
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--background: #ffffff;
|
|
10
|
+
--foreground: #171717;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@theme inline {
|
|
14
|
+
--color-background: var(--background);
|
|
15
|
+
--color-foreground: var(--foreground);
|
|
16
|
+
--font-sans: var(--font-geist-sans);
|
|
17
|
+
--font-mono: var(--font-geist-mono);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@media (prefers-color-scheme: dark) {
|
|
21
|
+
:root {
|
|
22
|
+
--background: #0a0a0a;
|
|
23
|
+
--foreground: #ededed;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
body {
|
|
28
|
+
background: var(--background);
|
|
29
|
+
color: var(--foreground);
|
|
30
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## app/layout.tsx
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import type { Metadata } from "next";
|
|
39
|
+
import { Geist, Geist_Mono } from "next/font/google";
|
|
40
|
+
import "./globals.css";
|
|
41
|
+
|
|
42
|
+
const geistSans = Geist({
|
|
43
|
+
variable: "--font-geist-sans",
|
|
44
|
+
subsets: ["latin"],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const geistMono = Geist_Mono({
|
|
48
|
+
variable: "--font-geist-mono",
|
|
49
|
+
subsets: ["latin"],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const metadata: Metadata = {
|
|
53
|
+
title: "Create Next App",
|
|
54
|
+
description: "Generated by create next app",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export default function RootLayout({
|
|
58
|
+
children,
|
|
59
|
+
}: Readonly<{
|
|
60
|
+
children: React.ReactNode;
|
|
61
|
+
}>) {
|
|
62
|
+
return (
|
|
63
|
+
<html lang="en">
|
|
64
|
+
<body
|
|
65
|
+
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## app/page.tsx
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { ExampleApp } from "@/lib/example-app";
|
|
79
|
+
|
|
80
|
+
export default function Home() {
|
|
81
|
+
return (
|
|
82
|
+
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100 p-8 dark:from-gray-900 dark:to-gray-800">
|
|
83
|
+
<div className="mx-auto max-w-4xl">
|
|
84
|
+
<div className="mb-8">
|
|
85
|
+
<h1 className="mb-2 font-bold text-4xl text-gray-900 dark:text-white">
|
|
86
|
+
@assistant-ui/store Example
|
|
87
|
+
</h1>
|
|
88
|
+
<p className="text-gray-600 text-lg dark:text-gray-400">
|
|
89
|
+
Demonstrating tap-based state management with scopes, lists, and
|
|
90
|
+
providers
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<ExampleApp />
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## lib/example-app.tsx
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
"use client";
|
|
106
|
+
|
|
107
|
+
import { useState } from "react";
|
|
108
|
+
import {
|
|
109
|
+
useAssistantClient,
|
|
110
|
+
AssistantProvider,
|
|
111
|
+
useAssistantState,
|
|
112
|
+
useAssistantEvent,
|
|
113
|
+
} from "@assistant-ui/store";
|
|
114
|
+
import { FooList, FooListResource } from "./store/foo-store";
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Single Foo component - displays and allows editing a single foo
|
|
118
|
+
*/
|
|
119
|
+
const Foo = () => {
|
|
120
|
+
const aui = useAssistantClient();
|
|
121
|
+
const fooState = useAssistantState(({ foo }) => {
|
|
122
|
+
console.log("selector called with state", foo);
|
|
123
|
+
return foo;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const handleUpdate = () => {
|
|
127
|
+
aui.foo().updateBar(`Updated at ${new Date().toLocaleTimeString()}`);
|
|
128
|
+
console.log("Foo state", aui.foo().getState(), fooState);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<div className="rounded-lg border border-gray-200 bg-white p-6 shadow-md transition-shadow hover:shadow-lg dark:border-gray-700 dark:bg-gray-800">
|
|
133
|
+
<div className="space-y-3">
|
|
134
|
+
<div className="flex items-center gap-2">
|
|
135
|
+
<span className="font-semibold text-gray-500 text-sm dark:text-gray-400">
|
|
136
|
+
ID:
|
|
137
|
+
</span>
|
|
138
|
+
<span className="font-mono text-gray-900 text-sm dark:text-white">
|
|
139
|
+
{fooState.id}
|
|
140
|
+
</span>
|
|
141
|
+
</div>
|
|
142
|
+
<div className="flex items-center gap-2">
|
|
143
|
+
<span className="font-semibold text-gray-500 text-sm dark:text-gray-400">
|
|
144
|
+
Value:
|
|
145
|
+
</span>
|
|
146
|
+
<span className="text-gray-900 dark:text-white">{fooState.bar}</span>
|
|
147
|
+
</div>
|
|
148
|
+
<div className="mt-2 flex gap-2">
|
|
149
|
+
<button
|
|
150
|
+
onClick={handleUpdate}
|
|
151
|
+
className="flex-1 rounded-md bg-blue-600 px-4 py-2 font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800"
|
|
152
|
+
>
|
|
153
|
+
Update
|
|
154
|
+
</button>
|
|
155
|
+
<button
|
|
156
|
+
onClick={() => aui.foo().remove()}
|
|
157
|
+
className="rounded-md bg-red-600 px-4 py-2 font-medium text-white transition-colors hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800"
|
|
158
|
+
>
|
|
159
|
+
Delete
|
|
160
|
+
</button>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const FooListLength = () => {
|
|
168
|
+
const fooListLength = useAssistantState(({ fooList }) => fooList.foos.length);
|
|
169
|
+
return (
|
|
170
|
+
<span className="text-gray-500 dark:text-gray-400">
|
|
171
|
+
({fooListLength} items)
|
|
172
|
+
</span>
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const AddFooButton = () => {
|
|
177
|
+
const aui = useAssistantClient();
|
|
178
|
+
return (
|
|
179
|
+
<button
|
|
180
|
+
onClick={() => aui.fooList().addFoo()}
|
|
181
|
+
className="rounded-md bg-green-600 px-4 py-2 font-medium text-white transition-colors hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800"
|
|
182
|
+
>
|
|
183
|
+
Add New
|
|
184
|
+
</button>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
type EventLogEntry = {
|
|
189
|
+
id: number;
|
|
190
|
+
event: string;
|
|
191
|
+
payload: unknown;
|
|
192
|
+
timestamp: Date;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
let idCounter = 0;
|
|
196
|
+
/**
|
|
197
|
+
* EventLog component - demonstrates event subscription
|
|
198
|
+
*/
|
|
199
|
+
const EventLog = () => {
|
|
200
|
+
const [logs, setLogs] = useState<EventLogEntry[]>([]);
|
|
201
|
+
|
|
202
|
+
// Subscribe to all events using the wildcard selector
|
|
203
|
+
useAssistantEvent("*", (data) => {
|
|
204
|
+
setLogs((prev) => [
|
|
205
|
+
{
|
|
206
|
+
id: ++idCounter,
|
|
207
|
+
event: data.event,
|
|
208
|
+
payload: data.payload,
|
|
209
|
+
timestamp: new Date(),
|
|
210
|
+
},
|
|
211
|
+
...prev.slice(0, 9), // Keep last 10 entries
|
|
212
|
+
]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
217
|
+
<h3 className="mb-3 font-semibold text-gray-900 dark:text-white">
|
|
218
|
+
Event Log
|
|
219
|
+
</h3>
|
|
220
|
+
<div className="max-h-48 space-y-2 overflow-y-auto">
|
|
221
|
+
{logs.length === 0 ? (
|
|
222
|
+
<p className="text-gray-500 text-sm dark:text-gray-400">
|
|
223
|
+
No events yet. Try updating or deleting a foo.
|
|
224
|
+
</p>
|
|
225
|
+
) : (
|
|
226
|
+
logs.map((log) => (
|
|
227
|
+
<div
|
|
228
|
+
key={log.id}
|
|
229
|
+
className="rounded border border-gray-100 bg-gray-50 p-2 font-mono text-xs dark:border-gray-600 dark:bg-gray-700"
|
|
230
|
+
>
|
|
231
|
+
<span className="font-semibold text-blue-600 dark:text-blue-400">
|
|
232
|
+
{log.event}
|
|
233
|
+
</span>
|
|
234
|
+
<span className="ml-2 text-gray-600 dark:text-gray-300">
|
|
235
|
+
{JSON.stringify(log.payload)}
|
|
236
|
+
</span>
|
|
237
|
+
<span className="ml-2 text-gray-400 dark:text-gray-500">
|
|
238
|
+
{log.timestamp.toLocaleTimeString()}
|
|
239
|
+
</span>
|
|
240
|
+
</div>
|
|
241
|
+
))
|
|
242
|
+
)}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Example App - demonstrates the store with styled components
|
|
250
|
+
*
|
|
251
|
+
* Note: The fooList scope is also registered in foo-scope.ts as a default,
|
|
252
|
+
* but we're explicitly passing it here for clarity in the example.
|
|
253
|
+
*/
|
|
254
|
+
export const ExampleApp = () => {
|
|
255
|
+
const rootClient = useAssistantClient({
|
|
256
|
+
fooList: FooListResource(),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<AssistantProvider client={rootClient}>
|
|
261
|
+
<div className="space-y-6">
|
|
262
|
+
<div className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
263
|
+
<div className="flex items-center justify-between">
|
|
264
|
+
<h2 className="font-semibold text-gray-900 text-xl dark:text-white">
|
|
265
|
+
Foo List <FooListLength />
|
|
266
|
+
</h2>
|
|
267
|
+
<AddFooButton />
|
|
268
|
+
</div>
|
|
269
|
+
<p className="mt-1 text-gray-600 text-sm dark:text-gray-400">
|
|
270
|
+
Each item is rendered in its own FooProvider with scoped access
|
|
271
|
+
</p>
|
|
272
|
+
</div>
|
|
273
|
+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
274
|
+
<FooList components={{ Foo }} />
|
|
275
|
+
</div>
|
|
276
|
+
<EventLog />
|
|
277
|
+
</div>
|
|
278
|
+
</AssistantProvider>
|
|
279
|
+
);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## lib/store/foo-scope.ts
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { registerAssistantScope } from "@assistant-ui/store";
|
|
288
|
+
|
|
289
|
+
declare module "@assistant-ui/store" {
|
|
290
|
+
interface AssistantScopeRegistry {
|
|
291
|
+
foo: {
|
|
292
|
+
value: {
|
|
293
|
+
getState: () => { id: string; bar: string };
|
|
294
|
+
updateBar: (newBar: string) => void;
|
|
295
|
+
remove: () => void;
|
|
296
|
+
};
|
|
297
|
+
meta: { source: "fooList"; query: { index: number } | { id: string } };
|
|
298
|
+
events: {
|
|
299
|
+
"foo.updated": { id: string; newValue: string };
|
|
300
|
+
"foo.removed": { id: string };
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
fooList: {
|
|
304
|
+
value: {
|
|
305
|
+
getState: () => { foos: Array<{ id: string; bar: string }> };
|
|
306
|
+
foo: (
|
|
307
|
+
lookup: { index: number } | { id: string },
|
|
308
|
+
) => AssistantScopeRegistry["foo"]["value"];
|
|
309
|
+
addFoo: (id?: string) => void;
|
|
310
|
+
};
|
|
311
|
+
meta: { source: "root"; query: Record<string, never> };
|
|
312
|
+
events: {
|
|
313
|
+
"fooList.added": { id: string };
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
registerAssistantScope({
|
|
320
|
+
name: "fooList",
|
|
321
|
+
defaultInitialize: { error: "FooList is not configured" },
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
registerAssistantScope({
|
|
325
|
+
name: "foo",
|
|
326
|
+
defaultInitialize: { error: "Foo is not configured" },
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## lib/store/foo-store.tsx
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
"use client";
|
|
335
|
+
|
|
336
|
+
// Import scope types first to ensure module augmentation is available
|
|
337
|
+
import "./foo-scope";
|
|
338
|
+
|
|
339
|
+
import React from "react";
|
|
340
|
+
import { resource, tapState } from "@assistant-ui/tap";
|
|
341
|
+
import {
|
|
342
|
+
useAssistantClient,
|
|
343
|
+
AssistantProvider,
|
|
344
|
+
tapApi,
|
|
345
|
+
tapStoreList,
|
|
346
|
+
DerivedScope,
|
|
347
|
+
useAssistantState,
|
|
348
|
+
tapStoreContext,
|
|
349
|
+
} from "@assistant-ui/store";
|
|
350
|
+
|
|
351
|
+
export const FooItemResource = resource(
|
|
352
|
+
({
|
|
353
|
+
initialValue: { id, initialBar },
|
|
354
|
+
remove,
|
|
355
|
+
}: {
|
|
356
|
+
initialValue: { id: string; initialBar: string };
|
|
357
|
+
remove: () => void;
|
|
358
|
+
}) => {
|
|
359
|
+
const { events } = tapStoreContext();
|
|
360
|
+
|
|
361
|
+
const [state, setState] = tapState<{ id: string; bar: string }>({
|
|
362
|
+
id,
|
|
363
|
+
bar: initialBar,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const updateBar = (newBar: string) => {
|
|
367
|
+
setState({ ...state, bar: newBar });
|
|
368
|
+
events.emit("foo.updated", { id, newValue: newBar });
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
const handleRemove = () => {
|
|
372
|
+
events.emit("foo.removed", { id });
|
|
373
|
+
remove();
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
return tapApi(
|
|
377
|
+
{
|
|
378
|
+
getState: () => state,
|
|
379
|
+
updateBar,
|
|
380
|
+
remove: handleRemove,
|
|
381
|
+
},
|
|
382
|
+
{ key: id },
|
|
383
|
+
);
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* FooList resource implementation
|
|
389
|
+
* Manages a list of foos using tapStoreList
|
|
390
|
+
*/
|
|
391
|
+
let counter = 3;
|
|
392
|
+
export const FooListResource = resource(() => {
|
|
393
|
+
const { events } = tapStoreContext();
|
|
394
|
+
const idGenerator = () => `foo-${++counter}`;
|
|
395
|
+
|
|
396
|
+
const foos = tapStoreList({
|
|
397
|
+
initialValues: [
|
|
398
|
+
{ id: "foo-1", initialBar: "First Foo" },
|
|
399
|
+
{ id: "foo-2", initialBar: "Second Foo" },
|
|
400
|
+
{ id: "foo-3", initialBar: "Third Foo" },
|
|
401
|
+
],
|
|
402
|
+
resource: FooItemResource,
|
|
403
|
+
idGenerator,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const addFoo = (id?: string) => {
|
|
407
|
+
const newId = id ?? idGenerator();
|
|
408
|
+
foos.add(newId);
|
|
409
|
+
events.emit("fooList.added", { id: newId });
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
return tapApi({
|
|
413
|
+
getState: () => ({ foos: foos.state }),
|
|
414
|
+
foo: foos.api,
|
|
415
|
+
addFoo,
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* FooProvider - Provides foo scope for a specific index
|
|
421
|
+
*/
|
|
422
|
+
export const FooProvider = ({
|
|
423
|
+
index,
|
|
424
|
+
children,
|
|
425
|
+
}: {
|
|
426
|
+
index: number;
|
|
427
|
+
children: React.ReactNode;
|
|
428
|
+
}) => {
|
|
429
|
+
// Create a derived client with the foo scope at the specified index
|
|
430
|
+
const aui = useAssistantClient({
|
|
431
|
+
foo: DerivedScope({
|
|
432
|
+
source: "fooList",
|
|
433
|
+
query: { index },
|
|
434
|
+
get: (aui) => aui.fooList().foo({ index }),
|
|
435
|
+
}),
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
return <AssistantProvider client={aui}>{children}</AssistantProvider>;
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* FooList component - minimal mapping component
|
|
443
|
+
* Maps over the list and renders each item in a FooProvider
|
|
444
|
+
*/
|
|
445
|
+
export const FooList = ({
|
|
446
|
+
components,
|
|
447
|
+
}: {
|
|
448
|
+
components: { Foo: React.ComponentType };
|
|
449
|
+
}) => {
|
|
450
|
+
const fooListState = useAssistantState(({ fooList }) => fooList.foos.length);
|
|
451
|
+
|
|
452
|
+
return (
|
|
453
|
+
<>
|
|
454
|
+
{Array.from({ length: fooListState }, (_, index) => (
|
|
455
|
+
<FooProvider key={index} index={index}>
|
|
456
|
+
<components.Foo />
|
|
457
|
+
</FooProvider>
|
|
458
|
+
))}
|
|
459
|
+
</>
|
|
460
|
+
);
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## next.config.ts
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
import type { NextConfig } from "next";
|
|
469
|
+
|
|
470
|
+
const nextConfig: NextConfig = {
|
|
471
|
+
/* config options here */
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
export default nextConfig;
|
|
475
|
+
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## package.json
|
|
479
|
+
|
|
480
|
+
```json
|
|
481
|
+
{
|
|
482
|
+
"name": "store-example",
|
|
483
|
+
"version": "0.1.0",
|
|
484
|
+
"private": true,
|
|
485
|
+
"scripts": {
|
|
486
|
+
"dev": "next dev",
|
|
487
|
+
"build": "next build",
|
|
488
|
+
"start": "next start"
|
|
489
|
+
},
|
|
490
|
+
"dependencies": {
|
|
491
|
+
"@assistant-ui/store": "workspace:*",
|
|
492
|
+
"@assistant-ui/tap": "workspace:*",
|
|
493
|
+
"next": "16.0.7",
|
|
494
|
+
"react": "19.2.1",
|
|
495
|
+
"react-dom": "19.2.1"
|
|
496
|
+
},
|
|
497
|
+
"devDependencies": {
|
|
498
|
+
"@assistant-ui/x-buildutils": "workspace:*",
|
|
499
|
+
"@tailwindcss/postcss": "^4",
|
|
500
|
+
"@types/node": "^24",
|
|
501
|
+
"@types/react": "19.2.7",
|
|
502
|
+
"@types/react-dom": "19.2.3",
|
|
503
|
+
"tailwindcss": "^4",
|
|
504
|
+
"typescript": "^5"
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
## README.md
|
|
511
|
+
|
|
512
|
+
```markdown
|
|
513
|
+
# @assistant-ui/store Example App
|
|
514
|
+
|
|
515
|
+
This is a Next.js application demonstrating the `@assistant-ui/store` package.
|
|
516
|
+
|
|
517
|
+
## Features Demonstrated
|
|
518
|
+
|
|
519
|
+
- **Scope Definition**: Module augmentation for type-safe scopes
|
|
520
|
+
- **tapApi**: Wrapping API objects for stability and reactivity
|
|
521
|
+
- **tapLookupResources**: Managing lists with index and ID lookup
|
|
522
|
+
- **Provider Pattern**: Scoped access to list items via FooProvider
|
|
523
|
+
- **Component Composition**: Render props pattern with components prop
|
|
524
|
+
|
|
525
|
+
## Getting Started
|
|
526
|
+
|
|
527
|
+
```bash
|
|
528
|
+
# Install dependencies (from monorepo root)
|
|
529
|
+
pnpm install
|
|
530
|
+
|
|
531
|
+
# Run the development server
|
|
532
|
+
cd examples/store-example
|
|
533
|
+
pnpm dev
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Open [http://localhost:3000](http://localhost:3000) to see the example.
|
|
537
|
+
|
|
538
|
+
## Project Structure
|
|
539
|
+
|
|
540
|
+
- `lib/store/foo-store.tsx` - Clean store implementation with:
|
|
541
|
+
- Scope definitions (foo, fooList) via module augmentation
|
|
542
|
+
- Resource implementations (FooItemResource, FooListResource)
|
|
543
|
+
- Provider component (FooProvider)
|
|
544
|
+
- Minimal FooList mapping component
|
|
545
|
+
- `lib/example-app.tsx` - Example app with styled components:
|
|
546
|
+
- Styled Foo component
|
|
547
|
+
- ExampleApp with layout and styling
|
|
548
|
+
- `app/page.tsx` - Main page that renders the ExampleApp
|
|
549
|
+
|
|
550
|
+
## Key Concepts
|
|
551
|
+
|
|
552
|
+
### Scope Definition
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
declare module "@assistant-ui/store" {
|
|
556
|
+
namespace AssistantStore {
|
|
557
|
+
interface Scopes {
|
|
558
|
+
foo: {
|
|
559
|
+
value: {
|
|
560
|
+
getState: () => { id: string; bar: string };
|
|
561
|
+
updateBar: (newBar: string) => void;
|
|
562
|
+
};
|
|
563
|
+
source: "fooList";
|
|
564
|
+
query: { index: number } | { id: string };
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Resource Implementation
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
const FooListResource = resource(() => {
|
|
575
|
+
const items = [
|
|
576
|
+
/* ... */
|
|
577
|
+
];
|
|
578
|
+
const foos = tapLookupResources(
|
|
579
|
+
items.map((item) => FooItemResource(item, { key: item.id })),
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
return tapApi({
|
|
583
|
+
getState: () => ({ foos: foos.state }),
|
|
584
|
+
foo: foos.api,
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Provider Pattern
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
const FooProvider = ({ index, children }) => {
|
|
593
|
+
const parentAui = useAssistantClient();
|
|
594
|
+
const aui = useAssistantClient({
|
|
595
|
+
foo: resource(() => parentAui.fooList().foo({ index }))(),
|
|
596
|
+
});
|
|
597
|
+
return <AssistantClientProvider client={aui}>{children}</AssistantClientProvider>;
|
|
598
|
+
};
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
## Learn More
|
|
602
|
+
|
|
603
|
+
- [@assistant-ui/store Documentation](../store/README.md)
|
|
604
|
+
- [Next.js Documentation](https://nextjs.org/docs)
|
|
605
|
+
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## tsconfig.json
|
|
609
|
+
|
|
610
|
+
```json
|
|
611
|
+
{
|
|
612
|
+
"extends": "@assistant-ui/x-buildutils/ts/base",
|
|
613
|
+
"compilerOptions": {
|
|
614
|
+
"paths": {
|
|
615
|
+
"@/*": ["./*"],
|
|
616
|
+
"@assistant-ui/*": ["../../packages/*/src"],
|
|
617
|
+
"@assistant-ui/react/*": ["../../packages/react/src/*"],
|
|
618
|
+
"@assistant-ui/tap/*": ["../../packages/tap/src/*"],
|
|
619
|
+
"assistant-stream": ["../../packages/assistant-stream/src"],
|
|
620
|
+
"assistant-stream/*": ["../../packages/assistant-stream/src/*"]
|
|
621
|
+
}
|
|
622
|
+
},
|
|
623
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
624
|
+
"exclude": ["node_modules"]
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
```
|
|
628
|
+
|