@tanstack/ai-svelte 0.19.4 → 0.21.0

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 (46) hide show
  1. package/dist/chat-ui/create-chat-hook.d.ts +27 -0
  2. package/dist/chat-ui/create-chat-hook.js +26 -0
  3. package/dist/chat-ui/create-ui.d.ts +131 -0
  4. package/dist/chat-ui/create-ui.js +100 -0
  5. package/dist/chat-ui/selected-part.svelte +45 -0
  6. package/dist/chat-ui/selected-part.svelte.d.ts +9 -0
  7. package/dist/chat-ui/ui-chat.svelte +42 -0
  8. package/dist/chat-ui/ui-chat.svelte.d.ts +9 -0
  9. package/dist/chat-ui/ui-interrupt.svelte +30 -0
  10. package/dist/chat-ui/ui-interrupt.svelte.d.ts +9 -0
  11. package/dist/chat-ui/ui-interrupts.svelte +30 -0
  12. package/dist/chat-ui/ui-interrupts.svelte.d.ts +9 -0
  13. package/dist/chat-ui/ui-message.svelte +45 -0
  14. package/dist/chat-ui/ui-message.svelte.d.ts +12 -0
  15. package/dist/chat-ui/ui-messages.svelte +31 -0
  16. package/dist/chat-ui/ui-messages.svelte.d.ts +10 -0
  17. package/dist/chat-ui/ui-part.svelte +30 -0
  18. package/dist/chat-ui/ui-part.svelte.d.ts +9 -0
  19. package/dist/chat-ui/ui-provider.svelte +29 -0
  20. package/dist/chat-ui/ui-provider.svelte.d.ts +10 -0
  21. package/dist/chat-ui/ui-queue.svelte +21 -0
  22. package/dist/chat-ui/ui-queue.svelte.d.ts +7 -0
  23. package/dist/create-chat-hook.d.ts +35 -0
  24. package/dist/create-chat-hook.js +31 -0
  25. package/dist/create-web-mcp-tools.svelte.d.ts +24 -0
  26. package/dist/create-web-mcp-tools.svelte.js +29 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.js +2 -0
  29. package/dist/ui.d.ts +11 -0
  30. package/dist/ui.js +11 -0
  31. package/package.json +9 -4
  32. package/src/chat-ui/create-chat-hook.ts +62 -0
  33. package/src/chat-ui/create-ui.ts +310 -0
  34. package/src/chat-ui/selected-part.svelte +45 -0
  35. package/src/chat-ui/ui-chat.svelte +42 -0
  36. package/src/chat-ui/ui-interrupt.svelte +30 -0
  37. package/src/chat-ui/ui-interrupts.svelte +30 -0
  38. package/src/chat-ui/ui-message.svelte +45 -0
  39. package/src/chat-ui/ui-messages.svelte +31 -0
  40. package/src/chat-ui/ui-part.svelte +30 -0
  41. package/src/chat-ui/ui-provider.svelte +29 -0
  42. package/src/chat-ui/ui-queue.svelte +21 -0
  43. package/src/create-chat-hook.ts +67 -0
  44. package/src/create-web-mcp-tools.svelte.ts +57 -0
  45. package/src/index.ts +3 -0
  46. package/src/ui.ts +25 -0
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import {
3
+ getChatContext,
4
+ getComponentsContext,
5
+ readQueue,
6
+ type UIDescriptor,
7
+ } from './create-ui'
8
+
9
+ let { ui }: { ui: UIDescriptor } = $props()
10
+
11
+ const chat = $derived(getChatContext(ui))
12
+ const comps = $derived(getComponentsContext(ui))
13
+ const items = $derived(readQueue(chat))
14
+ const QueueItem = $derived(comps.components.queue as any)
15
+ </script>
16
+
17
+ {#if QueueItem}
18
+ {#each items as item (item.id)}
19
+ <QueueItem {item} />
20
+ {/each}
21
+ {/if}
@@ -0,0 +1,7 @@
1
+ import { type UIDescriptor } from './create-ui';
2
+ type $$ComponentProps = {
3
+ ui: UIDescriptor;
4
+ };
5
+ declare const UiQueue: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type UiQueue = ReturnType<typeof UiQueue>;
7
+ export default UiQueue;
@@ -0,0 +1,35 @@
1
+ import type { AnyClientTool, InterruptDefinition, SchemaInput } from '@tanstack/ai';
2
+ import type { InferredClientContext } from '@tanstack/ai-client';
3
+ import type { CreateChatOptions } from './types';
4
+ type ChatHookOverrides<TTools extends ReadonlyArray<AnyClientTool>, TSchema extends SchemaInput | undefined, TContext, TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>>> = {
5
+ threadId?: string;
6
+ live?: boolean;
7
+ forwardedProps?: Record<string, any>;
8
+ body?: Record<string, any>;
9
+ initialMessages?: CreateChatOptions<TTools, TSchema, TContext, TInterrupts>['initialMessages'];
10
+ };
11
+ /**
12
+ * Bind chat options once at module scope. The returned `createChat`
13
+ * function creates a chat instance from those options.
14
+ *
15
+ * Pass per-call overrides for instance keys such as `threadId`,
16
+ * `initialMessages`, `live`, and `forwardedProps`. Do not change `tools`,
17
+ * `interrupts`, or `outputSchema` here. Those stay on the factory options.
18
+ *
19
+ * Rename the function at the call site if you already import `createChat` from
20
+ * `@tanstack/ai-svelte`: `const { createChat: createAppChat } = createChatHook(chatOptions)`.
21
+ *
22
+ * @example
23
+ * ```svelte
24
+ * <script>
25
+ * import { createChatHook } from '@tanstack/ai-svelte'
26
+ *
27
+ * const { createChat } = createChatHook(chatOptions)
28
+ * const chat = createChat({ threadId: 'support-1' })
29
+ * </script>
30
+ * ```
31
+ */
32
+ export declare function createChatHook<const TTools extends ReadonlyArray<AnyClientTool> = any, TSchema extends SchemaInput | undefined = undefined, TContext = InferredClientContext<TTools>, const TInterrupts extends ReadonlyArray<InterruptDefinition<any, any, any, any>> = readonly []>(options: CreateChatOptions<TTools, TSchema, TContext, TInterrupts>): {
33
+ createChat: (overrides?: ChatHookOverrides<TTools, TSchema, TContext, TInterrupts>) => import("./types").CreateChatReturn<TTools, TSchema, TContext, TInterrupts>;
34
+ };
35
+ export {};
@@ -0,0 +1,31 @@
1
+ import { createChat as createUnboundChat } from './create-chat.svelte';
2
+ /**
3
+ * Bind chat options once at module scope. The returned `createChat`
4
+ * function creates a chat instance from those options.
5
+ *
6
+ * Pass per-call overrides for instance keys such as `threadId`,
7
+ * `initialMessages`, `live`, and `forwardedProps`. Do not change `tools`,
8
+ * `interrupts`, or `outputSchema` here. Those stay on the factory options.
9
+ *
10
+ * Rename the function at the call site if you already import `createChat` from
11
+ * `@tanstack/ai-svelte`: `const { createChat: createAppChat } = createChatHook(chatOptions)`.
12
+ *
13
+ * @example
14
+ * ```svelte
15
+ * <script>
16
+ * import { createChatHook } from '@tanstack/ai-svelte'
17
+ *
18
+ * const { createChat } = createChatHook(chatOptions)
19
+ * const chat = createChat({ threadId: 'support-1' })
20
+ * </script>
21
+ * ```
22
+ */
23
+ export function createChatHook(options) {
24
+ function createChat(overrides) {
25
+ if (!overrides) {
26
+ return createUnboundChat(options);
27
+ }
28
+ return createUnboundChat({ ...options, ...overrides });
29
+ }
30
+ return { createChat };
31
+ }
@@ -0,0 +1,24 @@
1
+ import type { AnyClientTool, InferredClientContext, RegisterWebMCPToolsOptions } from '@tanstack/ai-client';
2
+ /** Options for {@link createWebMCPTools}. */
3
+ export type CreateWebMCPToolsOptions<TTools extends ReadonlyArray<AnyClientTool>, TContext = InferredClientContext<TTools>> = Omit<RegisterWebMCPToolsOptions<TTools, TContext>, 'signal'> & {
4
+ /** Receives an asynchronous registration error. */
5
+ onError?: (error: unknown) => void;
6
+ };
7
+ type CreateWebMCPToolsArguments<TTools extends ReadonlyArray<AnyClientTool>, TContext> = {} extends CreateWebMCPToolsOptions<TTools, TContext> ? [options?: CreateWebMCPToolsOptions<TTools, TContext>] : [options: CreateWebMCPToolsOptions<TTools, TContext>];
8
+ /**
9
+ * Registers executable client tools with WebMCP for the current Svelte component.
10
+ *
11
+ * Component destruction removes every tool registered by this call.
12
+ *
13
+ * @param tools - The executable client tools to expose through WebMCP.
14
+ * @param options - Runtime context, per-tool options, and error handling.
15
+ *
16
+ * @example
17
+ * ```svelte
18
+ * <script lang="ts">
19
+ * createWebMCPTools([searchProducts])
20
+ * </script>
21
+ * ```
22
+ */
23
+ export declare function createWebMCPTools<const TTools extends ReadonlyArray<AnyClientTool>, TContext = InferredClientContext<TTools>>(tools: TTools, ...[options]: CreateWebMCPToolsArguments<TTools, TContext>): void;
24
+ export {};
@@ -0,0 +1,29 @@
1
+ import { onDestroy } from 'svelte';
2
+ import { registerWebMCPTools } from '@tanstack/ai-client';
3
+ /**
4
+ * Registers executable client tools with WebMCP for the current Svelte component.
5
+ *
6
+ * Component destruction removes every tool registered by this call.
7
+ *
8
+ * @param tools - The executable client tools to expose through WebMCP.
9
+ * @param options - Runtime context, per-tool options, and error handling.
10
+ *
11
+ * @example
12
+ * ```svelte
13
+ * <script lang="ts">
14
+ * createWebMCPTools([searchProducts])
15
+ * </script>
16
+ * ```
17
+ */
18
+ export function createWebMCPTools(tools, ...[options]) {
19
+ const controller = new AbortController();
20
+ void registerWebMCPTools(tools, {
21
+ ...options,
22
+ signal: controller.signal,
23
+ }).catch((error) => {
24
+ if (!controller.signal.aborted) {
25
+ options?.onError?.(error);
26
+ }
27
+ });
28
+ onDestroy(() => controller.abort());
29
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { createChat } from './create-chat.svelte';
2
+ export { createChatHook } from './create-chat-hook';
2
3
  export { createByok } from './create-byok.svelte';
4
+ export { createWebMCPTools } from './create-web-mcp-tools.svelte';
5
+ export type { CreateWebMCPToolsOptions } from './create-web-mcp-tools.svelte';
3
6
  export type { CreateChatOptions, CreateChatReturn, DeepPartial, UIMessage, ChatRequestBody, QueuedMessage, SendMessageOptions, WhenBusy, QueueConfig, QueueStrategy, QueueOption, } from './types';
4
7
  export { createGeneration } from './create-generation.svelte';
5
8
  export type { CreateGenerationOptions, CreateGenerationReturn, } from './create-generation.svelte';
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export { createChat } from './create-chat.svelte';
2
+ export { createChatHook } from './create-chat-hook';
2
3
  export { createByok } from './create-byok.svelte';
4
+ export { createWebMCPTools } from './create-web-mcp-tools.svelte';
3
5
  // Generation hooks
4
6
  export { createGeneration } from './create-generation.svelte';
5
7
  export { createGenerateImage } from './create-generate-image.svelte';
package/dist/ui.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { createChatUI, getUIContext } from './chat-ui/create-ui';
2
+ export { createChatHook } from './chat-ui/create-chat-hook';
3
+ export type { ChatUIComponents, ChatUIFactoryConfig, ChatUIHost, ChatUIQueueItem, InputProps, InterruptProps, LayoutProps, MessageProps, PartProps, QueueProps, ToolProps, UIDescriptor, } from './chat-ui/create-ui';
4
+ export { default as UIChat } from './chat-ui/ui-chat.svelte';
5
+ export { default as UIProvider } from './chat-ui/ui-provider.svelte';
6
+ export { default as UIMessages } from './chat-ui/ui-messages.svelte';
7
+ export { default as UIMessage } from './chat-ui/ui-message.svelte';
8
+ export { default as UIPart } from './chat-ui/ui-part.svelte';
9
+ export { default as UIInterrupts } from './chat-ui/ui-interrupts.svelte';
10
+ export { default as UIInterrupt } from './chat-ui/ui-interrupt.svelte';
11
+ export { default as UIQueue } from './chat-ui/ui-queue.svelte';
package/dist/ui.js ADDED
@@ -0,0 +1,11 @@
1
+ // Barrel entry for the `@tanstack/ai-svelte/ui` subpath.
2
+ export { createChatUI, getUIContext } from './chat-ui/create-ui';
3
+ export { createChatHook } from './chat-ui/create-chat-hook';
4
+ export { default as UIChat } from './chat-ui/ui-chat.svelte';
5
+ export { default as UIProvider } from './chat-ui/ui-provider.svelte';
6
+ export { default as UIMessages } from './chat-ui/ui-messages.svelte';
7
+ export { default as UIMessage } from './chat-ui/ui-message.svelte';
8
+ export { default as UIPart } from './chat-ui/ui-part.svelte';
9
+ export { default as UIInterrupts } from './chat-ui/ui-interrupts.svelte';
10
+ export { default as UIInterrupt } from './chat-ui/ui-interrupt.svelte';
11
+ export { default as UIQueue } from './chat-ui/ui-queue.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-svelte",
3
- "version": "0.19.4",
3
+ "version": "0.21.0",
4
4
  "description": "Svelte 5 bindings for TanStack AI streaming chat, structured outputs, and media generation.",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -26,6 +26,11 @@
26
26
  "types": "./dist/index.d.ts",
27
27
  "svelte": "./dist/index.js",
28
28
  "import": "./dist/index.js"
29
+ },
30
+ "./ui": {
31
+ "types": "./dist/ui.d.ts",
32
+ "svelte": "./dist/ui.js",
33
+ "import": "./dist/ui.js"
29
34
  }
30
35
  },
31
36
  "files": [
@@ -46,11 +51,11 @@
46
51
  "media-generation"
47
52
  ],
48
53
  "dependencies": {
49
- "@tanstack/ai-client": "^0.29.2"
54
+ "@tanstack/ai-client": "^0.31.0"
50
55
  },
51
56
  "peerDependencies": {
52
57
  "svelte": "^5.0.0",
53
- "@tanstack/ai": "^0.52.0"
58
+ "@tanstack/ai": "^0.53.0"
54
59
  },
55
60
  "devDependencies": {
56
61
  "@standard-schema/spec": "^1.1.0",
@@ -63,7 +68,7 @@
63
68
  "svelte-check": "^4.2.0",
64
69
  "typescript": "5.9.3",
65
70
  "vite": "^8.2.1",
66
- "@tanstack/ai": "0.52.0"
71
+ "@tanstack/ai": "0.53.0"
67
72
  },
68
73
  "scripts": {
69
74
  "clean": "rm -rf ./dist",
@@ -0,0 +1,62 @@
1
+ import type { InferredClientContext } from '@tanstack/ai-client'
2
+ import type {
3
+ ChatUIInterruptsOf,
4
+ ChatUISchemaOf,
5
+ ChatUIToolsOf,
6
+ } from '@tanstack/ai-client/ui'
7
+ import { createChat as createUnboundChat } from '../create-chat.svelte'
8
+ import type { CreateChatOptions } from '../types'
9
+ import { createChatUI } from './create-ui'
10
+ import type { ChatUIFactoryConfig, ChatUIHost } from './create-ui'
11
+
12
+ type HeadlessOptions<TOptions> = CreateChatOptions<
13
+ ChatUIToolsOf<TOptions>,
14
+ ChatUISchemaOf<TOptions>,
15
+ InferredClientContext<ChatUIToolsOf<TOptions>>,
16
+ ChatUIInterruptsOf<TOptions>
17
+ >
18
+
19
+ type ChatInstanceOverrides<TOptions> = {
20
+ threadId?: string
21
+ live?: boolean
22
+ forwardedProps?: Record<string, any>
23
+ body?: Record<string, any>
24
+ initialMessages?: HeadlessOptions<TOptions>['initialMessages']
25
+ }
26
+
27
+ /**
28
+ * Bind chat options and UI widgets once at module scope.
29
+ *
30
+ * Returns a bound `createAppChat`, the UI descriptor (`ui`), and
31
+ * `useChatContext`. Pass `{ui}` into `UIChat`. Pass instance overrides
32
+ * such as `threadId` into `createAppChat()`.
33
+ */
34
+ export function createChatHook<const TOptions>({
35
+ options,
36
+ ...chatComponents
37
+ }: {
38
+ options: TOptions
39
+ } & ChatUIFactoryConfig<NoInfer<TOptions>>) {
40
+ const ui = createChatUI(
41
+ options,
42
+ chatComponents as ChatUIFactoryConfig<NoInfer<TOptions>>,
43
+ )
44
+
45
+ function createAppChat(overrides?: ChatInstanceOverrides<TOptions>) {
46
+ if (!overrides) {
47
+ return createUnboundChat(
48
+ options as HeadlessOptions<TOptions>,
49
+ ) as ChatUIHost<TOptions>
50
+ }
51
+ return createUnboundChat({
52
+ ...(options as HeadlessOptions<TOptions>),
53
+ ...overrides,
54
+ }) as ChatUIHost<TOptions>
55
+ }
56
+
57
+ return {
58
+ createAppChat,
59
+ ui,
60
+ useChatContext: ui.useChatContext,
61
+ }
62
+ }
@@ -0,0 +1,310 @@
1
+ import { getContext, setContext } from 'svelte'
2
+ import {
3
+ automaticPartsForMessage,
4
+ collectInlineToolNames,
5
+ resolveInterruptComponent,
6
+ selectChatUI,
7
+ selectMessageUI,
8
+ } from '@tanstack/ai-client/ui'
9
+ import type {
10
+ ChatUIData,
11
+ ChatUIHasNamedInterrupts,
12
+ ChatUIHasNamedTools,
13
+ ChatUIInterrupt,
14
+ ChatUIInterruptName,
15
+ ChatUIInterruptOf,
16
+ ChatUIInterruptsOf,
17
+ ChatUINamedInterruptId,
18
+ ChatUIPartKey,
19
+ ChatUIPartOf,
20
+ ChatUISchemaOf,
21
+ ChatUISelectedPart,
22
+ ChatUIToolApproval,
23
+ ChatUIToolName,
24
+ ChatUIToolsOf,
25
+ } from '@tanstack/ai-client/ui'
26
+ import type {
27
+ QueuedMessage,
28
+ ToolCallPart,
29
+ ToolResultPart,
30
+ UIMessage,
31
+ } from '@tanstack/ai-client'
32
+ import type { CreateChatReturn } from '../types'
33
+
34
+ export type ChatUIHost<TOptions = unknown> = CreateChatReturn<
35
+ ChatUIToolsOf<TOptions>,
36
+ ChatUISchemaOf<TOptions>,
37
+ unknown,
38
+ ChatUIInterruptsOf<TOptions>
39
+ >
40
+
41
+ type GenericInterruptComponents<TOptions> =
42
+ ChatUIHasNamedInterrupts<TOptions> extends true
43
+ ? {
44
+ [K in ChatUINamedInterruptId<TOptions>]: unknown
45
+ } & {
46
+ fallback?: unknown
47
+ }
48
+ : {
49
+ fallback?: unknown
50
+ }
51
+
52
+ type ToolApprovalMap<TOptions> = {
53
+ [K in ChatUIToolName<TOptions>]?: unknown
54
+ }
55
+
56
+ /** The chrome around the message list: `layout`, `message`, and `input`. */
57
+ export type ChatUIChromeComponents = {
58
+ layout: unknown
59
+ message: unknown
60
+ input?: unknown
61
+ queue?: unknown
62
+ }
63
+
64
+ export type ChatUIPartsComponents = {
65
+ [K in ChatUIPartKey]?: unknown
66
+ } & {
67
+ fallback?: unknown
68
+ }
69
+
70
+ export type ChatUIInterruptsComponents<TOptions> = {
71
+ tools?: ToolApprovalMap<TOptions>
72
+ generic: GenericInterruptComponents<TOptions>
73
+ }
74
+
75
+ /** The flat shape the runtime reads, assembled from the factory config. */
76
+ export type ChatUIComponents<TOptions> = ChatUIChromeComponents & {
77
+ parts: ChatUIPartsComponents
78
+ tools?: { [K in ChatUIToolName<TOptions>]?: unknown }
79
+ interrupts?: {
80
+ tools?: ToolApprovalMap<TOptions>
81
+ generic?: GenericInterruptComponents<TOptions>
82
+ }
83
+ }
84
+
85
+ export type ChatUIFactoryConfig<TOptions> = {
86
+ components: ChatUIChromeComponents
87
+ partsComponents: ChatUIPartsComponents
88
+ } & (ChatUIHasNamedTools<TOptions> extends true
89
+ ? { toolsComponents: { [K in ChatUIToolName<TOptions>]: unknown } }
90
+ : { toolsComponents?: { [K in ChatUIToolName<TOptions>]?: unknown } }) &
91
+ (ChatUIHasNamedInterrupts<TOptions> extends true
92
+ ? { interruptsComponents: ChatUIInterruptsComponents<TOptions> }
93
+ : {
94
+ interruptsComponents?: {
95
+ tools?: ToolApprovalMap<TOptions>
96
+ generic?: GenericInterruptComponents<TOptions>
97
+ }
98
+ })
99
+
100
+ export type UIDescriptor<TOptions = unknown> = {
101
+ key: symbol
102
+ warn: (key: string, message: string) => void
103
+ components: ChatUIComponents<TOptions>
104
+ useChatContext: () => ChatUIHost<TOptions>
105
+ }
106
+
107
+ export type UIContextValue<TOptions = unknown> = {
108
+ chat: ChatUIHost<TOptions>
109
+ components: ChatUIComponents<TOptions>
110
+ ui: UIDescriptor<TOptions>
111
+ }
112
+
113
+ type ComponentsValue<TOptions = unknown> = {
114
+ components: ChatUIComponents<TOptions>
115
+ warn: (key: string, message: string) => void
116
+ inlineToolNames: ReadonlyArray<string>
117
+ }
118
+
119
+ type UIRuntime<TOptions = unknown> = UIDescriptor<TOptions> & {
120
+ componentsKey: symbol
121
+ }
122
+
123
+ function createWarnOnce() {
124
+ const seen = new Set<string>()
125
+ return (key: string, message: string) => {
126
+ if (process.env.NODE_ENV === 'production') return
127
+ if (seen.has(key)) return
128
+ seen.add(key)
129
+ console.warn(message)
130
+ }
131
+ }
132
+
133
+ export function createChatUI<const TOptions>(
134
+ options: TOptions,
135
+ config: ChatUIFactoryConfig<NoInfer<TOptions>>,
136
+ ): UIDescriptor<TOptions> {
137
+ void options
138
+ const {
139
+ components: chrome,
140
+ partsComponents,
141
+ toolsComponents,
142
+ interruptsComponents,
143
+ } = config as ChatUIFactoryConfig<TOptions> & {
144
+ toolsComponents?: Record<string, unknown>
145
+ interruptsComponents?: {
146
+ tools?: Record<string, unknown>
147
+ generic?: Record<string, unknown>
148
+ }
149
+ }
150
+ // The runtime keeps the flat shape the rest of this module reads.
151
+ const components = {
152
+ ...chrome,
153
+ parts: partsComponents,
154
+ tools: toolsComponents,
155
+ interrupts: interruptsComponents,
156
+ } as ChatUIComponents<TOptions>
157
+ const ui: UIRuntime<TOptions> = {
158
+ key: Symbol('tanstack-ai-ui-chat'),
159
+ componentsKey: Symbol('tanstack-ai-ui-components'),
160
+ warn: createWarnOnce(),
161
+ components,
162
+ useChatContext() {
163
+ return getChatContext(ui)
164
+ },
165
+ }
166
+ return ui
167
+ }
168
+
169
+ export function setUIContext<TOptions>(value: UIContextValue<TOptions>) {
170
+ setContext(value.ui.key, value.chat)
171
+ setContext((value.ui as UIRuntime).componentsKey, {
172
+ components: value.components,
173
+ warn: value.ui.warn,
174
+ inlineToolNames: inlineNames(value.components),
175
+ } satisfies ComponentsValue<TOptions>)
176
+ }
177
+
178
+ export function getChatContext<TOptions>(ui: UIDescriptor<TOptions>) {
179
+ const chat = getContext<ChatUIHost<TOptions> | undefined>(ui.key)
180
+ if (!chat) {
181
+ throw new Error(
182
+ 'Chat UI components must be wrapped in UIProvider or UIChat.',
183
+ )
184
+ }
185
+ return chat
186
+ }
187
+
188
+ export function getComponentsContext<TOptions>(ui: UIDescriptor<TOptions>) {
189
+ const value = getContext<ComponentsValue<TOptions> | undefined>(
190
+ (ui as UIRuntime).componentsKey,
191
+ )
192
+ if (!value) {
193
+ throw new Error(
194
+ 'Chat UI components must be wrapped in UIProvider or UIChat.',
195
+ )
196
+ }
197
+ return value
198
+ }
199
+
200
+ export function getUIContext<TOptions>(ui: UIDescriptor<TOptions>) {
201
+ return {
202
+ ui,
203
+ chat: getChatContext(ui),
204
+ components: getComponentsContext(ui).components,
205
+ } satisfies UIContextValue<TOptions>
206
+ }
207
+
208
+ export function readMessages<TOptions>(chat: ChatUIHost<TOptions>) {
209
+ return chat.messages ?? []
210
+ }
211
+
212
+ export function readInterrupts<TOptions>(chat: ChatUIHost<TOptions>) {
213
+ return chat.interrupts ?? []
214
+ }
215
+
216
+ export function readQueue<TOptions>(chat: ChatUIHost<TOptions>) {
217
+ const items = chat.queue ?? []
218
+ return items.map((item) => ({
219
+ ...item,
220
+ cancelQueued: () => {
221
+ chat.cancelQueued(item.id)
222
+ },
223
+ }))
224
+ }
225
+
226
+ export function inlineNames<TOptions>(components: ChatUIComponents<TOptions>) {
227
+ return collectInlineToolNames(
228
+ components.interrupts?.tools as Record<string, unknown> | undefined,
229
+ Object.keys(components.tools ?? {}),
230
+ )
231
+ }
232
+
233
+ export function messageParts(
234
+ message: UIMessage,
235
+ interrupts: ReadonlyArray<ChatUIInterrupt>,
236
+ inlineToolNames: ReadonlyArray<string>,
237
+ ): Array<ChatUISelectedPart> {
238
+ return automaticPartsForMessage(
239
+ selectMessageUI(message, { interrupts, inlineToolNames }),
240
+ )
241
+ }
242
+
243
+ export function listInterrupts<TOptions>(
244
+ chat: ChatUIHost<TOptions>,
245
+ inlineToolNames: ReadonlyArray<string>,
246
+ ) {
247
+ return selectChatUI({
248
+ messages: readMessages(chat),
249
+ interrupts: readInterrupts(chat),
250
+ inlineToolNames,
251
+ }).interrupts
252
+ }
253
+
254
+ export function toolComponent<TOptions>(
255
+ components: ChatUIComponents<TOptions>,
256
+ name: string,
257
+ ) {
258
+ return components.tools?.[name as never]
259
+ }
260
+
261
+ export function partComponent<TOptions>(
262
+ components: ChatUIComponents<TOptions>,
263
+ key: string,
264
+ ) {
265
+ return components.parts[key as ChatUIPartKey] ?? components.parts.fallback
266
+ }
267
+
268
+ export function interruptComponent<TOptions>(
269
+ components: ChatUIComponents<TOptions>,
270
+ interrupt: ChatUIInterrupt,
271
+ ) {
272
+ return resolveInterruptComponent(interrupt, components.interrupts)
273
+ }
274
+
275
+ export type ChatUIQueueItem = QueuedMessage & {
276
+ cancelQueued: () => void
277
+ }
278
+
279
+ export type LayoutProps<TOptions> = {
280
+ readonly __ui?: TOptions
281
+ }
282
+ export type MessageProps<TOptions> = {
283
+ message: UIMessage<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>
284
+ }
285
+ export type InputProps<TOptions> = {
286
+ readonly __ui?: TOptions
287
+ }
288
+ export type QueueProps<TOptions> = {
289
+ item: ChatUIQueueItem
290
+ readonly __ui?: TOptions
291
+ }
292
+ export type PartProps<TOptions, TKey extends ChatUIPartKey = ChatUIPartKey> = {
293
+ part: ChatUIPartOf<TOptions, TKey>
294
+ }
295
+ export type ToolProps<
296
+ TOptions,
297
+ TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>,
298
+ > = {
299
+ part: Extract<ToolCallPart<ChatUIToolsOf<TOptions>>, { name: TName }>
300
+ result?: ToolResultPart
301
+ interrupt?: ChatUIToolApproval<TOptions, TName>
302
+ readonly __ui?: TOptions
303
+ }
304
+ export type InterruptProps<
305
+ TOptions,
306
+ TName extends ChatUIInterruptName<TOptions> = never,
307
+ > = {
308
+ interrupt: ChatUIInterruptOf<TOptions, TName>
309
+ readonly __ui?: TOptions
310
+ }
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import {
3
+ getComponentsContext,
4
+ partComponent,
5
+ toolComponent,
6
+ type UIDescriptor,
7
+ } from './create-ui'
8
+ import type { ChatUISelectedPart } from '@tanstack/ai-client/ui'
9
+
10
+ let {
11
+ ui,
12
+ selected,
13
+ }: {
14
+ ui: UIDescriptor
15
+ selected: ChatUISelectedPart
16
+ } = $props()
17
+
18
+ const comps = $derived(getComponentsContext(ui))
19
+ </script>
20
+
21
+ {#if selected.key === 'toolCall'}
22
+ {@const Tool = toolComponent(comps.components, selected.part.name) as any}
23
+ {#if Tool}
24
+ <Tool
25
+ part={selected.part}
26
+ result={selected.result}
27
+ interrupt={selected.interrupt}
28
+ />
29
+ {:else}
30
+ {comps.warn(
31
+ `tool:${selected.part.name}`,
32
+ `[tanstack-ai-ui] Missing tools.${selected.part.name} component`,
33
+ )}
34
+ {/if}
35
+ {:else}
36
+ {@const Part = partComponent(comps.components, selected.key) as any}
37
+ {#if Part}
38
+ <Part part={selected.part} />
39
+ {:else}
40
+ {comps.warn(
41
+ `part:${selected.key}`,
42
+ `[tanstack-ai-ui] Missing parts.${selected.key} component`,
43
+ )}
44
+ {/if}
45
+ {/if}