@tanstack/ai-react 0.22.4 → 0.23.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.
- package/README.md +11 -15
- package/dist/esm/chat-ui/chat-input.d.ts +57 -0
- package/dist/esm/chat-ui/chat-input.js +124 -0
- package/dist/esm/chat-ui/chat-input.js.map +1 -0
- package/dist/esm/chat-ui/chat-message.d.ts +91 -0
- package/dist/esm/chat-ui/chat-message.js +157 -0
- package/dist/esm/chat-ui/chat-message.js.map +1 -0
- package/dist/esm/chat-ui/chat-messages.d.ts +33 -0
- package/dist/esm/chat-ui/chat-messages.js +44 -0
- package/dist/esm/chat-ui/chat-messages.js.map +1 -0
- package/dist/esm/chat-ui/chat.d.ts +53 -0
- package/dist/esm/chat-ui/chat.js +57 -0
- package/dist/esm/chat-ui/chat.js.map +1 -0
- package/dist/esm/chat-ui/create-chat-hook.d.ts +48 -0
- package/dist/esm/chat-ui/create-chat-hook.js +57 -0
- package/dist/esm/chat-ui/create-chat-hook.js.map +1 -0
- package/dist/esm/chat-ui/create-ui-contexts.d.ts +29 -0
- package/dist/esm/chat-ui/create-ui-contexts.js +36 -0
- package/dist/esm/chat-ui/create-ui-contexts.js.map +1 -0
- package/dist/esm/chat-ui/create-ui.d.ts +153 -0
- package/dist/esm/chat-ui/create-ui.js +294 -0
- package/dist/esm/chat-ui/create-ui.js.map +1 -0
- package/dist/esm/chat-ui/markdown-plugins.d.ts +15 -0
- package/dist/esm/chat-ui/markdown-plugins.js +28 -0
- package/dist/esm/chat-ui/markdown-plugins.js.map +1 -0
- package/dist/esm/chat-ui/text-part.d.ts +57 -0
- package/dist/esm/chat-ui/text-part.js +46 -0
- package/dist/esm/chat-ui/text-part.js.map +1 -0
- package/dist/esm/chat-ui/thinking-part.d.ts +41 -0
- package/dist/esm/chat-ui/thinking-part.js +73 -0
- package/dist/esm/chat-ui/thinking-part.js.map +1 -0
- package/dist/esm/chat-ui/tool-approval.d.ts +52 -0
- package/dist/esm/chat-ui/tool-approval.js +83 -0
- package/dist/esm/chat-ui/tool-approval.js.map +1 -0
- package/dist/esm/chat-ui/tool-result-content.d.ts +18 -0
- package/dist/esm/chat-ui/tool-result-content.js +19 -0
- package/dist/esm/chat-ui/tool-result-content.js.map +1 -0
- package/dist/esm/create-chat-hook.d.ts +35 -0
- package/dist/esm/create-chat-hook.js +37 -0
- package/dist/esm/create-chat-hook.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/ui.d.ts +10 -0
- package/dist/esm/ui.js +11 -0
- package/package.json +18 -4
- package/src/chat-ui/chat-input.tsx +177 -0
- package/src/chat-ui/chat-message.tsx +271 -0
- package/src/chat-ui/chat-messages.tsx +89 -0
- package/src/chat-ui/chat.tsx +98 -0
- package/src/chat-ui/create-chat-hook.tsx +103 -0
- package/src/chat-ui/create-ui-contexts.tsx +57 -0
- package/src/chat-ui/create-ui.tsx +761 -0
- package/src/chat-ui/markdown-plugins.ts +48 -0
- package/src/chat-ui/text-part.tsx +97 -0
- package/src/chat-ui/thinking-part.tsx +83 -0
- package/src/chat-ui/tool-approval.tsx +132 -0
- package/src/chat-ui/tool-result-content.ts +32 -0
- package/src/create-chat-hook.ts +67 -0
- package/src/index.ts +1 -0
- package/src/ui.ts +43 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useChat } from "../use-chat.js";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/chat-ui/chat.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Chat context - provides chat state to all child components
|
|
7
|
+
*/
|
|
8
|
+
var ChatContext = createContext(null);
|
|
9
|
+
/**
|
|
10
|
+
* Hook to access chat context
|
|
11
|
+
* @throws Error if used outside of Chat component
|
|
12
|
+
*/
|
|
13
|
+
/** @deprecated Use `createChatUI().useChatContext()` instead. Deprecated in 0.9.0. Removed in 1.0.0. */
|
|
14
|
+
function useChatContext() {
|
|
15
|
+
const context = useContext(ChatContext);
|
|
16
|
+
if (!context) throw new Error("Chat components must be wrapped in <Chat>. Make sure you're using Chat.Messages, Chat.Input, etc. inside a <Chat> component.");
|
|
17
|
+
return context;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Since 0.9.0. Use `createChatHook()` from `@tanstack/ai-react/ui` instead.
|
|
21
|
+
* See https://tanstack.com/ai/latest/docs/ui/react
|
|
22
|
+
* Removed in 1.0.0.
|
|
23
|
+
*
|
|
24
|
+
* Root Chat component - provides context for all chat subcomponents
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <Chat connection={fetchServerSentEvents("/api/chat")}>
|
|
29
|
+
* <Chat.Messages />
|
|
30
|
+
* <Chat.Input />
|
|
31
|
+
* </Chat>
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function Chat({ children, className, connection, initialMessages, id, body, onResponse, onChunk, onFinish, onError }) {
|
|
35
|
+
const chat = useChat({
|
|
36
|
+
connection,
|
|
37
|
+
...initialMessages !== void 0 ? { initialMessages } : {},
|
|
38
|
+
...id !== void 0 ? { id } : {},
|
|
39
|
+
...body !== void 0 ? { body } : {},
|
|
40
|
+
...onResponse !== void 0 ? { onResponse } : {},
|
|
41
|
+
...onChunk !== void 0 ? { onChunk } : {},
|
|
42
|
+
...onFinish !== void 0 ? { onFinish } : {},
|
|
43
|
+
...onError !== void 0 ? { onError } : {}
|
|
44
|
+
});
|
|
45
|
+
return /* @__PURE__ */ jsx(ChatContext.Provider, {
|
|
46
|
+
value: chat,
|
|
47
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
48
|
+
className,
|
|
49
|
+
"data-chat-root": true,
|
|
50
|
+
children
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { Chat, useChatContext };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","names":[],"sources":["../../../src/chat-ui/chat.tsx"],"sourcesContent":["import { createContext, useContext } from 'react'\nimport type { ConnectionAdapter } from '@tanstack/ai-client'\nimport { useChat } from '../use-chat'\nimport type { ReactNode } from 'react'\nimport type { UIMessage, UseChatReturn } from '../types'\n\n/**\n * Chat context - provides chat state to all child components\n */\nconst ChatContext = createContext<UseChatReturn | null>(null)\n\n/**\n * Hook to access chat context\n * @throws Error if used outside of Chat component\n */\n/** @deprecated Use `createChatUI().useChatContext()` instead. Deprecated in 0.9.0. Removed in 1.0.0. */\nexport function useChatContext() {\n const context = useContext(ChatContext)\n if (!context) {\n throw new Error(\n \"Chat components must be wrapped in <Chat>. Make sure you're using Chat.Messages, Chat.Input, etc. inside a <Chat> component.\",\n )\n }\n return context\n}\n\n/** @deprecated Use `createChatUI()` Chat/Provider instead. Deprecated in 0.9.0. Removed in 1.0.0. */\nexport interface ChatProps {\n /** Child components (Chat.Messages, Chat.Input, etc.) */\n children: ReactNode\n /** CSS class name for the root element */\n className?: string\n /** Connection adapter for communicating with your API */\n connection: ConnectionAdapter\n /** Initial messages to display */\n initialMessages?: Array<UIMessage>\n /** Custom message ID generator */\n id?: string\n /** Additional body data to send with requests */\n body?: any\n /** Callback when a response is received */\n onResponse?: (response?: Response) => void | Promise<void>\n /** Callback when each chunk arrives */\n onChunk?: (chunk: any) => void\n /** Callback when a message is complete */\n onFinish?: (message: UIMessage) => void\n /** Callback when an error occurs */\n onError?: (error: Error) => void\n /** Custom tool components registry */\n tools?: Record<string, React.ComponentType<{ input: any; output?: any }>>\n}\n\n/**\n * @deprecated Since 0.9.0. Use `createChatHook()` from `@tanstack/ai-react/ui` instead.\n * See https://tanstack.com/ai/latest/docs/ui/react\n * Removed in 1.0.0.\n *\n * Root Chat component - provides context for all chat subcomponents\n *\n * @example\n * ```tsx\n * <Chat connection={fetchServerSentEvents(\"/api/chat\")}>\n * <Chat.Messages />\n * <Chat.Input />\n * </Chat>\n * ```\n */\nexport function Chat({\n children,\n className,\n connection,\n initialMessages,\n id,\n body,\n onResponse,\n onChunk,\n onFinish,\n onError,\n}: ChatProps) {\n const chat = useChat({\n connection,\n ...(initialMessages !== undefined ? { initialMessages } : {}),\n ...(id !== undefined ? { id } : {}),\n ...(body !== undefined ? { body } : {}),\n ...(onResponse !== undefined ? { onResponse } : {}),\n ...(onChunk !== undefined ? { onChunk } : {}),\n ...(onFinish !== undefined ? { onFinish } : {}),\n ...(onError !== undefined ? { onError } : {}),\n })\n\n return (\n <ChatContext.Provider value={chat}>\n <div className={className} data-chat-root>\n {children}\n </div>\n </ChatContext.Provider>\n )\n}\n"],"mappings":";;;;;;;AASA,IAAM,cAAc,cAAoC,IAAI;;;;;;AAO5D,SAAgB,iBAAiB;CAC/B,MAAM,UAAU,WAAW,WAAW;CACtC,IAAI,CAAC,SACH,MAAM,IAAI,MACR,8HACF;CAEF,OAAO;AACT;;;;;;;;;;;;;;;;AA2CA,SAAgB,KAAK,EACnB,UACA,WACA,YACA,iBACA,IACA,MACA,YACA,SACA,UACA,WACY;CACZ,MAAM,OAAO,QAAQ;EACnB;EACA,GAAI,oBAAoB,KAAA,IAAY,EAAE,gBAAgB,IAAI,CAAC;EAC3D,GAAI,OAAO,KAAA,IAAY,EAAE,GAAG,IAAI,CAAC;EACjC,GAAI,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,CAAC;EACrC,GAAI,eAAe,KAAA,IAAY,EAAE,WAAW,IAAI,CAAC;EACjD,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;EAC3C,GAAI,aAAa,KAAA,IAAY,EAAE,SAAS,IAAI,CAAC;EAC7C,GAAI,YAAY,KAAA,IAAY,EAAE,QAAQ,IAAI,CAAC;CAC7C,CAAC;CAED,OACE,oBAAC,YAAY,UAAb;EAAsB,OAAO;EAC3B,UAAA,oBAAC,OAAD;GAAgB;GAAW,kBAAA;GACxB;EACE,CAAA;CACe,CAAA;AAE1B"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { InferredClientContext } from '@tanstack/ai-client';
|
|
3
|
+
import { ChatUIInterruptsOf, ChatUISchemaOf, ChatUIToolsOf } from '@tanstack/ai-client/ui';
|
|
4
|
+
import { UseChatOptions } from '../types.js';
|
|
5
|
+
import { ChatUIFactoryConfig, ChatUIHost, InputProps } from './create-ui.js';
|
|
6
|
+
type HeadlessOptions<TOptions> = UseChatOptions<ChatUIToolsOf<TOptions>, ChatUISchemaOf<TOptions>, InferredClientContext<ChatUIToolsOf<TOptions>>, ChatUIInterruptsOf<TOptions>>;
|
|
7
|
+
type ChatInstanceOverrides<TOptions> = {
|
|
8
|
+
threadId?: string;
|
|
9
|
+
live?: boolean;
|
|
10
|
+
forwardedProps?: Record<string, any>;
|
|
11
|
+
body?: Record<string, any>;
|
|
12
|
+
initialMessages?: HeadlessOptions<TOptions>['initialMessages'];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Bind chat options and UI widgets once at module scope.
|
|
16
|
+
*
|
|
17
|
+
* This matches Form `createFormHook` and Table `createTableHook`. The
|
|
18
|
+
* returned `useAppChat` creates a chat instance. `chat.AppChat` renders it.
|
|
19
|
+
* `useChatContext` reads that instance. Part and interrupt widgets take
|
|
20
|
+
* `part` / `interrupt` as props. Do not read those from context.
|
|
21
|
+
*
|
|
22
|
+
* Pass instance overrides such as `threadId` into `useAppChat()`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```tsx
|
|
26
|
+
* const { useAppChat, useChatContext } = createChatHook({
|
|
27
|
+
* options: chatOptions,
|
|
28
|
+
* components: { layout: ChatLayout, message: ChatMessage },
|
|
29
|
+
* partsComponents: { fallback: FallbackPart },
|
|
30
|
+
* toolsComponents: { getWeather: WeatherTool, purchaseItem: PurchaseTool },
|
|
31
|
+
* interruptsComponents: { generic: { choosePlan: ChoosePlan } },
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* function Support() {
|
|
35
|
+
* const chat = useAppChat({ threadId: 'support-1' })
|
|
36
|
+
* return <chat.AppChat />
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createChatHook<const TOptions, TInput extends ComponentType<any> | undefined = ComponentType<InputProps<NoInfer<TOptions>>> | undefined>({ options, ...chatComponents }: {
|
|
41
|
+
options: TOptions;
|
|
42
|
+
} & ChatUIFactoryConfig<NoInfer<TOptions>, TInput>): {
|
|
43
|
+
useAppChat: (overrides?: ChatInstanceOverrides<TOptions>) => ChatUIHost<TOptions> & {
|
|
44
|
+
AppChat: ComponentType;
|
|
45
|
+
};
|
|
46
|
+
useChatContext: () => ChatUIHost<TOptions>;
|
|
47
|
+
};
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useChat } from "../use-chat.js";
|
|
2
|
+
import { createChatUI } from "./create-ui.js";
|
|
3
|
+
import { useMemo, useRef } from "react";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/chat-ui/create-chat-hook.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Bind chat options and UI widgets once at module scope.
|
|
8
|
+
*
|
|
9
|
+
* This matches Form `createFormHook` and Table `createTableHook`. The
|
|
10
|
+
* returned `useAppChat` creates a chat instance. `chat.AppChat` renders it.
|
|
11
|
+
* `useChatContext` reads that instance. Part and interrupt widgets take
|
|
12
|
+
* `part` / `interrupt` as props. Do not read those from context.
|
|
13
|
+
*
|
|
14
|
+
* Pass instance overrides such as `threadId` into `useAppChat()`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* const { useAppChat, useChatContext } = createChatHook({
|
|
19
|
+
* options: chatOptions,
|
|
20
|
+
* components: { layout: ChatLayout, message: ChatMessage },
|
|
21
|
+
* partsComponents: { fallback: FallbackPart },
|
|
22
|
+
* toolsComponents: { getWeather: WeatherTool, purchaseItem: PurchaseTool },
|
|
23
|
+
* interruptsComponents: { generic: { choosePlan: ChoosePlan } },
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* function Support() {
|
|
27
|
+
* const chat = useAppChat({ threadId: 'support-1' })
|
|
28
|
+
* return <chat.AppChat />
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function createChatHook({ options, ...chatComponents }) {
|
|
33
|
+
const ui = createChatUI(options, chatComponents);
|
|
34
|
+
function useAppChat(overrides) {
|
|
35
|
+
const chat = overrides ? useChat({
|
|
36
|
+
...options,
|
|
37
|
+
...overrides
|
|
38
|
+
}) : useChat(options);
|
|
39
|
+
const chatRef = useRef(chat);
|
|
40
|
+
chatRef.current = chat;
|
|
41
|
+
const AppChat = useMemo(() => {
|
|
42
|
+
function AppChatImpl() {
|
|
43
|
+
return /* @__PURE__ */ jsx(ui.Chat, { chat: chatRef.current });
|
|
44
|
+
}
|
|
45
|
+
return AppChatImpl;
|
|
46
|
+
}, []);
|
|
47
|
+
return Object.assign(chat, { AppChat });
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
useAppChat,
|
|
51
|
+
useChatContext: ui.useChatContext
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { createChatHook };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=create-chat-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-chat-hook.js","names":[],"sources":["../../../src/chat-ui/create-chat-hook.tsx"],"sourcesContent":["import { useMemo, useRef } from 'react'\nimport type { ComponentType } from 'react'\nimport type { InferredClientContext } from '@tanstack/ai-client'\nimport type {\n ChatUIInterruptsOf,\n ChatUISchemaOf,\n ChatUIToolsOf,\n} from '@tanstack/ai-client/ui'\nimport { useChat as useUnboundChat } from '../use-chat'\nimport type { UseChatOptions } from '../types'\nimport { createChatUI } from './create-ui'\nimport type { ChatUIFactoryConfig, ChatUIHost, InputProps } from './create-ui'\n\ntype HeadlessOptions<TOptions> = UseChatOptions<\n ChatUIToolsOf<TOptions>,\n ChatUISchemaOf<TOptions>,\n InferredClientContext<ChatUIToolsOf<TOptions>>,\n ChatUIInterruptsOf<TOptions>\n>\n\ntype ChatInstanceOverrides<TOptions> = {\n threadId?: string\n live?: boolean\n forwardedProps?: Record<string, any>\n body?: Record<string, any>\n initialMessages?: HeadlessOptions<TOptions>['initialMessages']\n}\n\n/**\n * Bind chat options and UI widgets once at module scope.\n *\n * This matches Form `createFormHook` and Table `createTableHook`. The\n * returned `useAppChat` creates a chat instance. `chat.AppChat` renders it.\n * `useChatContext` reads that instance. Part and interrupt widgets take\n * `part` / `interrupt` as props. Do not read those from context.\n *\n * Pass instance overrides such as `threadId` into `useAppChat()`.\n *\n * @example\n * ```tsx\n * const { useAppChat, useChatContext } = createChatHook({\n * options: chatOptions,\n * components: { layout: ChatLayout, message: ChatMessage },\n * partsComponents: { fallback: FallbackPart },\n * toolsComponents: { getWeather: WeatherTool, purchaseItem: PurchaseTool },\n * interruptsComponents: { generic: { choosePlan: ChoosePlan } },\n * })\n *\n * function Support() {\n * const chat = useAppChat({ threadId: 'support-1' })\n * return <chat.AppChat />\n * }\n * ```\n */\nexport function createChatHook<\n const TOptions,\n TInput extends ComponentType<any> | undefined =\n | ComponentType<InputProps<NoInfer<TOptions>>>\n | undefined,\n>({\n options,\n ...chatComponents\n}: {\n options: TOptions\n} & ChatUIFactoryConfig<NoInfer<TOptions>, TInput>) {\n const ui = createChatUI(\n options,\n chatComponents as ChatUIFactoryConfig<NoInfer<TOptions>, TInput>,\n )\n\n function useAppChat(overrides?: ChatInstanceOverrides<TOptions>) {\n const chat = (\n overrides\n ? useUnboundChat({\n ...(options as HeadlessOptions<TOptions>),\n ...overrides,\n })\n : useUnboundChat(options as HeadlessOptions<TOptions>)\n ) as ChatUIHost<TOptions>\n\n // `useChat` returns a fresh object on every render. The mixed Chat\n // component must stay stable, or React remounts the tree on each\n // update. Read the current instance from a ref.\n const chatRef = useRef(chat)\n chatRef.current = chat\n\n const AppChat = useMemo(() => {\n function AppChatImpl() {\n return <ui.Chat chat={chatRef.current} />\n }\n return AppChatImpl\n }, [])\n\n return Object.assign(chat, { AppChat }) as ChatUIHost<TOptions> & {\n AppChat: ComponentType\n }\n }\n\n return {\n useAppChat,\n useChatContext: ui.useChatContext,\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAgB,eAKd,EACA,SACA,GAAG,kBAG+C;CAClD,MAAM,KAAK,aACT,SACA,cACF;CAEA,SAAS,WAAW,WAA6C;EAC/D,MAAM,OACJ,YACI,QAAe;GACb,GAAI;GACJ,GAAG;EACL,CAAC,IACD,QAAe,OAAoC;EAMzD,MAAM,UAAU,OAAO,IAAI;EAC3B,QAAQ,UAAU;EAElB,MAAM,UAAU,cAAc;GAC5B,SAAS,cAAc;IACrB,OAAO,oBAAC,GAAG,MAAJ,EAAS,MAAM,QAAQ,QAAU,CAAA;GAC1C;GACA,OAAO;EACT,GAAG,CAAC,CAAC;EAEL,OAAO,OAAO,OAAO,MAAM,EAAE,QAAQ,CAAC;CAGxC;CAEA,OAAO;EACL;EACA,gBAAgB,GAAG;CACrB;AACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Context } from 'react';
|
|
2
|
+
import { ChatUIInterrupt, ChatUISelectedPart } from '@tanstack/ai-client/ui';
|
|
3
|
+
import { UseChatReturn } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Scoped chat, part, and interrupt contexts plus matching context hooks.
|
|
6
|
+
* Pass these into {@link createChatHook} when widgets live in other files, or
|
|
7
|
+
* when two chat trees nest and must not share the default contexts.
|
|
8
|
+
*/
|
|
9
|
+
export interface ChatUIContexts {
|
|
10
|
+
chatContext: Context<UseChatReturn<any, any, any> | null>;
|
|
11
|
+
partContext: Context<ChatUISelectedPart | null>;
|
|
12
|
+
interruptContext: Context<ChatUIInterrupt | null>;
|
|
13
|
+
useChatContext: () => UseChatReturn<any, any, any>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Create a fresh set of chat UI contexts. This matches Form
|
|
17
|
+
* `createFormHookContexts` and Table `createTableHookContexts`.
|
|
18
|
+
*
|
|
19
|
+
* Most apps can skip this. `createChatHook` uses shared module contexts by
|
|
20
|
+
* default. The hooks on this object are the escape hatch for a widget file
|
|
21
|
+
* that cannot import the `createChatHook` result (circular import), or for
|
|
22
|
+
* nested chats that need isolated providers.
|
|
23
|
+
*
|
|
24
|
+
* Prefer the `useChatContext` hook returned from `createChatHook` when you
|
|
25
|
+
* can import that result. That hook knows the `chatComponents` types. Part
|
|
26
|
+
* and interrupt widgets take `part` / `interrupt` as props.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createChatHookContexts(): ChatUIContexts;
|
|
29
|
+
export declare const defaultChatUIContexts: ChatUIContexts;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
//#region src/chat-ui/create-ui-contexts.tsx
|
|
3
|
+
/**
|
|
4
|
+
* Create a fresh set of chat UI contexts. This matches Form
|
|
5
|
+
* `createFormHookContexts` and Table `createTableHookContexts`.
|
|
6
|
+
*
|
|
7
|
+
* Most apps can skip this. `createChatHook` uses shared module contexts by
|
|
8
|
+
* default. The hooks on this object are the escape hatch for a widget file
|
|
9
|
+
* that cannot import the `createChatHook` result (circular import), or for
|
|
10
|
+
* nested chats that need isolated providers.
|
|
11
|
+
*
|
|
12
|
+
* Prefer the `useChatContext` hook returned from `createChatHook` when you
|
|
13
|
+
* can import that result. That hook knows the `chatComponents` types. Part
|
|
14
|
+
* and interrupt widgets take `part` / `interrupt` as props.
|
|
15
|
+
*/
|
|
16
|
+
function createChatHookContexts() {
|
|
17
|
+
const chatContext = createContext(null);
|
|
18
|
+
const partContext = createContext(null);
|
|
19
|
+
const interruptContext = createContext(null);
|
|
20
|
+
function useChatContext() {
|
|
21
|
+
const chat = useContext(chatContext);
|
|
22
|
+
if (!chat) throw new Error("`useChatContext` must be used within `UI.Provider` or `UI.Chat`.");
|
|
23
|
+
return chat;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
chatContext,
|
|
27
|
+
partContext,
|
|
28
|
+
interruptContext,
|
|
29
|
+
useChatContext
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
var defaultChatUIContexts = createChatHookContexts();
|
|
33
|
+
//#endregion
|
|
34
|
+
export { createChatHookContexts, defaultChatUIContexts };
|
|
35
|
+
|
|
36
|
+
//# sourceMappingURL=create-ui-contexts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-ui-contexts.js","names":[],"sources":["../../../src/chat-ui/create-ui-contexts.tsx"],"sourcesContent":["import { createContext, useContext } from 'react'\nimport type { Context } from 'react'\nimport type {\n ChatUIInterrupt,\n ChatUISelectedPart,\n} from '@tanstack/ai-client/ui'\nimport type { UseChatReturn } from '../types'\n\n/**\n * Scoped chat, part, and interrupt contexts plus matching context hooks.\n * Pass these into {@link createChatHook} when widgets live in other files, or\n * when two chat trees nest and must not share the default contexts.\n */\nexport interface ChatUIContexts {\n chatContext: Context<UseChatReturn<any, any, any> | null>\n partContext: Context<ChatUISelectedPart | null>\n interruptContext: Context<ChatUIInterrupt | null>\n useChatContext: () => UseChatReturn<any, any, any>\n}\n\n/**\n * Create a fresh set of chat UI contexts. This matches Form\n * `createFormHookContexts` and Table `createTableHookContexts`.\n *\n * Most apps can skip this. `createChatHook` uses shared module contexts by\n * default. The hooks on this object are the escape hatch for a widget file\n * that cannot import the `createChatHook` result (circular import), or for\n * nested chats that need isolated providers.\n *\n * Prefer the `useChatContext` hook returned from `createChatHook` when you\n * can import that result. That hook knows the `chatComponents` types. Part\n * and interrupt widgets take `part` / `interrupt` as props.\n */\nexport function createChatHookContexts(): ChatUIContexts {\n const chatContext = createContext<UseChatReturn<any, any, any> | null>(null)\n const partContext = createContext<ChatUISelectedPart | null>(null)\n const interruptContext = createContext<ChatUIInterrupt | null>(null)\n\n function useChatContext() {\n const chat = useContext(chatContext)\n if (!chat) {\n throw new Error(\n '`useChatContext` must be used within `UI.Provider` or `UI.Chat`.',\n )\n }\n return chat\n }\n\n return {\n chatContext,\n partContext,\n interruptContext,\n useChatContext,\n }\n}\n\nexport const defaultChatUIContexts = createChatHookContexts()\n"],"mappings":";;;;;;;;;;;;;;;AAiCA,SAAgB,yBAAyC;CACvD,MAAM,cAAc,cAAmD,IAAI;CAC3E,MAAM,cAAc,cAAyC,IAAI;CACjE,MAAM,mBAAmB,cAAsC,IAAI;CAEnE,SAAS,iBAAiB;EACxB,MAAM,OAAO,WAAW,WAAW;EACnC,IAAI,CAAC,MACH,MAAM,IAAI,MACR,kEACF;EAEF,OAAO;CACT;CAEA,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;AAEA,IAAa,wBAAwB,uBAAuB"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
import { ChatUIData, ChatUIHasNamedInterrupts, ChatUIHasNamedTools, ChatUIInterrupt, ChatUIInterruptName, ChatUIInterruptOf, ChatUIInterruptsOf, ChatUIMessages, ChatUINamedInterruptId, ChatUIPartKey, ChatUIPartOf, ChatUISchemaOf, ChatUISelectedPart, ChatUIToolApproval, ChatUIToolName, ChatUIToolsOf } from '@tanstack/ai-client/ui';
|
|
3
|
+
import { MessagePart, QueuedMessage, ToolCallPart, ToolResultPart, UIMessage } from '@tanstack/ai-client';
|
|
4
|
+
import { UseChatReturn } from '../types.js';
|
|
5
|
+
import { ChatUIContexts } from './create-ui-contexts.js';
|
|
6
|
+
export type ChatUIHost<TOptions = unknown> = UseChatReturn<ChatUIToolsOf<TOptions>, ChatUISchemaOf<TOptions>, ChatUIInterruptsOf<TOptions>>;
|
|
7
|
+
export type ChatUIQueueItem = QueuedMessage & {
|
|
8
|
+
cancelQueued: () => void;
|
|
9
|
+
};
|
|
10
|
+
export type LayoutProps<TOptions, TInput extends ComponentType<any> | undefined = ComponentType<InputProps<TOptions>>> = {
|
|
11
|
+
Messages: ComponentType;
|
|
12
|
+
Interrupts: ComponentType;
|
|
13
|
+
Queue: ComponentType;
|
|
14
|
+
readonly __ui?: TOptions;
|
|
15
|
+
} & (TInput extends ComponentType<any> ? {
|
|
16
|
+
Input: ComponentType;
|
|
17
|
+
} : {});
|
|
18
|
+
export type MessageProps<TOptions> = {
|
|
19
|
+
message: UIMessage<ChatUIToolsOf<TOptions>, ChatUIData<TOptions>>;
|
|
20
|
+
Parts: ComponentType;
|
|
21
|
+
};
|
|
22
|
+
export type InputProps<TOptions> = {
|
|
23
|
+
readonly __ui?: TOptions;
|
|
24
|
+
};
|
|
25
|
+
export type QueueProps<TOptions> = {
|
|
26
|
+
item: ChatUIQueueItem;
|
|
27
|
+
readonly __ui?: TOptions;
|
|
28
|
+
};
|
|
29
|
+
export type PartProps<TOptions, TKey extends ChatUIPartKey = ChatUIPartKey> = {
|
|
30
|
+
part: ChatUIPartOf<TOptions, TKey>;
|
|
31
|
+
};
|
|
32
|
+
export type ToolProps<TOptions, TName extends ChatUIToolName<TOptions> = ChatUIToolName<TOptions>> = {
|
|
33
|
+
part: Extract<ToolCallPart<ChatUIToolsOf<TOptions>>, {
|
|
34
|
+
name: TName;
|
|
35
|
+
}>;
|
|
36
|
+
result?: ToolResultPart;
|
|
37
|
+
interrupt?: ChatUIToolApproval<TOptions, TName>;
|
|
38
|
+
};
|
|
39
|
+
export type InterruptProps<TOptions, TName extends ChatUIInterruptName<TOptions> = never> = {
|
|
40
|
+
interrupt: ChatUIInterruptOf<TOptions, TName>;
|
|
41
|
+
readonly __ui?: TOptions;
|
|
42
|
+
};
|
|
43
|
+
type GenericInterruptComponents<TOptions> = ChatUIHasNamedInterrupts<TOptions> extends true ? {
|
|
44
|
+
[K in ChatUINamedInterruptId<TOptions>]: ComponentType<InterruptProps<TOptions, K & ChatUIInterruptName<TOptions>>>;
|
|
45
|
+
} & {
|
|
46
|
+
fallback?: ComponentType<InterruptProps<TOptions>>;
|
|
47
|
+
} : {
|
|
48
|
+
fallback?: ComponentType<InterruptProps<TOptions>>;
|
|
49
|
+
};
|
|
50
|
+
type ToolApprovalMap<TOptions> = {
|
|
51
|
+
[K in ChatUIToolName<TOptions>]?: ComponentType<InterruptProps<TOptions, K & ChatUIInterruptName<TOptions>>>;
|
|
52
|
+
};
|
|
53
|
+
/** The chrome around the message list: `layout`, `message`, and `input`. */
|
|
54
|
+
export type ChatUIChromeComponents<TOptions, TInput extends ComponentType<InputProps<TOptions>> | undefined = ComponentType<InputProps<TOptions>>> = {
|
|
55
|
+
layout: ComponentType<LayoutProps<TOptions, TInput>>;
|
|
56
|
+
message: ComponentType<MessageProps<TOptions>>;
|
|
57
|
+
input?: TInput;
|
|
58
|
+
queue?: ComponentType<QueueProps<TOptions>>;
|
|
59
|
+
};
|
|
60
|
+
export type ChatUIPartsComponents<TOptions> = {
|
|
61
|
+
[K in ChatUIPartKey]?: ComponentType<PartProps<TOptions, K>>;
|
|
62
|
+
} & {
|
|
63
|
+
fallback?: ComponentType<PartProps<TOptions>>;
|
|
64
|
+
};
|
|
65
|
+
export type ChatUIInterruptsComponents<TOptions> = {
|
|
66
|
+
tools?: ToolApprovalMap<TOptions>;
|
|
67
|
+
generic: GenericInterruptComponents<TOptions>;
|
|
68
|
+
};
|
|
69
|
+
export type ChatUIComponents<TOptions, TInput extends ComponentType<InputProps<TOptions>> | undefined = ComponentType<InputProps<TOptions>>> = {
|
|
70
|
+
components: ChatUIChromeComponents<TOptions, TInput>;
|
|
71
|
+
partsComponents: ChatUIPartsComponents<TOptions>;
|
|
72
|
+
} & (ChatUIHasNamedTools<TOptions> extends true ? {
|
|
73
|
+
toolsComponents: {
|
|
74
|
+
[K in ChatUIToolName<TOptions>]: ComponentType<ToolProps<TOptions, K>>;
|
|
75
|
+
};
|
|
76
|
+
} : {
|
|
77
|
+
toolsComponents?: {
|
|
78
|
+
[K in ChatUIToolName<TOptions>]?: ComponentType<ToolProps<TOptions, K>>;
|
|
79
|
+
};
|
|
80
|
+
}) & (ChatUIHasNamedInterrupts<TOptions> extends true ? {
|
|
81
|
+
interruptsComponents: ChatUIInterruptsComponents<TOptions>;
|
|
82
|
+
} : {
|
|
83
|
+
interruptsComponents?: {
|
|
84
|
+
tools?: ToolApprovalMap<TOptions>;
|
|
85
|
+
generic?: GenericInterruptComponents<TOptions>;
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
/** Scoped contexts, for widgets in other files or nested chat trees. */
|
|
89
|
+
export type ChatUIContextConfig = {
|
|
90
|
+
chatContext?: ChatUIContexts['chatContext'];
|
|
91
|
+
partContext?: ChatUIContexts['partContext'];
|
|
92
|
+
interruptContext?: ChatUIContexts['interruptContext'];
|
|
93
|
+
};
|
|
94
|
+
export type ChatUIFactoryConfig<TOptions, TInput extends ComponentType<InputProps<TOptions>> | undefined = ComponentType<InputProps<TOptions>>> = ChatUIComponents<TOptions, TInput> & {
|
|
95
|
+
context?: ChatUIContextConfig;
|
|
96
|
+
};
|
|
97
|
+
type BoundWidget = ComponentType<Record<string, never>>;
|
|
98
|
+
type PartMixins<TOptions> = {
|
|
99
|
+
[K in ChatUIPartKey]?: BoundWidget;
|
|
100
|
+
} & {
|
|
101
|
+
[K in ChatUIToolName<TOptions>]: BoundWidget;
|
|
102
|
+
} & {
|
|
103
|
+
Render: BoundWidget;
|
|
104
|
+
};
|
|
105
|
+
type InterruptMixins<TOptions> = {
|
|
106
|
+
[K in ChatUINamedInterruptId<TOptions>]: BoundWidget;
|
|
107
|
+
} & {
|
|
108
|
+
[K in ChatUIToolName<TOptions>]?: BoundWidget;
|
|
109
|
+
} & {
|
|
110
|
+
fallback?: BoundWidget;
|
|
111
|
+
Render: BoundWidget;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Bind chat options and UI widgets once at module scope. This matches Form
|
|
115
|
+
* `createFormHook` and Table `createTableHook`: widgets register here, mix
|
|
116
|
+
* onto Part / Interrupt / the UI kit, and automatic dispatch still walks the
|
|
117
|
+
* message list.
|
|
118
|
+
*
|
|
119
|
+
* `chatOptions` is type-only at runtime. Pass optional `chatContext`,
|
|
120
|
+
* `partContext`, and `interruptContext` from {@link createChatHookContexts}
|
|
121
|
+
* when widgets live in other files or when nested chats need isolation.
|
|
122
|
+
*/
|
|
123
|
+
export declare function createChatUI<const TOptions, TInput extends ComponentType<any> | undefined = ComponentType<InputProps<NoInfer<TOptions>>> | undefined>(options: TOptions, config: ChatUIFactoryConfig<NoInfer<TOptions>, TInput>): {
|
|
124
|
+
Chat: ({ chat }: {
|
|
125
|
+
chat: ChatUIHost<TOptions>;
|
|
126
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
127
|
+
Provider: ({ chat, children, }: {
|
|
128
|
+
chat: ChatUIHost<TOptions>;
|
|
129
|
+
children?: ReactNode;
|
|
130
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
131
|
+
Messages: ({ children, }?: {
|
|
132
|
+
children?: (messages: ChatUIMessages<TOptions>) => ReactNode;
|
|
133
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
134
|
+
Message: ({ message, children, }: {
|
|
135
|
+
message: ChatUIMessages<TOptions>[number];
|
|
136
|
+
children?: (parts: Array<ChatUISelectedPart>) => ReactNode;
|
|
137
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
138
|
+
Part: ({ part, children, }: {
|
|
139
|
+
part: MessagePart | ChatUISelectedPart;
|
|
140
|
+
children?: (mixed: ChatUISelectedPart & PartMixins<TOptions>) => ReactNode;
|
|
141
|
+
}) => import("react/jsx-runtime").JSX.Element | null;
|
|
142
|
+
Interrupts: ({ children, }?: {
|
|
143
|
+
children?: (interrupts: ReadonlyArray<ChatUIInterrupt>) => ReactNode;
|
|
144
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
145
|
+
Interrupt: import('react').MemoExoticComponent<({ interrupt, children, }: {
|
|
146
|
+
interrupt: ChatUIInterrupt;
|
|
147
|
+
children?: (mixed: ChatUIInterrupt & InterruptMixins<TOptions>) => ReactNode;
|
|
148
|
+
}) => import("react/jsx-runtime").JSX.Element>;
|
|
149
|
+
Queue: () => import("react/jsx-runtime").JSX.Element | null;
|
|
150
|
+
useChatContext: () => ChatUIHost<TOptions>;
|
|
151
|
+
Input: TInput | undefined;
|
|
152
|
+
};
|
|
153
|
+
export {};
|