@tanstack/ai-react 0.22.4 → 0.24.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 (64) hide show
  1. package/README.md +11 -15
  2. package/dist/esm/chat-ui/chat-input.d.ts +57 -0
  3. package/dist/esm/chat-ui/chat-input.js +124 -0
  4. package/dist/esm/chat-ui/chat-input.js.map +1 -0
  5. package/dist/esm/chat-ui/chat-message.d.ts +91 -0
  6. package/dist/esm/chat-ui/chat-message.js +157 -0
  7. package/dist/esm/chat-ui/chat-message.js.map +1 -0
  8. package/dist/esm/chat-ui/chat-messages.d.ts +33 -0
  9. package/dist/esm/chat-ui/chat-messages.js +44 -0
  10. package/dist/esm/chat-ui/chat-messages.js.map +1 -0
  11. package/dist/esm/chat-ui/chat.d.ts +53 -0
  12. package/dist/esm/chat-ui/chat.js +57 -0
  13. package/dist/esm/chat-ui/chat.js.map +1 -0
  14. package/dist/esm/chat-ui/create-chat-hook.d.ts +48 -0
  15. package/dist/esm/chat-ui/create-chat-hook.js +57 -0
  16. package/dist/esm/chat-ui/create-chat-hook.js.map +1 -0
  17. package/dist/esm/chat-ui/create-ui-contexts.d.ts +29 -0
  18. package/dist/esm/chat-ui/create-ui-contexts.js +36 -0
  19. package/dist/esm/chat-ui/create-ui-contexts.js.map +1 -0
  20. package/dist/esm/chat-ui/create-ui.d.ts +153 -0
  21. package/dist/esm/chat-ui/create-ui.js +294 -0
  22. package/dist/esm/chat-ui/create-ui.js.map +1 -0
  23. package/dist/esm/chat-ui/markdown-plugins.d.ts +15 -0
  24. package/dist/esm/chat-ui/markdown-plugins.js +28 -0
  25. package/dist/esm/chat-ui/markdown-plugins.js.map +1 -0
  26. package/dist/esm/chat-ui/text-part.d.ts +57 -0
  27. package/dist/esm/chat-ui/text-part.js +46 -0
  28. package/dist/esm/chat-ui/text-part.js.map +1 -0
  29. package/dist/esm/chat-ui/thinking-part.d.ts +41 -0
  30. package/dist/esm/chat-ui/thinking-part.js +73 -0
  31. package/dist/esm/chat-ui/thinking-part.js.map +1 -0
  32. package/dist/esm/chat-ui/tool-approval.d.ts +52 -0
  33. package/dist/esm/chat-ui/tool-approval.js +83 -0
  34. package/dist/esm/chat-ui/tool-approval.js.map +1 -0
  35. package/dist/esm/chat-ui/tool-result-content.d.ts +18 -0
  36. package/dist/esm/chat-ui/tool-result-content.js +19 -0
  37. package/dist/esm/chat-ui/tool-result-content.js.map +1 -0
  38. package/dist/esm/create-chat-hook.d.ts +35 -0
  39. package/dist/esm/create-chat-hook.js +37 -0
  40. package/dist/esm/create-chat-hook.js.map +1 -0
  41. package/dist/esm/index.d.ts +3 -0
  42. package/dist/esm/index.js +3 -1
  43. package/dist/esm/ui.d.ts +10 -0
  44. package/dist/esm/ui.js +11 -0
  45. package/dist/esm/use-web-mcp-tools.d.ts +27 -0
  46. package/dist/esm/use-web-mcp-tools.js +36 -0
  47. package/dist/esm/use-web-mcp-tools.js.map +1 -0
  48. package/package.json +18 -4
  49. package/src/chat-ui/chat-input.tsx +177 -0
  50. package/src/chat-ui/chat-message.tsx +271 -0
  51. package/src/chat-ui/chat-messages.tsx +89 -0
  52. package/src/chat-ui/chat.tsx +98 -0
  53. package/src/chat-ui/create-chat-hook.tsx +103 -0
  54. package/src/chat-ui/create-ui-contexts.tsx +57 -0
  55. package/src/chat-ui/create-ui.tsx +761 -0
  56. package/src/chat-ui/markdown-plugins.ts +48 -0
  57. package/src/chat-ui/text-part.tsx +97 -0
  58. package/src/chat-ui/thinking-part.tsx +83 -0
  59. package/src/chat-ui/tool-approval.tsx +132 -0
  60. package/src/chat-ui/tool-result-content.ts +32 -0
  61. package/src/create-chat-hook.ts +67 -0
  62. package/src/index.ts +3 -0
  63. package/src/ui.ts +43 -0
  64. package/src/use-web-mcp-tools.ts +59 -0
package/README.md CHANGED
@@ -61,12 +61,8 @@ framework bindings as your app needs them.
61
61
 
