@tanstack/ai-preact 0.14.4 → 0.16.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/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 +154 -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/create-chat-hook.d.ts +22 -0
- package/dist/esm/create-chat-hook.js +24 -0
- package/dist/esm/create-chat-hook.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +3 -1
- package/dist/esm/ui.d.ts +3 -0
- package/dist/esm/ui.js +4 -0
- package/dist/esm/use-web-mcp-tools.d.ts +27 -0
- package/dist/esm/use-web-mcp-tools.js +36 -0
- package/dist/esm/use-web-mcp-tools.js.map +1 -0
- package/package.json +8 -4
- package/src/chat-ui/create-chat-hook.tsx +98 -0
- package/src/chat-ui/create-ui-contexts.tsx +57 -0
- package/src/chat-ui/create-ui.tsx +764 -0
- package/src/create-chat-hook.ts +47 -0
- package/src/index.ts +3 -0
- package/src/ui.ts +20 -0
- package/src/use-web-mcp-tools.ts +59 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'preact/compat'
|
|
2
|
+
import type { ComponentType } from 'preact/compat'
|
|
3
|
+
import type { InferredClientContext } from '@tanstack/ai-client'
|
|
4
|
+
import type { ChatUIInterruptsOf, ChatUIToolsOf } from '@tanstack/ai-client/ui'
|
|
5
|
+
import { useChat as useUnboundChat } from '../use-chat'
|
|
6
|
+
import type { UseChatOptions } from '../types'
|
|
7
|
+
import { createChatUI } from './create-ui'
|
|
8
|
+
import type { ChatUIFactoryConfig, ChatUIHost, InputProps } from './create-ui'
|
|
9
|
+
|
|
10
|
+
type HeadlessOptions<TOptions> = UseChatOptions<
|
|
11
|
+
ChatUIToolsOf<TOptions>,
|
|
12
|
+
InferredClientContext<ChatUIToolsOf<TOptions>>,
|
|
13
|
+
ChatUIInterruptsOf<TOptions>
|
|
14
|
+
>
|
|
15
|
+
|
|
16
|
+
type ChatInstanceOverrides<TOptions> = {
|
|
17
|
+
threadId?: string
|
|
18
|
+
live?: boolean
|
|
19
|
+
forwardedProps?: Record<string, any>
|
|
20
|
+
body?: Record<string, any>
|
|
21
|
+
initialMessages?: HeadlessOptions<TOptions>['initialMessages']
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bind chat options and UI widgets once at module scope.
|
|
26
|
+
*
|
|
27
|
+
* This matches Form `createFormHook` and Table `createTableHook`. The
|
|
28
|
+
* returned `useAppChat` creates a chat instance. `chat.AppChat` renders it.
|
|
29
|
+
* `useChatContext` reads that instance. Part and interrupt widgets take
|
|
30
|
+
* `part` / `interrupt` as props. Do not read those from context.
|
|
31
|
+
*
|
|
32
|
+
* Pass instance overrides such as `threadId` into `useAppChat()`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* const { useAppChat, useChatContext } = createChatHook({
|
|
37
|
+
* options: chatOptions,
|
|
38
|
+
* components: { layout: ChatLayout, message: ChatMessage },
|
|
39
|
+
* partsComponents: { fallback: FallbackPart },
|
|
40
|
+
* toolsComponents: { getWeather: WeatherTool, purchaseItem: PurchaseTool },
|
|
41
|
+
* interruptsComponents: { generic: { choosePlan: ChoosePlan } },
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* function Support() {
|
|
45
|
+
* const chat = useAppChat({ threadId: 'support-1' })
|
|
46
|
+
* return <chat.AppChat />
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function createChatHook<
|
|
51
|
+
const TOptions,
|
|
52
|
+
TInput extends ComponentType<any> | undefined =
|
|
53
|
+
| ComponentType<InputProps<NoInfer<TOptions>>>
|
|
54
|
+
| undefined,
|
|
55
|
+
>({
|
|
56
|
+
options,
|
|
57
|
+
...chatComponents
|
|
58
|
+
}: {
|
|
59
|
+
options: TOptions
|
|
60
|
+
} & ChatUIFactoryConfig<NoInfer<TOptions>, TInput>) {
|
|
61
|
+
const ui = createChatUI(
|
|
62
|
+
options,
|
|
63
|
+
chatComponents as ChatUIFactoryConfig<NoInfer<TOptions>, TInput>,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
function useAppChat(overrides?: ChatInstanceOverrides<TOptions>) {
|
|
67
|
+
const chat = (
|
|
68
|
+
overrides
|
|
69
|
+
? useUnboundChat({
|
|
70
|
+
...(options as HeadlessOptions<TOptions>),
|
|
71
|
+
...overrides,
|
|
72
|
+
})
|
|
73
|
+
: useUnboundChat(options as HeadlessOptions<TOptions>)
|
|
74
|
+
) as ChatUIHost<TOptions>
|
|
75
|
+
|
|
76
|
+
// `useChat` returns a fresh object on every render. The mixed Chat
|
|
77
|
+
// component must stay stable, or React remounts the tree on each
|
|
78
|
+
// update. Read the current instance from a ref.
|
|
79
|
+
const chatRef = useRef(chat)
|
|
80
|
+
chatRef.current = chat
|
|
81
|
+
|
|
82
|
+
const AppChat = useMemo(() => {
|
|
83
|
+
function AppChatImpl() {
|
|
84
|
+
return <ui.Chat chat={chatRef.current} />
|
|
85
|
+
}
|
|
86
|
+
return AppChatImpl
|
|
87
|
+
}, [])
|
|
88
|
+
|
|
89
|
+
return Object.assign(chat, { AppChat }) as ChatUIHost<TOptions> & {
|
|
90
|
+
AppChat: ComponentType
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
useAppChat,
|
|
96
|
+
useChatContext: ui.useChatContext,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createContext, useContext } from 'preact/compat'
|
|
2
|
+
import type { Context } from 'preact/compat'
|
|
3
|
+
import type {
|
|
4
|
+
ChatUIInterrupt,
|
|
5
|
+
ChatUISelectedPart,
|
|
6
|
+
} from '@tanstack/ai-client/ui'
|
|
7
|
+
import type { UseChatReturn } from '../types'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Scoped chat, part, and interrupt contexts plus matching context hooks.
|
|
11
|
+
* Pass these into {@link createChatHook} when widgets live in other files, or
|
|
12
|
+
* when two chat trees nest and must not share the default contexts.
|
|
13
|
+
*/
|
|
14
|
+
export interface ChatUIContexts {
|
|
15
|
+
chatContext: Context<UseChatReturn<any, any> | null>
|
|
16
|
+
partContext: Context<ChatUISelectedPart | null>
|
|
17
|
+
interruptContext: Context<ChatUIInterrupt | null>
|
|
18
|
+
useChatContext: () => UseChatReturn<any, any>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a fresh set of chat UI contexts. This matches Form
|
|
23
|
+
* `createFormHookContexts` and Table `createTableHookContexts`.
|
|
24
|
+
*
|
|
25
|
+
* Most apps can skip this. `createChatHook` uses shared module contexts by
|
|
26
|
+
* default. The hooks on this object are the escape hatch for a widget file
|
|
27
|
+
* that cannot import the `createChatHook` result (circular import), or for
|
|
28
|
+
* nested chats that need isolated providers.
|
|
29
|
+
*
|
|
30
|
+
* Prefer the `useChatContext` hook returned from `createChatHook` when you
|
|
31
|
+
* can import that result. That hook knows the `chatComponents` types. Part
|
|
32
|
+
* and interrupt widgets take `part` / `interrupt` as props.
|
|
33
|
+
*/
|
|
34
|
+
export function createChatHookContexts(): ChatUIContexts {
|
|
35
|
+
const chatContext = createContext<UseChatReturn<any, any> | null>(null)
|
|
36
|
+
const partContext = createContext<ChatUISelectedPart | null>(null)
|
|
37
|
+
const interruptContext = createContext<ChatUIInterrupt | null>(null)
|
|
38
|
+
|
|
39
|
+
function useChatContext() {
|
|
40
|
+
const chat = useContext(chatContext)
|
|
41
|
+
if (!chat) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
'`useChatContext` must be used within `UI.Provider` or `UI.Chat`.',
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
return chat
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
chatContext,
|
|
51
|
+
partContext,
|
|
52
|
+
interruptContext,
|
|
53
|
+
useChatContext,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const defaultChatUIContexts = createChatHookContexts()
|