@tomny-dev/uzi 0.7.0 → 0.7.1-pr.82.252.1.0f7aa55
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 +4 -0
- package/dist/components/ai-chat/AIChat.d.ts +63 -0
- package/dist/components/ai-chat/AIChat.d.ts.map +1 -0
- package/dist/components/ai-chat/AIChat.test.d.ts +2 -0
- package/dist/components/ai-chat/AIChat.test.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index14.js.map +1 -1
- package/dist/index15.cjs.map +1 -1
- package/dist/index20.js.map +1 -1
- package/dist/index21.cjs.map +1 -1
- package/dist/index26.js.map +1 -1
- package/dist/index27.cjs.map +1 -1
- package/dist/index28.js.map +1 -1
- package/dist/index29.cjs.map +1 -1
- package/dist/index4.js.map +1 -1
- package/dist/index5.cjs.map +1 -1
- package/dist/index75.js +65 -0
- package/dist/index75.js.map +1 -0
- package/dist/index76.cjs +94 -0
- package/dist/index76.cjs.map +1 -0
- package/dist/index76.js +303 -0
- package/dist/index76.js.map +1 -0
- package/dist/index77.cjs +303 -0
- package/dist/index77.cjs.map +1 -0
- package/dist/style.css +389 -17
- package/package.json +3 -1
package/dist/index76.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import ai_chat_module_default from "./index75.js";
|
|
3
|
+
import { useMemo, useState } from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { AuiConfig, AuiProvider, ThreadPrimitive } from "@assistant-ui/react";
|
|
6
|
+
import { AISDKChat, AssistantChatTransport, useAISDKChat } from "@assistant-ui/ai-sdk";
|
|
7
|
+
//#region src/components/ai-chat/AIChat.tsx
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function asString(value) {
|
|
12
|
+
return typeof value === "string" ? value : void 0;
|
|
13
|
+
}
|
|
14
|
+
function safeHttpUrl(value) {
|
|
15
|
+
const url = asString(value);
|
|
16
|
+
return url && /^https?:\/\//i.test(url) ? url : void 0;
|
|
17
|
+
}
|
|
18
|
+
function DefaultMessageRenderer({ message, renderMessagePart }) {
|
|
19
|
+
return /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: message.role === "user" ? ai_chat_module_default.userMessage : ai_chat_module_default.assistantMessage,
|
|
21
|
+
children: message.parts.map((part, index) => /* @__PURE__ */ jsx("div", { children: isRecord(part) ? renderMessagePart(part, index) : null }, isRecord(part) && typeof part.id === "string" ? part.id : index))
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function DefaultReasoningRenderer({ children, title = "Reasoning" }) {
|
|
25
|
+
return /* @__PURE__ */ jsxs("details", {
|
|
26
|
+
className: ai_chat_module_default.reasoning,
|
|
27
|
+
children: [/* @__PURE__ */ jsx("summary", {
|
|
28
|
+
className: ai_chat_module_default.reasoningSummary,
|
|
29
|
+
children: title
|
|
30
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
31
|
+
className: ai_chat_module_default.reasoningText,
|
|
32
|
+
children
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function DefaultSourceRenderer({ sources, renderSource }) {
|
|
37
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
38
|
+
className: ai_chat_module_default.sources,
|
|
39
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
40
|
+
className: ai_chat_module_default.sourcesLabel,
|
|
41
|
+
children: "Sources"
|
|
42
|
+
}), /* @__PURE__ */ jsx("ul", { children: sources.map((source, index) => /* @__PURE__ */ jsx("li", { children: renderSource(source, index) }, source.id)) })]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function DefaultDataRenderer({ data, renderDataItem }) {
|
|
46
|
+
return /* @__PURE__ */ jsx("div", {
|
|
47
|
+
className: ai_chat_module_default.data,
|
|
48
|
+
children: data.map((item, index) => /* @__PURE__ */ jsx("div", { children: renderDataItem(item, index) }, index))
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function DefaultErrorRenderer({ error, onRetry, onClear }) {
|
|
52
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: ai_chat_module_default.error,
|
|
54
|
+
role: "alert",
|
|
55
|
+
children: [/* @__PURE__ */ jsx("p", { children: error.message }), /* @__PURE__ */ jsxs("div", {
|
|
56
|
+
className: ai_chat_module_default.errorActions,
|
|
57
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
58
|
+
type: "button",
|
|
59
|
+
onClick: onRetry,
|
|
60
|
+
children: "Retry response"
|
|
61
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
62
|
+
type: "button",
|
|
63
|
+
onClick: onClear,
|
|
64
|
+
children: "Start new chat"
|
|
65
|
+
})]
|
|
66
|
+
})]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function renderPart(part, index, components) {
|
|
70
|
+
const type = asString(part.type) ?? "";
|
|
71
|
+
if (type === "step-start") return null;
|
|
72
|
+
if (type === "text") return /* @__PURE__ */ jsx("span", {
|
|
73
|
+
className: ai_chat_module_default.textPart,
|
|
74
|
+
children: asString(part.text) ?? ""
|
|
75
|
+
});
|
|
76
|
+
if (type === "reasoning") return /* @__PURE__ */ jsx(components?.ReasoningRenderer ?? DefaultReasoningRenderer, { children: asString(part.text) ?? "" });
|
|
77
|
+
if (type === "source-url" || type === "source-document") return /* @__PURE__ */ jsx(components?.SourceRenderer ?? DefaultSourceRenderer, {
|
|
78
|
+
sources: [{
|
|
79
|
+
id: asString(part.sourceId) ?? asString(part.id) ?? `source-${index}`,
|
|
80
|
+
title: asString(part.title) ?? asString(part.filename),
|
|
81
|
+
url: safeHttpUrl(part.url)
|
|
82
|
+
}],
|
|
83
|
+
renderSource: (item) => item.url ? /* @__PURE__ */ jsx("a", {
|
|
84
|
+
href: item.url,
|
|
85
|
+
target: "_blank",
|
|
86
|
+
rel: "noopener noreferrer",
|
|
87
|
+
children: item.title ?? item.url
|
|
88
|
+
}) : /* @__PURE__ */ jsx("span", { children: item.title ?? item.id })
|
|
89
|
+
});
|
|
90
|
+
if (type.startsWith("data-")) return /* @__PURE__ */ jsx(components?.DataRenderer ?? DefaultDataRenderer, {
|
|
91
|
+
data: [part],
|
|
92
|
+
renderDataItem: (item) => /* @__PURE__ */ jsx("pre", {
|
|
93
|
+
className: ai_chat_module_default.structuredData,
|
|
94
|
+
children: JSON.stringify(item.data ?? item, null, 2)
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
if (type.startsWith("tool-") || type === "dynamic-tool") return /* @__PURE__ */ jsxs("details", {
|
|
98
|
+
className: ai_chat_module_default.toolPart,
|
|
99
|
+
children: [/* @__PURE__ */ jsx("summary", { children: asString(part.toolName) ?? type.replace(/^tool-/, "") }), /* @__PURE__ */ jsx("pre", {
|
|
100
|
+
className: ai_chat_module_default.structuredData,
|
|
101
|
+
children: JSON.stringify(part.output ?? part.input ?? part, null, 2)
|
|
102
|
+
})]
|
|
103
|
+
});
|
|
104
|
+
if (type === "file" || type === "image") {
|
|
105
|
+
const url = safeHttpUrl(part.url);
|
|
106
|
+
return url ? /* @__PURE__ */ jsx("a", {
|
|
107
|
+
href: url,
|
|
108
|
+
target: "_blank",
|
|
109
|
+
rel: "noopener noreferrer",
|
|
110
|
+
children: asString(part.filename) ?? "View attachment"
|
|
111
|
+
}) : /* @__PURE__ */ jsx("span", { children: "Attachment" });
|
|
112
|
+
}
|
|
113
|
+
return /* @__PURE__ */ jsx("pre", {
|
|
114
|
+
className: ai_chat_module_default.structuredData,
|
|
115
|
+
children: JSON.stringify(part, null, 2)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
var DEFAULT_MESSAGE_LIMIT = 8;
|
|
119
|
+
function AIChat({ api, scope, scopeLabel, fetch: requestFetch, headers, credentials, sessionKey, messageLimit = DEFAULT_MESSAGE_LIMIT, inputPlaceholder = "Ask a question...", inputMaxLength = 2e3, components, onClose, className, style }) {
|
|
120
|
+
const [session, setSession] = useState(0);
|
|
121
|
+
const transport = useMemo(() => new AssistantChatTransport({
|
|
122
|
+
api,
|
|
123
|
+
...scope ? { body: { scope } } : {},
|
|
124
|
+
...requestFetch ? { fetch: requestFetch } : {},
|
|
125
|
+
...headers ? { headers } : {},
|
|
126
|
+
...credentials ? { credentials } : {}
|
|
127
|
+
}), [
|
|
128
|
+
api,
|
|
129
|
+
scope,
|
|
130
|
+
requestFetch,
|
|
131
|
+
headers,
|
|
132
|
+
credentials
|
|
133
|
+
]);
|
|
134
|
+
const config = useMemo(() => AuiConfig({ threads: AISDKChat({ transport }) }), [transport, session]);
|
|
135
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
136
|
+
className: [ai_chat_module_default.container, className].filter(Boolean).join(" "),
|
|
137
|
+
style,
|
|
138
|
+
children: [
|
|
139
|
+
scopeLabel && /* @__PURE__ */ jsxs("div", {
|
|
140
|
+
className: ai_chat_module_default.scope,
|
|
141
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
142
|
+
className: ai_chat_module_default.scopeLabel,
|
|
143
|
+
children: "Current scope"
|
|
144
|
+
}), /* @__PURE__ */ jsx("strong", { children: scopeLabel })]
|
|
145
|
+
}),
|
|
146
|
+
onClose && /* @__PURE__ */ jsx("button", {
|
|
147
|
+
className: ai_chat_module_default.closeBtn,
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: onClose,
|
|
150
|
+
"aria-label": "Close chat",
|
|
151
|
+
children: "✕"
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ jsx(AuiProvider, {
|
|
154
|
+
config,
|
|
155
|
+
children: /* @__PURE__ */ jsx(ThreadPrimitive.Root, {
|
|
156
|
+
className: ai_chat_module_default.thread,
|
|
157
|
+
children: /* @__PURE__ */ jsx(ChatViewport, {
|
|
158
|
+
messageLimit: Number.isFinite(messageLimit) ? Math.max(1, Math.floor(messageLimit)) : DEFAULT_MESSAGE_LIMIT,
|
|
159
|
+
inputPlaceholder,
|
|
160
|
+
inputMaxLength,
|
|
161
|
+
components,
|
|
162
|
+
onNewChat: () => setSession((previous) => previous + 1)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
}, JSON.stringify([
|
|
166
|
+
api,
|
|
167
|
+
scope,
|
|
168
|
+
sessionKey,
|
|
169
|
+
session
|
|
170
|
+
]))
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function ChatViewport({ messageLimit, inputPlaceholder, inputMaxLength, components, onNewChat }) {
|
|
175
|
+
const chat = useAISDKChat();
|
|
176
|
+
const [draft, setDraft] = useState("");
|
|
177
|
+
const messages = chat?.messages ?? [];
|
|
178
|
+
const status = chat?.status ?? "ready";
|
|
179
|
+
const isBusy = status === "submitted" || status === "streaming";
|
|
180
|
+
const historyFull = messages.filter((message) => message.role === "user").length >= messageLimit;
|
|
181
|
+
const MessageRenderer = components?.MessageRenderer ?? DefaultMessageRenderer;
|
|
182
|
+
const ErrorRenderer = components?.ErrorRenderer ?? DefaultErrorRenderer;
|
|
183
|
+
const handleSubmit = () => {
|
|
184
|
+
const text = draft.trim();
|
|
185
|
+
if (!chat || !text || isBusy || historyFull || chat.error) return;
|
|
186
|
+
setDraft("");
|
|
187
|
+
chat.sendMessage({ text });
|
|
188
|
+
};
|
|
189
|
+
const handleNewChat = () => {
|
|
190
|
+
chat?.stop();
|
|
191
|
+
onNewChat();
|
|
192
|
+
};
|
|
193
|
+
const handleRetry = () => {
|
|
194
|
+
if (!chat || isBusy) return;
|
|
195
|
+
chat.clearError();
|
|
196
|
+
chat.regenerate();
|
|
197
|
+
};
|
|
198
|
+
const handleKeyDown = (event) => {
|
|
199
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
handleSubmit();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const handleFormSubmit = (event) => {
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
handleSubmit();
|
|
207
|
+
};
|
|
208
|
+
return /* @__PURE__ */ jsxs(ThreadPrimitive.Viewport, {
|
|
209
|
+
autoScroll: true,
|
|
210
|
+
className: ai_chat_module_default.viewport,
|
|
211
|
+
children: [
|
|
212
|
+
messages.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
213
|
+
className: ai_chat_module_default.chatToolbar,
|
|
214
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
215
|
+
type: "button",
|
|
216
|
+
onClick: handleNewChat,
|
|
217
|
+
children: "New chat"
|
|
218
|
+
})
|
|
219
|
+
}),
|
|
220
|
+
/* @__PURE__ */ jsxs("div", {
|
|
221
|
+
className: ai_chat_module_default.messageList,
|
|
222
|
+
role: "log",
|
|
223
|
+
"aria-label": "Chat history",
|
|
224
|
+
"aria-live": "polite",
|
|
225
|
+
children: [
|
|
226
|
+
messages.length === 0 && /* @__PURE__ */ jsx("div", {
|
|
227
|
+
className: ai_chat_module_default.emptyState,
|
|
228
|
+
children: /* @__PURE__ */ jsx("p", { children: "Start a conversation" })
|
|
229
|
+
}),
|
|
230
|
+
messages.filter((message) => message.role === "user" || message.role === "assistant").map((message) => /* @__PURE__ */ jsx(MessageRenderer, {
|
|
231
|
+
message: {
|
|
232
|
+
id: message.id,
|
|
233
|
+
role: message.role,
|
|
234
|
+
parts: message.parts,
|
|
235
|
+
...isRecord(message.metadata) ? { metadata: message.metadata } : {}
|
|
236
|
+
},
|
|
237
|
+
renderMessagePart: (part, index) => renderPart(part, index, components)
|
|
238
|
+
}, message.id)),
|
|
239
|
+
isBusy && /* @__PURE__ */ jsx("p", {
|
|
240
|
+
className: ai_chat_module_default.status,
|
|
241
|
+
children: "Thinking..."
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
}),
|
|
245
|
+
/* @__PURE__ */ jsxs(ThreadPrimitive.ViewportFooter, {
|
|
246
|
+
className: ai_chat_module_default.footer,
|
|
247
|
+
children: [
|
|
248
|
+
!chat && /* @__PURE__ */ jsx("p", {
|
|
249
|
+
className: ai_chat_module_default.error,
|
|
250
|
+
role: "alert",
|
|
251
|
+
children: "Chat runtime is unavailable."
|
|
252
|
+
}),
|
|
253
|
+
chat?.error && /* @__PURE__ */ jsx(ErrorRenderer, {
|
|
254
|
+
error: chat.error,
|
|
255
|
+
onRetry: handleRetry,
|
|
256
|
+
onClear: handleNewChat
|
|
257
|
+
}),
|
|
258
|
+
historyFull && !chat?.error && !isBusy && /* @__PURE__ */ jsxs("div", {
|
|
259
|
+
className: ai_chat_module_default.historyLimit,
|
|
260
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
261
|
+
"This chat has reached its ",
|
|
262
|
+
messageLimit,
|
|
263
|
+
"-question limit."
|
|
264
|
+
] }), /* @__PURE__ */ jsx("button", {
|
|
265
|
+
type: "button",
|
|
266
|
+
onClick: handleNewChat,
|
|
267
|
+
children: "Start new chat"
|
|
268
|
+
})]
|
|
269
|
+
}),
|
|
270
|
+
(!historyFull || isBusy) && !chat?.error && /* @__PURE__ */ jsxs("form", {
|
|
271
|
+
className: ai_chat_module_default.composerForm,
|
|
272
|
+
onSubmit: handleFormSubmit,
|
|
273
|
+
children: [/* @__PURE__ */ jsx("textarea", {
|
|
274
|
+
className: ai_chat_module_default.input,
|
|
275
|
+
"aria-label": "Message",
|
|
276
|
+
rows: 2,
|
|
277
|
+
value: draft,
|
|
278
|
+
onChange: (event) => setDraft(event.target.value),
|
|
279
|
+
onKeyDown: handleKeyDown,
|
|
280
|
+
placeholder: inputPlaceholder,
|
|
281
|
+
maxLength: inputMaxLength,
|
|
282
|
+
disabled: isBusy || !chat
|
|
283
|
+
}), isBusy ? /* @__PURE__ */ jsx("button", {
|
|
284
|
+
type: "button",
|
|
285
|
+
className: ai_chat_module_default.sendButton,
|
|
286
|
+
onClick: () => void chat?.stop(),
|
|
287
|
+
children: "Stop"
|
|
288
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
289
|
+
type: "submit",
|
|
290
|
+
className: ai_chat_module_default.sendButton,
|
|
291
|
+
disabled: !chat || !draft.trim(),
|
|
292
|
+
children: "Send"
|
|
293
|
+
})]
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
})
|
|
297
|
+
]
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
export { AIChat };
|
|
302
|
+
|
|
303
|
+
//# sourceMappingURL=index76.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index76.js","names":[],"sources":["../src/components/ai-chat/AIChat.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport type { CSSProperties, ComponentType, FormEvent, KeyboardEvent, ReactNode } from 'react';\n\nimport { AuiConfig, AuiProvider, ThreadPrimitive } from '@assistant-ui/react';\nimport { AISDKChat, AssistantChatTransport, useAISDKChat } from '@assistant-ui/ai-sdk';\n\nimport styles from './ai-chat.module.css';\n\nexport interface AIChatProps {\n /** AI SDK UI-message stream endpoint. */\n readonly api: string;\n /** Opaque scope identifier sent to the API. Server must authorize it. */\n readonly scope?: string;\n /** Human-readable display text; never used as an access-control identifier. */\n readonly scopeLabel?: string;\n /** Fetch adapter for authentication, request instrumentation and local demos. */\n readonly fetch?: typeof globalThis.fetch;\n readonly headers?: Record<string, string>;\n readonly credentials?: RequestCredentials;\n /** Change this when the authenticated identity changes to reset the transcript. */\n readonly sessionKey?: string | number;\n /** Maximum number of user turns (default: 8). */\n readonly messageLimit?: number;\n readonly inputPlaceholder?: string;\n readonly inputMaxLength?: number;\n readonly components?: AIChatComponents;\n readonly onClose?: () => void;\n readonly className?: string;\n readonly style?: CSSProperties;\n}\n\nexport interface AIChatComponents {\n readonly MessageRenderer?: ComponentType<AIChatMessageRendererProps>;\n readonly ReasoningRenderer?: ComponentType<AIChatReasoningRendererProps>;\n readonly SourceRenderer?: ComponentType<AIChatSourceRendererProps>;\n readonly DataRenderer?: ComponentType<AIChatDataRendererProps>;\n readonly ErrorRenderer?: ComponentType<AIChatErrorRendererProps>;\n}\n\nexport interface AIChatMessageRendererProps {\n readonly message: {\n readonly id: string;\n readonly role: 'user' | 'assistant';\n readonly parts: readonly unknown[];\n readonly metadata?: Record<string, unknown>;\n };\n readonly renderMessagePart: (part: Record<string, unknown>, index: number) => ReactNode;\n}\n\nexport interface AIChatReasoningRendererProps {\n readonly children: string;\n readonly title?: string;\n}\n\nexport interface AIChatSource {\n readonly id: string;\n readonly title?: string;\n readonly url?: string;\n}\n\nexport interface AIChatSourceRendererProps {\n readonly sources: readonly AIChatSource[];\n readonly renderSource: (source: AIChatSource, index: number) => ReactNode;\n}\n\nexport interface AIChatDataRendererProps {\n readonly data: readonly Record<string, unknown>[];\n readonly renderDataItem: (item: Record<string, unknown>, index: number) => ReactNode;\n}\n\nexport interface AIChatErrorRendererProps {\n readonly error: Error;\n readonly onRetry: () => void;\n readonly onClear: () => void;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction safeHttpUrl(value: unknown): string | undefined {\n const url = asString(value);\n return url && /^https?:\\/\\//i.test(url) ? url : undefined;\n}\n\nfunction DefaultMessageRenderer({ message, renderMessagePart }: AIChatMessageRendererProps) {\n return (\n <div className={message.role === 'user' ? styles.userMessage : styles.assistantMessage}>\n {message.parts.map((part, index) => (\n <div key={isRecord(part) && typeof part.id === 'string' ? part.id : index}>\n {isRecord(part) ? renderMessagePart(part, index) : null}\n </div>\n ))}\n </div>\n );\n}\n\nfunction DefaultReasoningRenderer({ children, title = 'Reasoning' }: AIChatReasoningRendererProps) {\n return (\n <details className={styles.reasoning}>\n <summary className={styles.reasoningSummary}>{title}</summary>\n <p className={styles.reasoningText}>{children}</p>\n </details>\n );\n}\n\nfunction DefaultSourceRenderer({ sources, renderSource }: AIChatSourceRendererProps) {\n return (\n <div className={styles.sources}>\n <span className={styles.sourcesLabel}>Sources</span>\n <ul>\n {sources.map((source, index) => (\n <li key={source.id}>{renderSource(source, index)}</li>\n ))}\n </ul>\n </div>\n );\n}\n\nfunction DefaultDataRenderer({ data, renderDataItem }: AIChatDataRendererProps) {\n return (\n <div className={styles.data}>\n {data.map((item, index) => (\n <div key={index}>{renderDataItem(item, index)}</div>\n ))}\n </div>\n );\n}\n\nfunction DefaultErrorRenderer({ error, onRetry, onClear }: AIChatErrorRendererProps) {\n return (\n <div className={styles.error} role=\"alert\">\n <p>{error.message}</p>\n <div className={styles.errorActions}>\n <button type=\"button\" onClick={onRetry}>Retry response</button>\n <button type=\"button\" onClick={onClear}>Start new chat</button>\n </div>\n </div>\n );\n}\n\nfunction renderPart(\n part: Record<string, unknown>,\n index: number,\n components: AIChatComponents | undefined,\n): ReactNode {\n const type = asString(part.type) ?? '';\n if (type === 'step-start') return null;\n if (type === 'text') {\n return <span className={styles.textPart}>{asString(part.text) ?? ''}</span>;\n }\n if (type === 'reasoning') {\n const ReasoningRenderer = components?.ReasoningRenderer ?? DefaultReasoningRenderer;\n return <ReasoningRenderer>{asString(part.text) ?? ''}</ReasoningRenderer>;\n }\n if (type === 'source-url' || type === 'source-document') {\n const SourceRenderer = components?.SourceRenderer ?? DefaultSourceRenderer;\n const source: AIChatSource = {\n id: asString(part.sourceId) ?? asString(part.id) ?? `source-${index}`,\n title: asString(part.title) ?? asString(part.filename),\n url: safeHttpUrl(part.url),\n };\n return (\n <SourceRenderer\n sources={[source]}\n renderSource={(item) => item.url ? (\n <a href={item.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {item.title ?? item.url}\n </a>\n ) : <span>{item.title ?? item.id}</span>}\n />\n );\n }\n if (type.startsWith('data-')) {\n const DataRenderer = components?.DataRenderer ?? DefaultDataRenderer;\n return (\n <DataRenderer\n data={[part]}\n renderDataItem={(item) => (\n <pre className={styles.structuredData}>{JSON.stringify(item.data ?? item, null, 2)}</pre>\n )}\n />\n );\n }\n if (type.startsWith('tool-') || type === 'dynamic-tool') {\n return (\n <details className={styles.toolPart}>\n <summary>{asString(part.toolName) ?? type.replace(/^tool-/, '')}</summary>\n <pre className={styles.structuredData}>\n {JSON.stringify(part.output ?? part.input ?? part, null, 2)}\n </pre>\n </details>\n );\n }\n if (type === 'file' || type === 'image') {\n const url = safeHttpUrl(part.url);\n return url ? (\n <a href={url} target=\"_blank\" rel=\"noopener noreferrer\">\n {asString(part.filename) ?? 'View attachment'}\n </a>\n ) : <span>Attachment</span>;\n }\n // Preserve unsupported part types as inspectable data instead of silently dropping them.\n return <pre className={styles.structuredData}>{JSON.stringify(part, null, 2)}</pre>;\n}\n\nconst DEFAULT_MESSAGE_LIMIT = 8;\n\nexport function AIChat({\n api,\n scope,\n scopeLabel,\n fetch: requestFetch,\n headers,\n credentials,\n sessionKey,\n messageLimit = DEFAULT_MESSAGE_LIMIT,\n inputPlaceholder = 'Ask a question...',\n inputMaxLength = 2000,\n components,\n onClose,\n className,\n style,\n}: AIChatProps) {\n // Remounting the provider creates a fresh AI SDK chat id, not merely an empty transcript.\n const [session, setSession] = useState(0);\n const transport = useMemo(\n () => new AssistantChatTransport({\n api,\n // The transport merges this with the AI SDK messages, id, model context and tools.\n // The endpoint, not the presentation component, must enforce the scope.\n ...(scope ? { body: { scope } } : {}),\n ...(requestFetch ? { fetch: requestFetch } : {}),\n ...(headers ? { headers } : {}),\n ...(credentials ? { credentials } : {}),\n }),\n [api, scope, requestFetch, headers, credentials],\n );\n const config = useMemo(\n () => AuiConfig({ threads: AISDKChat({ transport }) }),\n [transport, session],\n );\n\n return (\n <div className={[styles.container, className].filter(Boolean).join(' ')} style={style}>\n {scopeLabel && (\n <div className={styles.scope}>\n <span className={styles.scopeLabel}>Current scope</span>\n <strong>{scopeLabel}</strong>\n </div>\n )}\n {onClose && (\n <button className={styles.closeBtn} type=\"button\" onClick={onClose} aria-label=\"Close chat\">\n ✕\n </button>\n )}\n <AuiProvider key={JSON.stringify([api, scope, sessionKey, session])} config={config}>\n <ThreadPrimitive.Root className={styles.thread}>\n <ChatViewport\n messageLimit={Number.isFinite(messageLimit) ? Math.max(1, Math.floor(messageLimit)) : DEFAULT_MESSAGE_LIMIT}\n inputPlaceholder={inputPlaceholder}\n inputMaxLength={inputMaxLength}\n components={components}\n onNewChat={() => setSession((previous) => previous + 1)}\n />\n </ThreadPrimitive.Root>\n </AuiProvider>\n </div>\n );\n}\n\ninterface ChatViewportProps {\n readonly messageLimit: number;\n readonly inputPlaceholder: string;\n readonly inputMaxLength: number;\n readonly components?: AIChatComponents;\n readonly onNewChat: () => void;\n}\n\nfunction ChatViewport({\n messageLimit,\n inputPlaceholder,\n inputMaxLength,\n components,\n onNewChat,\n}: ChatViewportProps) {\n const chat = useAISDKChat();\n const [draft, setDraft] = useState('');\n const messages = chat?.messages ?? [];\n const status = chat?.status ?? 'ready';\n const isBusy = status === 'submitted' || status === 'streaming';\n const historyFull = messages.filter((message) => message.role === 'user').length >= messageLimit;\n const MessageRenderer = components?.MessageRenderer ?? DefaultMessageRenderer;\n const ErrorRenderer = components?.ErrorRenderer ?? DefaultErrorRenderer;\n\n const handleSubmit = () => {\n const text = draft.trim();\n if (!chat || !text || isBusy || historyFull || chat.error) return;\n setDraft('');\n void chat.sendMessage({ text });\n };\n\n const handleNewChat = () => {\n void chat?.stop();\n onNewChat();\n };\n\n const handleRetry = () => {\n if (!chat || isBusy) return;\n chat.clearError();\n void chat.regenerate();\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {\n event.preventDefault();\n handleSubmit();\n }\n };\n\n const handleFormSubmit = (event: FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n handleSubmit();\n };\n\n return (\n <ThreadPrimitive.Viewport autoScroll className={styles.viewport}>\n {messages.length > 0 && (\n <div className={styles.chatToolbar}>\n <button type=\"button\" onClick={handleNewChat}>New chat</button>\n </div>\n )}\n <div className={styles.messageList} role=\"log\" aria-label=\"Chat history\" aria-live=\"polite\">\n {messages.length === 0 && (\n <div className={styles.emptyState}>\n <p>Start a conversation</p>\n </div>\n )}\n {messages.filter((message) => message.role === 'user' || message.role === 'assistant').map((message) => (\n <MessageRenderer\n key={message.id}\n message={{\n id: message.id,\n role: message.role as 'user' | 'assistant',\n parts: message.parts,\n ...(isRecord(message.metadata) ? { metadata: message.metadata } : {}),\n }}\n renderMessagePart={(part, index) => renderPart(part, index, components)}\n />\n ))}\n {isBusy && <p className={styles.status}>Thinking...</p>}\n </div>\n <ThreadPrimitive.ViewportFooter className={styles.footer}>\n {!chat && <p className={styles.error} role=\"alert\">Chat runtime is unavailable.</p>}\n {chat?.error && (\n <ErrorRenderer error={chat.error} onRetry={handleRetry} onClear={handleNewChat} />\n )}\n {historyFull && !chat?.error && !isBusy && (\n <div className={styles.historyLimit}>\n <span>This chat has reached its {messageLimit}-question limit.</span>\n <button type=\"button\" onClick={handleNewChat}>Start new chat</button>\n </div>\n )}\n {(!historyFull || isBusy) && !chat?.error && (\n <form className={styles.composerForm} onSubmit={handleFormSubmit}>\n <textarea\n className={styles.input}\n aria-label=\"Message\"\n rows={2}\n value={draft}\n onChange={(event) => setDraft(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={inputPlaceholder}\n maxLength={inputMaxLength}\n disabled={isBusy || !chat}\n />\n {isBusy ? (\n <button type=\"button\" className={styles.sendButton} onClick={() => void chat?.stop()}>\n Stop\n </button>\n ) : (\n <button type=\"submit\" className={styles.sendButton} disabled={!chat || !draft.trim()}>\n Send\n </button>\n )}\n </form>\n )}\n </ThreadPrimitive.ViewportFooter>\n </ThreadPrimitive.Viewport>\n );\n}\n"],"mappings":"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"}
|
package/dist/index77.cjs
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const require_ai_chat_module = require("./index76.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
+
let _assistant_ui_react = require("@assistant-ui/react");
|
|
6
|
+
let _assistant_ui_ai_sdk = require("@assistant-ui/ai-sdk");
|
|
7
|
+
//#region src/components/ai-chat/AIChat.tsx
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function asString(value) {
|
|
12
|
+
return typeof value === "string" ? value : void 0;
|
|
13
|
+
}
|
|
14
|
+
function safeHttpUrl(value) {
|
|
15
|
+
const url = asString(value);
|
|
16
|
+
return url && /^https?:\/\//i.test(url) ? url : void 0;
|
|
17
|
+
}
|
|
18
|
+
function DefaultMessageRenderer({ message, renderMessagePart }) {
|
|
19
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
20
|
+
className: message.role === "user" ? require_ai_chat_module.default.userMessage : require_ai_chat_module.default.assistantMessage,
|
|
21
|
+
children: message.parts.map((part, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: isRecord(part) ? renderMessagePart(part, index) : null }, isRecord(part) && typeof part.id === "string" ? part.id : index))
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function DefaultReasoningRenderer({ children, title = "Reasoning" }) {
|
|
25
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
26
|
+
className: require_ai_chat_module.default.reasoning,
|
|
27
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", {
|
|
28
|
+
className: require_ai_chat_module.default.reasoningSummary,
|
|
29
|
+
children: title
|
|
30
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
31
|
+
className: require_ai_chat_module.default.reasoningText,
|
|
32
|
+
children
|
|
33
|
+
})]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function DefaultSourceRenderer({ sources, renderSource }) {
|
|
37
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
38
|
+
className: require_ai_chat_module.default.sources,
|
|
39
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
40
|
+
className: require_ai_chat_module.default.sourcesLabel,
|
|
41
|
+
children: "Sources"
|
|
42
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { children: sources.map((source, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: renderSource(source, index) }, source.id)) })]
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function DefaultDataRenderer({ data, renderDataItem }) {
|
|
46
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
47
|
+
className: require_ai_chat_module.default.data,
|
|
48
|
+
children: data.map((item, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: renderDataItem(item, index) }, index))
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function DefaultErrorRenderer({ error, onRetry, onClear }) {
|
|
52
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
53
|
+
className: require_ai_chat_module.default.error,
|
|
54
|
+
role: "alert",
|
|
55
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: error.message }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
56
|
+
className: require_ai_chat_module.default.errorActions,
|
|
57
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
58
|
+
type: "button",
|
|
59
|
+
onClick: onRetry,
|
|
60
|
+
children: "Retry response"
|
|
61
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
62
|
+
type: "button",
|
|
63
|
+
onClick: onClear,
|
|
64
|
+
children: "Start new chat"
|
|
65
|
+
})]
|
|
66
|
+
})]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function renderPart(part, index, components) {
|
|
70
|
+
const type = asString(part.type) ?? "";
|
|
71
|
+
if (type === "step-start") return null;
|
|
72
|
+
if (type === "text") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
73
|
+
className: require_ai_chat_module.default.textPart,
|
|
74
|
+
children: asString(part.text) ?? ""
|
|
75
|
+
});
|
|
76
|
+
if (type === "reasoning") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(components?.ReasoningRenderer ?? DefaultReasoningRenderer, { children: asString(part.text) ?? "" });
|
|
77
|
+
if (type === "source-url" || type === "source-document") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(components?.SourceRenderer ?? DefaultSourceRenderer, {
|
|
78
|
+
sources: [{
|
|
79
|
+
id: asString(part.sourceId) ?? asString(part.id) ?? `source-${index}`,
|
|
80
|
+
title: asString(part.title) ?? asString(part.filename),
|
|
81
|
+
url: safeHttpUrl(part.url)
|
|
82
|
+
}],
|
|
83
|
+
renderSource: (item) => item.url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
84
|
+
href: item.url,
|
|
85
|
+
target: "_blank",
|
|
86
|
+
rel: "noopener noreferrer",
|
|
87
|
+
children: item.title ?? item.url
|
|
88
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.title ?? item.id })
|
|
89
|
+
});
|
|
90
|
+
if (type.startsWith("data-")) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(components?.DataRenderer ?? DefaultDataRenderer, {
|
|
91
|
+
data: [part],
|
|
92
|
+
renderDataItem: (item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
93
|
+
className: require_ai_chat_module.default.structuredData,
|
|
94
|
+
children: JSON.stringify(item.data ?? item, null, 2)
|
|
95
|
+
})
|
|
96
|
+
});
|
|
97
|
+
if (type.startsWith("tool-") || type === "dynamic-tool") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
98
|
+
className: require_ai_chat_module.default.toolPart,
|
|
99
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: asString(part.toolName) ?? type.replace(/^tool-/, "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
100
|
+
className: require_ai_chat_module.default.structuredData,
|
|
101
|
+
children: JSON.stringify(part.output ?? part.input ?? part, null, 2)
|
|
102
|
+
})]
|
|
103
|
+
});
|
|
104
|
+
if (type === "file" || type === "image") {
|
|
105
|
+
const url = safeHttpUrl(part.url);
|
|
106
|
+
return url ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
107
|
+
href: url,
|
|
108
|
+
target: "_blank",
|
|
109
|
+
rel: "noopener noreferrer",
|
|
110
|
+
children: asString(part.filename) ?? "View attachment"
|
|
111
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Attachment" });
|
|
112
|
+
}
|
|
113
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
114
|
+
className: require_ai_chat_module.default.structuredData,
|
|
115
|
+
children: JSON.stringify(part, null, 2)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
var DEFAULT_MESSAGE_LIMIT = 8;
|
|
119
|
+
function AIChat({ api, scope, scopeLabel, fetch: requestFetch, headers, credentials, sessionKey, messageLimit = DEFAULT_MESSAGE_LIMIT, inputPlaceholder = "Ask a question...", inputMaxLength = 2e3, components, onClose, className, style }) {
|
|
120
|
+
const [session, setSession] = (0, react.useState)(0);
|
|
121
|
+
const transport = (0, react.useMemo)(() => new _assistant_ui_ai_sdk.AssistantChatTransport({
|
|
122
|
+
api,
|
|
123
|
+
...scope ? { body: { scope } } : {},
|
|
124
|
+
...requestFetch ? { fetch: requestFetch } : {},
|
|
125
|
+
...headers ? { headers } : {},
|
|
126
|
+
...credentials ? { credentials } : {}
|
|
127
|
+
}), [
|
|
128
|
+
api,
|
|
129
|
+
scope,
|
|
130
|
+
requestFetch,
|
|
131
|
+
headers,
|
|
132
|
+
credentials
|
|
133
|
+
]);
|
|
134
|
+
const config = (0, react.useMemo)(() => (0, _assistant_ui_react.AuiConfig)({ threads: (0, _assistant_ui_ai_sdk.AISDKChat)({ transport }) }), [transport, session]);
|
|
135
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
136
|
+
className: [require_ai_chat_module.default.container, className].filter(Boolean).join(" "),
|
|
137
|
+
style,
|
|
138
|
+
children: [
|
|
139
|
+
scopeLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
140
|
+
className: require_ai_chat_module.default.scope,
|
|
141
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
142
|
+
className: require_ai_chat_module.default.scopeLabel,
|
|
143
|
+
children: "Current scope"
|
|
144
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: scopeLabel })]
|
|
145
|
+
}),
|
|
146
|
+
onClose && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
147
|
+
className: require_ai_chat_module.default.closeBtn,
|
|
148
|
+
type: "button",
|
|
149
|
+
onClick: onClose,
|
|
150
|
+
"aria-label": "Close chat",
|
|
151
|
+
children: "✕"
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_assistant_ui_react.AuiProvider, {
|
|
154
|
+
config,
|
|
155
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_assistant_ui_react.ThreadPrimitive.Root, {
|
|
156
|
+
className: require_ai_chat_module.default.thread,
|
|
157
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChatViewport, {
|
|
158
|
+
messageLimit: Number.isFinite(messageLimit) ? Math.max(1, Math.floor(messageLimit)) : DEFAULT_MESSAGE_LIMIT,
|
|
159
|
+
inputPlaceholder,
|
|
160
|
+
inputMaxLength,
|
|
161
|
+
components,
|
|
162
|
+
onNewChat: () => setSession((previous) => previous + 1)
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
}, JSON.stringify([
|
|
166
|
+
api,
|
|
167
|
+
scope,
|
|
168
|
+
sessionKey,
|
|
169
|
+
session
|
|
170
|
+
]))
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
function ChatViewport({ messageLimit, inputPlaceholder, inputMaxLength, components, onNewChat }) {
|
|
175
|
+
const chat = (0, _assistant_ui_ai_sdk.useAISDKChat)();
|
|
176
|
+
const [draft, setDraft] = (0, react.useState)("");
|
|
177
|
+
const messages = chat?.messages ?? [];
|
|
178
|
+
const status = chat?.status ?? "ready";
|
|
179
|
+
const isBusy = status === "submitted" || status === "streaming";
|
|
180
|
+
const historyFull = messages.filter((message) => message.role === "user").length >= messageLimit;
|
|
181
|
+
const MessageRenderer = components?.MessageRenderer ?? DefaultMessageRenderer;
|
|
182
|
+
const ErrorRenderer = components?.ErrorRenderer ?? DefaultErrorRenderer;
|
|
183
|
+
const handleSubmit = () => {
|
|
184
|
+
const text = draft.trim();
|
|
185
|
+
if (!chat || !text || isBusy || historyFull || chat.error) return;
|
|
186
|
+
setDraft("");
|
|
187
|
+
chat.sendMessage({ text });
|
|
188
|
+
};
|
|
189
|
+
const handleNewChat = () => {
|
|
190
|
+
chat?.stop();
|
|
191
|
+
onNewChat();
|
|
192
|
+
};
|
|
193
|
+
const handleRetry = () => {
|
|
194
|
+
if (!chat || isBusy) return;
|
|
195
|
+
chat.clearError();
|
|
196
|
+
chat.regenerate();
|
|
197
|
+
};
|
|
198
|
+
const handleKeyDown = (event) => {
|
|
199
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
200
|
+
event.preventDefault();
|
|
201
|
+
handleSubmit();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const handleFormSubmit = (event) => {
|
|
205
|
+
event.preventDefault();
|
|
206
|
+
handleSubmit();
|
|
207
|
+
};
|
|
208
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_assistant_ui_react.ThreadPrimitive.Viewport, {
|
|
209
|
+
autoScroll: true,
|
|
210
|
+
className: require_ai_chat_module.default.viewport,
|
|
211
|
+
children: [
|
|
212
|
+
messages.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
213
|
+
className: require_ai_chat_module.default.chatToolbar,
|
|
214
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
215
|
+
type: "button",
|
|
216
|
+
onClick: handleNewChat,
|
|
217
|
+
children: "New chat"
|
|
218
|
+
})
|
|
219
|
+
}),
|
|
220
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
221
|
+
className: require_ai_chat_module.default.messageList,
|
|
222
|
+
role: "log",
|
|
223
|
+
"aria-label": "Chat history",
|
|
224
|
+
"aria-live": "polite",
|
|
225
|
+
children: [
|
|
226
|
+
messages.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
227
|
+
className: require_ai_chat_module.default.emptyState,
|
|
228
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "Start a conversation" })
|
|
229
|
+
}),
|
|
230
|
+
messages.filter((message) => message.role === "user" || message.role === "assistant").map((message) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageRenderer, {
|
|
231
|
+
message: {
|
|
232
|
+
id: message.id,
|
|
233
|
+
role: message.role,
|
|
234
|
+
parts: message.parts,
|
|
235
|
+
...isRecord(message.metadata) ? { metadata: message.metadata } : {}
|
|
236
|
+
},
|
|
237
|
+
renderMessagePart: (part, index) => renderPart(part, index, components)
|
|
238
|
+
}, message.id)),
|
|
239
|
+
isBusy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
240
|
+
className: require_ai_chat_module.default.status,
|
|
241
|
+
children: "Thinking..."
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
}),
|
|
245
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_assistant_ui_react.ThreadPrimitive.ViewportFooter, {
|
|
246
|
+
className: require_ai_chat_module.default.footer,
|
|
247
|
+
children: [
|
|
248
|
+
!chat && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
249
|
+
className: require_ai_chat_module.default.error,
|
|
250
|
+
role: "alert",
|
|
251
|
+
children: "Chat runtime is unavailable."
|
|
252
|
+
}),
|
|
253
|
+
chat?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorRenderer, {
|
|
254
|
+
error: chat.error,
|
|
255
|
+
onRetry: handleRetry,
|
|
256
|
+
onClear: handleNewChat
|
|
257
|
+
}),
|
|
258
|
+
historyFull && !chat?.error && !isBusy && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
259
|
+
className: require_ai_chat_module.default.historyLimit,
|
|
260
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
261
|
+
"This chat has reached its ",
|
|
262
|
+
messageLimit,
|
|
263
|
+
"-question limit."
|
|
264
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
265
|
+
type: "button",
|
|
266
|
+
onClick: handleNewChat,
|
|
267
|
+
children: "Start new chat"
|
|
268
|
+
})]
|
|
269
|
+
}),
|
|
270
|
+
(!historyFull || isBusy) && !chat?.error && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
271
|
+
className: require_ai_chat_module.default.composerForm,
|
|
272
|
+
onSubmit: handleFormSubmit,
|
|
273
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
274
|
+
className: require_ai_chat_module.default.input,
|
|
275
|
+
"aria-label": "Message",
|
|
276
|
+
rows: 2,
|
|
277
|
+
value: draft,
|
|
278
|
+
onChange: (event) => setDraft(event.target.value),
|
|
279
|
+
onKeyDown: handleKeyDown,
|
|
280
|
+
placeholder: inputPlaceholder,
|
|
281
|
+
maxLength: inputMaxLength,
|
|
282
|
+
disabled: isBusy || !chat
|
|
283
|
+
}), isBusy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
284
|
+
type: "button",
|
|
285
|
+
className: require_ai_chat_module.default.sendButton,
|
|
286
|
+
onClick: () => void chat?.stop(),
|
|
287
|
+
children: "Stop"
|
|
288
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
289
|
+
type: "submit",
|
|
290
|
+
className: require_ai_chat_module.default.sendButton,
|
|
291
|
+
disabled: !chat || !draft.trim(),
|
|
292
|
+
children: "Send"
|
|
293
|
+
})]
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
})
|
|
297
|
+
]
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
exports.AIChat = AIChat;
|
|
302
|
+
|
|
303
|
+
//# sourceMappingURL=index77.cjs.map
|