@iota-uz/sdk 0.1.1 → 0.3.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/dist/bichat/index.cjs +2993 -642
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.css +386 -43
- package/dist/bichat/index.css.map +1 -1
- package/dist/bichat/index.d.cts +1280 -88
- package/dist/bichat/index.d.ts +1280 -88
- package/dist/bichat/index.mjs +2871 -551
- package/dist/bichat/index.mjs.map +1 -1
- package/dist/bichat/styles.css +516 -46
- package/package.json +3 -2
- package/tailwind/compiled.css +1 -1
- package/tailwind/iota.css +0 -1
package/dist/bichat/index.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import React, { createContext, memo, lazy, forwardRef, useState, useRef, useImperativeHandle, useEffect, isValidElement, cloneElement, useMemo, useContext, useCallback, useId, Suspense, Component, Children } from 'react';
|
|
2
|
+
import { X, Paperclip, PaperPlaneRight, CircleNotch, MagnifyingGlass, FileXls, CaretLeft, CaretRight, Copy, PencilSimple, Download, Question, ArrowsClockwise, ArrowDown, ChartBar, FileText, Lightbulb, WarningCircle, ArrowClockwise, Check, Warning, Info, XCircle, CheckCircle } from '@phosphor-icons/react';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { Prism } from 'react-syntax-highlighter';
|
|
5
|
+
import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
1
6
|
import ReactMarkdown from 'react-markdown';
|
|
2
7
|
import remarkGfm from 'remark-gfm';
|
|
3
|
-
import rehypeSanitize from 'rehype-sanitize';
|
|
4
|
-
import { Prism } from 'react-syntax-highlighter';
|
|
5
|
-
import { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
6
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { createContext, lazy, forwardRef, useState, useRef, useImperativeHandle, useEffect, memo, useContext, useCallback, Suspense, useMemo } from 'react';
|
|
8
|
-
import { X, Paperclip, PaperPlaneRight, CircleNotch, MagnifyingGlass, CaretLeft, CaretRight, Copy, PencilSimple, ArrowsClockwise, ArrowDown, ChartBar, FileText, Lightbulb } from '@phosphor-icons/react';
|
|
9
8
|
import { formatDistanceToNow } from 'date-fns';
|
|
10
|
-
import {
|
|
9
|
+
import { createPortal } from 'react-dom';
|
|
10
|
+
import ReactApexChart from 'react-apexcharts';
|
|
11
|
+
import ApexCharts from 'apexcharts';
|
|
11
12
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
12
13
|
|
|
13
14
|
var __defProp = Object.defineProperty;
|
|
@@ -20,50 +21,333 @@ var __export = (target, all) => {
|
|
|
20
21
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
21
22
|
};
|
|
22
23
|
|
|
24
|
+
// ui/src/bichat/utils/citationProcessor.ts
|
|
25
|
+
function processCitations(content, citations) {
|
|
26
|
+
if (!citations || citations.length === 0) {
|
|
27
|
+
return { content, citations: [] };
|
|
28
|
+
}
|
|
29
|
+
const sortedCitations = [...citations].sort((a, b) => b.startIndex - a.startIndex);
|
|
30
|
+
let processedContent = content;
|
|
31
|
+
const processedCitations = [];
|
|
32
|
+
sortedCitations.forEach((citation, index) => {
|
|
33
|
+
const { startIndex, endIndex } = citation;
|
|
34
|
+
if (startIndex < 0 || endIndex > processedContent.length || startIndex >= endIndex) {
|
|
35
|
+
console.warn("[citationProcessor] Invalid citation indices:", {
|
|
36
|
+
startIndex,
|
|
37
|
+
endIndex,
|
|
38
|
+
contentLength: processedContent.length
|
|
39
|
+
});
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const displayIndex = citations.length - index;
|
|
43
|
+
const before = processedContent.slice(0, startIndex);
|
|
44
|
+
const after = processedContent.slice(endIndex);
|
|
45
|
+
processedContent = `${before}[${displayIndex}]${after}`;
|
|
46
|
+
processedCitations.unshift({
|
|
47
|
+
...citation,
|
|
48
|
+
displayIndex
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
content: processedContent,
|
|
53
|
+
citations: processedCitations
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
var init_citationProcessor = __esm({
|
|
57
|
+
"ui/src/bichat/utils/citationProcessor.ts"() {
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
var TableExportButton;
|
|
61
|
+
var init_TableExportButton = __esm({
|
|
62
|
+
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
63
|
+
TableExportButton = memo(function TableExportButton2({
|
|
64
|
+
onClick,
|
|
65
|
+
disabled = false,
|
|
66
|
+
label = "Export",
|
|
67
|
+
disabledTooltip = "Please wait..."
|
|
68
|
+
}) {
|
|
69
|
+
return /* @__PURE__ */ jsxs(
|
|
70
|
+
"button",
|
|
71
|
+
{
|
|
72
|
+
type: "button",
|
|
73
|
+
onClick,
|
|
74
|
+
disabled,
|
|
75
|
+
className: "inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-green-600 dark:text-green-500 opacity-60 hover:opacity-90 disabled:opacity-30 disabled:cursor-not-allowed transition-opacity",
|
|
76
|
+
"aria-label": label,
|
|
77
|
+
title: disabled ? disabledTooltip : label,
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ jsx(FileXls, { size: 16, weight: "fill" }),
|
|
80
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
var DEFAULT_EXPORT_MESSAGE, TableWithExport;
|
|
88
|
+
var init_TableWithExport = __esm({
|
|
89
|
+
"ui/src/bichat/components/TableWithExport.tsx"() {
|
|
90
|
+
init_TableExportButton();
|
|
91
|
+
DEFAULT_EXPORT_MESSAGE = "Export the table above to Excel";
|
|
92
|
+
TableWithExport = memo(function TableWithExport2({
|
|
93
|
+
children,
|
|
94
|
+
sendMessage,
|
|
95
|
+
disabled = false,
|
|
96
|
+
exportMessage = DEFAULT_EXPORT_MESSAGE,
|
|
97
|
+
exportLabel = "Export"
|
|
98
|
+
}) {
|
|
99
|
+
const handleExport = useCallback(() => {
|
|
100
|
+
sendMessage?.(exportMessage);
|
|
101
|
+
}, [sendMessage, exportMessage]);
|
|
102
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103
|
+
/* @__PURE__ */ jsx("div", { className: "markdown-table-wrapper overflow-x-auto", children: /* @__PURE__ */ jsx("table", { className: "markdown-table w-full border-collapse", children }) }),
|
|
104
|
+
sendMessage && /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsx(TableExportButton, { onClick: handleExport, disabled, label: exportLabel }) })
|
|
105
|
+
] });
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// ui/src/bichat/components/CodeBlock.tsx
|
|
111
|
+
var CodeBlock_exports = {};
|
|
112
|
+
__export(CodeBlock_exports, {
|
|
113
|
+
CodeBlock: () => MemoizedCodeBlock,
|
|
114
|
+
default: () => CodeBlock_default
|
|
115
|
+
});
|
|
116
|
+
function normalizeLanguage(lang) {
|
|
117
|
+
if (!lang) return "text";
|
|
118
|
+
return languageMap[lang.toLowerCase()] || lang.toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
function CodeBlock({
|
|
121
|
+
language,
|
|
122
|
+
value,
|
|
123
|
+
inline,
|
|
124
|
+
copyLabel = "Copy",
|
|
125
|
+
copiedLabel = "Copied!"
|
|
126
|
+
}) {
|
|
127
|
+
const [copied, setCopied] = useState(false);
|
|
128
|
+
const [isDarkMode, setIsDarkMode] = useState(getInitialDarkMode);
|
|
129
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
130
|
+
const copyTimeoutRef = useRef(null);
|
|
131
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
setIsDarkMode(document.documentElement.classList.contains("dark"));
|
|
134
|
+
setIsLoaded(true);
|
|
135
|
+
const observer = new MutationObserver(() => {
|
|
136
|
+
setIsDarkMode(document.documentElement.classList.contains("dark"));
|
|
137
|
+
});
|
|
138
|
+
observer.observe(document.documentElement, {
|
|
139
|
+
attributes: true,
|
|
140
|
+
attributeFilter: ["class"]
|
|
141
|
+
});
|
|
142
|
+
return () => observer.disconnect();
|
|
143
|
+
}, []);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
return () => {
|
|
146
|
+
if (copyTimeoutRef.current !== null) {
|
|
147
|
+
clearTimeout(copyTimeoutRef.current);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}, []);
|
|
151
|
+
const handleCopy = async () => {
|
|
152
|
+
try {
|
|
153
|
+
await navigator.clipboard.writeText(value);
|
|
154
|
+
setCopied(true);
|
|
155
|
+
if (copyTimeoutRef.current !== null) {
|
|
156
|
+
clearTimeout(copyTimeoutRef.current);
|
|
157
|
+
}
|
|
158
|
+
copyTimeoutRef.current = window.setTimeout(() => {
|
|
159
|
+
setCopied(false);
|
|
160
|
+
copyTimeoutRef.current = null;
|
|
161
|
+
}, 2e3);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error("Failed to copy:", err);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
if (inline) {
|
|
167
|
+
return /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 bg-gray-100 dark:bg-gray-800 text-red-600 dark:text-red-400 rounded text-sm font-mono", children: value });
|
|
168
|
+
}
|
|
169
|
+
if (!isLoaded) {
|
|
170
|
+
return /* @__PURE__ */ jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto my-4 border border-gray-300 dark:border-gray-700", children: /* @__PURE__ */ jsx("code", { className: "text-gray-700 dark:text-gray-300 text-sm font-mono", children: value }) });
|
|
171
|
+
}
|
|
172
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative group my-4 rounded-lg overflow-hidden border border-gray-300 dark:border-gray-700", children: [
|
|
173
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2 bg-gray-200 dark:bg-gray-800 border-b border-gray-300 dark:border-gray-700", children: [
|
|
174
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-600 dark:text-gray-400 font-medium uppercase", children: normalizedLanguage }),
|
|
175
|
+
/* @__PURE__ */ jsx(
|
|
176
|
+
"button",
|
|
177
|
+
{
|
|
178
|
+
onClick: handleCopy,
|
|
179
|
+
className: "text-xs text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors flex items-center gap-1.5",
|
|
180
|
+
title: copyLabel,
|
|
181
|
+
"aria-live": "polite",
|
|
182
|
+
children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
183
|
+
/* @__PURE__ */ jsx(Check, { size: 16, className: "w-4 h-4" }),
|
|
184
|
+
/* @__PURE__ */ jsx("span", { children: copiedLabel })
|
|
185
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
186
|
+
/* @__PURE__ */ jsx(Copy, { size: 16, className: "w-4 h-4" }),
|
|
187
|
+
/* @__PURE__ */ jsx("span", { children: copyLabel })
|
|
188
|
+
] })
|
|
189
|
+
}
|
|
190
|
+
)
|
|
191
|
+
] }),
|
|
192
|
+
/* @__PURE__ */ jsx(
|
|
193
|
+
Prism,
|
|
194
|
+
{
|
|
195
|
+
language: normalizedLanguage,
|
|
196
|
+
style: isDarkMode ? vscDarkPlus : vs,
|
|
197
|
+
customStyle: {
|
|
198
|
+
margin: 0,
|
|
199
|
+
borderRadius: 0,
|
|
200
|
+
fontSize: "0.875rem",
|
|
201
|
+
lineHeight: "1.5",
|
|
202
|
+
padding: "1rem"
|
|
203
|
+
},
|
|
204
|
+
showLineNumbers: false,
|
|
205
|
+
wrapLines: true,
|
|
206
|
+
codeTagProps: {
|
|
207
|
+
style: {
|
|
208
|
+
fontFamily: '"JetBrains Mono", "Fira Code", "Menlo", monospace'
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
children: value
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
var getInitialDarkMode, languageMap, MemoizedCodeBlock, CodeBlock_default;
|
|
217
|
+
var init_CodeBlock = __esm({
|
|
218
|
+
"ui/src/bichat/components/CodeBlock.tsx"() {
|
|
219
|
+
getInitialDarkMode = () => {
|
|
220
|
+
if (typeof document === "undefined") return false;
|
|
221
|
+
return document.documentElement.classList.contains("dark");
|
|
222
|
+
};
|
|
223
|
+
languageMap = {
|
|
224
|
+
js: "javascript",
|
|
225
|
+
ts: "typescript",
|
|
226
|
+
jsx: "jsx",
|
|
227
|
+
tsx: "tsx",
|
|
228
|
+
py: "python",
|
|
229
|
+
rb: "ruby",
|
|
230
|
+
yml: "yaml",
|
|
231
|
+
yaml: "yaml",
|
|
232
|
+
sh: "bash",
|
|
233
|
+
bash: "bash",
|
|
234
|
+
json: "json",
|
|
235
|
+
xml: "xml",
|
|
236
|
+
html: "html",
|
|
237
|
+
css: "css",
|
|
238
|
+
sql: "sql",
|
|
239
|
+
go: "go",
|
|
240
|
+
java: "java",
|
|
241
|
+
cpp: "cpp",
|
|
242
|
+
c: "c",
|
|
243
|
+
csharp: "csharp",
|
|
244
|
+
php: "php"
|
|
245
|
+
};
|
|
246
|
+
MemoizedCodeBlock = memo(CodeBlock);
|
|
247
|
+
MemoizedCodeBlock.displayName = "CodeBlock";
|
|
248
|
+
CodeBlock_default = MemoizedCodeBlock;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
23
252
|
// ui/src/bichat/components/MarkdownRenderer.tsx
|
|
24
253
|
var MarkdownRenderer_exports = {};
|
|
25
254
|
__export(MarkdownRenderer_exports, {
|
|
26
|
-
MarkdownRenderer: () =>
|
|
255
|
+
MarkdownRenderer: () => MemoizedMarkdownRenderer,
|
|
256
|
+
default: () => MarkdownRenderer_default
|
|
27
257
|
});
|
|
28
|
-
function MarkdownRenderer({
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
258
|
+
function MarkdownRenderer({
|
|
259
|
+
content,
|
|
260
|
+
citations,
|
|
261
|
+
sendMessage,
|
|
262
|
+
sendDisabled = false,
|
|
263
|
+
copyLabel = "Copy",
|
|
264
|
+
copiedLabel = "Copied!",
|
|
265
|
+
exportLabel = "Export"
|
|
266
|
+
}) {
|
|
267
|
+
const processed = useMemo(() => {
|
|
268
|
+
return processCitations(content, citations);
|
|
269
|
+
}, [content, citations]);
|
|
270
|
+
const components = {
|
|
271
|
+
// Remove <pre> wrapper for code blocks - CodeBlock provides its own container
|
|
272
|
+
pre: ({ children }) => /* @__PURE__ */ jsx(Fragment, { children }),
|
|
273
|
+
code({ inline, className, children }) {
|
|
274
|
+
const match = /language-(\w+)/.exec(className || "");
|
|
275
|
+
const language = match ? match[1] : "";
|
|
276
|
+
const value = String(children).replace(/\n$/, "");
|
|
277
|
+
const isInline = inline === true;
|
|
278
|
+
if (isInline) {
|
|
279
|
+
return /* @__PURE__ */ jsx("code", { className: "px-1.5 py-0.5 bg-gray-100 dark:bg-gray-800 text-red-600 dark:text-red-400 rounded text-sm font-mono", children: value });
|
|
280
|
+
}
|
|
281
|
+
return /* @__PURE__ */ jsx(
|
|
282
|
+
Suspense,
|
|
283
|
+
{
|
|
284
|
+
fallback: /* @__PURE__ */ jsx("pre", { className: "bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto my-4", children: /* @__PURE__ */ jsx("code", { className: "text-sm font-mono", children: value }) }),
|
|
285
|
+
children: /* @__PURE__ */ jsx(
|
|
286
|
+
CodeBlock2,
|
|
40
287
|
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
288
|
+
language,
|
|
289
|
+
value,
|
|
290
|
+
inline: false,
|
|
291
|
+
copyLabel,
|
|
292
|
+
copiedLabel
|
|
45
293
|
}
|
|
46
|
-
)
|
|
294
|
+
)
|
|
47
295
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
296
|
+
);
|
|
297
|
+
},
|
|
298
|
+
p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "markdown-p my-2", children }),
|
|
299
|
+
a: ({ href, children }) => /* @__PURE__ */ jsx(
|
|
300
|
+
"a",
|
|
301
|
+
{
|
|
302
|
+
href,
|
|
303
|
+
target: "_blank",
|
|
304
|
+
rel: "noopener noreferrer",
|
|
305
|
+
className: "markdown-link text-[var(--bichat-primary)] hover:underline",
|
|
306
|
+
children
|
|
307
|
+
}
|
|
308
|
+
),
|
|
309
|
+
h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "markdown-h1 text-2xl font-bold mt-6 mb-3", children }),
|
|
310
|
+
h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "markdown-h2 text-xl font-bold mt-5 mb-2", children }),
|
|
311
|
+
h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "markdown-h3 text-lg font-semibold mt-4 mb-2", children }),
|
|
312
|
+
h4: ({ children }) => /* @__PURE__ */ jsx("h4", { className: "markdown-h4 text-base font-semibold mt-3 mb-1", children }),
|
|
313
|
+
h5: ({ children }) => /* @__PURE__ */ jsx("h5", { className: "markdown-h5 text-sm font-semibold mt-2 mb-1", children }),
|
|
314
|
+
h6: ({ children }) => /* @__PURE__ */ jsx("h6", { className: "markdown-h6 text-sm font-medium mt-2 mb-1", children }),
|
|
315
|
+
ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "markdown-ul list-disc list-inside my-2 space-y-1", children }),
|
|
316
|
+
ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "markdown-ol list-decimal list-inside my-2 space-y-1", children }),
|
|
317
|
+
li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "markdown-li", children }),
|
|
318
|
+
blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { className: "markdown-blockquote border-l-4 border-gray-300 dark:border-gray-600 pl-4 my-2 italic text-gray-600 dark:text-gray-400", children }),
|
|
319
|
+
table: ({ children }) => /* @__PURE__ */ jsx(
|
|
320
|
+
TableWithExport,
|
|
321
|
+
{
|
|
322
|
+
sendMessage,
|
|
323
|
+
disabled: sendDisabled,
|
|
324
|
+
exportLabel,
|
|
325
|
+
children
|
|
326
|
+
}
|
|
327
|
+
),
|
|
328
|
+
thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "markdown-thead bg-gray-100 dark:bg-gray-800", children }),
|
|
329
|
+
tbody: ({ children }) => /* @__PURE__ */ jsx("tbody", { className: "markdown-tbody", children }),
|
|
330
|
+
tr: ({ children }) => /* @__PURE__ */ jsx("tr", { className: "markdown-tr border-b border-gray-200 dark:border-gray-700", children }),
|
|
331
|
+
th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "markdown-th px-3 py-2 text-left text-sm font-semibold text-gray-700 dark:text-gray-300", children }),
|
|
332
|
+
td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "markdown-td px-3 py-2 text-sm text-gray-600 dark:text-gray-400", children }),
|
|
333
|
+
hr: () => /* @__PURE__ */ jsx("hr", { className: "markdown-hr my-4 border-gray-200 dark:border-gray-700" }),
|
|
334
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "markdown-strong font-semibold", children }),
|
|
335
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { className: "markdown-em italic", children })
|
|
336
|
+
};
|
|
337
|
+
return /* @__PURE__ */ jsx("div", { className: "markdown-content", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: processed.content }) });
|
|
52
338
|
}
|
|
339
|
+
var CodeBlock2, MemoizedMarkdownRenderer, MarkdownRenderer_default;
|
|
53
340
|
var init_MarkdownRenderer = __esm({
|
|
54
341
|
"ui/src/bichat/components/MarkdownRenderer.tsx"() {
|
|
342
|
+
init_citationProcessor();
|
|
343
|
+
init_TableWithExport();
|
|
344
|
+
CodeBlock2 = lazy(() => Promise.resolve().then(() => (init_CodeBlock(), CodeBlock_exports)).then((module) => ({ default: module.CodeBlock })));
|
|
345
|
+
MemoizedMarkdownRenderer = memo(MarkdownRenderer);
|
|
346
|
+
MemoizedMarkdownRenderer.displayName = "MarkdownRenderer";
|
|
347
|
+
MarkdownRenderer_default = MemoizedMarkdownRenderer;
|
|
55
348
|
}
|
|
56
349
|
});
|
|
57
350
|
|
|
58
|
-
// ui/src/bichat/types/index.ts
|
|
59
|
-
var MessageRole = /* @__PURE__ */ ((MessageRole2) => {
|
|
60
|
-
MessageRole2["User"] = "user";
|
|
61
|
-
MessageRole2["Assistant"] = "assistant";
|
|
62
|
-
MessageRole2["System"] = "system";
|
|
63
|
-
MessageRole2["Tool"] = "tool";
|
|
64
|
-
return MessageRole2;
|
|
65
|
-
})(MessageRole || {});
|
|
66
|
-
|
|
67
351
|
// ui/src/bichat/utils/RateLimiter.ts
|
|
68
352
|
var RateLimiter = class {
|
|
69
353
|
constructor(config) {
|
|
@@ -111,6 +395,24 @@ var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
|
111
395
|
windowMs: 6e4
|
|
112
396
|
// 1 minute
|
|
113
397
|
};
|
|
398
|
+
function generateTempId(prefix) {
|
|
399
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
400
|
+
}
|
|
401
|
+
function createPendingTurn(sessionId, content, attachments = []) {
|
|
402
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
403
|
+
return {
|
|
404
|
+
id: generateTempId("turn"),
|
|
405
|
+
sessionId,
|
|
406
|
+
userTurn: {
|
|
407
|
+
id: generateTempId("user"),
|
|
408
|
+
content,
|
|
409
|
+
attachments,
|
|
410
|
+
createdAt: now
|
|
411
|
+
},
|
|
412
|
+
// No assistantTurn yet - it will be added when streaming completes
|
|
413
|
+
createdAt: now
|
|
414
|
+
};
|
|
415
|
+
}
|
|
114
416
|
function ChatSessionProvider({
|
|
115
417
|
dataSource,
|
|
116
418
|
sessionId,
|
|
@@ -118,7 +420,7 @@ function ChatSessionProvider({
|
|
|
118
420
|
children
|
|
119
421
|
}) {
|
|
120
422
|
const [message, setMessage] = useState("");
|
|
121
|
-
const [
|
|
423
|
+
const [turns, setTurns] = useState([]);
|
|
122
424
|
const [loading, setLoading] = useState(false);
|
|
123
425
|
const [error, setError] = useState(null);
|
|
124
426
|
const [currentSessionId, setCurrentSessionId] = useState(sessionId);
|
|
@@ -139,7 +441,7 @@ function ChatSessionProvider({
|
|
|
139
441
|
useEffect(() => {
|
|
140
442
|
if (!currentSessionId || currentSessionId === "new") {
|
|
141
443
|
setSession(null);
|
|
142
|
-
|
|
444
|
+
setTurns([]);
|
|
143
445
|
setPendingQuestion(null);
|
|
144
446
|
setFetching(false);
|
|
145
447
|
return;
|
|
@@ -151,7 +453,7 @@ function ChatSessionProvider({
|
|
|
151
453
|
if (cancelled) return;
|
|
152
454
|
if (state) {
|
|
153
455
|
setSession(state.session);
|
|
154
|
-
|
|
456
|
+
setTurns(state.turns);
|
|
155
457
|
setPendingQuestion(state.pendingQuestion || null);
|
|
156
458
|
} else {
|
|
157
459
|
setError("Session not found");
|
|
@@ -184,14 +486,8 @@ function ChatSessionProvider({
|
|
|
184
486
|
setError(null);
|
|
185
487
|
setStreamingContent("");
|
|
186
488
|
abortControllerRef.current = new AbortController();
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
sessionId: currentSessionId || "new",
|
|
190
|
-
role: "user" /* User */,
|
|
191
|
-
content,
|
|
192
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
193
|
-
};
|
|
194
|
-
setMessages((prev) => [...prev, tempUserMessage]);
|
|
489
|
+
const tempTurn = createPendingTurn(currentSessionId || "new", content, attachments);
|
|
490
|
+
setTurns((prev) => [...prev, tempTurn]);
|
|
195
491
|
try {
|
|
196
492
|
let activeSessionId = currentSessionId;
|
|
197
493
|
let shouldNavigateAfter = false;
|
|
@@ -229,7 +525,7 @@ function ChatSessionProvider({
|
|
|
229
525
|
const state = await dataSource.fetchSession(finalSessionId);
|
|
230
526
|
if (state) {
|
|
231
527
|
setSession(state.session);
|
|
232
|
-
|
|
528
|
+
setTurns(state.turns);
|
|
233
529
|
setPendingQuestion(state.pendingQuestion || null);
|
|
234
530
|
}
|
|
235
531
|
}
|
|
@@ -246,7 +542,7 @@ function ChatSessionProvider({
|
|
|
246
542
|
setMessage(content);
|
|
247
543
|
return;
|
|
248
544
|
}
|
|
249
|
-
|
|
545
|
+
setTurns((prev) => prev.filter((t) => t.id !== tempTurn.id));
|
|
250
546
|
const errorMessage = err instanceof Error ? err.message : "Failed to send message";
|
|
251
547
|
setError(errorMessage);
|
|
252
548
|
console.error("Send message error:", err);
|
|
@@ -295,32 +591,29 @@ function ChatSessionProvider({
|
|
|
295
591
|
};
|
|
296
592
|
}, [messageQueue]);
|
|
297
593
|
const handleRegenerate = useCallback(
|
|
298
|
-
async (
|
|
594
|
+
async (turnId) => {
|
|
299
595
|
if (!currentSessionId || currentSessionId === "new") return;
|
|
300
|
-
const
|
|
301
|
-
if (
|
|
596
|
+
const turn = turns.find((t) => t.id === turnId);
|
|
597
|
+
if (!turn) return;
|
|
302
598
|
setLoading(true);
|
|
303
599
|
setError(null);
|
|
304
600
|
try {
|
|
305
|
-
|
|
306
|
-
if (userMessage && userMessage.role === "user" /* User */) {
|
|
307
|
-
await sendMessageDirect(userMessage.content, []);
|
|
308
|
-
}
|
|
601
|
+
await sendMessageDirect(turn.userTurn.content, turn.userTurn.attachments);
|
|
309
602
|
} catch (err) {
|
|
310
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to regenerate
|
|
603
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to regenerate response";
|
|
311
604
|
setError(errorMessage);
|
|
312
605
|
console.error("Regenerate error:", err);
|
|
313
606
|
} finally {
|
|
314
607
|
setLoading(false);
|
|
315
608
|
}
|
|
316
609
|
},
|
|
317
|
-
[
|
|
610
|
+
[turns, currentSessionId, sendMessageDirect]
|
|
318
611
|
);
|
|
319
612
|
const handleEdit = useCallback(
|
|
320
|
-
async (
|
|
613
|
+
async (turnId, newContent) => {
|
|
321
614
|
if (!currentSessionId || currentSessionId === "new") {
|
|
322
615
|
setMessage(newContent);
|
|
323
|
-
|
|
616
|
+
setTurns((prev) => prev.filter((t) => t.id !== turnId));
|
|
324
617
|
return;
|
|
325
618
|
}
|
|
326
619
|
setLoading(true);
|
|
@@ -356,7 +649,7 @@ function ChatSessionProvider({
|
|
|
356
649
|
try {
|
|
357
650
|
const state = await dataSource.fetchSession(currentSessionId);
|
|
358
651
|
if (state) {
|
|
359
|
-
|
|
652
|
+
setTurns(state.turns);
|
|
360
653
|
setPendingQuestion(state.pendingQuestion || null);
|
|
361
654
|
} else {
|
|
362
655
|
setPendingQuestion(previousPendingQuestion);
|
|
@@ -400,7 +693,7 @@ function ChatSessionProvider({
|
|
|
400
693
|
const value = {
|
|
401
694
|
// State
|
|
402
695
|
message,
|
|
403
|
-
|
|
696
|
+
turns,
|
|
404
697
|
loading,
|
|
405
698
|
error,
|
|
406
699
|
currentSessionId,
|
|
@@ -594,7 +887,7 @@ function ChatHeader({ session, onBack, logoSlot, actionsSlot }) {
|
|
|
594
887
|
"button",
|
|
595
888
|
{
|
|
596
889
|
onClick: onBack,
|
|
597
|
-
className: "p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors",
|
|
890
|
+
className: "p-2 hover:bg-gray-100 dark:hover:bg-gray-700 active:bg-gray-200 dark:active:bg-gray-600 rounded-lg transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
598
891
|
"aria-label": t("chat.goBack"),
|
|
599
892
|
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
600
893
|
}
|
|
@@ -676,25 +969,70 @@ function AttachmentGrid({
|
|
|
676
969
|
attachments,
|
|
677
970
|
onRemove,
|
|
678
971
|
onView,
|
|
679
|
-
className = ""
|
|
972
|
+
className = "",
|
|
973
|
+
readonly = false,
|
|
974
|
+
maxDisplay,
|
|
975
|
+
maxCapacity = 10,
|
|
976
|
+
emptyMessage = "No images attached",
|
|
977
|
+
showCount = false
|
|
680
978
|
}) {
|
|
681
|
-
|
|
979
|
+
const displayedAttachments = useMemo(
|
|
980
|
+
() => maxDisplay && attachments.length > maxDisplay ? attachments.slice(0, maxDisplay) : attachments,
|
|
981
|
+
[attachments, maxDisplay]
|
|
982
|
+
);
|
|
983
|
+
const isAtMaxCapacity = attachments.length >= maxCapacity;
|
|
984
|
+
if (displayedAttachments.length === 0) {
|
|
985
|
+
if (!showCount) return null;
|
|
986
|
+
return /* @__PURE__ */ jsx("div", { className: "text-center text-gray-500 dark:text-gray-400 py-4", children: emptyMessage });
|
|
987
|
+
}
|
|
988
|
+
const isEditable = !readonly && !!onRemove;
|
|
989
|
+
const isViewable = !readonly && !!onView;
|
|
990
|
+
return /* @__PURE__ */ jsxs("div", { className: `space-y-2 ${className}`, children: [
|
|
991
|
+
showCount && /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-600 dark:text-gray-400", children: [
|
|
992
|
+
displayedAttachments.length,
|
|
993
|
+
" image",
|
|
994
|
+
displayedAttachments.length !== 1 ? "s" : "",
|
|
995
|
+
" attached"
|
|
996
|
+
] }),
|
|
997
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2", children: displayedAttachments.map((attachment, index) => /* @__PURE__ */ jsx(
|
|
998
|
+
MemoizedAttachmentItem,
|
|
999
|
+
{
|
|
1000
|
+
attachment,
|
|
1001
|
+
index,
|
|
1002
|
+
onRemove: isEditable ? onRemove : void 0,
|
|
1003
|
+
onView: isViewable ? onView : void 0
|
|
1004
|
+
},
|
|
1005
|
+
`${attachment.filename}-${index}`
|
|
1006
|
+
)) }),
|
|
1007
|
+
maxDisplay && attachments.length > maxDisplay && /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
1008
|
+
"+",
|
|
1009
|
+
attachments.length - maxDisplay,
|
|
1010
|
+
" more"
|
|
1011
|
+
] }),
|
|
1012
|
+
isAtMaxCapacity && isEditable && /* @__PURE__ */ jsxs("div", { className: "text-sm text-amber-600 dark:text-amber-400", children: [
|
|
1013
|
+
"Maximum ",
|
|
1014
|
+
maxCapacity,
|
|
1015
|
+
" images"
|
|
1016
|
+
] })
|
|
1017
|
+
] });
|
|
1018
|
+
}
|
|
1019
|
+
function AttachmentItem({ attachment, index, onRemove, onView }) {
|
|
682
1020
|
const isEditable = !!onRemove;
|
|
683
1021
|
const isViewable = !!onView;
|
|
684
|
-
return /* @__PURE__ */
|
|
1022
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
685
1023
|
/* @__PURE__ */ jsx(
|
|
686
1024
|
"img",
|
|
687
1025
|
{
|
|
688
1026
|
src: attachment.preview,
|
|
689
1027
|
alt: attachment.filename,
|
|
690
|
-
className: `w-full h-24 object-cover rounded-lg border border-gray-200 dark:border-gray-700 ${isViewable ? "cursor-pointer hover:opacity-80 transition-opacity" : ""}`,
|
|
691
|
-
onClick: () => isViewable && onView(index),
|
|
1028
|
+
className: `w-full h-24 object-cover rounded-lg border border-gray-200 dark:border-gray-700 ${isViewable ? "cursor-pointer hover:opacity-80 transition-opacity duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900" : ""}`,
|
|
1029
|
+
onClick: () => isViewable && onView?.(index),
|
|
692
1030
|
role: isViewable ? "button" : void 0,
|
|
693
1031
|
tabIndex: isViewable ? 0 : void 0,
|
|
694
1032
|
onKeyDown: (e) => {
|
|
695
1033
|
if (isViewable && (e.key === "Enter" || e.key === " ")) {
|
|
696
1034
|
e.preventDefault();
|
|
697
|
-
onView(index);
|
|
1035
|
+
onView?.(index);
|
|
698
1036
|
}
|
|
699
1037
|
}
|
|
700
1038
|
}
|
|
@@ -705,203 +1043,600 @@ function AttachmentGrid({
|
|
|
705
1043
|
type: "button",
|
|
706
1044
|
onClick: (e) => {
|
|
707
1045
|
e.stopPropagation();
|
|
708
|
-
onRemove(index);
|
|
1046
|
+
onRemove?.(index);
|
|
709
1047
|
},
|
|
710
|
-
className: "absolute top-1 right-1 p-1 bg-red-500 hover:bg-red-600 text-white rounded-full opacity-0 group-hover:opacity-100 transition-
|
|
1048
|
+
className: "absolute top-1 right-1 p-1 bg-red-500 hover:bg-red-600 active:bg-red-700 text-white rounded-full opacity-0 group-hover:opacity-100 transition-all duration-150 shadow-md focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50",
|
|
711
1049
|
"aria-label": `Remove ${attachment.filename}`,
|
|
712
1050
|
children: /* @__PURE__ */ jsx(X, { size: 16, weight: "bold" })
|
|
713
1051
|
}
|
|
714
1052
|
),
|
|
715
1053
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 px-1", children: [
|
|
716
|
-
/* @__PURE__ */ jsx(
|
|
1054
|
+
/* @__PURE__ */ jsx(
|
|
1055
|
+
"div",
|
|
1056
|
+
{
|
|
1057
|
+
className: "text-xs text-gray-600 dark:text-gray-400 truncate",
|
|
1058
|
+
title: attachment.filename,
|
|
1059
|
+
children: attachment.filename.length > 20 ? `${attachment.filename.substring(0, 20)}...` : attachment.filename
|
|
1060
|
+
}
|
|
1061
|
+
),
|
|
717
1062
|
/* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 dark:text-gray-500", children: formatFileSize(attachment.sizeBytes) })
|
|
718
1063
|
] })
|
|
719
|
-
] }
|
|
1064
|
+
] });
|
|
1065
|
+
}
|
|
1066
|
+
var MemoizedAttachmentItem = React.memo(
|
|
1067
|
+
AttachmentItem,
|
|
1068
|
+
(prevProps, nextProps) => {
|
|
1069
|
+
return prevProps.attachment.base64Data === nextProps.attachment.base64Data && prevProps.attachment.filename === nextProps.attachment.filename && prevProps.attachment.preview === nextProps.attachment.preview && prevProps.index === nextProps.index && prevProps.onRemove === nextProps.onRemove && prevProps.onView === nextProps.onView;
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
var MemoizedAttachmentGrid = React.memo(AttachmentGrid);
|
|
1073
|
+
MemoizedAttachmentGrid.displayName = "AttachmentGrid";
|
|
1074
|
+
var AttachmentGrid_default = MemoizedAttachmentGrid;
|
|
1075
|
+
function useModalLock(isOpen) {
|
|
1076
|
+
useEffect(() => {
|
|
1077
|
+
if (!isOpen) return;
|
|
1078
|
+
const originalOverflow = document.body.style.overflow;
|
|
1079
|
+
document.body.style.overflow = "hidden";
|
|
1080
|
+
return () => {
|
|
1081
|
+
document.body.style.overflow = originalOverflow;
|
|
1082
|
+
};
|
|
1083
|
+
}, [isOpen]);
|
|
1084
|
+
}
|
|
1085
|
+
function useFocusTrap(containerRef, isActive, restoreFocusOnDeactivate) {
|
|
1086
|
+
useEffect(() => {
|
|
1087
|
+
if (!isActive || !containerRef.current) return;
|
|
1088
|
+
const container = containerRef.current;
|
|
1089
|
+
const previouslyFocused = document.activeElement;
|
|
1090
|
+
const getFocusableElements = () => {
|
|
1091
|
+
const selector = [
|
|
1092
|
+
"button:not([disabled])",
|
|
1093
|
+
"[href]",
|
|
1094
|
+
"input:not([disabled])",
|
|
1095
|
+
"select:not([disabled])",
|
|
1096
|
+
"textarea:not([disabled])",
|
|
1097
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
1098
|
+
].join(", ");
|
|
1099
|
+
return Array.from(container.querySelectorAll(selector));
|
|
1100
|
+
};
|
|
1101
|
+
const focusableElements = getFocusableElements();
|
|
1102
|
+
if (focusableElements.length > 0) {
|
|
1103
|
+
focusableElements[0].focus();
|
|
1104
|
+
}
|
|
1105
|
+
const handleTabKey = (e) => {
|
|
1106
|
+
if (e.key !== "Tab") return;
|
|
1107
|
+
const focusableElements2 = getFocusableElements();
|
|
1108
|
+
if (focusableElements2.length === 0) return;
|
|
1109
|
+
const firstElement = focusableElements2[0];
|
|
1110
|
+
const lastElement = focusableElements2[focusableElements2.length - 1];
|
|
1111
|
+
if (e.shiftKey) {
|
|
1112
|
+
if (document.activeElement === firstElement) {
|
|
1113
|
+
e.preventDefault();
|
|
1114
|
+
lastElement.focus();
|
|
1115
|
+
}
|
|
1116
|
+
} else {
|
|
1117
|
+
if (document.activeElement === lastElement) {
|
|
1118
|
+
e.preventDefault();
|
|
1119
|
+
firstElement.focus();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
container.addEventListener("keydown", handleTabKey);
|
|
1124
|
+
return () => {
|
|
1125
|
+
container.removeEventListener("keydown", handleTabKey);
|
|
1126
|
+
if (restoreFocusOnDeactivate) {
|
|
1127
|
+
restoreFocusOnDeactivate.focus();
|
|
1128
|
+
} else if (previouslyFocused instanceof HTMLElement) {
|
|
1129
|
+
previouslyFocused.focus();
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
}, [containerRef, isActive, restoreFocusOnDeactivate]);
|
|
720
1133
|
}
|
|
721
|
-
function
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1134
|
+
function SpinnerLoader({
|
|
1135
|
+
size = "md",
|
|
1136
|
+
message
|
|
1137
|
+
}) {
|
|
1138
|
+
const sizeMap = {
|
|
1139
|
+
sm: 16,
|
|
1140
|
+
md: 32,
|
|
1141
|
+
lg: 48
|
|
725
1142
|
};
|
|
726
|
-
const
|
|
727
|
-
|
|
1143
|
+
const sizeClasses5 = {
|
|
1144
|
+
sm: "h-4 w-4",
|
|
1145
|
+
md: "h-8 w-8",
|
|
1146
|
+
lg: "h-12 w-12"
|
|
1147
|
+
};
|
|
1148
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center", role: "status", "aria-live": "polite", children: [
|
|
1149
|
+
/* @__PURE__ */ jsx(
|
|
1150
|
+
CircleNotch,
|
|
1151
|
+
{
|
|
1152
|
+
size: sizeMap[size],
|
|
1153
|
+
className: `${sizeClasses5[size]} animate-spin text-[var(--bichat-primary)]`
|
|
1154
|
+
}
|
|
1155
|
+
),
|
|
1156
|
+
message && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: message })
|
|
1157
|
+
] });
|
|
1158
|
+
}
|
|
1159
|
+
function DotsLoader({
|
|
1160
|
+
size = "md",
|
|
1161
|
+
message
|
|
1162
|
+
}) {
|
|
1163
|
+
const dotSizeClasses = {
|
|
1164
|
+
sm: "w-1.5 h-1.5",
|
|
1165
|
+
md: "w-2 h-2",
|
|
1166
|
+
lg: "w-3 h-3"
|
|
1167
|
+
};
|
|
1168
|
+
const gapClasses2 = {
|
|
1169
|
+
sm: "gap-0.5",
|
|
1170
|
+
md: "gap-1",
|
|
1171
|
+
lg: "gap-1.5"
|
|
1172
|
+
};
|
|
1173
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center", role: "status", "aria-live": "polite", children: [
|
|
1174
|
+
/* @__PURE__ */ jsx("div", { className: `flex ${gapClasses2[size]}`, children: [0, 1, 2].map((index) => /* @__PURE__ */ jsx(
|
|
1175
|
+
"div",
|
|
1176
|
+
{
|
|
1177
|
+
className: `${dotSizeClasses[size]} bg-[var(--bichat-primary)] rounded-full animate-bounce`,
|
|
1178
|
+
style: { animationDelay: `${index * 0.15}s` }
|
|
1179
|
+
},
|
|
1180
|
+
index
|
|
1181
|
+
)) }),
|
|
1182
|
+
message && /* @__PURE__ */ jsx("p", { className: "mt-3 text-sm text-gray-600 dark:text-gray-400", children: message })
|
|
1183
|
+
] });
|
|
1184
|
+
}
|
|
1185
|
+
function PulseLoader({
|
|
1186
|
+
size = "md",
|
|
1187
|
+
message
|
|
1188
|
+
}) {
|
|
1189
|
+
const sizeClasses5 = {
|
|
1190
|
+
sm: "h-4 w-4",
|
|
1191
|
+
md: "h-8 w-8",
|
|
1192
|
+
lg: "h-12 w-12"
|
|
728
1193
|
};
|
|
1194
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center", role: "status", "aria-live": "polite", children: [
|
|
1195
|
+
/* @__PURE__ */ jsx("div", { className: `${sizeClasses5[size]} bg-[var(--bichat-primary)] rounded-full animate-pulse` }),
|
|
1196
|
+
message && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", children: message })
|
|
1197
|
+
] });
|
|
1198
|
+
}
|
|
1199
|
+
function LoadingSpinner({ variant = "spinner", size = "md", message }) {
|
|
1200
|
+
switch (variant) {
|
|
1201
|
+
case "dots":
|
|
1202
|
+
return /* @__PURE__ */ jsx(DotsLoader, { size, message });
|
|
1203
|
+
case "pulse":
|
|
1204
|
+
return /* @__PURE__ */ jsx(PulseLoader, { size, message });
|
|
1205
|
+
case "spinner":
|
|
1206
|
+
default:
|
|
1207
|
+
return /* @__PURE__ */ jsx(SpinnerLoader, { size, message });
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
var MemoizedLoadingSpinner = memo(LoadingSpinner);
|
|
1211
|
+
MemoizedLoadingSpinner.displayName = "LoadingSpinner";
|
|
1212
|
+
var LoadingSpinner_default = MemoizedLoadingSpinner;
|
|
1213
|
+
function ImageModal({
|
|
1214
|
+
isOpen,
|
|
1215
|
+
onClose,
|
|
1216
|
+
attachment,
|
|
1217
|
+
allAttachments,
|
|
1218
|
+
currentIndex = 0,
|
|
1219
|
+
onNavigate
|
|
1220
|
+
}) {
|
|
1221
|
+
const modalRef = useRef(null);
|
|
1222
|
+
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
|
1223
|
+
const [imageError, setImageError] = useState(false);
|
|
1224
|
+
const hasMultipleImages = allAttachments && allAttachments.length > 1;
|
|
1225
|
+
const canNavigatePrev = hasMultipleImages && currentIndex > 0;
|
|
1226
|
+
const canNavigateNext = hasMultipleImages && currentIndex < (allAttachments?.length || 1) - 1;
|
|
1227
|
+
useModalLock(isOpen);
|
|
1228
|
+
useFocusTrap(modalRef, isOpen);
|
|
729
1229
|
useEffect(() => {
|
|
1230
|
+
if (!isOpen) return;
|
|
730
1231
|
const handleKeyDown = (e) => {
|
|
731
1232
|
if (e.key === "Escape") {
|
|
732
1233
|
onClose();
|
|
733
|
-
} else if (e.key === "ArrowLeft") {
|
|
734
|
-
|
|
735
|
-
} else if (e.key === "ArrowRight") {
|
|
736
|
-
|
|
1234
|
+
} else if (e.key === "ArrowLeft" && onNavigate && canNavigatePrev) {
|
|
1235
|
+
onNavigate("prev");
|
|
1236
|
+
} else if (e.key === "ArrowRight" && onNavigate && canNavigateNext) {
|
|
1237
|
+
onNavigate("next");
|
|
737
1238
|
}
|
|
738
1239
|
};
|
|
739
|
-
|
|
740
|
-
return () =>
|
|
741
|
-
}, [onClose]);
|
|
1240
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1241
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1242
|
+
}, [isOpen, onClose, onNavigate, canNavigatePrev, canNavigateNext]);
|
|
742
1243
|
useEffect(() => {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
return
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
"button",
|
|
769
|
-
{
|
|
770
|
-
onClick: (e) => {
|
|
771
|
-
e.stopPropagation();
|
|
772
|
-
handlePrevious();
|
|
773
|
-
},
|
|
774
|
-
className: "absolute left-4 p-2 text-white hover:bg-white/10 rounded-lg transition-colors",
|
|
775
|
-
"aria-label": "Previous image",
|
|
776
|
-
children: /* @__PURE__ */ jsx(CaretLeft, { size: 32, weight: "bold" })
|
|
777
|
-
}
|
|
778
|
-
),
|
|
779
|
-
/* @__PURE__ */ jsx(
|
|
780
|
-
"button",
|
|
1244
|
+
setIsImageLoaded(false);
|
|
1245
|
+
setImageError(false);
|
|
1246
|
+
}, [attachment]);
|
|
1247
|
+
if (!isOpen) return null;
|
|
1248
|
+
const previewUrl = attachment.preview || createDataUrl(attachment.base64Data, attachment.mimeType);
|
|
1249
|
+
return createPortal(
|
|
1250
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1251
|
+
/* @__PURE__ */ jsx(
|
|
1252
|
+
"div",
|
|
1253
|
+
{
|
|
1254
|
+
className: "fixed inset-0 bg-black/90 transition-opacity duration-200 z-40",
|
|
1255
|
+
onClick: onClose,
|
|
1256
|
+
"aria-hidden": "true"
|
|
1257
|
+
}
|
|
1258
|
+
),
|
|
1259
|
+
/* @__PURE__ */ jsx(
|
|
1260
|
+
"div",
|
|
1261
|
+
{
|
|
1262
|
+
className: "fixed inset-0 flex items-center justify-center z-50 p-4",
|
|
1263
|
+
role: "dialog",
|
|
1264
|
+
"aria-modal": "true",
|
|
1265
|
+
"aria-labelledby": "modal-image-title",
|
|
1266
|
+
"aria-describedby": "modal-image-description",
|
|
1267
|
+
children: /* @__PURE__ */ jsxs(
|
|
1268
|
+
"div",
|
|
781
1269
|
{
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
1270
|
+
ref: modalRef,
|
|
1271
|
+
className: "relative flex flex-col items-center justify-center w-full h-full",
|
|
1272
|
+
children: [
|
|
1273
|
+
/* @__PURE__ */ jsx(
|
|
1274
|
+
"button",
|
|
1275
|
+
{
|
|
1276
|
+
onClick: onClose,
|
|
1277
|
+
className: "absolute top-4 right-4 z-50 flex items-center justify-center w-10 h-10 bg-white/10 hover:bg-white/20 text-white rounded-full transition-colors duration-200",
|
|
1278
|
+
"aria-label": "Close modal",
|
|
1279
|
+
type: "button",
|
|
1280
|
+
children: /* @__PURE__ */ jsx(X, { size: 24, weight: "bold" })
|
|
1281
|
+
}
|
|
1282
|
+
),
|
|
1283
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center w-full h-full max-w-4xl", children: [
|
|
1284
|
+
!isImageLoaded && !imageError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(LoadingSpinner_default, {}) }),
|
|
1285
|
+
imageError && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center text-white", children: [
|
|
1286
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-medium mb-2", children: "Failed to load image" }),
|
|
1287
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300", children: attachment.filename })
|
|
1288
|
+
] }),
|
|
1289
|
+
/* @__PURE__ */ jsx(
|
|
1290
|
+
"img",
|
|
1291
|
+
{
|
|
1292
|
+
src: previewUrl,
|
|
1293
|
+
alt: attachment.filename,
|
|
1294
|
+
className: `
|
|
1295
|
+
max-w-4xl max-h-screen object-contain
|
|
1296
|
+
transition-opacity duration-200
|
|
1297
|
+
${isImageLoaded ? "opacity-100" : "opacity-0"}
|
|
1298
|
+
`,
|
|
1299
|
+
onLoad: () => setIsImageLoaded(true),
|
|
1300
|
+
onError: () => setImageError(true),
|
|
1301
|
+
loading: "lazy"
|
|
1302
|
+
}
|
|
1303
|
+
)
|
|
1304
|
+
] }),
|
|
1305
|
+
isImageLoaded && !imageError && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-0 left-0 right-0 flex flex-col items-center text-white text-center pb-4 bg-gradient-to-t from-black/50 to-transparent pt-8", children: [
|
|
1306
|
+
/* @__PURE__ */ jsx("p", { id: "modal-image-title", className: "text-lg font-medium", children: attachment.filename }),
|
|
1307
|
+
/* @__PURE__ */ jsxs("div", { id: "modal-image-description", className: "text-sm text-gray-300 space-x-2", children: [
|
|
1308
|
+
/* @__PURE__ */ jsx("span", { children: formatFileSize(attachment.sizeBytes) }),
|
|
1309
|
+
/* @__PURE__ */ jsx("span", { children: "\u2022" }),
|
|
1310
|
+
/* @__PURE__ */ jsx("span", { children: attachment.mimeType })
|
|
1311
|
+
] })
|
|
1312
|
+
] }),
|
|
1313
|
+
hasMultipleImages && /* @__PURE__ */ jsx(
|
|
1314
|
+
"button",
|
|
1315
|
+
{
|
|
1316
|
+
onClick: () => onNavigate?.("prev"),
|
|
1317
|
+
disabled: !canNavigatePrev,
|
|
1318
|
+
className: `
|
|
1319
|
+
absolute left-4 z-40 flex items-center justify-center w-12 h-12
|
|
1320
|
+
rounded-full transition-all duration-200
|
|
1321
|
+
${canNavigatePrev ? "bg-white/10 hover:bg-white/20 text-white cursor-pointer" : "bg-white/5 text-white/30 cursor-not-allowed"}
|
|
1322
|
+
`,
|
|
1323
|
+
"aria-label": "Previous image",
|
|
1324
|
+
type: "button",
|
|
1325
|
+
children: /* @__PURE__ */ jsx(CaretLeft, { size: 28, weight: "bold" })
|
|
1326
|
+
}
|
|
1327
|
+
),
|
|
1328
|
+
hasMultipleImages && /* @__PURE__ */ jsx(
|
|
1329
|
+
"button",
|
|
1330
|
+
{
|
|
1331
|
+
onClick: () => onNavigate?.("next"),
|
|
1332
|
+
disabled: !canNavigateNext,
|
|
1333
|
+
className: `
|
|
1334
|
+
absolute right-4 z-40 flex items-center justify-center w-12 h-12
|
|
1335
|
+
rounded-full transition-all duration-200
|
|
1336
|
+
${canNavigateNext ? "bg-white/10 hover:bg-white/20 text-white cursor-pointer" : "bg-white/5 text-white/30 cursor-not-allowed"}
|
|
1337
|
+
`,
|
|
1338
|
+
"aria-label": "Next image",
|
|
1339
|
+
type: "button",
|
|
1340
|
+
children: /* @__PURE__ */ jsx(CaretRight, { size: 28, weight: "bold" })
|
|
1341
|
+
}
|
|
1342
|
+
),
|
|
1343
|
+
hasMultipleImages && /* @__PURE__ */ jsxs("div", { className: "absolute top-4 left-4 bg-white/10 text-white px-3 py-1 rounded-full text-sm", children: [
|
|
1344
|
+
currentIndex + 1,
|
|
1345
|
+
" / ",
|
|
1346
|
+
allAttachments?.length
|
|
1347
|
+
] })
|
|
1348
|
+
]
|
|
789
1349
|
}
|
|
790
1350
|
)
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
src: images[currentIndex].preview,
|
|
796
|
-
alt: images[currentIndex].filename,
|
|
797
|
-
className: "max-w-[90vw] max-h-[90vh] object-contain",
|
|
798
|
-
onClick: (e) => e.stopPropagation()
|
|
799
|
-
}
|
|
800
|
-
),
|
|
801
|
-
/* @__PURE__ */ jsxs(
|
|
802
|
-
"div",
|
|
803
|
-
{
|
|
804
|
-
className: "absolute bottom-4 left-1/2 transform -translate-x-1/2 text-white text-sm bg-black/50 px-4 py-2 rounded-lg backdrop-blur",
|
|
805
|
-
onClick: (e) => e.stopPropagation(),
|
|
806
|
-
children: [
|
|
807
|
-
/* @__PURE__ */ jsx("div", { className: "text-center font-medium mb-1", children: images[currentIndex].filename }),
|
|
808
|
-
images.length > 1 && /* @__PURE__ */ jsxs("div", { className: "text-center text-xs opacity-80", children: [
|
|
809
|
-
currentIndex + 1,
|
|
810
|
-
" / ",
|
|
811
|
-
images.length
|
|
812
|
-
] })
|
|
813
|
-
]
|
|
814
|
-
}
|
|
815
|
-
)
|
|
816
|
-
]
|
|
817
|
-
}
|
|
1351
|
+
}
|
|
1352
|
+
)
|
|
1353
|
+
] }),
|
|
1354
|
+
document.body
|
|
818
1355
|
);
|
|
819
1356
|
}
|
|
820
|
-
|
|
821
|
-
|
|
1357
|
+
var ImageModal_default = ImageModal;
|
|
1358
|
+
var defaultClassNames = {
|
|
1359
|
+
root: "flex gap-3 justify-end group",
|
|
1360
|
+
wrapper: "flex-1 flex flex-col items-end max-w-[75%]",
|
|
1361
|
+
avatar: "flex-shrink-0 w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white font-medium text-sm",
|
|
1362
|
+
bubble: "bg-primary-600 text-white rounded-2xl rounded-br-sm px-4 py-3",
|
|
1363
|
+
content: "text-[15px] whitespace-pre-wrap break-words leading-relaxed",
|
|
1364
|
+
attachments: "mb-2 w-full",
|
|
1365
|
+
actions: "flex items-center gap-1 mt-2 opacity-0 group-hover:opacity-100 transition-opacity duration-150",
|
|
1366
|
+
actionButton: "p-1.5 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 active:bg-gray-200 dark:active:bg-gray-700 rounded-md transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
1367
|
+
timestamp: "text-xs text-gray-400 dark:text-gray-500 mr-1"
|
|
1368
|
+
};
|
|
1369
|
+
function mergeClassNames(defaults, overrides) {
|
|
1370
|
+
if (!overrides) return defaults;
|
|
1371
|
+
return {
|
|
1372
|
+
root: overrides.root ?? defaults.root,
|
|
1373
|
+
wrapper: overrides.wrapper ?? defaults.wrapper,
|
|
1374
|
+
avatar: overrides.avatar ?? defaults.avatar,
|
|
1375
|
+
bubble: overrides.bubble ?? defaults.bubble,
|
|
1376
|
+
content: overrides.content ?? defaults.content,
|
|
1377
|
+
attachments: overrides.attachments ?? defaults.attachments,
|
|
1378
|
+
actions: overrides.actions ?? defaults.actions,
|
|
1379
|
+
actionButton: overrides.actionButton ?? defaults.actionButton,
|
|
1380
|
+
timestamp: overrides.timestamp ?? defaults.timestamp
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
function UserMessage({
|
|
1384
|
+
turn,
|
|
1385
|
+
turnId,
|
|
1386
|
+
initials = "U",
|
|
1387
|
+
slots,
|
|
1388
|
+
classNames: classNameOverrides,
|
|
1389
|
+
onCopy,
|
|
1390
|
+
onEdit,
|
|
1391
|
+
hideAvatar = false,
|
|
1392
|
+
hideActions = false,
|
|
1393
|
+
hideTimestamp = false
|
|
1394
|
+
}) {
|
|
822
1395
|
const [selectedImageIndex, setSelectedImageIndex] = useState(null);
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
1396
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
1397
|
+
const [draftContent, setDraftContent] = useState("");
|
|
1398
|
+
const classes = mergeClassNames(defaultClassNames, classNameOverrides);
|
|
1399
|
+
const imageAttachments = turn.attachments.filter((a) => a.mimeType.startsWith("image/")).map((a) => ({
|
|
1400
|
+
filename: a.filename,
|
|
1401
|
+
mimeType: a.mimeType,
|
|
1402
|
+
sizeBytes: a.sizeBytes,
|
|
1403
|
+
base64Data: a.base64Data || "",
|
|
1404
|
+
preview: a.base64Data ? `data:${a.mimeType};base64,${a.base64Data}` : ""
|
|
1405
|
+
}));
|
|
1406
|
+
const handleCopyClick = useCallback(async () => {
|
|
1407
|
+
if (onCopy) {
|
|
1408
|
+
await onCopy(turn.content);
|
|
826
1409
|
} else {
|
|
827
1410
|
try {
|
|
828
|
-
await navigator.clipboard.writeText(
|
|
1411
|
+
await navigator.clipboard.writeText(turn.content);
|
|
829
1412
|
} catch (err) {
|
|
830
1413
|
console.error("Failed to copy:", err);
|
|
831
1414
|
}
|
|
832
1415
|
}
|
|
833
|
-
};
|
|
834
|
-
const handleEditClick = () => {
|
|
835
|
-
if (
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1416
|
+
}, [onCopy, turn.content]);
|
|
1417
|
+
const handleEditClick = useCallback(() => {
|
|
1418
|
+
if (onEdit && turnId) {
|
|
1419
|
+
setDraftContent(turn.content);
|
|
1420
|
+
setIsEditing(true);
|
|
1421
|
+
}
|
|
1422
|
+
}, [onEdit, turnId, turn.content]);
|
|
1423
|
+
const handleEditCancel = useCallback(() => {
|
|
1424
|
+
setIsEditing(false);
|
|
1425
|
+
setDraftContent("");
|
|
1426
|
+
}, []);
|
|
1427
|
+
const handleEditSave = useCallback(() => {
|
|
1428
|
+
if (!onEdit || !turnId) return;
|
|
1429
|
+
const newContent = draftContent;
|
|
1430
|
+
if (!newContent.trim()) return;
|
|
1431
|
+
if (newContent === turn.content) {
|
|
1432
|
+
setIsEditing(false);
|
|
1433
|
+
return;
|
|
840
1434
|
}
|
|
1435
|
+
onEdit(turnId, newContent);
|
|
1436
|
+
setIsEditing(false);
|
|
1437
|
+
}, [onEdit, turnId, draftContent, turn.content]);
|
|
1438
|
+
const handleNavigate = useCallback(
|
|
1439
|
+
(direction) => {
|
|
1440
|
+
if (selectedImageIndex === null) return;
|
|
1441
|
+
if (direction === "prev" && selectedImageIndex > 0) {
|
|
1442
|
+
setSelectedImageIndex(selectedImageIndex - 1);
|
|
1443
|
+
} else if (direction === "next" && selectedImageIndex < imageAttachments.length - 1) {
|
|
1444
|
+
setSelectedImageIndex(selectedImageIndex + 1);
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
[selectedImageIndex, imageAttachments.length]
|
|
1448
|
+
);
|
|
1449
|
+
const currentAttachment = selectedImageIndex !== null ? imageAttachments[selectedImageIndex] : null;
|
|
1450
|
+
const timestamp = formatDistanceToNow(new Date(turn.createdAt), { addSuffix: true });
|
|
1451
|
+
const avatarSlotProps = { initials };
|
|
1452
|
+
const contentSlotProps = { content: turn.content };
|
|
1453
|
+
const attachmentsSlotProps = {
|
|
1454
|
+
attachments: imageAttachments,
|
|
1455
|
+
onView: (index) => setSelectedImageIndex(index)
|
|
841
1456
|
};
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1457
|
+
const actionsSlotProps = {
|
|
1458
|
+
onCopy: handleCopyClick,
|
|
1459
|
+
onEdit: onEdit && turnId ? handleEditClick : void 0,
|
|
1460
|
+
timestamp,
|
|
1461
|
+
canCopy: true,
|
|
1462
|
+
canEdit: !!onEdit && !!turnId
|
|
1463
|
+
};
|
|
1464
|
+
const renderSlot = (slot, props, defaultContent) => {
|
|
1465
|
+
if (slot === void 0) return defaultContent;
|
|
1466
|
+
if (typeof slot === "function") return slot(props);
|
|
1467
|
+
return slot;
|
|
1468
|
+
};
|
|
1469
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
|
|
1470
|
+
/* @__PURE__ */ jsxs("div", { className: classes.wrapper, children: [
|
|
1471
|
+
imageAttachments.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.attachments, children: renderSlot(
|
|
1472
|
+
slots?.attachments,
|
|
1473
|
+
attachmentsSlotProps,
|
|
854
1474
|
/* @__PURE__ */ jsx(
|
|
855
|
-
|
|
1475
|
+
AttachmentGrid_default,
|
|
856
1476
|
{
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
"aria-label": "Copy message",
|
|
860
|
-
title: "Copy",
|
|
861
|
-
children: /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
1477
|
+
attachments: imageAttachments,
|
|
1478
|
+
onView: (index) => setSelectedImageIndex(index)
|
|
862
1479
|
}
|
|
863
|
-
)
|
|
864
|
-
|
|
865
|
-
|
|
1480
|
+
)
|
|
1481
|
+
) }),
|
|
1482
|
+
turn.content && /* @__PURE__ */ jsx("div", { className: classes.bubble, children: /* @__PURE__ */ jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1483
|
+
/* @__PURE__ */ jsx(
|
|
1484
|
+
"textarea",
|
|
866
1485
|
{
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
"
|
|
870
|
-
|
|
871
|
-
children: /* @__PURE__ */ jsx(PencilSimple, { size: 14, weight: "regular" })
|
|
1486
|
+
value: draftContent,
|
|
1487
|
+
onChange: (e) => setDraftContent(e.target.value),
|
|
1488
|
+
className: "w-full min-h-[80px] resize-y rounded-lg px-3 py-2 bg-white/10 text-white placeholder-white/70 outline-none focus:ring-2 focus:ring-white/30",
|
|
1489
|
+
"aria-label": "Edit message"
|
|
872
1490
|
}
|
|
873
|
-
)
|
|
874
|
-
|
|
1491
|
+
),
|
|
1492
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
1493
|
+
/* @__PURE__ */ jsx(
|
|
1494
|
+
"button",
|
|
1495
|
+
{
|
|
1496
|
+
type: "button",
|
|
1497
|
+
onClick: handleEditCancel,
|
|
1498
|
+
className: "px-3 py-1.5 rounded-lg bg-white/10 hover:bg-white/15 transition-colors text-sm font-medium",
|
|
1499
|
+
children: "Cancel"
|
|
1500
|
+
}
|
|
1501
|
+
),
|
|
1502
|
+
/* @__PURE__ */ jsx(
|
|
1503
|
+
"button",
|
|
1504
|
+
{
|
|
1505
|
+
type: "button",
|
|
1506
|
+
onClick: handleEditSave,
|
|
1507
|
+
className: "px-3 py-1.5 rounded-lg bg-white/20 hover:bg-white/25 transition-colors text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1508
|
+
disabled: !draftContent.trim() || draftContent === turn.content,
|
|
1509
|
+
children: "Save"
|
|
1510
|
+
}
|
|
1511
|
+
)
|
|
1512
|
+
] })
|
|
1513
|
+
] }) : renderSlot(slots?.content, contentSlotProps, turn.content) }) }),
|
|
1514
|
+
!hideActions && /* @__PURE__ */ jsx("div", { className: classes.actions, children: renderSlot(
|
|
1515
|
+
slots?.actions,
|
|
1516
|
+
actionsSlotProps,
|
|
1517
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1518
|
+
!hideTimestamp && /* @__PURE__ */ jsx("span", { className: classes.timestamp, children: timestamp }),
|
|
1519
|
+
/* @__PURE__ */ jsx(
|
|
1520
|
+
"button",
|
|
1521
|
+
{
|
|
1522
|
+
onClick: handleCopyClick,
|
|
1523
|
+
className: classes.actionButton,
|
|
1524
|
+
"aria-label": "Copy message",
|
|
1525
|
+
title: "Copy",
|
|
1526
|
+
children: /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
1527
|
+
}
|
|
1528
|
+
),
|
|
1529
|
+
onEdit && turnId && /* @__PURE__ */ jsx(
|
|
1530
|
+
"button",
|
|
1531
|
+
{
|
|
1532
|
+
onClick: handleEditClick,
|
|
1533
|
+
className: classes.actionButton,
|
|
1534
|
+
"aria-label": "Edit message",
|
|
1535
|
+
title: "Edit",
|
|
1536
|
+
disabled: isEditing,
|
|
1537
|
+
children: /* @__PURE__ */ jsx(PencilSimple, { size: 14, weight: "regular" })
|
|
1538
|
+
}
|
|
1539
|
+
)
|
|
1540
|
+
] })
|
|
1541
|
+
) })
|
|
875
1542
|
] }),
|
|
876
|
-
/* @__PURE__ */ jsx("div", { className:
|
|
877
|
-
|
|
878
|
-
|
|
1543
|
+
!hideAvatar && /* @__PURE__ */ jsx("div", { className: classes.avatar, children: renderSlot(slots?.avatar, avatarSlotProps, initials) }),
|
|
1544
|
+
currentAttachment && /* @__PURE__ */ jsx(
|
|
1545
|
+
ImageModal_default,
|
|
879
1546
|
{
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1547
|
+
isOpen: selectedImageIndex !== null,
|
|
1548
|
+
onClose: () => setSelectedImageIndex(null),
|
|
1549
|
+
attachment: currentAttachment,
|
|
1550
|
+
allAttachments: imageAttachments,
|
|
1551
|
+
currentIndex: selectedImageIndex ?? 0,
|
|
1552
|
+
onNavigate: handleNavigate
|
|
883
1553
|
}
|
|
884
1554
|
)
|
|
885
1555
|
] });
|
|
886
1556
|
}
|
|
1557
|
+
function UserTurnView({
|
|
1558
|
+
turn,
|
|
1559
|
+
slots,
|
|
1560
|
+
classNames,
|
|
1561
|
+
initials = "U",
|
|
1562
|
+
hideAvatar,
|
|
1563
|
+
hideActions,
|
|
1564
|
+
hideTimestamp
|
|
1565
|
+
}) {
|
|
1566
|
+
const { handleEdit, handleCopy } = useChat();
|
|
1567
|
+
return /* @__PURE__ */ jsx(
|
|
1568
|
+
UserMessage,
|
|
1569
|
+
{
|
|
1570
|
+
turn: turn.userTurn,
|
|
1571
|
+
turnId: turn.id,
|
|
1572
|
+
initials,
|
|
1573
|
+
slots,
|
|
1574
|
+
classNames,
|
|
1575
|
+
onCopy: handleCopy,
|
|
1576
|
+
onEdit: handleEdit,
|
|
1577
|
+
hideAvatar,
|
|
1578
|
+
hideActions,
|
|
1579
|
+
hideTimestamp
|
|
1580
|
+
}
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
function toBase64(str) {
|
|
1584
|
+
const bytes = new TextEncoder().encode(str);
|
|
1585
|
+
let binary = "";
|
|
1586
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
1587
|
+
binary += String.fromCharCode(bytes[i]);
|
|
1588
|
+
}
|
|
1589
|
+
return btoa(binary);
|
|
1590
|
+
}
|
|
887
1591
|
function CodeOutputsPanel({ outputs }) {
|
|
888
1592
|
if (!outputs || outputs.length === 0) return null;
|
|
889
1593
|
return /* @__PURE__ */ jsxs("div", { className: "mb-2 p-3 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700", children: [
|
|
890
1594
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-semibold text-gray-600 dark:text-gray-400 mb-2", children: "Code Output" }),
|
|
891
1595
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: outputs.map((output, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
892
|
-
output.type === "image" && /* @__PURE__ */
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1596
|
+
output.type === "image" && /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
1597
|
+
/* @__PURE__ */ jsx(
|
|
1598
|
+
"img",
|
|
1599
|
+
{
|
|
1600
|
+
src: output.content.startsWith("data:") ? output.content : `data:${output.mimeType || "image/png"};base64,${output.content}`,
|
|
1601
|
+
alt: output.filename || "Code output",
|
|
1602
|
+
className: "max-w-full rounded border border-gray-300 dark:border-gray-600"
|
|
1603
|
+
}
|
|
1604
|
+
),
|
|
1605
|
+
output.filename && /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-2 opacity-0 group-hover:opacity-100 transition-opacity", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white text-xs", children: [
|
|
1606
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: output.filename }),
|
|
1607
|
+
output.sizeBytes && /* @__PURE__ */ jsx("span", { className: "text-gray-300", children: formatFileSize(output.sizeBytes) })
|
|
1608
|
+
] }) })
|
|
1609
|
+
] }),
|
|
1610
|
+
output.type === "text" && /* @__PURE__ */ jsxs("div", { children: [
|
|
1611
|
+
/* @__PURE__ */ jsx("pre", { className: "text-xs bg-white dark:bg-gray-800 p-2 rounded overflow-x-auto border border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx("code", { className: "text-gray-900 dark:text-gray-100", children: output.content }) }),
|
|
1612
|
+
output.filename && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1 text-xs", children: [
|
|
1613
|
+
/* @__PURE__ */ jsxs(
|
|
1614
|
+
"a",
|
|
1615
|
+
{
|
|
1616
|
+
href: `data:${output.mimeType || "text/plain"};base64,${toBase64(output.content)}`,
|
|
1617
|
+
download: output.filename,
|
|
1618
|
+
className: "flex items-center gap-1 text-blue-600 dark:text-blue-400 hover:underline",
|
|
1619
|
+
children: [
|
|
1620
|
+
/* @__PURE__ */ jsx(Download, { size: 12, weight: "bold" }),
|
|
1621
|
+
output.filename
|
|
1622
|
+
]
|
|
1623
|
+
}
|
|
1624
|
+
),
|
|
1625
|
+
output.sizeBytes && /* @__PURE__ */ jsxs("span", { className: "text-gray-500 dark:text-gray-400", children: [
|
|
1626
|
+
"(",
|
|
1627
|
+
formatFileSize(output.sizeBytes),
|
|
1628
|
+
")"
|
|
1629
|
+
] })
|
|
1630
|
+
] })
|
|
1631
|
+
] }),
|
|
1632
|
+
output.type === "error" && /* @__PURE__ */ jsxs("div", { className: "text-xs text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 p-2 rounded border border-red-200 dark:border-red-800", children: [
|
|
1633
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold mb-1", children: "Error" }),
|
|
1634
|
+
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap", children: output.content })
|
|
1635
|
+
] })
|
|
902
1636
|
] }, index)) })
|
|
903
1637
|
] });
|
|
904
1638
|
}
|
|
1639
|
+
var CodeOutputsPanel_default = CodeOutputsPanel;
|
|
905
1640
|
function StreamingCursor() {
|
|
906
1641
|
return /* @__PURE__ */ jsx(
|
|
907
1642
|
"span",
|
|
@@ -911,63 +1646,97 @@ function StreamingCursor() {
|
|
|
911
1646
|
}
|
|
912
1647
|
);
|
|
913
1648
|
}
|
|
914
|
-
var
|
|
1649
|
+
var StreamingCursor_default = StreamingCursor;
|
|
1650
|
+
var DEFAULT_COLORS = ["#008FFB", "#00E396", "#FEB019", "#FF4560", "#775DD0"];
|
|
915
1651
|
function ChartCard({ chartData }) {
|
|
916
|
-
const
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1652
|
+
const chartId = useId().replace(/:/g, "_");
|
|
1653
|
+
const [isExporting, setIsExporting] = useState(false);
|
|
1654
|
+
const { chartType, title, series, labels, colors, height = 350 } = chartData;
|
|
1655
|
+
const hasValidData = series && series.length > 0 && series.some((s) => s.data && s.data.length > 0);
|
|
1656
|
+
if (!hasValidData) {
|
|
1657
|
+
return /* @__PURE__ */ jsx("div", { className: "bg-white dark:bg-gray-800 rounded-lg border border-[var(--bichat-border)] p-4 my-2 shadow-sm w-full max-w-full", children: /* @__PURE__ */ jsxs("p", { className: "text-gray-500 dark:text-gray-400 text-sm", children: [
|
|
1658
|
+
title && /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
|
|
1659
|
+
title,
|
|
1660
|
+
": "
|
|
1661
|
+
] }),
|
|
1662
|
+
"No data available for chart visualization."
|
|
1663
|
+
] }) });
|
|
1664
|
+
}
|
|
1665
|
+
const apexSeries = chartType === "pie" || chartType === "donut" ? series[0]?.data ?? [] : series.map((s) => ({ name: s.name, data: s.data }));
|
|
1666
|
+
const xaxisConfig = chartType !== "pie" && chartType !== "donut" ? { categories: (labels ?? []).filter((l) => l !== null) } : {};
|
|
1667
|
+
const labelsConfig = chartType === "pie" || chartType === "donut" ? (labels ?? []).filter((l) => l !== null) : [];
|
|
1668
|
+
const options = {
|
|
1669
|
+
chart: {
|
|
1670
|
+
id: chartId,
|
|
1671
|
+
type: chartType,
|
|
1672
|
+
toolbar: { show: false },
|
|
1673
|
+
animations: { enabled: false }
|
|
1674
|
+
},
|
|
1675
|
+
title: {
|
|
1676
|
+
text: title,
|
|
1677
|
+
align: "left",
|
|
1678
|
+
style: { fontSize: "14px", fontWeight: 600 }
|
|
1679
|
+
},
|
|
1680
|
+
colors: colors?.length ? colors : DEFAULT_COLORS,
|
|
1681
|
+
xaxis: xaxisConfig,
|
|
1682
|
+
labels: labelsConfig,
|
|
1683
|
+
legend: { position: "bottom", horizontalAlign: "center" },
|
|
1684
|
+
dataLabels: { enabled: chartType === "pie" || chartType === "donut" },
|
|
1685
|
+
stroke: {
|
|
1686
|
+
curve: "smooth",
|
|
1687
|
+
width: chartType === "line" || chartType === "area" ? 2 : 0
|
|
1688
|
+
},
|
|
1689
|
+
fill: { opacity: chartType === "area" ? 0.4 : 1 }
|
|
1690
|
+
};
|
|
1691
|
+
const handleExportPNG = async () => {
|
|
1692
|
+
setIsExporting(true);
|
|
1693
|
+
try {
|
|
1694
|
+
const chart = ApexCharts.getChartByID(chartId);
|
|
1695
|
+
if (!chart) {
|
|
1696
|
+
console.error("Chart instance not available");
|
|
1697
|
+
setIsExporting(false);
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
const result = await chart.dataURI({ scale: 2 });
|
|
1701
|
+
if (!("imgURI" in result)) {
|
|
1702
|
+
console.error("Unexpected dataURI result format");
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const link = document.createElement("a");
|
|
1706
|
+
link.href = result.imgURI;
|
|
1707
|
+
link.download = `${title.replace(/[^a-z0-9]/gi, "_").toLowerCase()}_chart.png`;
|
|
1708
|
+
link.click();
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
console.error("Failed to export chart:", error);
|
|
1711
|
+
} finally {
|
|
1712
|
+
setIsExporting(false);
|
|
966
1713
|
}
|
|
967
1714
|
};
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
/* @__PURE__ */ jsx(
|
|
1715
|
+
const chartWidth = 600;
|
|
1716
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white dark:bg-gray-800 rounded-lg border border-[var(--bichat-border)] p-4 my-2 shadow-sm w-full max-w-[632px] min-w-0 overflow-hidden", children: [
|
|
1717
|
+
/* @__PURE__ */ jsx("div", { className: "w-full min-w-0", children: /* @__PURE__ */ jsx(
|
|
1718
|
+
ReactApexChart,
|
|
1719
|
+
{
|
|
1720
|
+
options,
|
|
1721
|
+
series: apexSeries,
|
|
1722
|
+
type: chartType,
|
|
1723
|
+
width: chartWidth,
|
|
1724
|
+
height
|
|
1725
|
+
}
|
|
1726
|
+
) }),
|
|
1727
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end mt-2", children: /* @__PURE__ */ jsx(
|
|
1728
|
+
"button",
|
|
1729
|
+
{
|
|
1730
|
+
onClick: handleExportPNG,
|
|
1731
|
+
disabled: isExporting,
|
|
1732
|
+
className: "text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200 flex items-center gap-1 transition-colors",
|
|
1733
|
+
title: "Download chart as PNG",
|
|
1734
|
+
children: isExporting ? /* @__PURE__ */ jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1735
|
+
/* @__PURE__ */ jsx(Download, { className: "w-4 h-4" }),
|
|
1736
|
+
"Download PNG"
|
|
1737
|
+
] })
|
|
1738
|
+
}
|
|
1739
|
+
) })
|
|
971
1740
|
] });
|
|
972
1741
|
}
|
|
973
1742
|
function SourcesPanel({ citations }) {
|
|
@@ -981,13 +1750,13 @@ function SourcesPanel({ citations }) {
|
|
|
981
1750
|
{
|
|
982
1751
|
type: "button",
|
|
983
1752
|
onClick: () => setExpanded(!expanded),
|
|
984
|
-
className: "flex items-center gap-2 text-sm text-gray-600 hover:text-gray-800 transition-colors",
|
|
1753
|
+
className: "flex items-center gap-2 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 rounded-md p-1 -m-1",
|
|
985
1754
|
"aria-expanded": expanded,
|
|
986
1755
|
children: [
|
|
987
1756
|
/* @__PURE__ */ jsx(
|
|
988
1757
|
"svg",
|
|
989
1758
|
{
|
|
990
|
-
className: `w-4 h-4 transition-transform ${expanded ? "rotate-90" : ""}`,
|
|
1759
|
+
className: `w-4 h-4 transition-transform duration-150 ${expanded ? "rotate-90" : ""}`,
|
|
991
1760
|
fill: "none",
|
|
992
1761
|
stroke: "currentColor",
|
|
993
1762
|
viewBox: "0 0 24 24",
|
|
@@ -1013,22 +1782,22 @@ function SourcesPanel({ citations }) {
|
|
|
1013
1782
|
expanded && /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-2", children: citations.map((citation, index) => /* @__PURE__ */ jsx(
|
|
1014
1783
|
"div",
|
|
1015
1784
|
{
|
|
1016
|
-
className: "p-3 bg-gray-50 rounded-lg text-sm",
|
|
1785
|
+
className: "p-3 bg-gray-50 dark:bg-gray-800/50 rounded-lg text-sm",
|
|
1017
1786
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1018
1787
|
/* @__PURE__ */ jsx("span", { className: "flex-shrink-0 w-5 h-5 bg-[var(--bichat-primary)] text-white rounded-full flex items-center justify-center text-xs", children: index + 1 }),
|
|
1019
1788
|
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
1020
|
-
/* @__PURE__ */ jsx("div", { className: "font-medium text-gray-900", children: citation.source }),
|
|
1789
|
+
/* @__PURE__ */ jsx("div", { className: "font-medium text-gray-900 dark:text-gray-100", children: citation.title || citation.source }),
|
|
1021
1790
|
citation.url && /* @__PURE__ */ jsx(
|
|
1022
1791
|
"a",
|
|
1023
1792
|
{
|
|
1024
1793
|
href: citation.url,
|
|
1025
1794
|
target: "_blank",
|
|
1026
1795
|
rel: "noopener noreferrer",
|
|
1027
|
-
className: "text-[var(--bichat-primary)] hover:underline",
|
|
1796
|
+
className: "text-[var(--bichat-primary)] hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 rounded",
|
|
1028
1797
|
children: citation.url
|
|
1029
1798
|
}
|
|
1030
1799
|
),
|
|
1031
|
-
citation.excerpt && /* @__PURE__ */ jsxs("div", { className: "mt-1 text-gray-600 italic", children: [
|
|
1800
|
+
citation.excerpt && /* @__PURE__ */ jsxs("div", { className: "mt-1 text-gray-600 dark:text-gray-400 italic", children: [
|
|
1032
1801
|
'"',
|
|
1033
1802
|
citation.excerpt,
|
|
1034
1803
|
'"'
|
|
@@ -1098,218 +1867,516 @@ function DownloadCard({ artifact }) {
|
|
|
1098
1867
|
);
|
|
1099
1868
|
}
|
|
1100
1869
|
function InlineQuestionForm({ pendingQuestion }) {
|
|
1101
|
-
const { handleSubmitQuestionAnswers, handleCancelPendingQuestion } = useChat();
|
|
1870
|
+
const { handleSubmitQuestionAnswers, handleCancelPendingQuestion, loading } = useChat();
|
|
1871
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
1102
1872
|
const [answers, setAnswers] = useState({});
|
|
1103
|
-
const [
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1873
|
+
const [otherTexts, setOtherTexts] = useState({});
|
|
1874
|
+
const questions = pendingQuestion.questions;
|
|
1875
|
+
const currentQuestion = questions[currentStep];
|
|
1876
|
+
const isLastStep = currentStep === questions.length - 1;
|
|
1877
|
+
const isFirstStep = currentStep === 0;
|
|
1878
|
+
const totalSteps = questions.length;
|
|
1879
|
+
const currentAnswer = answers[currentQuestion?.id];
|
|
1880
|
+
const currentOtherText = otherTexts[currentQuestion?.id] || "";
|
|
1881
|
+
const handleOptionChange = useCallback(
|
|
1882
|
+
(optionLabel, checked) => {
|
|
1883
|
+
if (!currentQuestion) return;
|
|
1884
|
+
const questionId = currentQuestion.id;
|
|
1885
|
+
const existingAnswer = answers[questionId] || { options: [] };
|
|
1886
|
+
const isOtherOption = optionLabel === "__other__";
|
|
1887
|
+
const isMultiSelect2 = currentQuestion.type === "MULTIPLE_CHOICE";
|
|
1888
|
+
if (isOtherOption) {
|
|
1889
|
+
setAnswers({
|
|
1890
|
+
...answers,
|
|
1891
|
+
[questionId]: {
|
|
1892
|
+
options: [],
|
|
1893
|
+
customText: checked ? currentOtherText : void 0
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
let newOptions;
|
|
1899
|
+
if (isMultiSelect2) {
|
|
1900
|
+
if (!checked) {
|
|
1901
|
+
newOptions = existingAnswer.options.filter((o) => o !== optionLabel);
|
|
1902
|
+
} else if (existingAnswer.options.includes(optionLabel)) {
|
|
1903
|
+
newOptions = existingAnswer.options;
|
|
1904
|
+
} else {
|
|
1905
|
+
newOptions = [...existingAnswer.options, optionLabel];
|
|
1906
|
+
}
|
|
1907
|
+
} else {
|
|
1908
|
+
newOptions = checked ? [optionLabel] : [];
|
|
1909
|
+
}
|
|
1910
|
+
setAnswers({
|
|
1911
|
+
...answers,
|
|
1912
|
+
[questionId]: {
|
|
1913
|
+
options: newOptions,
|
|
1914
|
+
customText: void 0
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
},
|
|
1918
|
+
[currentQuestion, answers, currentOtherText]
|
|
1919
|
+
);
|
|
1920
|
+
const handleOtherTextChange = useCallback(
|
|
1921
|
+
(text) => {
|
|
1922
|
+
if (!currentQuestion) return;
|
|
1923
|
+
const questionId = currentQuestion.id;
|
|
1924
|
+
setOtherTexts({ ...otherTexts, [questionId]: text });
|
|
1925
|
+
setAnswers({
|
|
1926
|
+
...answers,
|
|
1927
|
+
[questionId]: {
|
|
1928
|
+
options: [],
|
|
1929
|
+
customText: text
|
|
1930
|
+
}
|
|
1931
|
+
});
|
|
1932
|
+
},
|
|
1933
|
+
[currentQuestion, answers, otherTexts]
|
|
1934
|
+
);
|
|
1935
|
+
const isCurrentAnswerValid = () => {
|
|
1936
|
+
if (!currentQuestion) return false;
|
|
1937
|
+
const answer = answers[currentQuestion.id];
|
|
1938
|
+
const required = currentQuestion.required ?? true;
|
|
1939
|
+
if (!answer) return !required;
|
|
1940
|
+
const hasOptionSelection = answer.options.length > 0;
|
|
1941
|
+
const hasOtherSelected = answer.customText !== void 0;
|
|
1942
|
+
const hasOtherText = (answer.customText?.trim().length ?? 0) > 0;
|
|
1943
|
+
if (!hasOptionSelection && !hasOtherSelected) {
|
|
1944
|
+
return !required;
|
|
1108
1945
|
}
|
|
1109
|
-
if (
|
|
1110
|
-
return;
|
|
1946
|
+
if (hasOptionSelection) {
|
|
1947
|
+
return true;
|
|
1948
|
+
}
|
|
1949
|
+
return !required || hasOtherText;
|
|
1950
|
+
};
|
|
1951
|
+
const handleNext = () => {
|
|
1952
|
+
if (!isCurrentAnswerValid()) return;
|
|
1953
|
+
if (isLastStep) {
|
|
1954
|
+
handleSubmitQuestionAnswers(answers);
|
|
1955
|
+
} else {
|
|
1956
|
+
setCurrentStep(currentStep + 1);
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
const handleBack = () => {
|
|
1960
|
+
if (!isFirstStep) {
|
|
1961
|
+
setCurrentStep(currentStep - 1);
|
|
1111
1962
|
}
|
|
1112
|
-
const finalAnswers = pendingQuestion.type === "MULTIPLE_CHOICE" ? answers : { answer: textInput };
|
|
1113
|
-
handleSubmitQuestionAnswers(finalAnswers);
|
|
1114
1963
|
};
|
|
1115
|
-
|
|
1964
|
+
const handleSubmit = (e) => {
|
|
1965
|
+
e.preventDefault();
|
|
1966
|
+
handleNext();
|
|
1967
|
+
};
|
|
1968
|
+
if (!currentQuestion) return null;
|
|
1969
|
+
const isMultiSelect = currentQuestion.type === "MULTIPLE_CHOICE";
|
|
1970
|
+
const options = currentQuestion.options || [];
|
|
1971
|
+
const isOtherSelected = currentAnswer?.customText !== void 0;
|
|
1972
|
+
return /* @__PURE__ */ jsx("div", { className: "border border-amber-200 dark:border-amber-800 rounded-lg p-4 bg-amber-50 dark:bg-amber-900/20", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
1116
1973
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 mb-4", children: [
|
|
1117
1974
|
/* @__PURE__ */ jsx(
|
|
1118
|
-
|
|
1975
|
+
Question,
|
|
1119
1976
|
{
|
|
1120
|
-
className: "w-5 h-5 text-
|
|
1121
|
-
|
|
1122
|
-
viewBox: "0 0 20 20",
|
|
1123
|
-
children: /* @__PURE__ */ jsx(
|
|
1124
|
-
"path",
|
|
1125
|
-
{
|
|
1126
|
-
fillRule: "evenodd",
|
|
1127
|
-
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z",
|
|
1128
|
-
clipRule: "evenodd"
|
|
1129
|
-
}
|
|
1130
|
-
)
|
|
1977
|
+
className: "w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5",
|
|
1978
|
+
weight: "fill"
|
|
1131
1979
|
}
|
|
1132
1980
|
),
|
|
1133
1981
|
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
1134
|
-
/* @__PURE__ */
|
|
1135
|
-
|
|
1982
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
1983
|
+
/* @__PURE__ */ jsx("h4", { className: "font-medium text-gray-900 dark:text-gray-100", children: "Question from AI" }),
|
|
1984
|
+
totalSteps > 1 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
1985
|
+
currentStep + 1,
|
|
1986
|
+
" of ",
|
|
1987
|
+
totalSteps
|
|
1988
|
+
] })
|
|
1989
|
+
] }),
|
|
1990
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-700 dark:text-gray-300", children: currentQuestion.text })
|
|
1136
1991
|
] })
|
|
1137
1992
|
] }),
|
|
1138
|
-
|
|
1139
|
-
"
|
|
1993
|
+
totalSteps > 1 && /* @__PURE__ */ jsx("div", { className: "flex gap-1 mb-4", children: questions.map((_, index) => /* @__PURE__ */ jsx(
|
|
1994
|
+
"div",
|
|
1140
1995
|
{
|
|
1141
|
-
className:
|
|
1142
|
-
children: [
|
|
1143
|
-
/* @__PURE__ */ jsx(
|
|
1144
|
-
"input",
|
|
1145
|
-
{
|
|
1146
|
-
type: "radio",
|
|
1147
|
-
name: "choice",
|
|
1148
|
-
value: option,
|
|
1149
|
-
checked: answers.choice === option,
|
|
1150
|
-
onChange: (e) => setAnswers({ ...answers, choice: e.target.value }),
|
|
1151
|
-
className: "w-4 h-4 text-[var(--bichat-primary)]"
|
|
1152
|
-
}
|
|
1153
|
-
),
|
|
1154
|
-
/* @__PURE__ */ jsx("span", { className: "text-gray-900", children: option })
|
|
1155
|
-
]
|
|
1996
|
+
className: `h-1 flex-1 rounded-full transition-colors ${index <= currentStep ? "bg-amber-500 dark:bg-amber-400" : "bg-gray-200 dark:bg-gray-700"}`
|
|
1156
1997
|
},
|
|
1157
1998
|
index
|
|
1158
1999
|
)) }),
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
2000
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 mb-4", children: [
|
|
2001
|
+
options.map((option) => /* @__PURE__ */ jsxs(
|
|
2002
|
+
"label",
|
|
2003
|
+
{
|
|
2004
|
+
className: "flex items-center gap-2 p-2 hover:bg-amber-100 dark:hover:bg-amber-900/30 rounded cursor-pointer",
|
|
2005
|
+
children: [
|
|
2006
|
+
/* @__PURE__ */ jsx(
|
|
2007
|
+
"input",
|
|
2008
|
+
{
|
|
2009
|
+
type: isMultiSelect ? "checkbox" : "radio",
|
|
2010
|
+
name: `question-${currentQuestion.id}`,
|
|
2011
|
+
value: option.value,
|
|
2012
|
+
checked: currentAnswer?.options.includes(option.label) || false,
|
|
2013
|
+
onChange: (e) => handleOptionChange(option.label, e.target.checked),
|
|
2014
|
+
className: "w-4 h-4 text-amber-600 focus:ring-amber-500"
|
|
2015
|
+
}
|
|
2016
|
+
),
|
|
2017
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-900 dark:text-gray-100", children: option.label })
|
|
2018
|
+
]
|
|
2019
|
+
},
|
|
2020
|
+
option.id
|
|
2021
|
+
)),
|
|
2022
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 p-2 hover:bg-amber-100 dark:hover:bg-amber-900/30 rounded cursor-pointer", children: [
|
|
2023
|
+
/* @__PURE__ */ jsx(
|
|
2024
|
+
"input",
|
|
2025
|
+
{
|
|
2026
|
+
type: isMultiSelect ? "checkbox" : "radio",
|
|
2027
|
+
name: `question-${currentQuestion.id}`,
|
|
2028
|
+
value: "__other__",
|
|
2029
|
+
checked: isOtherSelected,
|
|
2030
|
+
onChange: (e) => handleOptionChange("__other__", e.target.checked),
|
|
2031
|
+
className: "w-4 h-4 text-amber-600 focus:ring-amber-500"
|
|
2032
|
+
}
|
|
2033
|
+
),
|
|
2034
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-900 dark:text-gray-100", children: "Other" })
|
|
2035
|
+
] }),
|
|
2036
|
+
isOtherSelected && /* @__PURE__ */ jsx("div", { className: "ml-6", children: /* @__PURE__ */ jsx(
|
|
2037
|
+
"input",
|
|
1172
2038
|
{
|
|
1173
|
-
type: "
|
|
1174
|
-
|
|
1175
|
-
|
|
2039
|
+
type: "text",
|
|
2040
|
+
value: currentOtherText,
|
|
2041
|
+
onChange: (e) => handleOtherTextChange(e.target.value),
|
|
2042
|
+
placeholder: "Please specify...",
|
|
2043
|
+
className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-amber-500 focus:border-transparent"
|
|
1176
2044
|
}
|
|
1177
|
-
)
|
|
1178
|
-
|
|
2045
|
+
) })
|
|
2046
|
+
] }),
|
|
2047
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2048
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: !isFirstStep && /* @__PURE__ */ jsxs(
|
|
1179
2049
|
"button",
|
|
1180
2050
|
{
|
|
1181
2051
|
type: "button",
|
|
1182
|
-
onClick:
|
|
1183
|
-
className: "px-
|
|
1184
|
-
children:
|
|
2052
|
+
onClick: handleBack,
|
|
2053
|
+
className: "flex items-center gap-1 px-3 py-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 transition-colors",
|
|
2054
|
+
children: [
|
|
2055
|
+
/* @__PURE__ */ jsx(CaretLeft, { size: 16, weight: "bold" }),
|
|
2056
|
+
"Back"
|
|
2057
|
+
]
|
|
1185
2058
|
}
|
|
1186
|
-
)
|
|
2059
|
+
) }),
|
|
2060
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
2061
|
+
/* @__PURE__ */ jsx(
|
|
2062
|
+
"button",
|
|
2063
|
+
{
|
|
2064
|
+
type: "button",
|
|
2065
|
+
onClick: handleCancelPendingQuestion,
|
|
2066
|
+
disabled: loading,
|
|
2067
|
+
className: "px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors disabled:opacity-50",
|
|
2068
|
+
children: "Cancel"
|
|
2069
|
+
}
|
|
2070
|
+
),
|
|
2071
|
+
/* @__PURE__ */ jsxs(
|
|
2072
|
+
"button",
|
|
2073
|
+
{
|
|
2074
|
+
type: "submit",
|
|
2075
|
+
disabled: loading || !isCurrentAnswerValid(),
|
|
2076
|
+
className: "flex items-center gap-1 px-4 py-2 bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
2077
|
+
children: [
|
|
2078
|
+
isLastStep ? "Submit" : "Next",
|
|
2079
|
+
!isLastStep && /* @__PURE__ */ jsx(CaretRight, { size: 16, weight: "bold" })
|
|
2080
|
+
]
|
|
2081
|
+
}
|
|
2082
|
+
)
|
|
2083
|
+
] })
|
|
1187
2084
|
] })
|
|
1188
2085
|
] }) });
|
|
1189
2086
|
}
|
|
1190
2087
|
var MarkdownRenderer2 = lazy(
|
|
1191
2088
|
() => Promise.resolve().then(() => (init_MarkdownRenderer(), MarkdownRenderer_exports)).then((module) => ({ default: module.MarkdownRenderer }))
|
|
1192
2089
|
);
|
|
1193
|
-
|
|
1194
|
-
|
|
2090
|
+
var defaultClassNames2 = {
|
|
2091
|
+
root: "flex gap-3 group",
|
|
2092
|
+
wrapper: "flex-1 flex flex-col gap-3 max-w-[85%]",
|
|
2093
|
+
avatar: "flex-shrink-0 w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white font-medium text-xs",
|
|
2094
|
+
bubble: "bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-2xl rounded-bl-sm px-4 py-3",
|
|
2095
|
+
codeOutputs: "",
|
|
2096
|
+
charts: "mb-1 w-full",
|
|
2097
|
+
artifacts: "mb-1 flex flex-wrap gap-2",
|
|
2098
|
+
sources: "",
|
|
2099
|
+
explanation: "mt-4 border-t border-gray-100 dark:border-gray-700 pt-4",
|
|
2100
|
+
actions: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-150",
|
|
2101
|
+
actionButton: "p-1.5 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 active:bg-gray-200 dark:active:bg-gray-700 rounded-md transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
2102
|
+
timestamp: "text-xs text-gray-400 dark:text-gray-500 mr-1"
|
|
2103
|
+
};
|
|
2104
|
+
function mergeClassNames2(defaults, overrides) {
|
|
2105
|
+
if (!overrides) return defaults;
|
|
2106
|
+
return {
|
|
2107
|
+
root: overrides.root ?? defaults.root,
|
|
2108
|
+
wrapper: overrides.wrapper ?? defaults.wrapper,
|
|
2109
|
+
avatar: overrides.avatar ?? defaults.avatar,
|
|
2110
|
+
bubble: overrides.bubble ?? defaults.bubble,
|
|
2111
|
+
codeOutputs: overrides.codeOutputs ?? defaults.codeOutputs,
|
|
2112
|
+
charts: overrides.charts ?? defaults.charts,
|
|
2113
|
+
artifacts: overrides.artifacts ?? defaults.artifacts,
|
|
2114
|
+
sources: overrides.sources ?? defaults.sources,
|
|
2115
|
+
explanation: overrides.explanation ?? defaults.explanation,
|
|
2116
|
+
actions: overrides.actions ?? defaults.actions,
|
|
2117
|
+
actionButton: overrides.actionButton ?? defaults.actionButton,
|
|
2118
|
+
timestamp: overrides.timestamp ?? defaults.timestamp
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
function AssistantMessage({
|
|
2122
|
+
turn,
|
|
2123
|
+
turnId,
|
|
2124
|
+
isStreaming = false,
|
|
2125
|
+
pendingQuestion,
|
|
2126
|
+
slots,
|
|
2127
|
+
classNames: classNameOverrides,
|
|
2128
|
+
onCopy,
|
|
2129
|
+
onRegenerate,
|
|
2130
|
+
onSendMessage,
|
|
2131
|
+
sendDisabled = false,
|
|
2132
|
+
hideAvatar = false,
|
|
2133
|
+
hideActions = false,
|
|
2134
|
+
hideTimestamp = false
|
|
2135
|
+
}) {
|
|
1195
2136
|
const [explanationExpanded, setExplanationExpanded] = useState(false);
|
|
1196
|
-
const
|
|
1197
|
-
const
|
|
1198
|
-
const
|
|
1199
|
-
const
|
|
1200
|
-
|
|
1201
|
-
|
|
2137
|
+
const classes = mergeClassNames2(defaultClassNames2, classNameOverrides);
|
|
2138
|
+
const hasContent = turn.content?.trim().length > 0;
|
|
2139
|
+
const hasExplanation = !!turn.explanation?.trim();
|
|
2140
|
+
const hasPendingQuestion = !!pendingQuestion && pendingQuestion.status === "PENDING" && pendingQuestion.turnId === turnId;
|
|
2141
|
+
const handleCopyClick = useCallback(async () => {
|
|
2142
|
+
if (onCopy) {
|
|
2143
|
+
await onCopy(turn.content);
|
|
1202
2144
|
} else {
|
|
1203
2145
|
try {
|
|
1204
|
-
await navigator.clipboard.writeText(
|
|
2146
|
+
await navigator.clipboard.writeText(turn.content);
|
|
1205
2147
|
} catch (err) {
|
|
1206
2148
|
console.error("Failed to copy:", err);
|
|
1207
2149
|
}
|
|
1208
2150
|
}
|
|
1209
|
-
};
|
|
1210
|
-
const handleRegenerateClick = async () => {
|
|
1211
|
-
if (
|
|
1212
|
-
await
|
|
2151
|
+
}, [onCopy, turn.content]);
|
|
2152
|
+
const handleRegenerateClick = useCallback(async () => {
|
|
2153
|
+
if (onRegenerate && turnId) {
|
|
2154
|
+
await onRegenerate(turnId);
|
|
1213
2155
|
}
|
|
2156
|
+
}, [onRegenerate, turnId]);
|
|
2157
|
+
const timestamp = formatDistanceToNow(new Date(turn.createdAt), { addSuffix: true });
|
|
2158
|
+
const avatarSlotProps = { text: "AI" };
|
|
2159
|
+
const contentSlotProps = {
|
|
2160
|
+
content: turn.content,
|
|
2161
|
+
citations: turn.citations,
|
|
2162
|
+
isStreaming
|
|
1214
2163
|
};
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
2164
|
+
const sourcesSlotProps = {
|
|
2165
|
+
citations: turn.citations || []
|
|
2166
|
+
};
|
|
2167
|
+
const chartsSlotProps = {
|
|
2168
|
+
chartData: turn.chartData
|
|
2169
|
+
};
|
|
2170
|
+
const codeOutputsSlotProps = {
|
|
2171
|
+
outputs: turn.codeOutputs || []
|
|
2172
|
+
};
|
|
2173
|
+
const artifactsSlotProps = {
|
|
2174
|
+
artifacts: turn.artifacts || []
|
|
2175
|
+
};
|
|
2176
|
+
const actionsSlotProps = {
|
|
2177
|
+
onCopy: handleCopyClick,
|
|
2178
|
+
onRegenerate: onRegenerate && turnId ? handleRegenerateClick : void 0,
|
|
2179
|
+
timestamp,
|
|
2180
|
+
canCopy: hasContent,
|
|
2181
|
+
canRegenerate: !!onRegenerate && !!turnId
|
|
2182
|
+
};
|
|
2183
|
+
const explanationSlotProps = {
|
|
2184
|
+
explanation: turn.explanation || "",
|
|
2185
|
+
isExpanded: explanationExpanded,
|
|
2186
|
+
onToggle: () => setExplanationExpanded(!explanationExpanded)
|
|
2187
|
+
};
|
|
2188
|
+
const renderSlot = (slot, props, defaultContent) => {
|
|
2189
|
+
if (slot === void 0) return defaultContent;
|
|
2190
|
+
if (typeof slot === "function") return slot(props);
|
|
2191
|
+
return slot;
|
|
2192
|
+
};
|
|
2193
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
|
|
2194
|
+
!hideAvatar && /* @__PURE__ */ jsx("div", { className: classes.avatar, children: renderSlot(slots?.avatar, avatarSlotProps, "AI") }),
|
|
2195
|
+
/* @__PURE__ */ jsxs("div", { className: classes.wrapper, children: [
|
|
2196
|
+
turn.codeOutputs && turn.codeOutputs.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.codeOutputs, children: renderSlot(
|
|
2197
|
+
slots?.codeOutputs,
|
|
2198
|
+
codeOutputsSlotProps,
|
|
2199
|
+
/* @__PURE__ */ jsx(CodeOutputsPanel_default, { outputs: turn.codeOutputs })
|
|
2200
|
+
) }),
|
|
2201
|
+
turn.chartData && /* @__PURE__ */ jsx("div", { className: classes.charts, children: renderSlot(slots?.charts, chartsSlotProps, /* @__PURE__ */ jsx(ChartCard, { chartData: turn.chartData })) }),
|
|
2202
|
+
turn.artifacts && turn.artifacts.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.artifacts, children: renderSlot(
|
|
2203
|
+
slots?.artifacts,
|
|
2204
|
+
artifactsSlotProps,
|
|
2205
|
+
turn.artifacts.map((artifact, index) => /* @__PURE__ */ jsx(DownloadCard, { artifact }, `${artifact.filename}-${index}`))
|
|
2206
|
+
) }),
|
|
2207
|
+
hasContent && /* @__PURE__ */ jsxs("div", { className: classes.bubble, children: [
|
|
2208
|
+
renderSlot(
|
|
2209
|
+
slots?.content,
|
|
2210
|
+
contentSlotProps,
|
|
2211
|
+
/* @__PURE__ */ jsx(
|
|
2212
|
+
Suspense,
|
|
2213
|
+
{
|
|
2214
|
+
fallback: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-400 dark:text-gray-500", children: [
|
|
2215
|
+
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-gray-300 dark:border-gray-600 border-t-transparent rounded-full animate-spin" }),
|
|
2216
|
+
"Loading..."
|
|
2217
|
+
] }),
|
|
2218
|
+
children: /* @__PURE__ */ jsx(
|
|
2219
|
+
MarkdownRenderer2,
|
|
2220
|
+
{
|
|
2221
|
+
content: turn.content,
|
|
2222
|
+
citations: turn.citations,
|
|
2223
|
+
sendMessage: onSendMessage,
|
|
2224
|
+
sendDisabled: sendDisabled || isStreaming
|
|
2225
|
+
}
|
|
2226
|
+
)
|
|
2227
|
+
}
|
|
2228
|
+
)
|
|
1231
2229
|
),
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
2230
|
+
isStreaming && /* @__PURE__ */ jsx(StreamingCursor_default, {}),
|
|
2231
|
+
turn.citations && turn.citations.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.sources, children: renderSlot(
|
|
2232
|
+
slots?.sources,
|
|
2233
|
+
sourcesSlotProps,
|
|
2234
|
+
/* @__PURE__ */ jsx(SourcesPanel, { citations: turn.citations })
|
|
2235
|
+
) }),
|
|
2236
|
+
hasExplanation && /* @__PURE__ */ jsx("div", { className: classes.explanation, children: renderSlot(
|
|
2237
|
+
slots?.explanation,
|
|
2238
|
+
explanationSlotProps,
|
|
2239
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2240
|
+
/* @__PURE__ */ jsxs(
|
|
2241
|
+
"button",
|
|
2242
|
+
{
|
|
2243
|
+
type: "button",
|
|
2244
|
+
onClick: () => setExplanationExpanded(!explanationExpanded),
|
|
2245
|
+
className: "flex items-center gap-2 text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 rounded-md p-1 -m-1",
|
|
2246
|
+
"aria-expanded": explanationExpanded,
|
|
2247
|
+
children: [
|
|
2248
|
+
/* @__PURE__ */ jsx(
|
|
2249
|
+
"svg",
|
|
2250
|
+
{
|
|
2251
|
+
className: `w-4 h-4 transition-transform duration-150 ${explanationExpanded ? "rotate-90" : ""}`,
|
|
2252
|
+
fill: "none",
|
|
2253
|
+
stroke: "currentColor",
|
|
2254
|
+
viewBox: "0 0 24 24",
|
|
2255
|
+
children: /* @__PURE__ */ jsx(
|
|
2256
|
+
"path",
|
|
2257
|
+
{
|
|
2258
|
+
strokeLinecap: "round",
|
|
2259
|
+
strokeLinejoin: "round",
|
|
2260
|
+
strokeWidth: 2,
|
|
2261
|
+
d: "M9 5l7 7-7 7"
|
|
2262
|
+
}
|
|
2263
|
+
)
|
|
2264
|
+
}
|
|
2265
|
+
),
|
|
2266
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "How I arrived at this" })
|
|
2267
|
+
]
|
|
2268
|
+
}
|
|
2269
|
+
),
|
|
2270
|
+
explanationExpanded && /* @__PURE__ */ jsx("div", { className: "pt-3 text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { children: "Loading..." }), children: /* @__PURE__ */ jsx(MarkdownRenderer2, { content: turn.explanation }) }) })
|
|
2271
|
+
] })
|
|
2272
|
+
) })
|
|
2273
|
+
] }),
|
|
2274
|
+
hasPendingQuestion && pendingQuestion && /* @__PURE__ */ jsx(InlineQuestionForm, { pendingQuestion }),
|
|
2275
|
+
hasContent && !hideActions && /* @__PURE__ */ jsx("div", { className: classes.actions, children: renderSlot(
|
|
2276
|
+
slots?.actions,
|
|
2277
|
+
actionsSlotProps,
|
|
2278
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2279
|
+
!hideTimestamp && /* @__PURE__ */ jsx("span", { className: classes.timestamp, children: timestamp }),
|
|
2280
|
+
/* @__PURE__ */ jsx(
|
|
1236
2281
|
"button",
|
|
1237
2282
|
{
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
"
|
|
1242
|
-
children:
|
|
1243
|
-
/* @__PURE__ */ jsx(
|
|
1244
|
-
"svg",
|
|
1245
|
-
{
|
|
1246
|
-
className: `w-4 h-4 transition-transform duration-150 ${explanationExpanded ? "rotate-90" : ""}`,
|
|
1247
|
-
fill: "none",
|
|
1248
|
-
stroke: "currentColor",
|
|
1249
|
-
viewBox: "0 0 24 24",
|
|
1250
|
-
children: /* @__PURE__ */ jsx(
|
|
1251
|
-
"path",
|
|
1252
|
-
{
|
|
1253
|
-
strokeLinecap: "round",
|
|
1254
|
-
strokeLinejoin: "round",
|
|
1255
|
-
strokeWidth: 2,
|
|
1256
|
-
d: "M9 5l7 7-7 7"
|
|
1257
|
-
}
|
|
1258
|
-
)
|
|
1259
|
-
}
|
|
1260
|
-
),
|
|
1261
|
-
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "How I arrived at this" })
|
|
1262
|
-
]
|
|
2283
|
+
onClick: handleCopyClick,
|
|
2284
|
+
className: classes.actionButton,
|
|
2285
|
+
"aria-label": "Copy message",
|
|
2286
|
+
title: "Copy",
|
|
2287
|
+
children: /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
1263
2288
|
}
|
|
1264
2289
|
),
|
|
1265
|
-
|
|
2290
|
+
onRegenerate && turnId && /* @__PURE__ */ jsx(
|
|
2291
|
+
"button",
|
|
2292
|
+
{
|
|
2293
|
+
onClick: handleRegenerateClick,
|
|
2294
|
+
className: classes.actionButton,
|
|
2295
|
+
"aria-label": "Regenerate response",
|
|
2296
|
+
title: "Regenerate",
|
|
2297
|
+
children: /* @__PURE__ */ jsx(ArrowsClockwise, { size: 14, weight: "regular" })
|
|
2298
|
+
}
|
|
2299
|
+
)
|
|
1266
2300
|
] })
|
|
1267
|
-
|
|
1268
|
-
hasPendingQuestion && /* @__PURE__ */ jsx(InlineQuestionForm, { pendingQuestion }),
|
|
1269
|
-
hasContent && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-150", children: [
|
|
1270
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500 mr-1", children: formatDistanceToNow(new Date(message.createdAt), { addSuffix: true }) }),
|
|
1271
|
-
/* @__PURE__ */ jsx(
|
|
1272
|
-
"button",
|
|
1273
|
-
{
|
|
1274
|
-
onClick: handleCopyClick,
|
|
1275
|
-
className: "p-1.5 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors duration-150",
|
|
1276
|
-
"aria-label": "Copy message",
|
|
1277
|
-
title: "Copy",
|
|
1278
|
-
children: /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
1279
|
-
}
|
|
1280
|
-
),
|
|
1281
|
-
handleRegenerate && /* @__PURE__ */ jsx(
|
|
1282
|
-
"button",
|
|
1283
|
-
{
|
|
1284
|
-
onClick: handleRegenerateClick,
|
|
1285
|
-
className: "p-1.5 text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-md transition-colors duration-150",
|
|
1286
|
-
"aria-label": "Regenerate message",
|
|
1287
|
-
title: "Regenerate",
|
|
1288
|
-
children: /* @__PURE__ */ jsx(ArrowsClockwise, { size: 14, weight: "regular" })
|
|
1289
|
-
}
|
|
1290
|
-
)
|
|
1291
|
-
] })
|
|
2301
|
+
) })
|
|
1292
2302
|
] })
|
|
1293
2303
|
] });
|
|
1294
2304
|
}
|
|
1295
|
-
function
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
2305
|
+
function AssistantTurnView({
|
|
2306
|
+
turn,
|
|
2307
|
+
isStreaming = false,
|
|
2308
|
+
slots,
|
|
2309
|
+
classNames,
|
|
2310
|
+
hideAvatar,
|
|
2311
|
+
hideActions,
|
|
2312
|
+
hideTimestamp
|
|
1299
2313
|
}) {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
2314
|
+
const { handleCopy, handleRegenerate, pendingQuestion, sendMessage, loading } = useChat();
|
|
2315
|
+
const assistantTurn = turn.assistantTurn;
|
|
2316
|
+
if (!assistantTurn) return null;
|
|
2317
|
+
return /* @__PURE__ */ jsx(
|
|
2318
|
+
AssistantMessage,
|
|
2319
|
+
{
|
|
2320
|
+
turn: assistantTurn,
|
|
2321
|
+
turnId: turn.id,
|
|
2322
|
+
isStreaming,
|
|
2323
|
+
pendingQuestion,
|
|
2324
|
+
slots,
|
|
2325
|
+
classNames,
|
|
2326
|
+
onCopy: handleCopy,
|
|
2327
|
+
onRegenerate: handleRegenerate,
|
|
2328
|
+
onSendMessage: sendMessage,
|
|
2329
|
+
sendDisabled: loading || isStreaming,
|
|
2330
|
+
hideAvatar,
|
|
2331
|
+
hideActions,
|
|
2332
|
+
hideTimestamp
|
|
1309
2333
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
var defaultClassNames3 = {
|
|
2337
|
+
root: "space-y-4",
|
|
2338
|
+
userTurn: "",
|
|
2339
|
+
assistantTurn: ""
|
|
2340
|
+
};
|
|
2341
|
+
function TurnBubble({
|
|
2342
|
+
turn,
|
|
2343
|
+
renderUserTurn,
|
|
2344
|
+
renderAssistantTurn,
|
|
2345
|
+
userTurnProps,
|
|
2346
|
+
assistantTurnProps,
|
|
2347
|
+
userMessageSlots,
|
|
2348
|
+
assistantMessageSlots,
|
|
2349
|
+
userMessageClassNames,
|
|
2350
|
+
assistantMessageClassNames,
|
|
2351
|
+
classNames,
|
|
2352
|
+
isStreaming = false
|
|
2353
|
+
}) {
|
|
2354
|
+
const classes = {
|
|
2355
|
+
root: classNames?.root ?? defaultClassNames3.root,
|
|
2356
|
+
userTurn: classNames?.userTurn ?? defaultClassNames3.userTurn,
|
|
2357
|
+
assistantTurn: classNames?.assistantTurn ?? defaultClassNames3.assistantTurn
|
|
2358
|
+
};
|
|
2359
|
+
return /* @__PURE__ */ jsxs("div", { className: classes.root, "data-turn-id": turn.id, children: [
|
|
2360
|
+
/* @__PURE__ */ jsx("div", { className: classes.userTurn, children: renderUserTurn ? renderUserTurn(turn) : /* @__PURE__ */ jsx(
|
|
2361
|
+
UserTurnView,
|
|
2362
|
+
{
|
|
2363
|
+
turn,
|
|
2364
|
+
slots: userMessageSlots,
|
|
2365
|
+
classNames: userMessageClassNames,
|
|
2366
|
+
...userTurnProps
|
|
2367
|
+
}
|
|
2368
|
+
) }),
|
|
2369
|
+
turn.assistantTurn && /* @__PURE__ */ jsx("div", { className: classes.assistantTurn, children: renderAssistantTurn ? renderAssistantTurn(turn) : /* @__PURE__ */ jsx(
|
|
2370
|
+
AssistantTurnView,
|
|
2371
|
+
{
|
|
2372
|
+
turn,
|
|
2373
|
+
isStreaming,
|
|
2374
|
+
slots: assistantMessageSlots,
|
|
2375
|
+
classNames: assistantMessageClassNames,
|
|
2376
|
+
...assistantTurnProps
|
|
2377
|
+
}
|
|
2378
|
+
) })
|
|
2379
|
+
] });
|
|
1313
2380
|
}
|
|
1314
2381
|
function ScrollToBottomButton({
|
|
1315
2382
|
show,
|
|
@@ -1324,7 +2391,7 @@ function ScrollToBottomButton({
|
|
|
1324
2391
|
exit: { opacity: 0, y: 10 },
|
|
1325
2392
|
transition: { duration: 0.2 },
|
|
1326
2393
|
onClick,
|
|
1327
|
-
className: "absolute bottom-24 right-8 p-3 bg-white dark:bg-gray-800 rounded-full shadow-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors z-10",
|
|
2394
|
+
className: "absolute bottom-24 right-8 p-3 bg-white dark:bg-gray-800 rounded-full shadow-lg border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 active:bg-gray-100 dark:active:bg-gray-600 transition-colors duration-150 z-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900",
|
|
1328
2395
|
"aria-label": "Scroll to bottom",
|
|
1329
2396
|
children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1330
2397
|
/* @__PURE__ */ jsx(ArrowDown, { size: 20, weight: "bold", className: "text-gray-700 dark:text-gray-300" }),
|
|
@@ -1333,14 +2400,15 @@ function ScrollToBottomButton({
|
|
|
1333
2400
|
}
|
|
1334
2401
|
) });
|
|
1335
2402
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
2403
|
+
var ScrollToBottomButton_default = ScrollToBottomButton;
|
|
2404
|
+
function MessageList({ renderUserTurn, renderAssistantTurn }) {
|
|
2405
|
+
const { turns, streamingContent, isStreaming } = useChat();
|
|
1338
2406
|
const messagesEndRef = useRef(null);
|
|
1339
2407
|
const containerRef = useRef(null);
|
|
1340
2408
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
1341
2409
|
useEffect(() => {
|
|
1342
2410
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
1343
|
-
}, [
|
|
2411
|
+
}, [turns.length, streamingContent]);
|
|
1344
2412
|
useEffect(() => {
|
|
1345
2413
|
const container = containerRef.current;
|
|
1346
2414
|
if (!container) return;
|
|
@@ -1357,14 +2425,14 @@ function MessageList({ renderUserMessage, renderAssistantMessage }) {
|
|
|
1357
2425
|
};
|
|
1358
2426
|
return /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0", children: [
|
|
1359
2427
|
/* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full overflow-y-auto px-4 py-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-4xl mx-auto space-y-6", children: [
|
|
1360
|
-
|
|
2428
|
+
turns.map((turn) => /* @__PURE__ */ jsx(
|
|
1361
2429
|
TurnBubble,
|
|
1362
2430
|
{
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
2431
|
+
turn,
|
|
2432
|
+
renderUserTurn,
|
|
2433
|
+
renderAssistantTurn
|
|
1366
2434
|
},
|
|
1367
|
-
|
|
2435
|
+
turn.id
|
|
1368
2436
|
)),
|
|
1369
2437
|
isStreaming && streamingContent && /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
1370
2438
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0 w-8 h-8 rounded-full bg-primary-600 flex items-center justify-center text-white", children: "AI" }),
|
|
@@ -1375,7 +2443,7 @@ function MessageList({ renderUserMessage, renderAssistantMessage }) {
|
|
|
1375
2443
|
] }),
|
|
1376
2444
|
/* @__PURE__ */ jsx("div", { ref: messagesEndRef })
|
|
1377
2445
|
] }) }),
|
|
1378
|
-
/* @__PURE__ */ jsx(
|
|
2446
|
+
/* @__PURE__ */ jsx(ScrollToBottomButton_default, { show: showScrollButton, onClick: scrollToBottom })
|
|
1379
2447
|
] });
|
|
1380
2448
|
}
|
|
1381
2449
|
var MAX_FILES_DEFAULT = 10;
|
|
@@ -1524,7 +2592,7 @@ var MessageInput = forwardRef(
|
|
|
1524
2592
|
)
|
|
1525
2593
|
] }),
|
|
1526
2594
|
messageQueue.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-3 text-xs text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx("span", { className: "px-2.5 py-1 bg-primary-50 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 rounded font-medium", children: t("input.messagesQueued", { count: messageQueue.length }) }) }),
|
|
1527
|
-
attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(
|
|
2595
|
+
attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx(AttachmentGrid_default, { attachments, onRemove: handleRemoveAttachment }) }),
|
|
1528
2596
|
/* @__PURE__ */ jsxs(
|
|
1529
2597
|
"div",
|
|
1530
2598
|
{
|
|
@@ -1540,7 +2608,7 @@ var MessageInput = forwardRef(
|
|
|
1540
2608
|
/* @__PURE__ */ jsxs(
|
|
1541
2609
|
"div",
|
|
1542
2610
|
{
|
|
1543
|
-
className: `flex items-center gap-2 rounded-2xl p-2.5 bg-white dark:bg-gray-800 border shadow-sm transition-all duration-150 ${isFocused ? "border-primary-400 dark:border-primary-500 ring-
|
|
2611
|
+
className: `flex items-center gap-2 rounded-2xl p-2.5 bg-white dark:bg-gray-800 border shadow-sm transition-all duration-150 ${isFocused ? "border-primary-400 dark:border-primary-500 ring-2 ring-primary-500/15 dark:ring-primary-500/20" : "border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600"}`,
|
|
1544
2612
|
children: [
|
|
1545
2613
|
/* @__PURE__ */ jsx(
|
|
1546
2614
|
"button",
|
|
@@ -1657,9 +2725,9 @@ function WelcomeContent({
|
|
|
1657
2725
|
description = "Your intelligent business analytics assistant. Ask questions about your data, generate reports, or explore insights.",
|
|
1658
2726
|
disabled = false
|
|
1659
2727
|
}) {
|
|
1660
|
-
const handlePromptClick = (
|
|
2728
|
+
const handlePromptClick = (prompt) => {
|
|
1661
2729
|
if (onPromptSelect && !disabled) {
|
|
1662
|
-
onPromptSelect(
|
|
2730
|
+
onPromptSelect(prompt);
|
|
1663
2731
|
}
|
|
1664
2732
|
};
|
|
1665
2733
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1688,26 +2756,26 @@ function WelcomeContent({
|
|
|
1688
2756
|
),
|
|
1689
2757
|
/* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, children: [
|
|
1690
2758
|
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-gray-400 dark:text-gray-500 uppercase tracking-wide mb-4", children: "Try asking" }),
|
|
1691
|
-
/* @__PURE__ */ jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: EXAMPLE_PROMPTS.map((
|
|
2759
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: EXAMPLE_PROMPTS.map((prompt, index) => /* @__PURE__ */ jsxs(
|
|
1692
2760
|
motion.button,
|
|
1693
2761
|
{
|
|
1694
|
-
onClick: () => handlePromptClick(
|
|
2762
|
+
onClick: () => handlePromptClick(prompt.text),
|
|
1695
2763
|
disabled,
|
|
1696
2764
|
className: "group flex flex-col items-start text-left p-5 rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-sm hover:shadow-md hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-150 focus:outline-none focus:ring-2 focus:ring-primary-500/50 focus:ring-offset-2 dark:focus:ring-offset-gray-900 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1697
2765
|
variants: itemVariants,
|
|
1698
|
-
whileHover: disabled ? {} : { y: -2 },
|
|
2766
|
+
whileHover: disabled ? {} : { y: -2, scale: 1.01 },
|
|
1699
2767
|
whileTap: disabled ? {} : { scale: 0.98 },
|
|
1700
2768
|
children: [
|
|
1701
|
-
/* @__PURE__ */ jsx("div", { className: "w-9 h-9 rounded-lg bg-gray-100 dark:bg-gray-700 flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx(
|
|
1702
|
-
|
|
2769
|
+
/* @__PURE__ */ jsx("div", { className: "w-9 h-9 rounded-lg bg-gray-100 dark:bg-gray-700 group-hover:bg-primary-50 dark:group-hover:bg-primary-900/30 flex items-center justify-center mb-3 transition-colors duration-200", children: /* @__PURE__ */ jsx(
|
|
2770
|
+
prompt.icon,
|
|
1703
2771
|
{
|
|
1704
2772
|
size: 18,
|
|
1705
2773
|
weight: "regular",
|
|
1706
|
-
className: "text-gray-600 dark:text-gray-300"
|
|
2774
|
+
className: "text-gray-600 dark:text-gray-300 group-hover:text-primary-600 dark:group-hover:text-primary-400 transition-colors duration-200"
|
|
1707
2775
|
}
|
|
1708
2776
|
) }),
|
|
1709
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-400 dark:text-gray-500 mb-1.5", children:
|
|
1710
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-700 dark:text-gray-200 leading-relaxed", children:
|
|
2777
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-400 dark:text-gray-500 mb-1.5", children: prompt.category }),
|
|
2778
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-700 dark:text-gray-200 leading-relaxed", children: prompt.text })
|
|
1711
2779
|
]
|
|
1712
2780
|
},
|
|
1713
2781
|
index
|
|
@@ -1717,10 +2785,11 @@ function WelcomeContent({
|
|
|
1717
2785
|
}
|
|
1718
2786
|
);
|
|
1719
2787
|
}
|
|
2788
|
+
var WelcomeContent_default = WelcomeContent;
|
|
1720
2789
|
function ChatSessionCore({
|
|
1721
2790
|
isReadOnly,
|
|
1722
|
-
|
|
1723
|
-
|
|
2791
|
+
renderUserTurn,
|
|
2792
|
+
renderAssistantTurn,
|
|
1724
2793
|
className = "",
|
|
1725
2794
|
headerSlot,
|
|
1726
2795
|
welcomeSlot,
|
|
@@ -1731,7 +2800,7 @@ function ChatSessionCore({
|
|
|
1731
2800
|
const { t } = useTranslation();
|
|
1732
2801
|
const {
|
|
1733
2802
|
session,
|
|
1734
|
-
|
|
2803
|
+
turns,
|
|
1735
2804
|
fetching,
|
|
1736
2805
|
error,
|
|
1737
2806
|
message,
|
|
@@ -1751,9 +2820,9 @@ function ChatSessionCore({
|
|
|
1751
2820
|
error
|
|
1752
2821
|
] }) });
|
|
1753
2822
|
}
|
|
1754
|
-
const showWelcome = !session &&
|
|
1755
|
-
const handlePromptSelect = (
|
|
1756
|
-
setMessage(
|
|
2823
|
+
const showWelcome = !session && turns.length === 0;
|
|
2824
|
+
const handlePromptSelect = (prompt) => {
|
|
2825
|
+
setMessage(prompt);
|
|
1757
2826
|
};
|
|
1758
2827
|
return /* @__PURE__ */ jsxs(
|
|
1759
2828
|
"main",
|
|
@@ -1761,11 +2830,11 @@ function ChatSessionCore({
|
|
|
1761
2830
|
className: `flex-1 flex flex-col overflow-hidden min-h-0 bg-gray-50 dark:bg-gray-900 ${className}`,
|
|
1762
2831
|
children: [
|
|
1763
2832
|
headerSlot || /* @__PURE__ */ jsx(ChatHeader, { session, onBack, logoSlot, actionsSlot }),
|
|
1764
|
-
showWelcome ? /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center overflow-auto", children: welcomeSlot || /* @__PURE__ */ jsx(
|
|
2833
|
+
showWelcome ? /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center overflow-auto", children: welcomeSlot || /* @__PURE__ */ jsx(WelcomeContent_default, { onPromptSelect: handlePromptSelect, disabled: loading }) }) : /* @__PURE__ */ jsx(
|
|
1765
2834
|
MessageList,
|
|
1766
2835
|
{
|
|
1767
|
-
|
|
1768
|
-
|
|
2836
|
+
renderUserTurn,
|
|
2837
|
+
renderAssistantTurn
|
|
1769
2838
|
}
|
|
1770
2839
|
),
|
|
1771
2840
|
!isReadOnly && /* @__PURE__ */ jsx(
|
|
@@ -2059,7 +3128,8 @@ function EmptyState({
|
|
|
2059
3128
|
}
|
|
2060
3129
|
);
|
|
2061
3130
|
}
|
|
2062
|
-
var
|
|
3131
|
+
var MemoizedEmptyState = memo(EmptyState);
|
|
3132
|
+
MemoizedEmptyState.displayName = "EmptyState";
|
|
2063
3133
|
var sizeClasses2 = {
|
|
2064
3134
|
sm: "text-sm",
|
|
2065
3135
|
md: "text-base",
|
|
@@ -2181,7 +3251,7 @@ var EditableText = forwardRef(
|
|
|
2181
3251
|
}
|
|
2182
3252
|
);
|
|
2183
3253
|
EditableText.displayName = "EditableText";
|
|
2184
|
-
var
|
|
3254
|
+
var MemoizedEditableText = memo(EditableText);
|
|
2185
3255
|
var sizeClasses3 = {
|
|
2186
3256
|
sm: {
|
|
2187
3257
|
container: "py-1.5 pl-8 pr-8 text-xs",
|
|
@@ -2272,7 +3342,8 @@ function SearchInput({
|
|
|
2272
3342
|
)
|
|
2273
3343
|
] });
|
|
2274
3344
|
}
|
|
2275
|
-
var
|
|
3345
|
+
var MemoizedSearchInput = memo(SearchInput);
|
|
3346
|
+
MemoizedSearchInput.displayName = "SearchInput";
|
|
2276
3347
|
var variantClasses = {
|
|
2277
3348
|
text: "rounded",
|
|
2278
3349
|
circular: "rounded-full",
|
|
@@ -2366,60 +3437,698 @@ function ListItemSkeleton({ className = "" }) {
|
|
|
2366
3437
|
/* @__PURE__ */ jsx(Skeleton, { variant: "text", height: 16, className: "flex-1" })
|
|
2367
3438
|
] });
|
|
2368
3439
|
}
|
|
2369
|
-
var
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
3440
|
+
var MemoizedSkeleton = memo(Skeleton);
|
|
3441
|
+
MemoizedSkeleton.displayName = "Skeleton";
|
|
3442
|
+
|
|
3443
|
+
// ui/src/bichat/index.ts
|
|
3444
|
+
init_CodeBlock();
|
|
3445
|
+
init_TableExportButton();
|
|
3446
|
+
init_TableWithExport();
|
|
3447
|
+
var typeConfig = {
|
|
3448
|
+
success: {
|
|
3449
|
+
bgColor: "bg-green-500",
|
|
3450
|
+
darkBgColor: "dark:bg-green-600",
|
|
3451
|
+
icon: /* @__PURE__ */ jsx(CheckCircle, { size: 20, className: "w-5 h-5 flex-shrink-0", weight: "fill" })
|
|
3452
|
+
},
|
|
3453
|
+
error: {
|
|
3454
|
+
bgColor: "bg-red-500",
|
|
3455
|
+
darkBgColor: "dark:bg-red-600",
|
|
3456
|
+
icon: /* @__PURE__ */ jsx(XCircle, { size: 20, className: "w-5 h-5 flex-shrink-0", weight: "fill" })
|
|
3457
|
+
},
|
|
3458
|
+
info: {
|
|
3459
|
+
bgColor: "bg-blue-500",
|
|
3460
|
+
darkBgColor: "dark:bg-blue-600",
|
|
3461
|
+
icon: /* @__PURE__ */ jsx(Info, { size: 20, className: "w-5 h-5 flex-shrink-0", weight: "fill" })
|
|
3462
|
+
},
|
|
3463
|
+
warning: {
|
|
3464
|
+
bgColor: "bg-yellow-500",
|
|
3465
|
+
darkBgColor: "dark:bg-yellow-600",
|
|
3466
|
+
icon: /* @__PURE__ */ jsx(Warning, { size: 20, className: "w-5 h-5 flex-shrink-0", weight: "fill" })
|
|
3467
|
+
}
|
|
3468
|
+
};
|
|
3469
|
+
function Toast({
|
|
3470
|
+
id,
|
|
3471
|
+
type,
|
|
3472
|
+
message,
|
|
3473
|
+
duration = 5e3,
|
|
3474
|
+
onDismiss,
|
|
3475
|
+
dismissLabel = "Dismiss"
|
|
3476
|
+
}) {
|
|
3477
|
+
const config = typeConfig[type];
|
|
3478
|
+
const ariaLive = type === "error" ? "assertive" : "polite";
|
|
3479
|
+
const role = type === "error" || type === "warning" ? "alert" : "status";
|
|
3480
|
+
useEffect(() => {
|
|
3481
|
+
const timer = setTimeout(() => onDismiss(id), duration);
|
|
3482
|
+
return () => clearTimeout(timer);
|
|
3483
|
+
}, [id, duration, onDismiss]);
|
|
3484
|
+
return /* @__PURE__ */ jsxs(
|
|
3485
|
+
motion.div,
|
|
3486
|
+
{
|
|
3487
|
+
initial: { opacity: 0, y: -50, scale: 0.95 },
|
|
3488
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
3489
|
+
exit: { opacity: 0, scale: 0.95 },
|
|
3490
|
+
transition: { duration: 0.2 },
|
|
3491
|
+
className: `flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg backdrop-blur-sm min-w-[300px] max-w-[400px] text-white ${config.bgColor} ${config.darkBgColor}`,
|
|
3492
|
+
role,
|
|
3493
|
+
"aria-live": ariaLive,
|
|
3494
|
+
"aria-atomic": "true",
|
|
3495
|
+
children: [
|
|
3496
|
+
config.icon,
|
|
3497
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 text-sm font-medium", children: message }),
|
|
3498
|
+
/* @__PURE__ */ jsx(
|
|
3499
|
+
"button",
|
|
3500
|
+
{
|
|
3501
|
+
onClick: () => onDismiss(id),
|
|
3502
|
+
className: "ml-2 text-white hover:bg-white/20 p-1 rounded transition-colors flex-shrink-0",
|
|
3503
|
+
"aria-label": dismissLabel,
|
|
3504
|
+
children: /* @__PURE__ */ jsx(X, { size: 16, className: "w-4 h-4", weight: "bold" })
|
|
3505
|
+
}
|
|
3506
|
+
)
|
|
3507
|
+
]
|
|
2401
3508
|
}
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3511
|
+
function ToastContainer({ toasts, onDismiss, dismissLabel }) {
|
|
3512
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed top-4 right-4 z-50 flex flex-col gap-2 pointer-events-none", children: /* @__PURE__ */ jsx(AnimatePresence, { children: toasts.map((toast) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(Toast, { ...toast, onDismiss, dismissLabel }) }, toast.id)) }) });
|
|
3513
|
+
}
|
|
3514
|
+
function ConfirmModalBase({
|
|
3515
|
+
isOpen,
|
|
3516
|
+
title,
|
|
3517
|
+
message,
|
|
3518
|
+
onConfirm,
|
|
3519
|
+
onCancel,
|
|
3520
|
+
confirmText = "Confirm",
|
|
3521
|
+
cancelText = "Cancel",
|
|
3522
|
+
isDanger = false
|
|
3523
|
+
}) {
|
|
3524
|
+
const modalRef = useRef(null);
|
|
3525
|
+
const confirmButtonRef = useRef(null);
|
|
3526
|
+
useFocusTrap(modalRef, isOpen);
|
|
3527
|
+
useEffect(() => {
|
|
3528
|
+
if (!isOpen) return;
|
|
3529
|
+
const handleKeyDown = (e) => {
|
|
3530
|
+
if (e.key === "Escape") {
|
|
3531
|
+
onCancel();
|
|
3532
|
+
} else if (e.key === "Enter") {
|
|
3533
|
+
if (document.activeElement === confirmButtonRef.current) {
|
|
3534
|
+
e.preventDefault();
|
|
3535
|
+
onConfirm();
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
3540
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
3541
|
+
}, [isOpen, onConfirm, onCancel, confirmButtonRef]);
|
|
3542
|
+
if (!isOpen) return null;
|
|
3543
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3544
|
+
/* @__PURE__ */ jsx(
|
|
3545
|
+
"div",
|
|
3546
|
+
{
|
|
3547
|
+
className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity z-40",
|
|
3548
|
+
onClick: onCancel,
|
|
3549
|
+
"aria-hidden": "true"
|
|
3550
|
+
}
|
|
3551
|
+
),
|
|
3552
|
+
/* @__PURE__ */ jsx(
|
|
3553
|
+
"div",
|
|
3554
|
+
{
|
|
3555
|
+
className: "fixed inset-0 flex items-center justify-center z-50",
|
|
3556
|
+
role: "alertdialog",
|
|
3557
|
+
"aria-modal": "true",
|
|
3558
|
+
"aria-labelledby": "confirm-modal-title",
|
|
3559
|
+
"aria-describedby": "confirm-modal-description",
|
|
3560
|
+
children: /* @__PURE__ */ jsxs(
|
|
3561
|
+
"div",
|
|
3562
|
+
{
|
|
3563
|
+
ref: modalRef,
|
|
3564
|
+
className: "bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-sm w-full mx-4",
|
|
3565
|
+
children: [
|
|
3566
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4 border-b border-gray-200 dark:border-gray-700", children: /* @__PURE__ */ jsx(
|
|
3567
|
+
"h2",
|
|
3568
|
+
{
|
|
3569
|
+
id: "confirm-modal-title",
|
|
3570
|
+
className: "text-lg font-semibold text-gray-900 dark:text-white",
|
|
3571
|
+
children: title
|
|
3572
|
+
}
|
|
3573
|
+
) }),
|
|
3574
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx("p", { id: "confirm-modal-description", className: "text-gray-600 dark:text-gray-300", children: message }) }),
|
|
3575
|
+
/* @__PURE__ */ jsxs("div", { className: "px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end gap-3", children: [
|
|
3576
|
+
/* @__PURE__ */ jsx(
|
|
3577
|
+
"button",
|
|
3578
|
+
{
|
|
3579
|
+
onClick: onCancel,
|
|
3580
|
+
className: "px-4 py-2 rounded-lg bg-gray-100 dark:bg-gray-700 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors font-medium",
|
|
3581
|
+
"aria-label": `Cancel ${title.toLowerCase()}`,
|
|
3582
|
+
"data-testid": "confirm-modal-cancel",
|
|
3583
|
+
children: cancelText
|
|
3584
|
+
}
|
|
3585
|
+
),
|
|
3586
|
+
/* @__PURE__ */ jsx(
|
|
3587
|
+
"button",
|
|
3588
|
+
{
|
|
3589
|
+
ref: confirmButtonRef,
|
|
3590
|
+
onClick: onConfirm,
|
|
3591
|
+
className: `px-4 py-2 rounded-lg text-white font-medium transition-colors ${isDanger ? "bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-800" : "bg-primary-600 dark:bg-primary-700 hover:bg-primary-700 dark:hover:bg-primary-800"}`,
|
|
3592
|
+
"aria-label": `Confirm ${title.toLowerCase()}`,
|
|
3593
|
+
"data-testid": "confirm-modal-confirm",
|
|
3594
|
+
children: confirmText
|
|
3595
|
+
}
|
|
3596
|
+
)
|
|
3597
|
+
] })
|
|
3598
|
+
]
|
|
3599
|
+
}
|
|
3600
|
+
)
|
|
3601
|
+
}
|
|
3602
|
+
)
|
|
3603
|
+
] });
|
|
3604
|
+
}
|
|
3605
|
+
var ConfirmModal = memo(ConfirmModalBase);
|
|
3606
|
+
ConfirmModal.displayName = "ConfirmModal";
|
|
3607
|
+
function hashString(str) {
|
|
3608
|
+
let hash = 0;
|
|
3609
|
+
for (let i = 0; i < str.length; i++) {
|
|
3610
|
+
const char = str.charCodeAt(i);
|
|
3611
|
+
hash = (hash << 5) - hash + char;
|
|
3612
|
+
hash = hash & hash;
|
|
2402
3613
|
}
|
|
2403
|
-
return
|
|
3614
|
+
return Math.abs(hash);
|
|
2404
3615
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
3616
|
+
var colorPalette = [
|
|
3617
|
+
{ bg: "bg-blue-500", text: "text-white" },
|
|
3618
|
+
{ bg: "bg-green-500", text: "text-white" },
|
|
3619
|
+
{ bg: "bg-purple-500", text: "text-white" },
|
|
3620
|
+
{ bg: "bg-pink-500", text: "text-white" },
|
|
3621
|
+
{ bg: "bg-indigo-500", text: "text-white" },
|
|
3622
|
+
{ bg: "bg-teal-500", text: "text-white" },
|
|
3623
|
+
{ bg: "bg-orange-500", text: "text-white" },
|
|
3624
|
+
{ bg: "bg-cyan-500", text: "text-white" },
|
|
3625
|
+
{ bg: "bg-amber-500", text: "text-white" },
|
|
3626
|
+
{ bg: "bg-lime-500", text: "text-white" }
|
|
3627
|
+
];
|
|
3628
|
+
var sizeClasses4 = {
|
|
3629
|
+
sm: "w-8 h-8 text-xs",
|
|
3630
|
+
md: "w-10 h-10 text-sm",
|
|
3631
|
+
lg: "w-12 h-12 text-base"
|
|
3632
|
+
};
|
|
3633
|
+
function UserAvatar({
|
|
3634
|
+
firstName,
|
|
3635
|
+
lastName,
|
|
3636
|
+
initials: providedInitials,
|
|
3637
|
+
size = "md",
|
|
3638
|
+
className = ""
|
|
3639
|
+
}) {
|
|
3640
|
+
const derivedInitials = (() => {
|
|
3641
|
+
const firstChar = firstName?.trim()?.charAt(0) || "";
|
|
3642
|
+
const lastChar = lastName?.trim()?.charAt(0) || "";
|
|
3643
|
+
const combined = `${firstChar}${lastChar}`.trim();
|
|
3644
|
+
return combined || "U";
|
|
3645
|
+
})();
|
|
3646
|
+
const initials = (providedInitials?.trim() || derivedInitials).toUpperCase();
|
|
3647
|
+
const fullName = `${firstName}${lastName}`;
|
|
3648
|
+
const colorIndex = hashString(fullName) % colorPalette.length;
|
|
3649
|
+
const colors = colorPalette[colorIndex];
|
|
3650
|
+
return /* @__PURE__ */ jsx(
|
|
3651
|
+
"div",
|
|
3652
|
+
{
|
|
3653
|
+
className: `
|
|
3654
|
+
${sizeClasses4[size]}
|
|
3655
|
+
${colors.bg}
|
|
3656
|
+
${colors.text}
|
|
3657
|
+
${className}
|
|
3658
|
+
rounded-full
|
|
3659
|
+
flex
|
|
3660
|
+
items-center
|
|
3661
|
+
justify-center
|
|
3662
|
+
font-semibold
|
|
3663
|
+
flex-shrink-0
|
|
3664
|
+
select-none
|
|
3665
|
+
`,
|
|
3666
|
+
"aria-label": `${firstName} ${lastName}`,
|
|
3667
|
+
title: `${firstName} ${lastName}`,
|
|
3668
|
+
children: initials
|
|
3669
|
+
}
|
|
3670
|
+
);
|
|
2407
3671
|
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
3672
|
+
var MemoizedUserAvatar = memo(UserAvatar);
|
|
3673
|
+
MemoizedUserAvatar.displayName = "UserAvatar";
|
|
3674
|
+
function PermissionGuard({
|
|
3675
|
+
permissions,
|
|
3676
|
+
mode = "all",
|
|
3677
|
+
hasPermission: hasPermission3,
|
|
3678
|
+
fallback = null,
|
|
3679
|
+
children
|
|
3680
|
+
}) {
|
|
3681
|
+
if (permissions.length === 0) {
|
|
3682
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
3683
|
+
}
|
|
3684
|
+
const permitted = mode === "all" ? permissions.every((p) => hasPermission3(p)) : permissions.some((p) => hasPermission3(p));
|
|
3685
|
+
return permitted ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
3686
|
+
}
|
|
3687
|
+
function DefaultErrorContent({
|
|
3688
|
+
error,
|
|
3689
|
+
onReset,
|
|
3690
|
+
resetLabel = "Try Again",
|
|
3691
|
+
errorTitle = "Something went wrong"
|
|
3692
|
+
}) {
|
|
3693
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: [
|
|
3694
|
+
/* @__PURE__ */ jsx(WarningCircle, { size: 48, className: "text-red-500 mb-4", weight: "fill" }),
|
|
3695
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-2", children: errorTitle }),
|
|
3696
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-4 max-w-md", children: error?.message || "An unexpected error occurred. Please try again." }),
|
|
3697
|
+
onReset && /* @__PURE__ */ jsxs(
|
|
3698
|
+
"button",
|
|
3699
|
+
{
|
|
3700
|
+
type: "button",
|
|
3701
|
+
onClick: onReset,
|
|
3702
|
+
className: "flex items-center gap-2 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors",
|
|
3703
|
+
children: [
|
|
3704
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
3705
|
+
resetLabel
|
|
3706
|
+
]
|
|
3707
|
+
}
|
|
3708
|
+
)
|
|
3709
|
+
] });
|
|
3710
|
+
}
|
|
3711
|
+
var ErrorBoundary = class extends Component {
|
|
3712
|
+
constructor(props) {
|
|
3713
|
+
super(props);
|
|
3714
|
+
this.handleReset = () => {
|
|
3715
|
+
this.setState({ hasError: false, error: null });
|
|
3716
|
+
};
|
|
3717
|
+
this.state = { hasError: false, error: null };
|
|
3718
|
+
}
|
|
3719
|
+
static getDerivedStateFromError(error) {
|
|
3720
|
+
return { hasError: true, error };
|
|
3721
|
+
}
|
|
3722
|
+
componentDidCatch(error, errorInfo) {
|
|
3723
|
+
console.error("React Error Boundary caught an error:", error, errorInfo);
|
|
3724
|
+
this.props.onError?.(error, errorInfo);
|
|
3725
|
+
}
|
|
3726
|
+
render() {
|
|
3727
|
+
if (this.state.hasError) {
|
|
3728
|
+
if (this.props.fallback) {
|
|
3729
|
+
if (typeof this.props.fallback === "function") {
|
|
3730
|
+
return this.props.fallback(this.state.error, this.handleReset);
|
|
3731
|
+
}
|
|
3732
|
+
return this.props.fallback;
|
|
3733
|
+
}
|
|
3734
|
+
return /* @__PURE__ */ jsx(DefaultErrorContent, { error: this.state.error, onReset: this.handleReset });
|
|
3735
|
+
}
|
|
3736
|
+
return this.props.children;
|
|
3737
|
+
}
|
|
3738
|
+
};
|
|
3739
|
+
var DEFAULT_MESSAGES = [
|
|
3740
|
+
"Thinking...",
|
|
3741
|
+
"Processing...",
|
|
3742
|
+
"Analyzing...",
|
|
3743
|
+
"Computing...",
|
|
3744
|
+
"Working on it..."
|
|
3745
|
+
];
|
|
3746
|
+
var prefersReducedMotion2 = () => {
|
|
3747
|
+
if (typeof window === "undefined") return false;
|
|
3748
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
3749
|
+
};
|
|
3750
|
+
var getRandomMessage = (messages, current) => {
|
|
3751
|
+
const available = messages.filter((m) => m !== current);
|
|
3752
|
+
if (available.length === 0) {
|
|
3753
|
+
return current || messages[0] || "Thinking...";
|
|
3754
|
+
}
|
|
3755
|
+
return available[Math.floor(Math.random() * available.length)];
|
|
3756
|
+
};
|
|
3757
|
+
var sizeConfig = {
|
|
3758
|
+
sm: { dot: "w-1.5 h-1.5", gap: "gap-1", text: "text-sm" },
|
|
3759
|
+
md: { dot: "w-2 h-2", gap: "gap-1.5", text: "text-base" },
|
|
3760
|
+
lg: { dot: "w-2.5 h-2.5", gap: "gap-2", text: "text-lg" }
|
|
3761
|
+
};
|
|
3762
|
+
function DotsIndicator({ size = "md" }) {
|
|
3763
|
+
const config = sizeConfig[size];
|
|
3764
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex ${config.gap}`, role: "status", "aria-live": "polite", children: [
|
|
3765
|
+
[0, 1, 2].map((index) => /* @__PURE__ */ jsx(
|
|
3766
|
+
"div",
|
|
3767
|
+
{
|
|
3768
|
+
className: `${config.dot} bg-gray-400 dark:bg-gray-500 rounded-full animate-bounce`,
|
|
3769
|
+
style: { animationDelay: `${index * 0.15}s` }
|
|
3770
|
+
},
|
|
3771
|
+
index
|
|
3772
|
+
)),
|
|
3773
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." })
|
|
3774
|
+
] });
|
|
3775
|
+
}
|
|
3776
|
+
function PulseIndicator({ size = "md" }) {
|
|
3777
|
+
const config = sizeConfig[size];
|
|
3778
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center", role: "status", "aria-live": "polite", children: [
|
|
3779
|
+
/* @__PURE__ */ jsx("div", { className: `${config.dot} bg-primary-500 rounded-full animate-pulse` }),
|
|
3780
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Loading..." })
|
|
3781
|
+
] });
|
|
3782
|
+
}
|
|
3783
|
+
function TextIndicator({
|
|
3784
|
+
messages = DEFAULT_MESSAGES,
|
|
3785
|
+
rotationInterval = 3e3,
|
|
3786
|
+
size = "md"
|
|
3787
|
+
}) {
|
|
3788
|
+
const config = sizeConfig[size];
|
|
3789
|
+
const [message, setMessage] = useState(() => messages[Math.floor(Math.random() * messages.length)]);
|
|
3790
|
+
useEffect(() => {
|
|
3791
|
+
if (prefersReducedMotion2()) return;
|
|
3792
|
+
const interval = setInterval(() => {
|
|
3793
|
+
setMessage((prev) => getRandomMessage(messages, prev));
|
|
3794
|
+
}, rotationInterval);
|
|
3795
|
+
return () => clearInterval(interval);
|
|
3796
|
+
}, [messages, rotationInterval]);
|
|
3797
|
+
return /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "overflow-hidden h-6 relative", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
|
|
3798
|
+
motion.span,
|
|
3799
|
+
{
|
|
3800
|
+
initial: { opacity: 0, y: 10 },
|
|
3801
|
+
animate: { opacity: 1, y: 0 },
|
|
3802
|
+
exit: { opacity: 0, y: -10 },
|
|
3803
|
+
transition: { duration: 0.2 },
|
|
3804
|
+
className: `${config.text} text-gray-500 dark:text-gray-400 block`,
|
|
3805
|
+
children: message
|
|
3806
|
+
},
|
|
3807
|
+
message
|
|
3808
|
+
) }) });
|
|
3809
|
+
}
|
|
3810
|
+
function TypingIndicator({
|
|
3811
|
+
variant = "dots",
|
|
3812
|
+
messages = DEFAULT_MESSAGES,
|
|
3813
|
+
rotationInterval = 3e3,
|
|
3814
|
+
size = "md",
|
|
3815
|
+
className = ""
|
|
3816
|
+
}) {
|
|
3817
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex items-center ${className}`, children: [
|
|
3818
|
+
variant === "dots" && /* @__PURE__ */ jsx(DotsIndicator, { size }),
|
|
3819
|
+
variant === "pulse" && /* @__PURE__ */ jsx(PulseIndicator, { size }),
|
|
3820
|
+
variant === "text" && /* @__PURE__ */ jsx(TextIndicator, { messages, rotationInterval, size })
|
|
3821
|
+
] });
|
|
3822
|
+
}
|
|
3823
|
+
var MemoizedTypingIndicator = memo(TypingIndicator);
|
|
3824
|
+
MemoizedTypingIndicator.displayName = "TypingIndicator";
|
|
3825
|
+
function mergeProps(slotProps, childProps) {
|
|
3826
|
+
const overrideProps = { ...childProps };
|
|
3827
|
+
for (const propName in childProps) {
|
|
3828
|
+
const slotPropValue = slotProps[propName];
|
|
3829
|
+
const childPropValue = childProps[propName];
|
|
3830
|
+
const isHandler = /^on[A-Z]/.test(propName);
|
|
3831
|
+
if (isHandler) {
|
|
3832
|
+
if (slotPropValue && childPropValue) {
|
|
3833
|
+
overrideProps[propName] = (...args) => {
|
|
3834
|
+
childPropValue(...args);
|
|
3835
|
+
slotPropValue(...args);
|
|
3836
|
+
};
|
|
3837
|
+
} else if (slotPropValue) {
|
|
3838
|
+
overrideProps[propName] = slotPropValue;
|
|
3839
|
+
}
|
|
3840
|
+
} else if (propName === "style") {
|
|
3841
|
+
overrideProps[propName] = { ...slotPropValue, ...childPropValue };
|
|
3842
|
+
} else if (propName === "className") {
|
|
3843
|
+
overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
return { ...slotProps, ...overrideProps };
|
|
3847
|
+
}
|
|
3848
|
+
var Slot = forwardRef((props, forwardedRef) => {
|
|
3849
|
+
const { children, ...slotProps } = props;
|
|
3850
|
+
if (!isValidElement(children)) {
|
|
3851
|
+
return null;
|
|
3852
|
+
}
|
|
3853
|
+
const childrenRef = children.ref;
|
|
3854
|
+
return cloneElement(children, {
|
|
3855
|
+
...mergeProps(slotProps, children.props),
|
|
3856
|
+
ref: forwardedRef ? composeRefs(forwardedRef, childrenRef) : childrenRef
|
|
3857
|
+
});
|
|
3858
|
+
});
|
|
3859
|
+
Slot.displayName = "Slot";
|
|
3860
|
+
function composeRefs(...refs) {
|
|
3861
|
+
return (node) => {
|
|
3862
|
+
refs.forEach((ref) => {
|
|
3863
|
+
if (typeof ref === "function") {
|
|
3864
|
+
ref(node);
|
|
3865
|
+
} else if (ref != null) {
|
|
3866
|
+
ref.current = node;
|
|
3867
|
+
}
|
|
3868
|
+
});
|
|
3869
|
+
};
|
|
3870
|
+
}
|
|
3871
|
+
function getValidChildren(children) {
|
|
3872
|
+
return Children.toArray(children).filter(isValidElement);
|
|
3873
|
+
}
|
|
3874
|
+
var TurnContext = createContext(void 0);
|
|
3875
|
+
function useTurnContext() {
|
|
3876
|
+
const context = useContext(TurnContext);
|
|
3877
|
+
if (!context) {
|
|
3878
|
+
throw new Error("Turn components must be used within Turn.Root");
|
|
3879
|
+
}
|
|
3880
|
+
return context;
|
|
3881
|
+
}
|
|
3882
|
+
var TurnRoot = forwardRef((props, ref) => {
|
|
3883
|
+
const { asChild, turnId, children, ...domProps } = props;
|
|
3884
|
+
const Comp = asChild ? Slot : "div";
|
|
3885
|
+
return /* @__PURE__ */ jsx(TurnContext.Provider, { value: { turnId }, children: /* @__PURE__ */ jsx(Comp, { ref, "data-turn-id": turnId, ...domProps, children }) });
|
|
3886
|
+
});
|
|
3887
|
+
TurnRoot.displayName = "Turn.Root";
|
|
3888
|
+
var TurnUser = forwardRef((props, ref) => {
|
|
3889
|
+
const { asChild, children, ...domProps } = props;
|
|
3890
|
+
const Comp = asChild ? Slot : "div";
|
|
3891
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-turn-role": "user", ...domProps, children });
|
|
3892
|
+
});
|
|
3893
|
+
TurnUser.displayName = "Turn.User";
|
|
3894
|
+
var TurnAssistant = forwardRef((props, ref) => {
|
|
3895
|
+
const { asChild, children, ...domProps } = props;
|
|
3896
|
+
const Comp = asChild ? Slot : "div";
|
|
3897
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-turn-role": "assistant", ...domProps, children });
|
|
3898
|
+
});
|
|
3899
|
+
TurnAssistant.displayName = "Turn.Assistant";
|
|
3900
|
+
var defaultFormatter = (date) => date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
|
|
3901
|
+
var TurnTimestamp = forwardRef((props, ref) => {
|
|
3902
|
+
const { asChild, date, formatter = defaultFormatter, children, ...domProps } = props;
|
|
3903
|
+
const Comp = asChild ? Slot : "time";
|
|
3904
|
+
const dateObj = date ? typeof date === "string" ? new Date(date) : date : null;
|
|
3905
|
+
const formattedTime = dateObj ? formatter(dateObj) : "";
|
|
3906
|
+
const isoString = dateObj?.toISOString();
|
|
3907
|
+
return /* @__PURE__ */ jsx(Comp, { ref, dateTime: isoString, ...domProps, children: children ?? formattedTime });
|
|
3908
|
+
});
|
|
3909
|
+
TurnTimestamp.displayName = "Turn.Timestamp";
|
|
3910
|
+
var TurnActions = forwardRef((props, ref) => {
|
|
3911
|
+
const { asChild, children, ...domProps } = props;
|
|
3912
|
+
const Comp = asChild ? Slot : "div";
|
|
3913
|
+
return /* @__PURE__ */ jsx(Comp, { ref, role: "group", "aria-label": "Message actions", ...domProps, children });
|
|
3914
|
+
});
|
|
3915
|
+
TurnActions.displayName = "Turn.Actions";
|
|
3916
|
+
var Turn = {
|
|
3917
|
+
Root: TurnRoot,
|
|
3918
|
+
User: TurnUser,
|
|
3919
|
+
Assistant: TurnAssistant,
|
|
3920
|
+
Timestamp: TurnTimestamp,
|
|
3921
|
+
Actions: TurnActions
|
|
3922
|
+
};
|
|
3923
|
+
var AvatarContext = createContext(void 0);
|
|
3924
|
+
function useAvatarContext() {
|
|
3925
|
+
const context = useContext(AvatarContext);
|
|
3926
|
+
if (!context) {
|
|
3927
|
+
throw new Error("Avatar components must be used within Avatar.Root");
|
|
3928
|
+
}
|
|
3929
|
+
return context;
|
|
3930
|
+
}
|
|
3931
|
+
var AvatarRoot = forwardRef((props, ref) => {
|
|
3932
|
+
const { asChild, children, ...domProps } = props;
|
|
3933
|
+
const Comp = asChild ? Slot : "span";
|
|
3934
|
+
const [imageLoadingStatus, setImageLoadingStatus] = useState("idle");
|
|
3935
|
+
return /* @__PURE__ */ jsx(AvatarContext.Provider, { value: { imageLoadingStatus, setImageLoadingStatus }, children: /* @__PURE__ */ jsx(Comp, { ref, ...domProps, children }) });
|
|
3936
|
+
});
|
|
3937
|
+
AvatarRoot.displayName = "Avatar.Root";
|
|
3938
|
+
var AvatarImage = forwardRef((props, ref) => {
|
|
3939
|
+
const { asChild, src, alt, onLoadingStatusChange, onLoad, onError, ...domProps } = props;
|
|
3940
|
+
const { setImageLoadingStatus } = useAvatarContext();
|
|
3941
|
+
const Comp = asChild ? Slot : "img";
|
|
3942
|
+
const handleLoad = (e) => {
|
|
3943
|
+
setImageLoadingStatus("loaded");
|
|
3944
|
+
onLoadingStatusChange?.("loaded");
|
|
3945
|
+
onLoad?.(e);
|
|
3946
|
+
};
|
|
3947
|
+
const handleError = (e) => {
|
|
3948
|
+
setImageLoadingStatus("error");
|
|
3949
|
+
onLoadingStatusChange?.("error");
|
|
3950
|
+
onError?.(e);
|
|
3951
|
+
};
|
|
3952
|
+
if (src) {
|
|
3953
|
+
return /* @__PURE__ */ jsx(
|
|
3954
|
+
Comp,
|
|
3955
|
+
{
|
|
3956
|
+
ref,
|
|
3957
|
+
src,
|
|
3958
|
+
alt: alt || "",
|
|
3959
|
+
onLoad: handleLoad,
|
|
3960
|
+
onError: handleError,
|
|
3961
|
+
...domProps
|
|
3962
|
+
}
|
|
2413
3963
|
);
|
|
2414
3964
|
}
|
|
2415
|
-
return
|
|
3965
|
+
return null;
|
|
3966
|
+
});
|
|
3967
|
+
AvatarImage.displayName = "Avatar.Image";
|
|
3968
|
+
var AvatarFallback = forwardRef((props, ref) => {
|
|
3969
|
+
const { asChild, delayMs = 0, children, ...domProps } = props;
|
|
3970
|
+
const { imageLoadingStatus } = useAvatarContext();
|
|
3971
|
+
const Comp = asChild ? Slot : "span";
|
|
3972
|
+
const [canRender, setCanRender] = useState(delayMs === 0);
|
|
3973
|
+
if (delayMs > 0 && !canRender) {
|
|
3974
|
+
setTimeout(() => setCanRender(true), delayMs);
|
|
3975
|
+
}
|
|
3976
|
+
if (imageLoadingStatus === "loaded") {
|
|
3977
|
+
return null;
|
|
3978
|
+
}
|
|
3979
|
+
if (!canRender) {
|
|
3980
|
+
return null;
|
|
3981
|
+
}
|
|
3982
|
+
return /* @__PURE__ */ jsx(Comp, { ref, ...domProps, children });
|
|
3983
|
+
});
|
|
3984
|
+
AvatarFallback.displayName = "Avatar.Fallback";
|
|
3985
|
+
var Avatar = {
|
|
3986
|
+
Root: AvatarRoot,
|
|
3987
|
+
Image: AvatarImage,
|
|
3988
|
+
Fallback: AvatarFallback
|
|
3989
|
+
};
|
|
3990
|
+
var BubbleContext = createContext(void 0);
|
|
3991
|
+
function useBubbleContext() {
|
|
3992
|
+
const context = useContext(BubbleContext);
|
|
3993
|
+
if (!context) {
|
|
3994
|
+
throw new Error("Bubble components must be used within Bubble.Root");
|
|
3995
|
+
}
|
|
3996
|
+
return context;
|
|
2416
3997
|
}
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
3998
|
+
var BubbleRoot = forwardRef((props, ref) => {
|
|
3999
|
+
const { asChild, variant, children, ...domProps } = props;
|
|
4000
|
+
const Comp = asChild ? Slot : "div";
|
|
4001
|
+
return /* @__PURE__ */ jsx(BubbleContext.Provider, { value: { variant }, children: /* @__PURE__ */ jsx(Comp, { ref, "data-bubble-variant": variant, ...domProps, children }) });
|
|
4002
|
+
});
|
|
4003
|
+
BubbleRoot.displayName = "Bubble.Root";
|
|
4004
|
+
var BubbleContent = forwardRef((props, ref) => {
|
|
4005
|
+
const { asChild, children, ...domProps } = props;
|
|
4006
|
+
const Comp = asChild ? Slot : "div";
|
|
4007
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-bubble-part": "content", ...domProps, children });
|
|
4008
|
+
});
|
|
4009
|
+
BubbleContent.displayName = "Bubble.Content";
|
|
4010
|
+
var BubbleHeader = forwardRef((props, ref) => {
|
|
4011
|
+
const { asChild, children, ...domProps } = props;
|
|
4012
|
+
const Comp = asChild ? Slot : "div";
|
|
4013
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-bubble-part": "header", ...domProps, children });
|
|
4014
|
+
});
|
|
4015
|
+
BubbleHeader.displayName = "Bubble.Header";
|
|
4016
|
+
var BubbleFooter = forwardRef((props, ref) => {
|
|
4017
|
+
const { asChild, children, ...domProps } = props;
|
|
4018
|
+
const Comp = asChild ? Slot : "div";
|
|
4019
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-bubble-part": "footer", ...domProps, children });
|
|
4020
|
+
});
|
|
4021
|
+
BubbleFooter.displayName = "Bubble.Footer";
|
|
4022
|
+
var BubbleMetadata = forwardRef((props, ref) => {
|
|
4023
|
+
const { asChild, children, ...domProps } = props;
|
|
4024
|
+
const Comp = asChild ? Slot : "div";
|
|
4025
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "data-bubble-part": "metadata", ...domProps, children });
|
|
4026
|
+
});
|
|
4027
|
+
BubbleMetadata.displayName = "Bubble.Metadata";
|
|
4028
|
+
var Bubble = {
|
|
4029
|
+
Root: BubbleRoot,
|
|
4030
|
+
Content: BubbleContent,
|
|
4031
|
+
Header: BubbleHeader,
|
|
4032
|
+
Footer: BubbleFooter,
|
|
4033
|
+
Metadata: BubbleMetadata
|
|
4034
|
+
};
|
|
4035
|
+
var ActionButtonContext = createContext(void 0);
|
|
4036
|
+
function useActionButtonContext() {
|
|
4037
|
+
const context = useContext(ActionButtonContext);
|
|
4038
|
+
if (!context) {
|
|
4039
|
+
throw new Error("ActionButton components must be used within ActionButton.Root");
|
|
2420
4040
|
}
|
|
2421
|
-
return
|
|
4041
|
+
return context;
|
|
2422
4042
|
}
|
|
4043
|
+
var ActionButtonRoot = forwardRef((props, ref) => {
|
|
4044
|
+
const { asChild, disabled, children, onMouseEnter, onMouseLeave, onFocus, onBlur, onMouseDown, onMouseUp, ...domProps } = props;
|
|
4045
|
+
const Comp = asChild ? Slot : "button";
|
|
4046
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
4047
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
4048
|
+
const [isPressed, setIsPressed] = useState(false);
|
|
4049
|
+
const handleMouseEnter = (e) => {
|
|
4050
|
+
setIsHovered(true);
|
|
4051
|
+
onMouseEnter?.(e);
|
|
4052
|
+
};
|
|
4053
|
+
const handleMouseLeave = (e) => {
|
|
4054
|
+
setIsHovered(false);
|
|
4055
|
+
setIsPressed(false);
|
|
4056
|
+
onMouseLeave?.(e);
|
|
4057
|
+
};
|
|
4058
|
+
const handleFocus = (e) => {
|
|
4059
|
+
setIsFocused(true);
|
|
4060
|
+
onFocus?.(e);
|
|
4061
|
+
};
|
|
4062
|
+
const handleBlur = (e) => {
|
|
4063
|
+
setIsFocused(false);
|
|
4064
|
+
onBlur?.(e);
|
|
4065
|
+
};
|
|
4066
|
+
const handleMouseDown = (e) => {
|
|
4067
|
+
setIsPressed(true);
|
|
4068
|
+
onMouseDown?.(e);
|
|
4069
|
+
};
|
|
4070
|
+
const handleMouseUp = (e) => {
|
|
4071
|
+
setIsPressed(false);
|
|
4072
|
+
onMouseUp?.(e);
|
|
4073
|
+
};
|
|
4074
|
+
return /* @__PURE__ */ jsx(
|
|
4075
|
+
ActionButtonContext.Provider,
|
|
4076
|
+
{
|
|
4077
|
+
value: {
|
|
4078
|
+
isHovered,
|
|
4079
|
+
isFocused,
|
|
4080
|
+
isPressed,
|
|
4081
|
+
isDisabled: !!disabled
|
|
4082
|
+
},
|
|
4083
|
+
children: /* @__PURE__ */ jsx(
|
|
4084
|
+
Comp,
|
|
4085
|
+
{
|
|
4086
|
+
ref,
|
|
4087
|
+
type: "button",
|
|
4088
|
+
disabled,
|
|
4089
|
+
onMouseEnter: handleMouseEnter,
|
|
4090
|
+
onMouseLeave: handleMouseLeave,
|
|
4091
|
+
onFocus: handleFocus,
|
|
4092
|
+
onBlur: handleBlur,
|
|
4093
|
+
onMouseDown: handleMouseDown,
|
|
4094
|
+
onMouseUp: handleMouseUp,
|
|
4095
|
+
...domProps,
|
|
4096
|
+
children
|
|
4097
|
+
}
|
|
4098
|
+
)
|
|
4099
|
+
}
|
|
4100
|
+
);
|
|
4101
|
+
});
|
|
4102
|
+
ActionButtonRoot.displayName = "ActionButton.Root";
|
|
4103
|
+
var ActionButtonIcon = forwardRef((props, ref) => {
|
|
4104
|
+
const { asChild, children, ...domProps } = props;
|
|
4105
|
+
const Comp = asChild ? Slot : "span";
|
|
4106
|
+
return /* @__PURE__ */ jsx(Comp, { ref, "aria-hidden": "true", ...domProps, children });
|
|
4107
|
+
});
|
|
4108
|
+
ActionButtonIcon.displayName = "ActionButton.Icon";
|
|
4109
|
+
var ActionButtonLabel = forwardRef((props, ref) => {
|
|
4110
|
+
const { asChild, srOnly = false, children, className, ...domProps } = props;
|
|
4111
|
+
const Comp = asChild ? Slot : "span";
|
|
4112
|
+
const srOnlyClass = srOnly ? "absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap border-0" : "";
|
|
4113
|
+
return /* @__PURE__ */ jsx(Comp, { ref, className: [srOnlyClass, className].filter(Boolean).join(" "), ...domProps, children });
|
|
4114
|
+
});
|
|
4115
|
+
ActionButtonLabel.displayName = "ActionButton.Label";
|
|
4116
|
+
var ActionButtonTooltip = forwardRef((props, ref) => {
|
|
4117
|
+
const { asChild, position = "top", showOnHover = true, children, ...domProps } = props;
|
|
4118
|
+
const context = useActionButtonContext();
|
|
4119
|
+
const Comp = asChild ? Slot : "span";
|
|
4120
|
+
if (showOnHover && !context.isHovered && !context.isFocused) {
|
|
4121
|
+
return null;
|
|
4122
|
+
}
|
|
4123
|
+
return /* @__PURE__ */ jsx(Comp, { ref, role: "tooltip", "data-tooltip-position": position, ...domProps, children });
|
|
4124
|
+
});
|
|
4125
|
+
ActionButtonTooltip.displayName = "ActionButton.Tooltip";
|
|
4126
|
+
var ActionButton = {
|
|
4127
|
+
Root: ActionButtonRoot,
|
|
4128
|
+
Icon: ActionButtonIcon,
|
|
4129
|
+
Label: ActionButtonLabel,
|
|
4130
|
+
Tooltip: ActionButtonTooltip
|
|
4131
|
+
};
|
|
2423
4132
|
function useStreaming(options = {}) {
|
|
2424
4133
|
const [content, setContent] = useState("");
|
|
2425
4134
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
@@ -2493,6 +4202,572 @@ function useStreaming(options = {}) {
|
|
|
2493
4202
|
reset
|
|
2494
4203
|
};
|
|
2495
4204
|
}
|
|
4205
|
+
function generateId() {
|
|
4206
|
+
return Math.random().toString(36).substring(7);
|
|
4207
|
+
}
|
|
4208
|
+
function useToast() {
|
|
4209
|
+
const [toasts, setToasts] = useState([]);
|
|
4210
|
+
const showToast = useCallback(
|
|
4211
|
+
(type, message, duration) => {
|
|
4212
|
+
const id = generateId();
|
|
4213
|
+
setToasts((prev) => [...prev, { id, type, message, duration }]);
|
|
4214
|
+
},
|
|
4215
|
+
[]
|
|
4216
|
+
);
|
|
4217
|
+
const dismiss = useCallback((id) => {
|
|
4218
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
4219
|
+
}, []);
|
|
4220
|
+
const dismissAll = useCallback(() => {
|
|
4221
|
+
setToasts([]);
|
|
4222
|
+
}, []);
|
|
4223
|
+
return {
|
|
4224
|
+
toasts,
|
|
4225
|
+
success: (msg, duration) => showToast("success", msg, duration),
|
|
4226
|
+
error: (msg, duration) => showToast("error", msg, duration),
|
|
4227
|
+
info: (msg, duration) => showToast("info", msg, duration),
|
|
4228
|
+
warning: (msg, duration) => showToast("warning", msg, duration),
|
|
4229
|
+
dismiss,
|
|
4230
|
+
dismissAll
|
|
4231
|
+
};
|
|
4232
|
+
}
|
|
4233
|
+
function useImageGallery(options = {}) {
|
|
4234
|
+
const { images: initialImages = [], wrap = false, onOpen, onClose, onNavigate } = options;
|
|
4235
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
4236
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
4237
|
+
const [images, setImages] = useState(initialImages);
|
|
4238
|
+
const currentImage = useMemo(() => images[currentIndex], [images, currentIndex]);
|
|
4239
|
+
const hasPrev = useMemo(() => {
|
|
4240
|
+
if (wrap) return images.length > 1;
|
|
4241
|
+
return currentIndex > 0;
|
|
4242
|
+
}, [currentIndex, images.length, wrap]);
|
|
4243
|
+
const hasNext = useMemo(() => {
|
|
4244
|
+
if (wrap) return images.length > 1;
|
|
4245
|
+
return currentIndex < images.length - 1;
|
|
4246
|
+
}, [currentIndex, images.length, wrap]);
|
|
4247
|
+
const open = useCallback(
|
|
4248
|
+
(index, newImages) => {
|
|
4249
|
+
if (newImages) {
|
|
4250
|
+
setImages(newImages);
|
|
4251
|
+
}
|
|
4252
|
+
const targetImages = newImages || images;
|
|
4253
|
+
if (targetImages.length === 0) {
|
|
4254
|
+
setCurrentIndex(0);
|
|
4255
|
+
setIsOpen(true);
|
|
4256
|
+
return;
|
|
4257
|
+
}
|
|
4258
|
+
const safeIndex = Math.max(0, Math.min(index, targetImages.length - 1));
|
|
4259
|
+
setCurrentIndex(safeIndex);
|
|
4260
|
+
setIsOpen(true);
|
|
4261
|
+
onOpen?.(safeIndex);
|
|
4262
|
+
},
|
|
4263
|
+
[images, onOpen]
|
|
4264
|
+
);
|
|
4265
|
+
const close = useCallback(() => {
|
|
4266
|
+
setIsOpen(false);
|
|
4267
|
+
onClose?.();
|
|
4268
|
+
}, [onClose]);
|
|
4269
|
+
const prev = useCallback(() => {
|
|
4270
|
+
if (images.length < 2) return;
|
|
4271
|
+
if (!hasPrev && !wrap) return;
|
|
4272
|
+
setCurrentIndex((current) => {
|
|
4273
|
+
const newIndex = wrap ? (current - 1 + images.length) % images.length : Math.max(0, current - 1);
|
|
4274
|
+
onNavigate?.(newIndex, "prev");
|
|
4275
|
+
return newIndex;
|
|
4276
|
+
});
|
|
4277
|
+
}, [hasPrev, wrap, images.length, onNavigate]);
|
|
4278
|
+
const next = useCallback(() => {
|
|
4279
|
+
if (images.length < 2) return;
|
|
4280
|
+
if (!hasNext && !wrap) return;
|
|
4281
|
+
setCurrentIndex((current) => {
|
|
4282
|
+
const newIndex = wrap ? (current + 1) % images.length : Math.min(images.length - 1, current + 1);
|
|
4283
|
+
onNavigate?.(newIndex, "next");
|
|
4284
|
+
return newIndex;
|
|
4285
|
+
});
|
|
4286
|
+
}, [hasNext, wrap, images.length, onNavigate]);
|
|
4287
|
+
const goTo = useCallback(
|
|
4288
|
+
(index) => {
|
|
4289
|
+
const safeIndex = Math.max(0, Math.min(index, images.length - 1));
|
|
4290
|
+
setCurrentIndex(safeIndex);
|
|
4291
|
+
},
|
|
4292
|
+
[images.length]
|
|
4293
|
+
);
|
|
4294
|
+
const setImagesHandler = useCallback((newImages) => {
|
|
4295
|
+
setImages(newImages);
|
|
4296
|
+
if (newImages.length === 0) {
|
|
4297
|
+
setCurrentIndex(0);
|
|
4298
|
+
} else {
|
|
4299
|
+
setCurrentIndex((current) => Math.min(current, newImages.length - 1));
|
|
4300
|
+
}
|
|
4301
|
+
}, []);
|
|
4302
|
+
return {
|
|
4303
|
+
isOpen,
|
|
4304
|
+
currentIndex,
|
|
4305
|
+
currentImage,
|
|
4306
|
+
images,
|
|
4307
|
+
hasPrev,
|
|
4308
|
+
hasNext,
|
|
4309
|
+
open,
|
|
4310
|
+
close,
|
|
4311
|
+
prev,
|
|
4312
|
+
next,
|
|
4313
|
+
goTo,
|
|
4314
|
+
setImages: setImagesHandler
|
|
4315
|
+
};
|
|
4316
|
+
}
|
|
4317
|
+
function useAutoScroll(options = {}) {
|
|
4318
|
+
const { threshold = 100, smooth = true, onScroll } = options;
|
|
4319
|
+
const containerRef = useRef(null);
|
|
4320
|
+
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
4321
|
+
const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
|
|
4322
|
+
const checkIsAtBottom = useCallback(
|
|
4323
|
+
(container) => {
|
|
4324
|
+
const { scrollTop, scrollHeight, clientHeight } = container;
|
|
4325
|
+
return scrollHeight - scrollTop - clientHeight <= threshold;
|
|
4326
|
+
},
|
|
4327
|
+
[threshold]
|
|
4328
|
+
);
|
|
4329
|
+
const handleScroll = useCallback(
|
|
4330
|
+
(e) => {
|
|
4331
|
+
const container = e.currentTarget;
|
|
4332
|
+
const atBottom = checkIsAtBottom(container);
|
|
4333
|
+
setIsAtBottom(atBottom);
|
|
4334
|
+
setShouldAutoScroll(atBottom);
|
|
4335
|
+
onScroll?.(atBottom);
|
|
4336
|
+
},
|
|
4337
|
+
[checkIsAtBottom, onScroll]
|
|
4338
|
+
);
|
|
4339
|
+
const scrollToBottom = useCallback(
|
|
4340
|
+
(useSmooth) => {
|
|
4341
|
+
const container = containerRef.current;
|
|
4342
|
+
if (!container) return;
|
|
4343
|
+
const shouldSmooth = useSmooth ?? smooth;
|
|
4344
|
+
container.scrollTo({
|
|
4345
|
+
top: container.scrollHeight,
|
|
4346
|
+
behavior: shouldSmooth ? "smooth" : "auto"
|
|
4347
|
+
});
|
|
4348
|
+
setIsAtBottom(true);
|
|
4349
|
+
setShouldAutoScroll(true);
|
|
4350
|
+
},
|
|
4351
|
+
[smooth]
|
|
4352
|
+
);
|
|
4353
|
+
const setAutoScroll = useCallback((enabled) => {
|
|
4354
|
+
setShouldAutoScroll(enabled);
|
|
4355
|
+
if (enabled) {
|
|
4356
|
+
const container = containerRef.current;
|
|
4357
|
+
if (container) {
|
|
4358
|
+
container.scrollTo({
|
|
4359
|
+
top: container.scrollHeight,
|
|
4360
|
+
behavior: "auto"
|
|
4361
|
+
});
|
|
4362
|
+
setIsAtBottom(true);
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
}, []);
|
|
4366
|
+
useEffect(() => {
|
|
4367
|
+
const container = containerRef.current;
|
|
4368
|
+
if (!container) return;
|
|
4369
|
+
let rafId = null;
|
|
4370
|
+
const observer = new MutationObserver(() => {
|
|
4371
|
+
if (shouldAutoScroll) {
|
|
4372
|
+
if (rafId === null) {
|
|
4373
|
+
rafId = requestAnimationFrame(() => {
|
|
4374
|
+
container.scrollTo({
|
|
4375
|
+
top: container.scrollHeight,
|
|
4376
|
+
behavior: "instant"
|
|
4377
|
+
});
|
|
4378
|
+
rafId = null;
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
});
|
|
4383
|
+
observer.observe(container, {
|
|
4384
|
+
childList: true,
|
|
4385
|
+
subtree: true,
|
|
4386
|
+
characterData: true
|
|
4387
|
+
});
|
|
4388
|
+
return () => {
|
|
4389
|
+
observer.disconnect();
|
|
4390
|
+
if (rafId !== null) {
|
|
4391
|
+
cancelAnimationFrame(rafId);
|
|
4392
|
+
}
|
|
4393
|
+
};
|
|
4394
|
+
}, [shouldAutoScroll]);
|
|
4395
|
+
return {
|
|
4396
|
+
containerRef,
|
|
4397
|
+
isAtBottom,
|
|
4398
|
+
shouldAutoScroll,
|
|
4399
|
+
scrollToBottom,
|
|
4400
|
+
setAutoScroll,
|
|
4401
|
+
handleScroll
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
function useMessageActions(options = {}) {
|
|
4405
|
+
const { onCopy, onCopyError, onRegenerate, onEdit, copiedDuration = 2e3 } = options;
|
|
4406
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
4407
|
+
const [isRegenerating, setIsRegenerating] = useState(false);
|
|
4408
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
4409
|
+
const copiedTimeoutRef = useRef(null);
|
|
4410
|
+
useEffect(() => {
|
|
4411
|
+
return () => {
|
|
4412
|
+
if (copiedTimeoutRef.current) {
|
|
4413
|
+
clearTimeout(copiedTimeoutRef.current);
|
|
4414
|
+
copiedTimeoutRef.current = null;
|
|
4415
|
+
}
|
|
4416
|
+
};
|
|
4417
|
+
}, []);
|
|
4418
|
+
const copy = useCallback(
|
|
4419
|
+
async (content) => {
|
|
4420
|
+
try {
|
|
4421
|
+
await navigator.clipboard.writeText(content);
|
|
4422
|
+
setIsCopied(true);
|
|
4423
|
+
onCopy?.(content);
|
|
4424
|
+
if (copiedTimeoutRef.current) {
|
|
4425
|
+
clearTimeout(copiedTimeoutRef.current);
|
|
4426
|
+
}
|
|
4427
|
+
copiedTimeoutRef.current = setTimeout(() => {
|
|
4428
|
+
setIsCopied(false);
|
|
4429
|
+
copiedTimeoutRef.current = null;
|
|
4430
|
+
}, copiedDuration);
|
|
4431
|
+
} catch (error) {
|
|
4432
|
+
const err = error instanceof Error ? error : new Error("Failed to copy");
|
|
4433
|
+
onCopyError?.(err);
|
|
4434
|
+
throw err;
|
|
4435
|
+
}
|
|
4436
|
+
},
|
|
4437
|
+
[onCopy, onCopyError, copiedDuration]
|
|
4438
|
+
);
|
|
4439
|
+
const regenerate = useCallback(async () => {
|
|
4440
|
+
if (!onRegenerate) return;
|
|
4441
|
+
if (isRegenerating) return;
|
|
4442
|
+
setIsRegenerating(true);
|
|
4443
|
+
try {
|
|
4444
|
+
await onRegenerate();
|
|
4445
|
+
} finally {
|
|
4446
|
+
setIsRegenerating(false);
|
|
4447
|
+
}
|
|
4448
|
+
}, [onRegenerate, isRegenerating]);
|
|
4449
|
+
const edit = useCallback(
|
|
4450
|
+
async (content) => {
|
|
4451
|
+
if (!onEdit) return;
|
|
4452
|
+
if (isEditing) return;
|
|
4453
|
+
setIsEditing(true);
|
|
4454
|
+
try {
|
|
4455
|
+
await onEdit(content);
|
|
4456
|
+
} finally {
|
|
4457
|
+
setIsEditing(false);
|
|
4458
|
+
}
|
|
4459
|
+
},
|
|
4460
|
+
[onEdit, isEditing]
|
|
4461
|
+
);
|
|
4462
|
+
const reset = useCallback(() => {
|
|
4463
|
+
setIsCopied(false);
|
|
4464
|
+
setIsRegenerating(false);
|
|
4465
|
+
setIsEditing(false);
|
|
4466
|
+
if (copiedTimeoutRef.current) {
|
|
4467
|
+
clearTimeout(copiedTimeoutRef.current);
|
|
4468
|
+
copiedTimeoutRef.current = null;
|
|
4469
|
+
}
|
|
4470
|
+
}, []);
|
|
4471
|
+
return {
|
|
4472
|
+
isCopied,
|
|
4473
|
+
isRegenerating,
|
|
4474
|
+
isEditing,
|
|
4475
|
+
copy,
|
|
4476
|
+
regenerate,
|
|
4477
|
+
edit,
|
|
4478
|
+
reset
|
|
4479
|
+
};
|
|
4480
|
+
}
|
|
4481
|
+
var DEFAULT_MAX_FILES = 10;
|
|
4482
|
+
var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
4483
|
+
var DEFAULT_ALLOWED_TYPES = ["image/jpeg", "image/png", "image/gif", "image/webp"];
|
|
4484
|
+
function generateId2() {
|
|
4485
|
+
return `attachment-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
4486
|
+
}
|
|
4487
|
+
function isImageFile(file) {
|
|
4488
|
+
return file.type.startsWith("image/");
|
|
4489
|
+
}
|
|
4490
|
+
async function createImagePreview(file) {
|
|
4491
|
+
return new Promise((resolve, reject) => {
|
|
4492
|
+
const reader = new FileReader();
|
|
4493
|
+
reader.onload = () => resolve(reader.result);
|
|
4494
|
+
reader.onerror = () => reject(new Error("Failed to read file"));
|
|
4495
|
+
reader.readAsDataURL(file);
|
|
4496
|
+
});
|
|
4497
|
+
}
|
|
4498
|
+
function useAttachments(options = {}) {
|
|
4499
|
+
const {
|
|
4500
|
+
maxFiles = DEFAULT_MAX_FILES,
|
|
4501
|
+
maxFileSize = DEFAULT_MAX_FILE_SIZE,
|
|
4502
|
+
allowedTypes = DEFAULT_ALLOWED_TYPES,
|
|
4503
|
+
validate,
|
|
4504
|
+
onAdd,
|
|
4505
|
+
onRemove,
|
|
4506
|
+
onError
|
|
4507
|
+
} = options;
|
|
4508
|
+
const [files, setFiles] = useState([]);
|
|
4509
|
+
const [errors, setErrors] = useState([]);
|
|
4510
|
+
const [isProcessing, setIsProcessing] = useState(false);
|
|
4511
|
+
const removedRef = useRef(null);
|
|
4512
|
+
const isMaxReached = useMemo(() => files.length >= maxFiles, [files.length, maxFiles]);
|
|
4513
|
+
const remainingSlots = useMemo(() => Math.max(0, maxFiles - files.length), [files.length, maxFiles]);
|
|
4514
|
+
const validateFile = useCallback(
|
|
4515
|
+
(file, currentCount) => {
|
|
4516
|
+
if (currentCount >= maxFiles) {
|
|
4517
|
+
return {
|
|
4518
|
+
file,
|
|
4519
|
+
reason: "count",
|
|
4520
|
+
message: `Maximum ${maxFiles} files allowed`
|
|
4521
|
+
};
|
|
4522
|
+
}
|
|
4523
|
+
if (file.size > maxFileSize) {
|
|
4524
|
+
const maxSizeMB = (maxFileSize / (1024 * 1024)).toFixed(1);
|
|
4525
|
+
return {
|
|
4526
|
+
file,
|
|
4527
|
+
reason: "size",
|
|
4528
|
+
message: `File "${file.name}" exceeds ${maxSizeMB}MB limit`
|
|
4529
|
+
};
|
|
4530
|
+
}
|
|
4531
|
+
if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
|
|
4532
|
+
return {
|
|
4533
|
+
file,
|
|
4534
|
+
reason: "type",
|
|
4535
|
+
message: `File type "${file.type || "unknown"}" not allowed`
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
if (validate) {
|
|
4539
|
+
const customError = validate(file);
|
|
4540
|
+
if (customError) {
|
|
4541
|
+
return {
|
|
4542
|
+
file,
|
|
4543
|
+
reason: "custom",
|
|
4544
|
+
message: customError
|
|
4545
|
+
};
|
|
4546
|
+
}
|
|
4547
|
+
}
|
|
4548
|
+
return null;
|
|
4549
|
+
},
|
|
4550
|
+
[maxFiles, maxFileSize, allowedTypes, validate]
|
|
4551
|
+
);
|
|
4552
|
+
const add = useCallback(
|
|
4553
|
+
async (newFiles) => {
|
|
4554
|
+
if (isProcessing) {
|
|
4555
|
+
const fileArray2 = Array.from(newFiles);
|
|
4556
|
+
if (fileArray2.length > 0) {
|
|
4557
|
+
const processingErrors = fileArray2.map((file) => ({
|
|
4558
|
+
file,
|
|
4559
|
+
reason: "custom",
|
|
4560
|
+
message: "Please wait for the current files to finish processing."
|
|
4561
|
+
}));
|
|
4562
|
+
setErrors(processingErrors);
|
|
4563
|
+
onError?.(processingErrors);
|
|
4564
|
+
}
|
|
4565
|
+
return;
|
|
4566
|
+
}
|
|
4567
|
+
setIsProcessing(true);
|
|
4568
|
+
const fileArray = Array.from(newFiles);
|
|
4569
|
+
const validationErrors = [];
|
|
4570
|
+
const validFiles = [];
|
|
4571
|
+
let currentCount = files.length;
|
|
4572
|
+
for (const file of fileArray) {
|
|
4573
|
+
const error = validateFile(file, currentCount);
|
|
4574
|
+
if (error) {
|
|
4575
|
+
validationErrors.push(error);
|
|
4576
|
+
continue;
|
|
4577
|
+
}
|
|
4578
|
+
const attachment = {
|
|
4579
|
+
id: generateId2(),
|
|
4580
|
+
filename: file.name,
|
|
4581
|
+
mimeType: file.type,
|
|
4582
|
+
sizeBytes: file.size
|
|
4583
|
+
};
|
|
4584
|
+
if (isImageFile(file)) {
|
|
4585
|
+
try {
|
|
4586
|
+
const preview = await createImagePreview(file);
|
|
4587
|
+
attachment.base64Data = preview;
|
|
4588
|
+
} catch {
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
validFiles.push(attachment);
|
|
4592
|
+
currentCount++;
|
|
4593
|
+
}
|
|
4594
|
+
if (validationErrors.length > 0) {
|
|
4595
|
+
setErrors(validationErrors);
|
|
4596
|
+
onError?.(validationErrors);
|
|
4597
|
+
}
|
|
4598
|
+
if (validFiles.length > 0) {
|
|
4599
|
+
setFiles((prev) => [...prev, ...validFiles]);
|
|
4600
|
+
onAdd?.(validFiles);
|
|
4601
|
+
}
|
|
4602
|
+
setIsProcessing(false);
|
|
4603
|
+
},
|
|
4604
|
+
[files.length, validateFile, onAdd, onError, isProcessing]
|
|
4605
|
+
);
|
|
4606
|
+
const remove = useCallback(
|
|
4607
|
+
(fileOrId) => {
|
|
4608
|
+
const id = typeof fileOrId === "string" ? fileOrId : fileOrId.id;
|
|
4609
|
+
setFiles((prev) => {
|
|
4610
|
+
const removed2 = prev.find((f) => f.id === id) ?? null;
|
|
4611
|
+
const next = prev.filter((f) => f.id !== id);
|
|
4612
|
+
removedRef.current = removed2;
|
|
4613
|
+
return next;
|
|
4614
|
+
});
|
|
4615
|
+
const removed = removedRef.current;
|
|
4616
|
+
if (removed) {
|
|
4617
|
+
onRemove?.(removed);
|
|
4618
|
+
removedRef.current = null;
|
|
4619
|
+
}
|
|
4620
|
+
},
|
|
4621
|
+
[onRemove]
|
|
4622
|
+
);
|
|
4623
|
+
const clear = useCallback(() => {
|
|
4624
|
+
setFiles([]);
|
|
4625
|
+
setErrors([]);
|
|
4626
|
+
}, []);
|
|
4627
|
+
const clearErrors = useCallback(() => {
|
|
4628
|
+
setErrors([]);
|
|
4629
|
+
}, []);
|
|
4630
|
+
const setFilesHandler = useCallback((newFiles) => {
|
|
4631
|
+
setFiles(newFiles);
|
|
4632
|
+
}, []);
|
|
4633
|
+
return {
|
|
4634
|
+
files,
|
|
4635
|
+
errors,
|
|
4636
|
+
isProcessing,
|
|
4637
|
+
isMaxReached,
|
|
4638
|
+
remainingSlots,
|
|
4639
|
+
add,
|
|
4640
|
+
remove,
|
|
4641
|
+
clear,
|
|
4642
|
+
clearErrors,
|
|
4643
|
+
setFiles: setFilesHandler
|
|
4644
|
+
};
|
|
4645
|
+
}
|
|
4646
|
+
function useMarkdownCopy(options = {}) {
|
|
4647
|
+
const { copiedDuration = 2e3, onCopy, onError } = options;
|
|
4648
|
+
const [copiedStates, setCopiedStates] = useState(/* @__PURE__ */ new Map());
|
|
4649
|
+
const timeoutsRef = useRef(/* @__PURE__ */ new Map());
|
|
4650
|
+
useEffect(() => {
|
|
4651
|
+
return () => {
|
|
4652
|
+
timeoutsRef.current.forEach((timeout) => clearTimeout(timeout));
|
|
4653
|
+
timeoutsRef.current.clear();
|
|
4654
|
+
};
|
|
4655
|
+
}, []);
|
|
4656
|
+
const isCopied = useCallback(
|
|
4657
|
+
(blockId) => {
|
|
4658
|
+
return copiedStates.get(blockId) ?? false;
|
|
4659
|
+
},
|
|
4660
|
+
[copiedStates]
|
|
4661
|
+
);
|
|
4662
|
+
const copy = useCallback(
|
|
4663
|
+
async (blockId, content, language) => {
|
|
4664
|
+
try {
|
|
4665
|
+
await navigator.clipboard.writeText(content);
|
|
4666
|
+
setCopiedStates((prev) => {
|
|
4667
|
+
const next = new Map(prev);
|
|
4668
|
+
next.set(blockId, true);
|
|
4669
|
+
return next;
|
|
4670
|
+
});
|
|
4671
|
+
onCopy?.(content, language);
|
|
4672
|
+
const existingTimeout = timeoutsRef.current.get(blockId);
|
|
4673
|
+
if (existingTimeout) {
|
|
4674
|
+
clearTimeout(existingTimeout);
|
|
4675
|
+
}
|
|
4676
|
+
const timeout = setTimeout(() => {
|
|
4677
|
+
setCopiedStates((prev) => {
|
|
4678
|
+
const next = new Map(prev);
|
|
4679
|
+
next.set(blockId, false);
|
|
4680
|
+
return next;
|
|
4681
|
+
});
|
|
4682
|
+
timeoutsRef.current.delete(blockId);
|
|
4683
|
+
}, copiedDuration);
|
|
4684
|
+
timeoutsRef.current.set(blockId, timeout);
|
|
4685
|
+
} catch (error) {
|
|
4686
|
+
const err = error instanceof Error ? error : new Error("Failed to copy");
|
|
4687
|
+
onError?.(err);
|
|
4688
|
+
throw err;
|
|
4689
|
+
}
|
|
4690
|
+
},
|
|
4691
|
+
[copiedDuration, onCopy, onError]
|
|
4692
|
+
);
|
|
4693
|
+
const reset = useCallback((blockId) => {
|
|
4694
|
+
setCopiedStates((prev) => {
|
|
4695
|
+
const next = new Map(prev);
|
|
4696
|
+
next.set(blockId, false);
|
|
4697
|
+
return next;
|
|
4698
|
+
});
|
|
4699
|
+
const timeout = timeoutsRef.current.get(blockId);
|
|
4700
|
+
if (timeout) {
|
|
4701
|
+
clearTimeout(timeout);
|
|
4702
|
+
timeoutsRef.current.delete(blockId);
|
|
4703
|
+
}
|
|
4704
|
+
}, []);
|
|
4705
|
+
const resetAll = useCallback(() => {
|
|
4706
|
+
setCopiedStates(/* @__PURE__ */ new Map());
|
|
4707
|
+
timeoutsRef.current.forEach((timeout) => clearTimeout(timeout));
|
|
4708
|
+
timeoutsRef.current.clear();
|
|
4709
|
+
}, []);
|
|
4710
|
+
return {
|
|
4711
|
+
copiedStates,
|
|
4712
|
+
isCopied,
|
|
4713
|
+
copy,
|
|
4714
|
+
reset,
|
|
4715
|
+
resetAll
|
|
4716
|
+
};
|
|
4717
|
+
}
|
|
4718
|
+
var ConfigContext = createContext(null);
|
|
4719
|
+
function ConfigProvider({ config, useGlobalConfig = false, children }) {
|
|
4720
|
+
let resolvedConfig = null;
|
|
4721
|
+
if (config) {
|
|
4722
|
+
resolvedConfig = config;
|
|
4723
|
+
} else if (useGlobalConfig && typeof window !== "undefined") {
|
|
4724
|
+
const globalContext = window.__BICHAT_CONTEXT__;
|
|
4725
|
+
const globalCSRF = window.__CSRF_TOKEN__;
|
|
4726
|
+
if (globalContext) {
|
|
4727
|
+
resolvedConfig = {
|
|
4728
|
+
user: {
|
|
4729
|
+
id: String(globalContext.user?.id || ""),
|
|
4730
|
+
email: globalContext.user?.email || "",
|
|
4731
|
+
firstName: globalContext.user?.firstName || "",
|
|
4732
|
+
lastName: globalContext.user?.lastName || "",
|
|
4733
|
+
permissions: globalContext.user?.permissions || []
|
|
4734
|
+
},
|
|
4735
|
+
tenant: {
|
|
4736
|
+
id: globalContext.tenant?.id || "",
|
|
4737
|
+
name: globalContext.tenant?.name || ""
|
|
4738
|
+
},
|
|
4739
|
+
locale: {
|
|
4740
|
+
language: globalContext.locale?.language || "en",
|
|
4741
|
+
translations: globalContext.locale?.translations || {}
|
|
4742
|
+
},
|
|
4743
|
+
endpoints: {
|
|
4744
|
+
graphQL: globalContext.config?.graphQLEndpoint || "/graphql",
|
|
4745
|
+
stream: globalContext.config?.streamEndpoint || "/stream"
|
|
4746
|
+
},
|
|
4747
|
+
csrfToken: globalCSRF
|
|
4748
|
+
};
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: resolvedConfig, children });
|
|
4752
|
+
}
|
|
4753
|
+
function useConfig() {
|
|
4754
|
+
return useContext(ConfigContext);
|
|
4755
|
+
}
|
|
4756
|
+
function useRequiredConfig() {
|
|
4757
|
+
const config = useContext(ConfigContext);
|
|
4758
|
+
if (!config) {
|
|
4759
|
+
throw new Error(
|
|
4760
|
+
"BiChat configuration not found. Wrap your app with <ConfigProvider config={...}> or use useGlobalConfig={true}."
|
|
4761
|
+
);
|
|
4762
|
+
}
|
|
4763
|
+
return config;
|
|
4764
|
+
}
|
|
4765
|
+
function hasPermission2(config, permission) {
|
|
4766
|
+
if (!config) {
|
|
4767
|
+
return false;
|
|
4768
|
+
}
|
|
4769
|
+
return config.user.permissions.includes(permission);
|
|
4770
|
+
}
|
|
2496
4771
|
|
|
2497
4772
|
// ui/src/bichat/theme/themes.ts
|
|
2498
4773
|
var lightTheme = {
|
|
@@ -2725,7 +5000,7 @@ var HttpDataSource = class {
|
|
|
2725
5000
|
return data.createChatSession;
|
|
2726
5001
|
}
|
|
2727
5002
|
/**
|
|
2728
|
-
* Fetch an existing session with
|
|
5003
|
+
* Fetch an existing session with turns (turn-based architecture)
|
|
2729
5004
|
*/
|
|
2730
5005
|
async fetchSession(id) {
|
|
2731
5006
|
const query = `
|
|
@@ -2739,46 +5014,79 @@ var HttpDataSource = class {
|
|
|
2739
5014
|
createdAt
|
|
2740
5015
|
updatedAt
|
|
2741
5016
|
}
|
|
2742
|
-
|
|
5017
|
+
turns {
|
|
2743
5018
|
id
|
|
2744
5019
|
sessionId
|
|
2745
|
-
role
|
|
2746
|
-
content
|
|
2747
5020
|
createdAt
|
|
2748
|
-
|
|
5021
|
+
userTurn {
|
|
2749
5022
|
id
|
|
2750
|
-
|
|
2751
|
-
|
|
5023
|
+
content
|
|
5024
|
+
attachments {
|
|
5025
|
+
id
|
|
5026
|
+
filename
|
|
5027
|
+
mimeType
|
|
5028
|
+
sizeBytes
|
|
5029
|
+
base64Data
|
|
5030
|
+
}
|
|
5031
|
+
createdAt
|
|
2752
5032
|
}
|
|
2753
|
-
|
|
5033
|
+
assistantTurn {
|
|
2754
5034
|
id
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
5035
|
+
content
|
|
5036
|
+
explanation
|
|
5037
|
+
citations {
|
|
5038
|
+
id
|
|
5039
|
+
type
|
|
5040
|
+
title
|
|
5041
|
+
url
|
|
5042
|
+
startIndex
|
|
5043
|
+
endIndex
|
|
5044
|
+
excerpt
|
|
5045
|
+
source
|
|
5046
|
+
}
|
|
5047
|
+
chartData {
|
|
5048
|
+
chartType
|
|
5049
|
+
title
|
|
5050
|
+
series {
|
|
5051
|
+
name
|
|
5052
|
+
data
|
|
5053
|
+
}
|
|
5054
|
+
labels
|
|
5055
|
+
colors
|
|
5056
|
+
height
|
|
5057
|
+
}
|
|
5058
|
+
artifacts {
|
|
5059
|
+
type
|
|
5060
|
+
filename
|
|
5061
|
+
url
|
|
5062
|
+
sizeReadable
|
|
5063
|
+
rowCount
|
|
5064
|
+
description
|
|
5065
|
+
}
|
|
5066
|
+
codeOutputs {
|
|
5067
|
+
type
|
|
5068
|
+
content
|
|
5069
|
+
filename
|
|
5070
|
+
mimeType
|
|
5071
|
+
sizeBytes
|
|
5072
|
+
}
|
|
5073
|
+
createdAt
|
|
2773
5074
|
}
|
|
2774
|
-
explanation
|
|
2775
5075
|
}
|
|
2776
5076
|
pendingQuestion {
|
|
2777
5077
|
id
|
|
2778
5078
|
turnId
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
5079
|
+
questions {
|
|
5080
|
+
id
|
|
5081
|
+
text
|
|
5082
|
+
type
|
|
5083
|
+
options {
|
|
5084
|
+
id
|
|
5085
|
+
label
|
|
5086
|
+
value
|
|
5087
|
+
}
|
|
5088
|
+
required
|
|
5089
|
+
}
|
|
2782
5090
|
status
|
|
2783
5091
|
}
|
|
2784
5092
|
}
|
|
@@ -2954,6 +5262,18 @@ function createHttpDataSource(config) {
|
|
|
2954
5262
|
return new HttpDataSource(config);
|
|
2955
5263
|
}
|
|
2956
5264
|
|
|
2957
|
-
|
|
5265
|
+
// ui/src/bichat/index.ts
|
|
5266
|
+
init_citationProcessor();
|
|
5267
|
+
|
|
5268
|
+
// ui/src/bichat/types/index.ts
|
|
5269
|
+
var MessageRole = /* @__PURE__ */ ((MessageRole2) => {
|
|
5270
|
+
MessageRole2["User"] = "user";
|
|
5271
|
+
MessageRole2["Assistant"] = "assistant";
|
|
5272
|
+
MessageRole2["System"] = "system";
|
|
5273
|
+
MessageRole2["Tool"] = "tool";
|
|
5274
|
+
return MessageRole2;
|
|
5275
|
+
})(MessageRole || {});
|
|
5276
|
+
|
|
5277
|
+
export { ActionButton, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, Avatar, Bubble, ChartCard, ChatHeader, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, ConfigProvider, ConfirmModal, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageInput, MessageList, MessageRole, PermissionGuard, RateLimiter, ScrollToBottomButton, MemoizedSearchInput as SearchInput, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, Slot, SourcesPanel, StreamingCursor, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getValidChildren, hasPermission2 as hasConfigPermission, hasPermission, lightTheme, listItemVariants, messageContainerVariants, messageVariants, processCitations, scaleFadeVariants, staggerContainerVariants, toastVariants, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBubbleContext, useChat, useConfig, useFocusTrap, useImageGallery, useIotaContext, useMarkdownCopy, useMessageActions, useModalLock, useRequiredConfig, useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateFileCount, validateImageFile };
|
|
2958
5278
|
//# sourceMappingURL=index.mjs.map
|
|
2959
5279
|
//# sourceMappingURL=index.mjs.map
|