@surf-kit/ai 0.1.1
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/LICENSE +12 -0
- package/README.md +71 -0
- package/dist/index.cjs +164 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +109 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +135 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2026 surf-kit contributors
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
purpose with or without fee is hereby granted.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
7
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
8
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
9
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
10
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
11
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
12
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @surf-kit/ai
|
|
2
|
+
|
|
3
|
+
Adapter package bridging [surf-kit](https://github.com/barney-w/surf-kit) agent components with [Vercel AI SDK v6](https://sdk.vercel.ai/).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @surf-kit/ai ai @ai-sdk/react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### useAIChat
|
|
14
|
+
|
|
15
|
+
Wraps `useChat` from `@ai-sdk/react` and maps to surf-kit types (`ChatMessage`, `StreamState`).
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useAIChat } from '@surf-kit/ai'
|
|
19
|
+
|
|
20
|
+
function Chat() {
|
|
21
|
+
const { messages, streamState, sendMessage, input, setInput, handleSubmit } =
|
|
22
|
+
useAIChat({ api: '/api/chat' })
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<form onSubmit={handleSubmit}>
|
|
26
|
+
<input value={input} onChange={(e) => setInput(e.target.value)} />
|
|
27
|
+
<button type="submit">Send</button>
|
|
28
|
+
</form>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### useAIStream
|
|
34
|
+
|
|
35
|
+
Wraps `useCompletion` from `@ai-sdk/react` for simple text streaming without chat history.
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { useAIStream } from '@surf-kit/ai'
|
|
39
|
+
|
|
40
|
+
function Completion() {
|
|
41
|
+
const { streamState, complete } = useAIStream({ api: '/api/complete' })
|
|
42
|
+
// ...
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### AIChat
|
|
47
|
+
|
|
48
|
+
Drop-in chat component powered by surf-kit agent UI and the AI SDK.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { AIChat } from '@surf-kit/ai'
|
|
52
|
+
|
|
53
|
+
function App() {
|
|
54
|
+
return <AIChat api="/api/chat" title="Assistant" />
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## When to Use This vs `@surf-kit/agent`
|
|
59
|
+
|
|
60
|
+
| | `@surf-kit/agent` | `@surf-kit/ai` |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| **Backend** | Custom SSE with surf-kit's bespoke protocol | Vercel AI SDK v6 (`/api/chat`) |
|
|
63
|
+
| **Entry point** | `<AgentChat>` | `<AIChat>` |
|
|
64
|
+
| **UI components** | surf-kit agent UI | Same surf-kit agent UI |
|
|
65
|
+
| **Best for** | Full control over streaming protocol | Standard AI SDK backends (OpenAI, Anthropic, etc.) |
|
|
66
|
+
|
|
67
|
+
Both packages render the same surf-kit UI components — the difference is how they connect to your backend.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
0BSD
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AIChat: () => AIChat,
|
|
24
|
+
useAIChat: () => useAIChat,
|
|
25
|
+
useAIStream: () => useAIStream
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
|
|
29
|
+
// src/hooks/useAIChat.ts
|
|
30
|
+
var import_react = require("@ai-sdk/react");
|
|
31
|
+
var import_ai = require("ai");
|
|
32
|
+
function getTextContent(message) {
|
|
33
|
+
return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
34
|
+
}
|
|
35
|
+
function useAIChat(options = {}) {
|
|
36
|
+
const chat = (0, import_react.useChat)({
|
|
37
|
+
transport: new import_ai.DefaultChatTransport({
|
|
38
|
+
api: options.api,
|
|
39
|
+
headers: options.headers,
|
|
40
|
+
body: options.body
|
|
41
|
+
}),
|
|
42
|
+
onError: options.onError,
|
|
43
|
+
onFinish: options.onFinish
|
|
44
|
+
});
|
|
45
|
+
const messages = chat.messages.map((m) => ({
|
|
46
|
+
id: m.id,
|
|
47
|
+
role: m.role,
|
|
48
|
+
content: getTextContent(m),
|
|
49
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
50
|
+
}));
|
|
51
|
+
const isStreaming = chat.status === "streaming";
|
|
52
|
+
const lastMessageContent = chat.messages.length > 0 ? getTextContent(chat.messages[chat.messages.length - 1]) : "";
|
|
53
|
+
const streamState = {
|
|
54
|
+
active: isStreaming,
|
|
55
|
+
phase: isStreaming ? "generating" : "idle",
|
|
56
|
+
content: isStreaming ? lastMessageContent : "",
|
|
57
|
+
sources: [],
|
|
58
|
+
agent: null,
|
|
59
|
+
agentLabel: null
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
messages,
|
|
63
|
+
streamState,
|
|
64
|
+
sendMessage: (content) => {
|
|
65
|
+
void chat.sendMessage({ text: content });
|
|
66
|
+
},
|
|
67
|
+
isLoading: isStreaming || chat.status === "submitted",
|
|
68
|
+
error: chat.error,
|
|
69
|
+
stop: chat.stop,
|
|
70
|
+
regenerate: () => {
|
|
71
|
+
void chat.regenerate();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/hooks/useAIStream.ts
|
|
77
|
+
var import_react2 = require("@ai-sdk/react");
|
|
78
|
+
function useAIStream(options = {}) {
|
|
79
|
+
const completion = (0, import_react2.useCompletion)({
|
|
80
|
+
api: options.api,
|
|
81
|
+
onFinish: options.onFinish,
|
|
82
|
+
onError: options.onError,
|
|
83
|
+
headers: options.headers,
|
|
84
|
+
body: options.body
|
|
85
|
+
});
|
|
86
|
+
const streamState = {
|
|
87
|
+
active: completion.isLoading,
|
|
88
|
+
phase: completion.isLoading ? "generating" : "idle",
|
|
89
|
+
content: completion.completion,
|
|
90
|
+
sources: [],
|
|
91
|
+
agent: null,
|
|
92
|
+
agentLabel: null
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
streamState,
|
|
96
|
+
completion: completion.completion,
|
|
97
|
+
complete: (prompt) => {
|
|
98
|
+
void completion.complete(prompt);
|
|
99
|
+
},
|
|
100
|
+
isLoading: completion.isLoading,
|
|
101
|
+
error: completion.error,
|
|
102
|
+
stop: completion.stop,
|
|
103
|
+
input: completion.input,
|
|
104
|
+
setInput: completion.setInput,
|
|
105
|
+
handleSubmit: completion.handleSubmit
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/components/AIChat.tsx
|
|
110
|
+
var import_agent = require("@surf-kit/agent");
|
|
111
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
112
|
+
function AIChat({
|
|
113
|
+
title = "Chat",
|
|
114
|
+
welcomeMessage = "How can I help you today?",
|
|
115
|
+
suggestedQuestions = [],
|
|
116
|
+
showHeader = true,
|
|
117
|
+
showSources,
|
|
118
|
+
showConfidence,
|
|
119
|
+
className,
|
|
120
|
+
...chatOptions
|
|
121
|
+
}) {
|
|
122
|
+
const { messages, streamState, sendMessage, isLoading } = useAIChat(chatOptions);
|
|
123
|
+
const hasMessages = messages.length > 0;
|
|
124
|
+
const handleQuestionSelect = (question) => {
|
|
125
|
+
sendMessage(question);
|
|
126
|
+
};
|
|
127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
128
|
+
"div",
|
|
129
|
+
{
|
|
130
|
+
className: [
|
|
131
|
+
"flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
|
|
132
|
+
className
|
|
133
|
+
].filter(Boolean).join(" "),
|
|
134
|
+
children: [
|
|
135
|
+
showHeader && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center justify-between border-b border-border px-4 py-3 bg-surface-raised shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-base font-semibold text-text-primary", children: title }) }),
|
|
136
|
+
hasMessages ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
137
|
+
import_agent.MessageThread,
|
|
138
|
+
{
|
|
139
|
+
messages,
|
|
140
|
+
streamingSlot: streamState.active ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_agent.StreamingMessage, { stream: streamState }) : void 0,
|
|
141
|
+
showSources,
|
|
142
|
+
showConfidence
|
|
143
|
+
}
|
|
144
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
145
|
+
import_agent.WelcomeScreen,
|
|
146
|
+
{
|
|
147
|
+
title,
|
|
148
|
+
message: welcomeMessage,
|
|
149
|
+
suggestedQuestions,
|
|
150
|
+
onQuestionSelect: handleQuestionSelect
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_agent.MessageComposer, { onSend: sendMessage, isLoading })
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
159
|
+
0 && (module.exports = {
|
|
160
|
+
AIChat,
|
|
161
|
+
useAIChat,
|
|
162
|
+
useAIStream
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/hooks/useAIChat.ts","../src/hooks/useAIStream.ts","../src/components/AIChat.tsx"],"sourcesContent":["// Types\nexport type {\n UseAIChatOptions,\n UseAIChatReturn,\n UseAIStreamOptions,\n UseAIStreamReturn,\n} from './types'\n\n// Re-exported surf-kit types for convenience\nexport type { ChatMessage, StreamState } from './types'\n\n// Hooks\nexport { useAIChat } from './hooks/useAIChat'\nexport { useAIStream } from './hooks/useAIStream'\n\n// Components\nexport { AIChat } from './components/AIChat'\nexport type { AIChatProps } from './components/AIChat'\n","import { useChat } from '@ai-sdk/react'\nimport { DefaultChatTransport } from 'ai'\nimport type { UIMessage } from 'ai'\nimport type { ChatMessage, StreamState } from '@surf-kit/agent'\nimport type { UseAIChatOptions, UseAIChatReturn } from '../types'\n\n/**\n * Extract the text content from a UIMessage by concatenating all text parts.\n */\nfunction getTextContent(message: UIMessage): string {\n return message.parts\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('')\n}\n\n/**\n * Adapter hook that wraps Vercel AI SDK v6's `useChat` and maps\n * its state to surf-kit `ChatMessage` and `StreamState` types.\n */\nexport function useAIChat(options: UseAIChatOptions = {}): UseAIChatReturn {\n const chat = useChat({\n transport: new DefaultChatTransport({\n api: options.api,\n headers: options.headers,\n body: options.body,\n }),\n onError: options.onError,\n onFinish: options.onFinish,\n })\n\n // Convert AI SDK v6 UIMessage (parts-based) to surf-kit ChatMessage format\n const messages: ChatMessage[] = chat.messages.map((m) => ({\n id: m.id,\n role: m.role as 'user' | 'assistant',\n content: getTextContent(m),\n timestamp: new Date(),\n }))\n\n const isStreaming = chat.status === 'streaming'\n const lastMessageContent = chat.messages.length > 0\n ? getTextContent(chat.messages[chat.messages.length - 1])\n : ''\n\n const streamState: StreamState = {\n active: isStreaming,\n phase: isStreaming ? 'generating' : 'idle',\n content: isStreaming ? lastMessageContent : '',\n sources: [],\n agent: null,\n agentLabel: null,\n }\n\n return {\n messages,\n streamState,\n sendMessage: (content: string) => {\n void chat.sendMessage({ text: content })\n },\n isLoading: isStreaming || chat.status === 'submitted',\n error: chat.error,\n stop: chat.stop,\n regenerate: () => {\n void chat.regenerate()\n },\n }\n}\n","import { useCompletion } from '@ai-sdk/react'\nimport type { StreamState } from '@surf-kit/agent'\nimport type { UseAIStreamOptions, UseAIStreamReturn } from '../types'\n\n/**\n * Adapter hook that wraps Vercel AI SDK v6's `useCompletion` for simple\n * text streaming (no chat history). Maps state to surf-kit `StreamState`.\n */\nexport function useAIStream(options: UseAIStreamOptions = {}): UseAIStreamReturn {\n const completion = useCompletion({\n api: options.api,\n onFinish: options.onFinish,\n onError: options.onError,\n headers: options.headers,\n body: options.body,\n })\n\n const streamState: StreamState = {\n active: completion.isLoading,\n phase: completion.isLoading ? 'generating' : 'idle',\n content: completion.completion,\n sources: [],\n agent: null,\n agentLabel: null,\n }\n\n return {\n streamState,\n completion: completion.completion,\n complete: (prompt: string) => {\n void completion.complete(prompt)\n },\n isLoading: completion.isLoading,\n error: completion.error,\n stop: completion.stop,\n input: completion.input,\n setInput: completion.setInput,\n handleSubmit: completion.handleSubmit,\n }\n}\n","import React from 'react'\nimport { MessageThread, MessageComposer, WelcomeScreen, StreamingMessage } from '@surf-kit/agent'\nimport { useAIChat } from '../hooks/useAIChat'\nimport type { UseAIChatOptions } from '../types'\n\nexport interface AIChatProps extends UseAIChatOptions {\n /** Title shown in the header */\n title?: string\n /** Message displayed on the welcome screen */\n welcomeMessage?: string\n /** Suggested questions shown on the welcome screen */\n suggestedQuestions?: string[]\n /** Whether to show the header bar */\n showHeader?: boolean\n /** Whether to show source citations */\n showSources?: boolean\n /** Whether to show confidence indicators */\n showConfidence?: boolean\n /** Additional CSS class names */\n className?: string\n}\n\n/**\n * Drop-in chat component powered by Vercel AI SDK v6 and surf-kit agent UI.\n * Composes `MessageThread`, `MessageComposer`, `StreamingMessage`, and\n * `WelcomeScreen` from `@surf-kit/agent`.\n */\nexport function AIChat({\n title = 'Chat',\n welcomeMessage = 'How can I help you today?',\n suggestedQuestions = [],\n showHeader = true,\n showSources,\n showConfidence,\n className,\n ...chatOptions\n}: AIChatProps) {\n const { messages, streamState, sendMessage, isLoading } = useAIChat(chatOptions)\n\n const hasMessages = messages.length > 0\n\n const handleQuestionSelect = (question: string) => {\n sendMessage(question)\n }\n\n return (\n <div\n className={[\n 'flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {showHeader && (\n <div className=\"flex items-center justify-between border-b border-border px-4 py-3 bg-surface-raised shrink-0\">\n <h1 className=\"text-base font-semibold text-text-primary\">{title}</h1>\n </div>\n )}\n\n {hasMessages ? (\n <MessageThread\n messages={messages}\n streamingSlot={\n streamState.active ? <StreamingMessage stream={streamState} /> : undefined\n }\n showSources={showSources}\n showConfidence={showConfidence}\n />\n ) : (\n <WelcomeScreen\n title={title}\n message={welcomeMessage}\n suggestedQuestions={suggestedQuestions}\n onQuestionSelect={handleQuestionSelect}\n />\n )}\n\n <MessageComposer onSend={sendMessage} isLoading={isLoading} />\n </div>\n )\n}\n"],"mappings":"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","names":["import_react"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ChatMessage, StreamState } from '@surf-kit/agent';
|
|
2
|
+
export { ChatMessage, StreamState } from '@surf-kit/agent';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/** Options for the useAIChat hook */
|
|
6
|
+
interface UseAIChatOptions {
|
|
7
|
+
/** API endpoint for chat completions. Defaults to '/api/chat'. */
|
|
8
|
+
api?: string;
|
|
9
|
+
/** Callback when a response finishes streaming */
|
|
10
|
+
onFinish?: (params: {
|
|
11
|
+
messages: unknown[];
|
|
12
|
+
}) => void;
|
|
13
|
+
/** Callback on error */
|
|
14
|
+
onError?: (error: Error) => void;
|
|
15
|
+
/** Additional headers to send with requests */
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
/** Additional body fields to send with requests */
|
|
18
|
+
body?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** Return type for the useAIChat hook */
|
|
21
|
+
interface UseAIChatReturn {
|
|
22
|
+
/** Messages converted to surf-kit ChatMessage format */
|
|
23
|
+
messages: ChatMessage[];
|
|
24
|
+
/** Current streaming state in surf-kit StreamState format */
|
|
25
|
+
streamState: StreamState;
|
|
26
|
+
/** Send a new user message */
|
|
27
|
+
sendMessage: (content: string) => void;
|
|
28
|
+
/** Whether a response is currently streaming */
|
|
29
|
+
isLoading: boolean;
|
|
30
|
+
/** Current error, if any */
|
|
31
|
+
error: Error | undefined;
|
|
32
|
+
/** Stop the current stream */
|
|
33
|
+
stop: () => void;
|
|
34
|
+
/** Regenerate the last assistant message */
|
|
35
|
+
regenerate: () => void;
|
|
36
|
+
}
|
|
37
|
+
/** Options for the useAIStream hook */
|
|
38
|
+
interface UseAIStreamOptions {
|
|
39
|
+
/** API endpoint for completions */
|
|
40
|
+
api?: string;
|
|
41
|
+
/** Callback when a completion finishes */
|
|
42
|
+
onFinish?: (prompt: string, completion: string) => void;
|
|
43
|
+
/** Callback on error */
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
/** Additional headers to send with requests */
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
/** Additional body fields to send with requests */
|
|
48
|
+
body?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** Return type for the useAIStream hook */
|
|
51
|
+
interface UseAIStreamReturn {
|
|
52
|
+
/** Current streaming state in surf-kit StreamState format */
|
|
53
|
+
streamState: StreamState;
|
|
54
|
+
/** The completed text */
|
|
55
|
+
completion: string;
|
|
56
|
+
/** Trigger a completion */
|
|
57
|
+
complete: (prompt: string) => void;
|
|
58
|
+
/** Whether a completion is currently streaming */
|
|
59
|
+
isLoading: boolean;
|
|
60
|
+
/** Current error, if any */
|
|
61
|
+
error: Error | undefined;
|
|
62
|
+
/** Stop the current stream */
|
|
63
|
+
stop: () => void;
|
|
64
|
+
/** Controlled input value */
|
|
65
|
+
input: string;
|
|
66
|
+
/** Set the controlled input value */
|
|
67
|
+
setInput: (input: string) => void;
|
|
68
|
+
/** Form submit handler */
|
|
69
|
+
handleSubmit: (e?: {
|
|
70
|
+
preventDefault?: () => void;
|
|
71
|
+
}) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Adapter hook that wraps Vercel AI SDK v6's `useChat` and maps
|
|
76
|
+
* its state to surf-kit `ChatMessage` and `StreamState` types.
|
|
77
|
+
*/
|
|
78
|
+
declare function useAIChat(options?: UseAIChatOptions): UseAIChatReturn;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Adapter hook that wraps Vercel AI SDK v6's `useCompletion` for simple
|
|
82
|
+
* text streaming (no chat history). Maps state to surf-kit `StreamState`.
|
|
83
|
+
*/
|
|
84
|
+
declare function useAIStream(options?: UseAIStreamOptions): UseAIStreamReturn;
|
|
85
|
+
|
|
86
|
+
interface AIChatProps extends UseAIChatOptions {
|
|
87
|
+
/** Title shown in the header */
|
|
88
|
+
title?: string;
|
|
89
|
+
/** Message displayed on the welcome screen */
|
|
90
|
+
welcomeMessage?: string;
|
|
91
|
+
/** Suggested questions shown on the welcome screen */
|
|
92
|
+
suggestedQuestions?: string[];
|
|
93
|
+
/** Whether to show the header bar */
|
|
94
|
+
showHeader?: boolean;
|
|
95
|
+
/** Whether to show source citations */
|
|
96
|
+
showSources?: boolean;
|
|
97
|
+
/** Whether to show confidence indicators */
|
|
98
|
+
showConfidence?: boolean;
|
|
99
|
+
/** Additional CSS class names */
|
|
100
|
+
className?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Drop-in chat component powered by Vercel AI SDK v6 and surf-kit agent UI.
|
|
104
|
+
* Composes `MessageThread`, `MessageComposer`, `StreamingMessage`, and
|
|
105
|
+
* `WelcomeScreen` from `@surf-kit/agent`.
|
|
106
|
+
*/
|
|
107
|
+
declare function AIChat({ title, welcomeMessage, suggestedQuestions, showHeader, showSources, showConfidence, className, ...chatOptions }: AIChatProps): react_jsx_runtime.JSX.Element;
|
|
108
|
+
|
|
109
|
+
export { AIChat, type AIChatProps, type UseAIChatOptions, type UseAIChatReturn, type UseAIStreamOptions, type UseAIStreamReturn, useAIChat, useAIStream };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ChatMessage, StreamState } from '@surf-kit/agent';
|
|
2
|
+
export { ChatMessage, StreamState } from '@surf-kit/agent';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/** Options for the useAIChat hook */
|
|
6
|
+
interface UseAIChatOptions {
|
|
7
|
+
/** API endpoint for chat completions. Defaults to '/api/chat'. */
|
|
8
|
+
api?: string;
|
|
9
|
+
/** Callback when a response finishes streaming */
|
|
10
|
+
onFinish?: (params: {
|
|
11
|
+
messages: unknown[];
|
|
12
|
+
}) => void;
|
|
13
|
+
/** Callback on error */
|
|
14
|
+
onError?: (error: Error) => void;
|
|
15
|
+
/** Additional headers to send with requests */
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
/** Additional body fields to send with requests */
|
|
18
|
+
body?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** Return type for the useAIChat hook */
|
|
21
|
+
interface UseAIChatReturn {
|
|
22
|
+
/** Messages converted to surf-kit ChatMessage format */
|
|
23
|
+
messages: ChatMessage[];
|
|
24
|
+
/** Current streaming state in surf-kit StreamState format */
|
|
25
|
+
streamState: StreamState;
|
|
26
|
+
/** Send a new user message */
|
|
27
|
+
sendMessage: (content: string) => void;
|
|
28
|
+
/** Whether a response is currently streaming */
|
|
29
|
+
isLoading: boolean;
|
|
30
|
+
/** Current error, if any */
|
|
31
|
+
error: Error | undefined;
|
|
32
|
+
/** Stop the current stream */
|
|
33
|
+
stop: () => void;
|
|
34
|
+
/** Regenerate the last assistant message */
|
|
35
|
+
regenerate: () => void;
|
|
36
|
+
}
|
|
37
|
+
/** Options for the useAIStream hook */
|
|
38
|
+
interface UseAIStreamOptions {
|
|
39
|
+
/** API endpoint for completions */
|
|
40
|
+
api?: string;
|
|
41
|
+
/** Callback when a completion finishes */
|
|
42
|
+
onFinish?: (prompt: string, completion: string) => void;
|
|
43
|
+
/** Callback on error */
|
|
44
|
+
onError?: (error: Error) => void;
|
|
45
|
+
/** Additional headers to send with requests */
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
/** Additional body fields to send with requests */
|
|
48
|
+
body?: Record<string, unknown>;
|
|
49
|
+
}
|
|
50
|
+
/** Return type for the useAIStream hook */
|
|
51
|
+
interface UseAIStreamReturn {
|
|
52
|
+
/** Current streaming state in surf-kit StreamState format */
|
|
53
|
+
streamState: StreamState;
|
|
54
|
+
/** The completed text */
|
|
55
|
+
completion: string;
|
|
56
|
+
/** Trigger a completion */
|
|
57
|
+
complete: (prompt: string) => void;
|
|
58
|
+
/** Whether a completion is currently streaming */
|
|
59
|
+
isLoading: boolean;
|
|
60
|
+
/** Current error, if any */
|
|
61
|
+
error: Error | undefined;
|
|
62
|
+
/** Stop the current stream */
|
|
63
|
+
stop: () => void;
|
|
64
|
+
/** Controlled input value */
|
|
65
|
+
input: string;
|
|
66
|
+
/** Set the controlled input value */
|
|
67
|
+
setInput: (input: string) => void;
|
|
68
|
+
/** Form submit handler */
|
|
69
|
+
handleSubmit: (e?: {
|
|
70
|
+
preventDefault?: () => void;
|
|
71
|
+
}) => void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Adapter hook that wraps Vercel AI SDK v6's `useChat` and maps
|
|
76
|
+
* its state to surf-kit `ChatMessage` and `StreamState` types.
|
|
77
|
+
*/
|
|
78
|
+
declare function useAIChat(options?: UseAIChatOptions): UseAIChatReturn;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Adapter hook that wraps Vercel AI SDK v6's `useCompletion` for simple
|
|
82
|
+
* text streaming (no chat history). Maps state to surf-kit `StreamState`.
|
|
83
|
+
*/
|
|
84
|
+
declare function useAIStream(options?: UseAIStreamOptions): UseAIStreamReturn;
|
|
85
|
+
|
|
86
|
+
interface AIChatProps extends UseAIChatOptions {
|
|
87
|
+
/** Title shown in the header */
|
|
88
|
+
title?: string;
|
|
89
|
+
/** Message displayed on the welcome screen */
|
|
90
|
+
welcomeMessage?: string;
|
|
91
|
+
/** Suggested questions shown on the welcome screen */
|
|
92
|
+
suggestedQuestions?: string[];
|
|
93
|
+
/** Whether to show the header bar */
|
|
94
|
+
showHeader?: boolean;
|
|
95
|
+
/** Whether to show source citations */
|
|
96
|
+
showSources?: boolean;
|
|
97
|
+
/** Whether to show confidence indicators */
|
|
98
|
+
showConfidence?: boolean;
|
|
99
|
+
/** Additional CSS class names */
|
|
100
|
+
className?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Drop-in chat component powered by Vercel AI SDK v6 and surf-kit agent UI.
|
|
104
|
+
* Composes `MessageThread`, `MessageComposer`, `StreamingMessage`, and
|
|
105
|
+
* `WelcomeScreen` from `@surf-kit/agent`.
|
|
106
|
+
*/
|
|
107
|
+
declare function AIChat({ title, welcomeMessage, suggestedQuestions, showHeader, showSources, showConfidence, className, ...chatOptions }: AIChatProps): react_jsx_runtime.JSX.Element;
|
|
108
|
+
|
|
109
|
+
export { AIChat, type AIChatProps, type UseAIChatOptions, type UseAIChatReturn, type UseAIStreamOptions, type UseAIStreamReturn, useAIChat, useAIStream };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// src/hooks/useAIChat.ts
|
|
2
|
+
import { useChat } from "@ai-sdk/react";
|
|
3
|
+
import { DefaultChatTransport } from "ai";
|
|
4
|
+
function getTextContent(message) {
|
|
5
|
+
return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
6
|
+
}
|
|
7
|
+
function useAIChat(options = {}) {
|
|
8
|
+
const chat = useChat({
|
|
9
|
+
transport: new DefaultChatTransport({
|
|
10
|
+
api: options.api,
|
|
11
|
+
headers: options.headers,
|
|
12
|
+
body: options.body
|
|
13
|
+
}),
|
|
14
|
+
onError: options.onError,
|
|
15
|
+
onFinish: options.onFinish
|
|
16
|
+
});
|
|
17
|
+
const messages = chat.messages.map((m) => ({
|
|
18
|
+
id: m.id,
|
|
19
|
+
role: m.role,
|
|
20
|
+
content: getTextContent(m),
|
|
21
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
22
|
+
}));
|
|
23
|
+
const isStreaming = chat.status === "streaming";
|
|
24
|
+
const lastMessageContent = chat.messages.length > 0 ? getTextContent(chat.messages[chat.messages.length - 1]) : "";
|
|
25
|
+
const streamState = {
|
|
26
|
+
active: isStreaming,
|
|
27
|
+
phase: isStreaming ? "generating" : "idle",
|
|
28
|
+
content: isStreaming ? lastMessageContent : "",
|
|
29
|
+
sources: [],
|
|
30
|
+
agent: null,
|
|
31
|
+
agentLabel: null
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
messages,
|
|
35
|
+
streamState,
|
|
36
|
+
sendMessage: (content) => {
|
|
37
|
+
void chat.sendMessage({ text: content });
|
|
38
|
+
},
|
|
39
|
+
isLoading: isStreaming || chat.status === "submitted",
|
|
40
|
+
error: chat.error,
|
|
41
|
+
stop: chat.stop,
|
|
42
|
+
regenerate: () => {
|
|
43
|
+
void chat.regenerate();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/hooks/useAIStream.ts
|
|
49
|
+
import { useCompletion } from "@ai-sdk/react";
|
|
50
|
+
function useAIStream(options = {}) {
|
|
51
|
+
const completion = useCompletion({
|
|
52
|
+
api: options.api,
|
|
53
|
+
onFinish: options.onFinish,
|
|
54
|
+
onError: options.onError,
|
|
55
|
+
headers: options.headers,
|
|
56
|
+
body: options.body
|
|
57
|
+
});
|
|
58
|
+
const streamState = {
|
|
59
|
+
active: completion.isLoading,
|
|
60
|
+
phase: completion.isLoading ? "generating" : "idle",
|
|
61
|
+
content: completion.completion,
|
|
62
|
+
sources: [],
|
|
63
|
+
agent: null,
|
|
64
|
+
agentLabel: null
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
streamState,
|
|
68
|
+
completion: completion.completion,
|
|
69
|
+
complete: (prompt) => {
|
|
70
|
+
void completion.complete(prompt);
|
|
71
|
+
},
|
|
72
|
+
isLoading: completion.isLoading,
|
|
73
|
+
error: completion.error,
|
|
74
|
+
stop: completion.stop,
|
|
75
|
+
input: completion.input,
|
|
76
|
+
setInput: completion.setInput,
|
|
77
|
+
handleSubmit: completion.handleSubmit
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/components/AIChat.tsx
|
|
82
|
+
import { MessageThread, MessageComposer, WelcomeScreen, StreamingMessage } from "@surf-kit/agent";
|
|
83
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
84
|
+
function AIChat({
|
|
85
|
+
title = "Chat",
|
|
86
|
+
welcomeMessage = "How can I help you today?",
|
|
87
|
+
suggestedQuestions = [],
|
|
88
|
+
showHeader = true,
|
|
89
|
+
showSources,
|
|
90
|
+
showConfidence,
|
|
91
|
+
className,
|
|
92
|
+
...chatOptions
|
|
93
|
+
}) {
|
|
94
|
+
const { messages, streamState, sendMessage, isLoading } = useAIChat(chatOptions);
|
|
95
|
+
const hasMessages = messages.length > 0;
|
|
96
|
+
const handleQuestionSelect = (question) => {
|
|
97
|
+
sendMessage(question);
|
|
98
|
+
};
|
|
99
|
+
return /* @__PURE__ */ jsxs(
|
|
100
|
+
"div",
|
|
101
|
+
{
|
|
102
|
+
className: [
|
|
103
|
+
"flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
|
|
104
|
+
className
|
|
105
|
+
].filter(Boolean).join(" "),
|
|
106
|
+
children: [
|
|
107
|
+
showHeader && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b border-border px-4 py-3 bg-surface-raised shrink-0", children: /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-text-primary", children: title }) }),
|
|
108
|
+
hasMessages ? /* @__PURE__ */ jsx(
|
|
109
|
+
MessageThread,
|
|
110
|
+
{
|
|
111
|
+
messages,
|
|
112
|
+
streamingSlot: streamState.active ? /* @__PURE__ */ jsx(StreamingMessage, { stream: streamState }) : void 0,
|
|
113
|
+
showSources,
|
|
114
|
+
showConfidence
|
|
115
|
+
}
|
|
116
|
+
) : /* @__PURE__ */ jsx(
|
|
117
|
+
WelcomeScreen,
|
|
118
|
+
{
|
|
119
|
+
title,
|
|
120
|
+
message: welcomeMessage,
|
|
121
|
+
suggestedQuestions,
|
|
122
|
+
onQuestionSelect: handleQuestionSelect
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
/* @__PURE__ */ jsx(MessageComposer, { onSend: sendMessage, isLoading })
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
export {
|
|
131
|
+
AIChat,
|
|
132
|
+
useAIChat,
|
|
133
|
+
useAIStream
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useAIChat.ts","../src/hooks/useAIStream.ts","../src/components/AIChat.tsx"],"sourcesContent":["import { useChat } from '@ai-sdk/react'\nimport { DefaultChatTransport } from 'ai'\nimport type { UIMessage } from 'ai'\nimport type { ChatMessage, StreamState } from '@surf-kit/agent'\nimport type { UseAIChatOptions, UseAIChatReturn } from '../types'\n\n/**\n * Extract the text content from a UIMessage by concatenating all text parts.\n */\nfunction getTextContent(message: UIMessage): string {\n return message.parts\n .filter((part): part is { type: 'text'; text: string } => part.type === 'text')\n .map((part) => part.text)\n .join('')\n}\n\n/**\n * Adapter hook that wraps Vercel AI SDK v6's `useChat` and maps\n * its state to surf-kit `ChatMessage` and `StreamState` types.\n */\nexport function useAIChat(options: UseAIChatOptions = {}): UseAIChatReturn {\n const chat = useChat({\n transport: new DefaultChatTransport({\n api: options.api,\n headers: options.headers,\n body: options.body,\n }),\n onError: options.onError,\n onFinish: options.onFinish,\n })\n\n // Convert AI SDK v6 UIMessage (parts-based) to surf-kit ChatMessage format\n const messages: ChatMessage[] = chat.messages.map((m) => ({\n id: m.id,\n role: m.role as 'user' | 'assistant',\n content: getTextContent(m),\n timestamp: new Date(),\n }))\n\n const isStreaming = chat.status === 'streaming'\n const lastMessageContent = chat.messages.length > 0\n ? getTextContent(chat.messages[chat.messages.length - 1])\n : ''\n\n const streamState: StreamState = {\n active: isStreaming,\n phase: isStreaming ? 'generating' : 'idle',\n content: isStreaming ? lastMessageContent : '',\n sources: [],\n agent: null,\n agentLabel: null,\n }\n\n return {\n messages,\n streamState,\n sendMessage: (content: string) => {\n void chat.sendMessage({ text: content })\n },\n isLoading: isStreaming || chat.status === 'submitted',\n error: chat.error,\n stop: chat.stop,\n regenerate: () => {\n void chat.regenerate()\n },\n }\n}\n","import { useCompletion } from '@ai-sdk/react'\nimport type { StreamState } from '@surf-kit/agent'\nimport type { UseAIStreamOptions, UseAIStreamReturn } from '../types'\n\n/**\n * Adapter hook that wraps Vercel AI SDK v6's `useCompletion` for simple\n * text streaming (no chat history). Maps state to surf-kit `StreamState`.\n */\nexport function useAIStream(options: UseAIStreamOptions = {}): UseAIStreamReturn {\n const completion = useCompletion({\n api: options.api,\n onFinish: options.onFinish,\n onError: options.onError,\n headers: options.headers,\n body: options.body,\n })\n\n const streamState: StreamState = {\n active: completion.isLoading,\n phase: completion.isLoading ? 'generating' : 'idle',\n content: completion.completion,\n sources: [],\n agent: null,\n agentLabel: null,\n }\n\n return {\n streamState,\n completion: completion.completion,\n complete: (prompt: string) => {\n void completion.complete(prompt)\n },\n isLoading: completion.isLoading,\n error: completion.error,\n stop: completion.stop,\n input: completion.input,\n setInput: completion.setInput,\n handleSubmit: completion.handleSubmit,\n }\n}\n","import React from 'react'\nimport { MessageThread, MessageComposer, WelcomeScreen, StreamingMessage } from '@surf-kit/agent'\nimport { useAIChat } from '../hooks/useAIChat'\nimport type { UseAIChatOptions } from '../types'\n\nexport interface AIChatProps extends UseAIChatOptions {\n /** Title shown in the header */\n title?: string\n /** Message displayed on the welcome screen */\n welcomeMessage?: string\n /** Suggested questions shown on the welcome screen */\n suggestedQuestions?: string[]\n /** Whether to show the header bar */\n showHeader?: boolean\n /** Whether to show source citations */\n showSources?: boolean\n /** Whether to show confidence indicators */\n showConfidence?: boolean\n /** Additional CSS class names */\n className?: string\n}\n\n/**\n * Drop-in chat component powered by Vercel AI SDK v6 and surf-kit agent UI.\n * Composes `MessageThread`, `MessageComposer`, `StreamingMessage`, and\n * `WelcomeScreen` from `@surf-kit/agent`.\n */\nexport function AIChat({\n title = 'Chat',\n welcomeMessage = 'How can I help you today?',\n suggestedQuestions = [],\n showHeader = true,\n showSources,\n showConfidence,\n className,\n ...chatOptions\n}: AIChatProps) {\n const { messages, streamState, sendMessage, isLoading } = useAIChat(chatOptions)\n\n const hasMessages = messages.length > 0\n\n const handleQuestionSelect = (question: string) => {\n sendMessage(question)\n }\n\n return (\n <div\n className={[\n 'flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {showHeader && (\n <div className=\"flex items-center justify-between border-b border-border px-4 py-3 bg-surface-raised shrink-0\">\n <h1 className=\"text-base font-semibold text-text-primary\">{title}</h1>\n </div>\n )}\n\n {hasMessages ? (\n <MessageThread\n messages={messages}\n streamingSlot={\n streamState.active ? <StreamingMessage stream={streamState} /> : undefined\n }\n showSources={showSources}\n showConfidence={showConfidence}\n />\n ) : (\n <WelcomeScreen\n title={title}\n message={welcomeMessage}\n suggestedQuestions={suggestedQuestions}\n onQuestionSelect={handleQuestionSelect}\n />\n )}\n\n <MessageComposer onSend={sendMessage} isLoading={isLoading} />\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@surf-kit/ai",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Adapter bridging surf-kit agent components with Vercel AI SDK v6",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"ai",
|
|
8
|
+
"vercel-ai-sdk",
|
|
9
|
+
"adapter",
|
|
10
|
+
"chat",
|
|
11
|
+
"streaming",
|
|
12
|
+
"design-system"
|
|
13
|
+
],
|
|
14
|
+
"license": "0BSD",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/barney-w/surf-kit.git",
|
|
23
|
+
"directory": "packages/ai"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/barney-w/surf-kit#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/barney-w/surf-kit/issues"
|
|
28
|
+
},
|
|
29
|
+
"type": "module",
|
|
30
|
+
"main": "dist/index.js",
|
|
31
|
+
"types": "dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
|
+
"require": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"ai": "^6.0.0",
|
|
41
|
+
"@ai-sdk/react": "^3.0.0",
|
|
42
|
+
"@surf-kit/agent": "0.2.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@testing-library/jest-dom": "^6.9.0",
|
|
46
|
+
"@testing-library/react": "^16.3.0",
|
|
47
|
+
"@types/react": "^19.2.0",
|
|
48
|
+
"@types/react-dom": "^19.2.0",
|
|
49
|
+
"jsdom": "^28.1.0",
|
|
50
|
+
"react": "^19.2.0",
|
|
51
|
+
"react-dom": "^19.2.0",
|
|
52
|
+
"tsup": "^8.0.0",
|
|
53
|
+
"typescript": "^5.9.0",
|
|
54
|
+
"vitest": "^4.0.0",
|
|
55
|
+
"@surf-kit/tsconfig": "0.1.0"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": ">=19",
|
|
59
|
+
"react-dom": ">=19"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public"
|
|
63
|
+
},
|
|
64
|
+
"scripts": {
|
|
65
|
+
"build": "tsup",
|
|
66
|
+
"dev": "tsup --watch",
|
|
67
|
+
"test": "vitest run",
|
|
68
|
+
"test:watch": "vitest",
|
|
69
|
+
"typecheck": "tsc --noEmit"
|
|
70
|
+
}
|
|
71
|
+
}
|