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