62
62
  - [Overview](https://tanstack.com/ai/latest/docs/getting-started/overview) -
63
63
  what TanStack AI is and how the packages fit together.
64
- - [Quick Start: React](https://tanstack.com/ai/latest/docs/getting-started/quick-start) -
65
- add streaming chat to a React app.
66
- - [Quick Start: Vue](https://tanstack.com/ai/latest/docs/getting-started/quick-start-vue) -
67
- build with Vue composables.
68
- - [Quick Start: Svelte](https://tanstack.com/ai/latest/docs/getting-started/quick-start-svelte) -
69
- build with Svelte 5 runes.
64
+ - [Quick Start](https://tanstack.com/ai/latest/docs/getting-started/quick-start) -
65
+ add streaming chat. Pick your framework at the top of the page.
70
66
  - [Quick Start: Server Only](https://tanstack.com/ai/latest/docs/getting-started/quick-start-server) -
71
67
  use TanStack AI from a server endpoint, script, or backend service.
72
68
  - [TanStack AI vs Vercel AI SDK](https://tanstack.com/ai/latest/docs/comparison/vercel-ai-sdk) -
@@ -226,15 +222,15 @@ on.
226
222
 
227
223
  ## Framework Packages
228
224
 
229
- | Package | What it provides |
230
- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
231
- | [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
232
- | [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
233
- | [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
234
- | [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
235
- | [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
236
- | [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
237
- | `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, `@tanstack/ai-vue-ui` | Headless UI components for chat interfaces |
225
+ | Package | What it provides |
226
+ | ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
227
+ | [`@tanstack/ai-client`](https://tanstack.com/ai/latest/docs/api/ai-client) | Headless chat, realtime, and generation clients |
228
+ | [`@tanstack/ai-react`](https://tanstack.com/ai/latest/docs/api/ai-react) | React hooks including `useChat`, `useRealtimeChat`, and generation hooks |
229
+ | [`@tanstack/ai-solid`](https://tanstack.com/ai/latest/docs/api/ai-solid) | Solid hooks for chat and generations |
230
+ | [`@tanstack/ai-vue`](https://tanstack.com/ai/latest/docs/api/ai-vue) | Vue composables for chat and generations |
231
+ | [`@tanstack/ai-svelte`](https://tanstack.com/ai/latest/docs/api/ai-svelte) | Svelte 5 factories for chat and generations |
232
+ | [`@tanstack/ai-preact`](https://tanstack.com/ai/latest/docs/api/ai-preact) | Preact hooks for chat |
233
+ | `@tanstack/ai-react/ui`, `@tanstack/ai-solid/ui`, `@tanstack/ai-vue/ui`, `@tanstack/ai-svelte/ui` | Headless UI components for chat interfaces. `@tanstack/ai-react-ui`, `@tanstack/ai-solid-ui`, and `@tanstack/ai-vue-ui` are deprecated. |
238
234
 
239
235
  ## Advanced Docs
240
236
 
@@ -0,0 +1,57 @@
1
+ import { ReactNode, RefObject } from 'react';
2
+ export interface ChatInputRenderProps {
3
+ /** Current input value */
4
+ value: string;
5
+ /** Set input value */
6
+ onChange: (value: string) => void;
7
+ /** Submit the message */
8
+ onSubmit: () => void;
9
+ /** Is the chat currently loading */
10
+ isLoading: boolean;
11
+ /** Is input disabled */
12
+ disabled: boolean;
13
+ /** Ref to the input element */
14
+ inputRef: RefObject<HTMLInputElement | HTMLTextAreaElement | null>;
15
+ }
16
+ export interface ChatInputProps {
17
+ /** Render prop for full control */
18
+ children?: (props: ChatInputRenderProps) => ReactNode;
19
+ /** CSS class name */
20
+ className?: string;
21
+ /** Placeholder text */
22
+ placeholder?: string;
23
+ /** Disable input */
24
+ disabled?: boolean;
25
+ /** Submit on Enter (Shift+Enter for new line) */
26
+ submitOnEnter?: boolean;
27
+ }
28
+ /**
29
+ * @deprecated Use `createChatUI()` and an application-owned input component.
30
+ * Deprecated in 0.9.0. Removed in 1.0.0.
31
+ *
32
+ * Chat input component - handles message input and submission
33
+ *
34
+ * Features:
35
+ * - Auto-growing textarea
36
+ * - Submit on Enter (Shift+Enter for new line)
37
+ * - Loading state management
38
+ * - Full render prop support for custom UIs
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * <Chat.Input placeholder="Type your message..." />
43
+ * ```
44
+ *
45
+ * @example Custom UI with render prop
46
+ * ```tsx
47
+ * <Chat.Input>
48
+ * {({ value, onChange, onSubmit, isLoading }) => (
49
+ * <div>
50
+ * <textarea value={value} onChange={(e) => onChange(e.target.value)} />
51
+ * <button onClick={onSubmit} disabled={isLoading}>Send</button>
52
+ * </div>
53
+ * )}
54
+ * </Chat.Input>
55
+ * ```
56
+ */
57
+ export declare function ChatInput({ children, className, placeholder, disabled: disabledProp, submitOnEnter, }: ChatInputProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,124 @@
1
+ import { useChatContext } from "./chat.js";
2
+ import { useRef, useState } from "react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/chat-ui/chat-input.tsx
5
+ /**
6
+ * @deprecated Use `createChatUI()` and an application-owned input component.
7
+ * Deprecated in 0.9.0. Removed in 1.0.0.
8
+ *
9
+ * Chat input component - handles message input and submission
10
+ *
11
+ * Features:
12
+ * - Auto-growing textarea
13
+ * - Submit on Enter (Shift+Enter for new line)
14
+ * - Loading state management
15
+ * - Full render prop support for custom UIs
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <Chat.Input placeholder="Type your message..." />
20
+ * ```
21
+ *
22
+ * @example Custom UI with render prop
23
+ * ```tsx
24
+ * <Chat.Input>
25
+ * {({ value, onChange, onSubmit, isLoading }) => (
26
+ * <div>
27
+ * <textarea value={value} onChange={(e) => onChange(e.target.value)} />
28
+ * <button onClick={onSubmit} disabled={isLoading}>Send</button>
29
+ * </div>
30
+ * )}
31
+ * </Chat.Input>
32
+ * ```
33
+ */
34
+ function ChatInput({ children, className, placeholder = "Type a message...", disabled: disabledProp, submitOnEnter = true }) {
35
+ const { sendMessage, isLoading } = useChatContext();
36
+ const [value, setValue] = useState("");
37
+ const inputRef = useRef(null);
38
+ const disabled = disabledProp || isLoading;
39
+ const handleSubmit = () => {
40
+ if (!value.trim() || disabled) return;
41
+ sendMessage(value);
42
+ setValue("");
43
+ };
44
+ const renderProps = {
45
+ value,
46
+ onChange: setValue,
47
+ onSubmit: handleSubmit,
48
+ isLoading,
49
+ disabled,
50
+ inputRef
51
+ };
52
+ if (children) return /* @__PURE__ */ jsx(Fragment, { children: children(renderProps) });
53
+ return /* @__PURE__ */ jsxs("div", {
54
+ className,
55
+ "data-chat-input": true,
56
+ style: {
57
+ display: "flex",
58
+ gap: "0.75rem",
59
+ alignItems: "center",
60
+ width: "100%"
61
+ },
62
+ children: [/* @__PURE__ */ jsx("input", {
63
+ ref: inputRef,
64
+ type: "text",
65
+ value,
66
+ onChange: (e) => setValue(e.target.value),
67
+ onKeyDown: (e) => {
68
+ if (submitOnEnter && e.key === "Enter") {
69
+ e.preventDefault();
70
+ handleSubmit();
71
+ }
72
+ },
73
+ placeholder,
74
+ disabled,
75
+ "data-chat-textarea": true,
76
+ style: {
77
+ flex: 1,
78
+ padding: "0.75rem 1rem",
79
+ fontSize: "0.875rem",
80
+ border: "1px solid rgba(255, 255, 255, 0.1)",
81
+ borderRadius: "0.75rem",
82
+ backgroundColor: "rgba(31, 41, 55, 0.5)",
83
+ color: "white",
84
+ outline: "none",
85
+ transition: "all 0.2s"
86
+ },
87
+ onFocus: (e) => {
88
+ e.target.style.borderColor = "rgba(249, 115, 22, 0.4)";
89
+ e.target.style.boxShadow = "0 0 0 2px rgba(249, 115, 22, 0.2)";
90
+ },
91
+ onBlur: (e) => {
92
+ e.target.style.borderColor = "rgba(255, 255, 255, 0.1)";
93
+ e.target.style.boxShadow = "none";
94
+ }
95
+ }), /* @__PURE__ */ jsx("button", {
96
+ onClick: handleSubmit,
97
+ disabled: disabled || !value.trim(),
98
+ "data-chat-submit": true,
99
+ style: {
100
+ padding: "0.75rem 1.5rem",
101
+ fontSize: "0.875rem",
102
+ fontWeight: 500,
103
+ color: "white",
104
+ backgroundColor: disabled || !value.trim() ? "rgba(107, 114, 128, 0.5)" : "rgb(249, 115, 22)",
105
+ border: "none",
106
+ borderRadius: "0.75rem",
107
+ cursor: disabled || !value.trim() ? "not-allowed" : "pointer",
108
+ transition: "all 0.2s",
109
+ whiteSpace: "nowrap"
110
+ },
111
+ onMouseEnter: (e) => {
112
+ if (!disabled && value.trim()) e.target.style.backgroundColor = "rgb(234, 88, 12)";
113
+ },
114
+ onMouseLeave: (e) => {
115
+ if (!disabled && value.trim()) e.target.style.backgroundColor = "rgb(249, 115, 22)";
116
+ },
117
+ children: isLoading ? "Sending..." : "Send"
118
+ })]
119
+ });
120
+ }
121
+ //#endregion
122
+ export { ChatInput };
123
+
124
+ //# sourceMappingURL=chat-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-input.js","names":[],"sources":["../../../src/chat-ui/chat-input.tsx"],"sourcesContent":["import { useRef, useState } from 'react'\nimport { useChatContext } from './chat'\nimport type { ReactNode, RefObject } from 'react'\n\nexport interface ChatInputRenderProps {\n /** Current input value */\n value: string\n /** Set input value */\n onChange: (value: string) => void\n /** Submit the message */\n onSubmit: () => void\n /** Is the chat currently loading */\n isLoading: boolean\n /** Is input disabled */\n disabled: boolean\n /** Ref to the input element */\n inputRef: RefObject<HTMLInputElement | HTMLTextAreaElement | null>\n}\n\nexport interface ChatInputProps {\n /** Render prop for full control */\n children?: (props: ChatInputRenderProps) => ReactNode\n /** CSS class name */\n className?: string\n /** Placeholder text */\n placeholder?: string\n /** Disable input */\n disabled?: boolean\n /** Submit on Enter (Shift+Enter for new line) */\n submitOnEnter?: boolean\n}\n\n/**\n * @deprecated Use `createChatUI()` and an application-owned input component.\n * Deprecated in 0.9.0. Removed in 1.0.0.\n *\n * Chat input component - handles message input and submission\n *\n * Features:\n * - Auto-growing textarea\n * - Submit on Enter (Shift+Enter for new line)\n * - Loading state management\n * - Full render prop support for custom UIs\n *\n * @example\n * ```tsx\n * <Chat.Input placeholder=\"Type your message...\" />\n * ```\n *\n * @example Custom UI with render prop\n * ```tsx\n * <Chat.Input>\n * {({ value, onChange, onSubmit, isLoading }) => (\n * <div>\n * <textarea value={value} onChange={(e) => onChange(e.target.value)} />\n * <button onClick={onSubmit} disabled={isLoading}>Send</button>\n * </div>\n * )}\n * </Chat.Input>\n * ```\n */\nexport function ChatInput({\n children,\n className,\n placeholder = 'Type a message...',\n disabled: disabledProp,\n submitOnEnter = true,\n}: ChatInputProps) {\n const { sendMessage, isLoading } = useChatContext()\n const [value, setValue] = useState('')\n const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null)\n\n const disabled = disabledProp || isLoading\n\n const handleSubmit = () => {\n if (!value.trim() || disabled) return\n void sendMessage(value)\n setValue('')\n }\n\n const renderProps: ChatInputRenderProps = {\n value,\n onChange: setValue,\n onSubmit: handleSubmit,\n isLoading,\n disabled,\n inputRef,\n }\n\n // Render prop pattern\n if (children) {\n return <>{children(renderProps)}</>\n }\n\n // Default implementation\n return (\n <div\n className={className}\n data-chat-input\n style={{\n display: 'flex',\n gap: '0.75rem',\n alignItems: 'center',\n width: '100%',\n }}\n >\n <input\n ref={inputRef as React.RefObject<HTMLInputElement>}\n type=\"text\"\n value={value}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={(e) => {\n if (submitOnEnter && e.key === 'Enter') {\n e.preventDefault()\n handleSubmit()\n }\n }}\n placeholder={placeholder}\n disabled={disabled}\n data-chat-textarea\n style={{\n flex: 1,\n padding: '0.75rem 1rem',\n fontSize: '0.875rem',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n borderRadius: '0.75rem',\n backgroundColor: 'rgba(31, 41, 55, 0.5)',\n color: 'white',\n outline: 'none',\n transition: 'all 0.2s',\n }}\n onFocus={(e) => {\n e.target.style.borderColor = 'rgba(249, 115, 22, 0.4)'\n e.target.style.boxShadow = '0 0 0 2px rgba(249, 115, 22, 0.2)'\n }}\n onBlur={(e) => {\n e.target.style.borderColor = 'rgba(255, 255, 255, 0.1)'\n e.target.style.boxShadow = 'none'\n }}\n />\n <button\n onClick={handleSubmit}\n disabled={disabled || !value.trim()}\n data-chat-submit\n style={{\n padding: '0.75rem 1.5rem',\n fontSize: '0.875rem',\n fontWeight: 500,\n color: 'white',\n backgroundColor:\n disabled || !value.trim()\n ? 'rgba(107, 114, 128, 0.5)'\n : 'rgb(249, 115, 22)',\n border: 'none',\n borderRadius: '0.75rem',\n cursor: disabled || !value.trim() ? 'not-allowed' : 'pointer',\n transition: 'all 0.2s',\n whiteSpace: 'nowrap',\n }}\n onMouseEnter={(e) => {\n if (!disabled && value.trim()) {\n ;(e.target as HTMLButtonElement).style.backgroundColor =\n 'rgb(234, 88, 12)'\n }\n }}\n onMouseLeave={(e) => {\n if (!disabled && value.trim()) {\n ;(e.target as HTMLButtonElement).style.backgroundColor =\n 'rgb(249, 115, 22)'\n }\n }}\n >\n {isLoading ? 'Sending...' : 'Send'}\n </button>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,UAAU,EACxB,UACA,WACA,cAAc,qBACd,UAAU,cACV,gBAAgB,QACC;CACjB,MAAM,EAAE,aAAa,cAAc,eAAe;CAClD,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,WAAW,OAA+C,IAAI;CAEpE,MAAM,WAAW,gBAAgB;CAEjC,MAAM,qBAAqB;EACzB,IAAI,CAAC,MAAM,KAAK,KAAK,UAAU;EAC/B,YAAiB,KAAK;EACtB,SAAS,EAAE;CACb;CAEA,MAAM,cAAoC;EACxC;EACA,UAAU;EACV,UAAU;EACV;EACA;EACA;CACF;CAGA,IAAI,UACF,OAAO,oBAAA,UAAA,EAAA,UAAG,SAAS,WAAW,EAAI,CAAA;CAIpC,OACE,qBAAC,OAAD;EACa;EACX,mBAAA;EACA,OAAO;GACL,SAAS;GACT,KAAK;GACL,YAAY;GACZ,OAAO;EACT;EARF,UAAA,CAUE,oBAAC,SAAD;GACE,KAAK;GACL,MAAK;GACE;GACP,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,YAAY,MAAM;IAChB,IAAI,iBAAiB,EAAE,QAAQ,SAAS;KACtC,EAAE,eAAe;KACjB,aAAa;IACf;GACF;GACa;GACH;GACV,sBAAA;GACA,OAAO;IACL,MAAM;IACN,SAAS;IACT,UAAU;IACV,QAAQ;IACR,cAAc;IACd,iBAAiB;IACjB,OAAO;IACP,SAAS;IACT,YAAY;GACd;GACA,UAAU,MAAM;IACd,EAAE,OAAO,MAAM,cAAc;IAC7B,EAAE,OAAO,MAAM,YAAY;GAC7B;GACA,SAAS,MAAM;IACb,EAAE,OAAO,MAAM,cAAc;IAC7B,EAAE,OAAO,MAAM,YAAY;GAC7B;EACD,CAAA,GACD,oBAAC,UAAD;GACE,SAAS;GACT,UAAU,YAAY,CAAC,MAAM,KAAK;GAClC,oBAAA;GACA,OAAO;IACL,SAAS;IACT,UAAU;IACV,YAAY;IACZ,OAAO;IACP,iBACE,YAAY,CAAC,MAAM,KAAK,IACpB,6BACA;IACN,QAAQ;IACR,cAAc;IACd,QAAQ,YAAY,CAAC,MAAM,KAAK,IAAI,gBAAgB;IACpD,YAAY;IACZ,YAAY;GACd;GACA,eAAe,MAAM;IACnB,IAAI,CAAC,YAAY,MAAM,KAAK,GACzB,EAAG,OAA6B,MAAM,kBACrC;GAEN;GACA,eAAe,MAAM;IACnB,IAAI,CAAC,YAAY,MAAM,KAAK,GACzB,EAAG,OAA6B,MAAM,kBACrC;GAEN;GAEC,UAAA,YAAY,eAAe;EACtB,CAAA,CACL;;AAET"}
@@ -0,0 +1,91 @@
1
+ import { ReactNode } from 'react';
2
+ import { UIMessage } from '../types.js';
3
+ export interface ToolCallRenderProps {
4
+ id: string;
5
+ name: string;
6
+ arguments: string;
7
+ state: string;
8
+ approval?: any;
9
+ output?: any;
10
+ }
11
+ export interface ChatMessageProps {
12
+ /** The message to render */
13
+ message: UIMessage;
14
+ /** Base CSS class name */
15
+ className?: string;
16
+ /** Additional className for user messages */
17
+ userClassName?: string;
18
+ /** Additional className for assistant messages */
19
+ assistantClassName?: string;
20
+ /** Custom renderer for text parts */
21
+ textPartRenderer?: (props: {
22
+ content: string;
23
+ }) => ReactNode;
24
+ /** Custom renderer for thinking parts */
25
+ thinkingPartRenderer?: (props: {
26
+ content: string;
27
+ isComplete?: boolean;
28
+ }) => ReactNode;
29
+ /** Named tool renderers - use the tool name as the key */
30
+ toolsRenderer?: Record<string, (props: ToolCallRenderProps) => ReactNode>;
31
+ /** Default tool renderer when tool name not found in toolsRenderer */
32
+ defaultToolRenderer?: (props: ToolCallRenderProps) => ReactNode;
33
+ /** Custom renderer for tool result parts */
34
+ toolResultRenderer?: (props: {
35
+ toolCallId: string;
36
+ content: string;
37
+ state: string;
38
+ }) => ReactNode;
39
+ }
40
+ /**
41
+ * @deprecated Use `createChatUI()` Message instead. Deprecated in 0.9.0. Removed in 1.0.0.
42
+ *
43
+ * Message component - renders a single message with all its parts
44
+ *
45
+ * This component natively understands TanStack AI's parts-based message format:
46
+ * - thinking parts: rendered as collapsible thinking/reasoning sections (auto-collapses when complete)
47
+ * - text parts: rendered as content
48
+ * - tool-call parts: rendered with state, approvals, etc.
49
+ * - tool-result parts: rendered with results
50
+ *
51
+ * @example Basic usage
52
+ * ```tsx
53
+ * <Chat.Message message={message} />
54
+ * ```
55
+ *
56
+ * @example With role-based styling
57
+ * ```tsx
58
+ * <ChatMessage
59
+ * message={message}
60
+ * className="flex"
61
+ * userClassName="justify-end"
62
+ * assistantClassName="justify-start"
63
+ * />
64
+ * ```
65
+ *
66
+ * @example With custom thinking renderer
67
+ * ```tsx
68
+ * <ChatMessage
69
+ * message={message}
70
+ * thinkingPartRenderer={({ content, isComplete }) => (
71
+ * <details open={!isComplete}>
72
+ * <summary>💭 Thinking...</summary>
73
+ * <pre>{content}</pre>
74
+ * </details>
75
+ * )}
76
+ * />
77
+ * ```
78
+ *
79
+ * @example With named tool renderers
80
+ * ```tsx
81
+ * <ChatMessage
82
+ * message={message}
83
+ * toolsRenderer={{
84
+ * recommendGuitar: ({ id, arguments: args }) => <GuitarCard {...JSON.parse(args)} />,
85
+ * weatherLookup: ({ id, arguments: args }) => <WeatherWidget {...JSON.parse(args)} />,
86
+ * }}
87
+ * defaultToolRenderer={() => null}
88
+ * />
89
+ * ```
90
+ */
91
+ export declare function ChatMessage({ message, className, userClassName, assistantClassName, textPartRenderer, thinkingPartRenderer, toolsRenderer, defaultToolRenderer, toolResultRenderer, }: ChatMessageProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,157 @@
1
+ import { ThinkingPart } from "./thinking-part.js";
2
+ import { toolResultContentToString } from "./tool-result-content.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/chat-ui/chat-message.tsx
5
+ /**
6
+ * @deprecated Use `createChatUI()` Message instead. Deprecated in 0.9.0. Removed in 1.0.0.
7
+ *
8
+ * Message component - renders a single message with all its parts
9
+ *
10
+ * This component natively understands TanStack AI's parts-based message format:
11
+ * - thinking parts: rendered as collapsible thinking/reasoning sections (auto-collapses when complete)
12
+ * - text parts: rendered as content
13
+ * - tool-call parts: rendered with state, approvals, etc.
14
+ * - tool-result parts: rendered with results
15
+ *
16
+ * @example Basic usage
17
+ * ```tsx
18
+ * <Chat.Message message={message} />
19
+ * ```
20
+ *
21
+ * @example With role-based styling
22
+ * ```tsx
23
+ * <ChatMessage
24
+ * message={message}
25
+ * className="flex"
26
+ * userClassName="justify-end"
27
+ * assistantClassName="justify-start"
28
+ * />
29
+ * ```
30
+ *
31
+ * @example With custom thinking renderer
32
+ * ```tsx
33
+ * <ChatMessage
34
+ * message={message}
35
+ * thinkingPartRenderer={({ content, isComplete }) => (
36
+ * <details open={!isComplete}>
37
+ * <summary>💭 Thinking...</summary>
38
+ * <pre>{content}</pre>
39
+ * </details>
40
+ * )}
41
+ * />
42
+ * ```
43
+ *
44
+ * @example With named tool renderers
45
+ * ```tsx
46
+ * <ChatMessage
47
+ * message={message}
48
+ * toolsRenderer={{
49
+ * recommendGuitar: ({ id, arguments: args }) => <GuitarCard {...JSON.parse(args)} />,
50
+ * weatherLookup: ({ id, arguments: args }) => <WeatherWidget {...JSON.parse(args)} />,
51
+ * }}
52
+ * defaultToolRenderer={() => null}
53
+ * />
54
+ * ```
55
+ */
56
+ function ChatMessage({ message, className = "", userClassName = "", assistantClassName = "", textPartRenderer, thinkingPartRenderer, toolsRenderer, defaultToolRenderer, toolResultRenderer }) {
57
+ const combinedClassName = [className, message.role === "user" ? userClassName : assistantClassName].filter(Boolean).join(" ");
58
+ return /* @__PURE__ */ jsx("div", {
59
+ className: combinedClassName || void 0,
60
+ "data-message-id": message.id,
61
+ "data-message-role": message.role,
62
+ "data-message-created": message.createdAt?.toISOString(),
63
+ children: message.parts.map((part, index) => {
64
+ const isThinkingComplete = part.type === "thinking" && message.parts.slice(index + 1).some((p) => p.type === "text");
65
+ return /* @__PURE__ */ jsx(MessagePart, {
66
+ part,
67
+ isThinkingComplete,
68
+ textPartRenderer,
69
+ thinkingPartRenderer,
70
+ toolsRenderer,
71
+ defaultToolRenderer,
72
+ toolResultRenderer
73
+ }, `${message.id}-part-${index}`);
74
+ })
75
+ });
76
+ }
77
+ function MessagePart({ part, isThinkingComplete, textPartRenderer, thinkingPartRenderer, toolsRenderer, defaultToolRenderer, toolResultRenderer }) {
78
+ if (part.type === "text") {
79
+ if (textPartRenderer) return /* @__PURE__ */ jsx(Fragment, { children: textPartRenderer({ content: part.content }) });
80
+ return /* @__PURE__ */ jsx("div", {
81
+ "data-part-type": "text",
82
+ "data-part-content": true,
83
+ children: part.content
84
+ });
85
+ }
86
+ if (part.type === "thinking") {
87
+ if (thinkingPartRenderer) return /* @__PURE__ */ jsx(Fragment, { children: thinkingPartRenderer({
88
+ content: part.content,
89
+ isComplete: isThinkingComplete
90
+ }) });
91
+ return /* @__PURE__ */ jsx(ThinkingPart, {
92
+ content: part.content,
93
+ isComplete: isThinkingComplete
94
+ });
95
+ }
96
+ if (part.type === "tool-call") {
97
+ const toolProps = {
98
+ id: part.id,
99
+ name: part.name,
100
+ arguments: part.arguments,
101
+ state: part.state,
102
+ approval: part.approval,
103
+ output: part.output
104
+ };
105
+ if (toolsRenderer?.[part.name]) return /* @__PURE__ */ jsx(Fragment, { children: toolsRenderer[part.name]?.(toolProps) });
106
+ if (defaultToolRenderer) return /* @__PURE__ */ jsx(Fragment, { children: defaultToolRenderer(toolProps) });
107
+ return /* @__PURE__ */ jsxs("div", {
108
+ "data-part-type": "tool-call",
109
+ "data-tool-name": part.name,
110
+ "data-tool-state": part.state,
111
+ "data-tool-id": part.id,
112
+ children: [
113
+ /* @__PURE__ */ jsxs("div", {
114
+ "data-tool-header": true,
115
+ children: [/* @__PURE__ */ jsx("strong", { children: part.name }), /* @__PURE__ */ jsx("span", {
116
+ "data-tool-state-badge": true,
117
+ children: part.state
118
+ })]
119
+ }),
120
+ part.arguments && /* @__PURE__ */ jsx("div", {
121
+ "data-tool-arguments": true,
122
+ children: /* @__PURE__ */ jsx("pre", { children: part.arguments })
123
+ }),
124
+ part.approval && /* @__PURE__ */ jsx("div", {
125
+ "data-tool-approval": true,
126
+ children: part.approval.approved !== void 0 ? part.approval.approved ? "✓ Approved" : "✗ Denied" : "⏳ Awaiting approval..."
127
+ }),
128
+ part.output && /* @__PURE__ */ jsx("div", {
129
+ "data-tool-output": true,
130
+ children: /* @__PURE__ */ jsx("pre", { children: JSON.stringify(part.output, null, 2) })
131
+ })
132
+ ]
133
+ });
134
+ }
135
+ if (part.type === "tool-result") {
136
+ const toolResultContent = toolResultContentToString(part.content);
137
+ if (toolResultRenderer) return /* @__PURE__ */ jsx(Fragment, { children: toolResultRenderer({
138
+ toolCallId: part.toolCallId,
139
+ content: toolResultContent,
140
+ state: part.state
141
+ }) });
142
+ return /* @__PURE__ */ jsx("div", {
143
+ "data-part-type": "tool-result",
144
+ "data-tool-call-id": part.toolCallId,
145
+ "data-tool-result-state": part.state,
146
+ children: /* @__PURE__ */ jsx("div", {
147
+ "data-tool-result-content": true,
148
+ children: toolResultContent
149
+ })
150
+ });
151
+ }
152
+ return null;
153
+ }
154
+ //#endregion
155
+ export { ChatMessage };
156
+
157
+ //# sourceMappingURL=chat-message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-message.js","names":[],"sources":["../../../src/chat-ui/chat-message.tsx"],"sourcesContent":["import { ThinkingPart } from './thinking-part'\nimport { toolResultContentToString } from './tool-result-content'\nimport type { ReactNode } from 'react'\nimport type { UIMessage } from '../types'\n\nexport interface ToolCallRenderProps {\n id: string\n name: string\n arguments: string\n state: string\n approval?: any\n output?: any\n}\n\nexport interface ChatMessageProps {\n /** The message to render */\n message: UIMessage\n /** Base CSS class name */\n className?: string\n /** Additional className for user messages */\n userClassName?: string\n /** Additional className for assistant messages */\n assistantClassName?: string\n /** Custom renderer for text parts */\n textPartRenderer?: (props: { content: string }) => ReactNode\n /** Custom renderer for thinking parts */\n thinkingPartRenderer?: (props: {\n content: string\n isComplete?: boolean\n }) => ReactNode\n /** Named tool renderers - use the tool name as the key */\n toolsRenderer?: Record<string, (props: ToolCallRenderProps) => ReactNode>\n /** Default tool renderer when tool name not found in toolsRenderer */\n defaultToolRenderer?: (props: ToolCallRenderProps) => ReactNode\n /** Custom renderer for tool result parts */\n toolResultRenderer?: (props: {\n toolCallId: string\n content: string\n state: string\n }) => ReactNode\n}\n\n/**\n * @deprecated Use `createChatUI()` Message instead. Deprecated in 0.9.0. Removed in 1.0.0.\n *\n * Message component - renders a single message with all its parts\n *\n * This component natively understands TanStack AI's parts-based message format:\n * - thinking parts: rendered as collapsible thinking/reasoning sections (auto-collapses when complete)\n * - text parts: rendered as content\n * - tool-call parts: rendered with state, approvals, etc.\n * - tool-result parts: rendered with results\n *\n * @example Basic usage\n * ```tsx\n * <Chat.Message message={message} />\n * ```\n *\n * @example With role-based styling\n * ```tsx\n * <ChatMessage\n * message={message}\n * className=\"flex\"\n * userClassName=\"justify-end\"\n * assistantClassName=\"justify-start\"\n * />\n * ```\n *\n * @example With custom thinking renderer\n * ```tsx\n * <ChatMessage\n * message={message}\n * thinkingPartRenderer={({ content, isComplete }) => (\n * <details open={!isComplete}>\n * <summary>💭 Thinking...</summary>\n * <pre>{content}</pre>\n * </details>\n * )}\n * />\n * ```\n *\n * @example With named tool renderers\n * ```tsx\n * <ChatMessage\n * message={message}\n * toolsRenderer={{\n * recommendGuitar: ({ id, arguments: args }) => <GuitarCard {...JSON.parse(args)} />,\n * weatherLookup: ({ id, arguments: args }) => <WeatherWidget {...JSON.parse(args)} />,\n * }}\n * defaultToolRenderer={() => null}\n * />\n * ```\n */\nexport function ChatMessage({\n message,\n className = '',\n userClassName = '',\n assistantClassName = '',\n textPartRenderer,\n thinkingPartRenderer,\n toolsRenderer,\n defaultToolRenderer,\n toolResultRenderer,\n}: ChatMessageProps) {\n // Combine classes based on role\n const roleClassName =\n message.role === 'user' ? userClassName : assistantClassName\n\n const combinedClassName = [className, roleClassName].filter(Boolean).join(' ')\n\n return (\n <div\n className={combinedClassName || undefined}\n data-message-id={message.id}\n data-message-role={message.role}\n data-message-created={message.createdAt?.toISOString()}\n >\n {message.parts.map((part, index) => {\n // Check if thinking is complete (if there's a text part after this thinking part)\n const isThinkingComplete =\n part.type === 'thinking' &&\n message.parts.slice(index + 1).some((p) => p.type === 'text')\n\n return (\n <MessagePart\n key={`${message.id}-part-${index}`}\n part={part}\n isThinkingComplete={isThinkingComplete}\n textPartRenderer={textPartRenderer}\n thinkingPartRenderer={thinkingPartRenderer}\n toolsRenderer={toolsRenderer}\n defaultToolRenderer={defaultToolRenderer}\n toolResultRenderer={toolResultRenderer}\n />\n )\n })}\n </div>\n )\n}\n\nfunction MessagePart({\n part,\n isThinkingComplete,\n textPartRenderer,\n thinkingPartRenderer,\n toolsRenderer,\n defaultToolRenderer,\n toolResultRenderer,\n}: {\n part: UIMessage['parts'][number]\n isThinkingComplete?: boolean\n textPartRenderer?: ChatMessageProps['textPartRenderer']\n thinkingPartRenderer?: ChatMessageProps['thinkingPartRenderer']\n toolsRenderer?: ChatMessageProps['toolsRenderer']\n defaultToolRenderer?: ChatMessageProps['defaultToolRenderer']\n toolResultRenderer?: ChatMessageProps['toolResultRenderer']\n}) {\n // Text part\n if (part.type === 'text') {\n if (textPartRenderer) {\n return <>{textPartRenderer({ content: part.content })}</>\n }\n return (\n <div data-part-type=\"text\" data-part-content>\n {part.content}\n </div>\n )\n }\n\n // Thinking part\n if (part.type === 'thinking') {\n if (thinkingPartRenderer) {\n return (\n <>\n {thinkingPartRenderer({\n content: part.content,\n isComplete: isThinkingComplete,\n })}\n </>\n )\n }\n return (\n <ThinkingPart content={part.content} isComplete={isThinkingComplete} />\n )\n }\n\n // Tool call part\n if (part.type === 'tool-call') {\n const toolProps: ToolCallRenderProps = {\n id: part.id,\n name: part.name,\n arguments: part.arguments,\n state: part.state,\n approval: part.approval,\n output: part.output,\n }\n\n // Check if there's a specific renderer for this tool\n if (toolsRenderer?.[part.name]) {\n return <>{toolsRenderer[part.name]?.(toolProps)}</>\n }\n\n // Use default tool renderer if provided\n if (defaultToolRenderer) {\n return <>{defaultToolRenderer(toolProps)}</>\n }\n\n // Fallback to built-in default renderer\n return (\n <div\n data-part-type=\"tool-call\"\n data-tool-name={part.name}\n data-tool-state={part.state}\n data-tool-id={part.id}\n >\n <div data-tool-header>\n <strong>{part.name}</strong>\n <span data-tool-state-badge>{part.state}</span>\n </div>\n {part.arguments && (\n <div data-tool-arguments>\n <pre>{part.arguments}</pre>\n </div>\n )}\n {part.approval && (\n <div data-tool-approval>\n {part.approval.approved !== undefined\n ? part.approval.approved\n ? '✓ Approved'\n : '✗ Denied'\n : '⏳ Awaiting approval...'}\n </div>\n )}\n {part.output && (\n <div data-tool-output>\n <pre>{JSON.stringify(part.output, null, 2)}</pre>\n </div>\n )}\n </div>\n )\n }\n\n // Tool result part\n if (part.type === 'tool-result') {\n const toolResultContent = toolResultContentToString(part.content)\n\n if (toolResultRenderer) {\n return (\n <>\n {toolResultRenderer({\n toolCallId: part.toolCallId,\n content: toolResultContent,\n state: part.state,\n })}\n </>\n )\n }\n\n return (\n <div\n data-part-type=\"tool-result\"\n data-tool-call-id={part.toolCallId}\n data-tool-result-state={part.state}\n >\n <div data-tool-result-content>{toolResultContent}</div>\n </div>\n )\n }\n\n return null\n}\n"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { ReactNode } from 'react';
2
+ import { UIMessage } from '../types.js';
3
+ /** @deprecated Use `createChatUI()` Messages instead. Deprecated in 0.9.0. Removed in 1.0.0. */
4
+ export interface ChatMessagesProps {
5
+ /** Custom render function for each message */
6
+ children?: (message: UIMessage, index: number) => ReactNode;
7
+ /** CSS class name */
8
+ className?: string;
9
+ /** Element to show when there are no messages */
10
+ emptyState?: ReactNode;
11
+ /** Element to show while loading the first message */
12
+ loadingState?: ReactNode;
13
+ /** Custom error renderer */
14
+ errorState?: (props: {
15
+ error: Error;
16
+ reload: () => Promise<void>;
17
+ }) => ReactNode;
18
+ /** Auto-scroll to bottom on new messages */
19
+ autoScroll?: boolean;
20
+ }
21
+ /**
22
+ * @deprecated Use `createChatUI()` Messages instead. Deprecated in 0.9.0. Removed in 1.0.0.
23
+ *
24
+ * Messages container - renders all messages in the conversation
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <Chat.Messages>
29
+ * {(message) => <Chat.Message message={message} />}
30
+ * </Chat.Messages>
31
+ * ```
32
+ */
33
+ export declare function ChatMessages({ children, className, emptyState, loadingState, errorState, autoScroll, }: ChatMessagesProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,44 @@
1
+ import { useChatContext } from "./chat.js";
2
+ import { ChatMessage } from "./chat-message.js";
3
+ import { useEffect, useRef } from "react";
4
+ import { Fragment, jsx } from "react/jsx-runtime";
5
+ //#region src/chat-ui/chat-messages.tsx
6
+ /**
7
+ * @deprecated Use `createChatUI()` Messages instead. Deprecated in 0.9.0. Removed in 1.0.0.
8
+ *
9
+ * Messages container - renders all messages in the conversation
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <Chat.Messages>
14
+ * {(message) => <Chat.Message message={message} />}
15
+ * </Chat.Messages>
16
+ * ```
17
+ */
18
+ function ChatMessages({ children, className, emptyState, loadingState, errorState, autoScroll = true }) {
19
+ const { messages, isLoading, error, reload } = useChatContext();
20
+ const containerRef = useRef(null);
21
+ useEffect(() => {
22
+ if (autoScroll && containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;
23
+ }, [messages, autoScroll]);
24
+ if (error && errorState) return /* @__PURE__ */ jsx(Fragment, { children: errorState({
25
+ error,
26
+ reload
27
+ }) });
28
+ if (isLoading && messages.length === 0 && loadingState) return /* @__PURE__ */ jsx(Fragment, { children: loadingState });
29
+ if (messages.length === 0 && emptyState) return /* @__PURE__ */ jsx(Fragment, { children: emptyState });
30
+ return /* @__PURE__ */ jsx("div", {
31
+ ref: containerRef,
32
+ className,
33
+ "data-chat-messages": true,
34
+ "data-message-count": messages.length,
35
+ children: messages.map((message, index) => children ? /* @__PURE__ */ jsx("div", {
36
+ "data-message-id": message.id,
37
+ children: children(message, index)
38
+ }, message.id) : /* @__PURE__ */ jsx(ChatMessage, { message }, message.id))
39
+ });
40
+ }
41
+ //#endregion
42
+ export { ChatMessages };
43
+
44
+ //# sourceMappingURL=chat-messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-messages.js","names":[],"sources":["../../../src/chat-ui/chat-messages.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react'\nimport { useChatContext } from './chat'\nimport { ChatMessage } from './chat-message'\nimport type { ReactNode } from 'react'\nimport type { UIMessage } from '../types'\n\n/** @deprecated Use `createChatUI()` Messages instead. Deprecated in 0.9.0. Removed in 1.0.0. */\nexport interface ChatMessagesProps {\n /** Custom render function for each message */\n children?: (message: UIMessage, index: number) => ReactNode\n /** CSS class name */\n className?: string\n /** Element to show when there are no messages */\n emptyState?: ReactNode\n /** Element to show while loading the first message */\n loadingState?: ReactNode\n /** Custom error renderer */\n errorState?: (props: {\n error: Error\n reload: () => Promise<void>\n }) => ReactNode\n /** Auto-scroll to bottom on new messages */\n autoScroll?: boolean\n}\n\n/**\n * @deprecated Use `createChatUI()` Messages instead. Deprecated in 0.9.0. Removed in 1.0.0.\n *\n * Messages container - renders all messages in the conversation\n *\n * @example\n * ```tsx\n * <Chat.Messages>\n * {(message) => <Chat.Message message={message} />}\n * </Chat.Messages>\n * ```\n */\nexport function ChatMessages({\n children,\n className,\n emptyState,\n loadingState,\n errorState,\n autoScroll = true,\n}: ChatMessagesProps) {\n const { messages, isLoading, error, reload } = useChatContext()\n const containerRef = useRef<HTMLDivElement>(null)\n\n // Auto-scroll to bottom on new messages\n useEffect(() => {\n if (autoScroll && containerRef.current) {\n containerRef.current.scrollTop = containerRef.current.scrollHeight\n }\n }, [messages, autoScroll])\n\n // Error state\n if (error && errorState) {\n return <>{errorState({ error, reload })}</>\n }\n\n // Loading state (only show if no messages yet)\n if (isLoading && messages.length === 0 && loadingState) {\n return <>{loadingState}</>\n }\n\n // Empty state\n if (messages.length === 0 && emptyState) {\n return <>{emptyState}</>\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n data-chat-messages\n data-message-count={messages.length}\n >\n {messages.map((message, index) =>\n children ? (\n <div key={message.id} data-message-id={message.id}>\n {children(message, index)}\n </div>\n ) : (\n <ChatMessage key={message.id} message={message} />\n ),\n )}\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAqCA,SAAgB,aAAa,EAC3B,UACA,WACA,YACA,cACA,YACA,aAAa,QACO;CACpB,MAAM,EAAE,UAAU,WAAW,OAAO,WAAW,eAAe;CAC9D,MAAM,eAAe,OAAuB,IAAI;CAGhD,gBAAgB;EACd,IAAI,cAAc,aAAa,SAC7B,aAAa,QAAQ,YAAY,aAAa,QAAQ;CAE1D,GAAG,CAAC,UAAU,UAAU,CAAC;CAGzB,IAAI,SAAS,YACX,OAAO,oBAAA,UAAA,EAAA,UAAG,WAAW;EAAE;EAAO;CAAO,CAAC,EAAI,CAAA;CAI5C,IAAI,aAAa,SAAS,WAAW,KAAK,cACxC,OAAO,oBAAA,UAAA,EAAA,UAAG,aAAe,CAAA;CAI3B,IAAI,SAAS,WAAW,KAAK,YAC3B,OAAO,oBAAA,UAAA,EAAA,UAAG,WAAa,CAAA;CAGzB,OACE,oBAAC,OAAD;EACE,KAAK;EACM;EACX,sBAAA;EACA,sBAAoB,SAAS;EAE5B,UAAA,SAAS,KAAK,SAAS,UACtB,WACE,oBAAC,OAAD;GAAsB,mBAAiB,QAAQ;GAC5C,UAAA,SAAS,SAAS,KAAK;EACrB,GAFK,QAAQ,EAEb,IAEL,oBAAC,aAAD,EAAuC,QAAU,GAA/B,QAAQ,EAAuB,CAErD;CACG,CAAA;AAET"}
@@ -0,0 +1,53 @@
1
+ import { ConnectionAdapter } from '@tanstack/ai-client';
2
+ import { ReactNode } from 'react';
3
+ import { UIMessage, UseChatReturn } from '../types.js';
4
+ /**
5
+ * Hook to access chat context
6
+ * @throws Error if used outside of Chat component
7
+ */
8
+ /** @deprecated Use `createChatUI().useChatContext()` instead. Deprecated in 0.9.0. Removed in 1.0.0. */
9
+ export declare function useChatContext(): UseChatReturn;
10
+ /** @deprecated Use `createChatUI()` Chat/Provider instead. Deprecated in 0.9.0. Removed in 1.0.0. */
11
+ export interface ChatProps {
12
+ /** Child components (Chat.Messages, Chat.Input, etc.) */
13
+ children: ReactNode;
14
+ /** CSS class name for the root element */
15
+ className?: string;
16
+ /** Connection adapter for communicating with your API */
17
+ connection: ConnectionAdapter;
18
+ /** Initial messages to display */
19
+ initialMessages?: Array<UIMessage>;
20
+ /** Custom message ID generator */
21
+ id?: string;
22
+ /** Additional body data to send with requests */
23
+ body?: any;
24
+ /** Callback when a response is received */
25
+ onResponse?: (response?: Response) => void | Promise<void>;
26
+ /** Callback when each chunk arrives */
27
+ onChunk?: (chunk: any) => void;
28
+ /** Callback when a message is complete */
29
+ onFinish?: (message: UIMessage) => void;
30
+ /** Callback when an error occurs */
31
+ onError?: (error: Error) => void;
32
+ /** Custom tool components registry */
33
+ tools?: Record<string, React.ComponentType<{
34
+ input: any;
35
+ output?: any;
36
+ }>>;
37
+ }
38
+ /**
39
+ * @deprecated Since 0.9.0. Use `createChatHook()` from `@tanstack/ai-react/ui` instead.
40
+ * See https://tanstack.com/ai/latest/docs/ui/react
41
+ * Removed in 1.0.0.
42
+ *
43
+ * Root Chat component - provides context for all chat subcomponents
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * <Chat connection={fetchServerSentEvents("/api/chat")}>
48
+ * <Chat.Messages />
49
+ * <Chat.Input />
50
+ * </Chat>
51
+ * ```
52
+ */
53
+ export declare function Chat({ children, className, connection, initialMessages, id, body, onResponse, onChunk, onFinish, onError, }: ChatProps): import("react/jsx-runtime").JSX.Element;