@iota-uz/sdk 0.4.12 → 0.4.13
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/applet/core.cjs.map +1 -1
- package/dist/applet/core.d.cts +2 -2
- package/dist/applet/core.d.ts +2 -2
- package/dist/applet/core.mjs.map +1 -1
- package/dist/applet/devtools.cjs +4 -4
- package/dist/applet/devtools.cjs.map +1 -1
- package/dist/applet/devtools.mjs +4 -4
- package/dist/applet/devtools.mjs.map +1 -1
- package/dist/applet/host.cjs +29 -6
- package/dist/applet/host.cjs.map +1 -1
- package/dist/applet/host.d.cts +3 -2
- package/dist/applet/host.d.ts +3 -2
- package/dist/applet/host.mjs +29 -6
- package/dist/applet/host.mjs.map +1 -1
- package/dist/bichat/index.cjs +2215 -1357
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.css +8 -0
- package/dist/bichat/index.css.map +1 -1
- package/dist/bichat/index.d.cts +434 -53
- package/dist/bichat/index.d.ts +434 -53
- package/dist/bichat/index.mjs +2216 -1360
- package/dist/bichat/index.mjs.map +1 -1
- package/dist/bichat/styles.css +5 -0
- package/dist/index.cjs +33 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +33 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tailwind/compiled.css +1 -1
package/dist/bichat/index.cjs
CHANGED
|
@@ -10,10 +10,10 @@ var prism = require('react-syntax-highlighter/dist/esm/styles/prism');
|
|
|
10
10
|
var ReactMarkdown = require('react-markdown');
|
|
11
11
|
var remarkGfm = require('remark-gfm');
|
|
12
12
|
var framerMotion = require('framer-motion');
|
|
13
|
+
require('react-dom/client');
|
|
13
14
|
var dateFns = require('date-fns');
|
|
14
15
|
var react$1 = require('@headlessui/react');
|
|
15
16
|
var reactDom = require('react-dom');
|
|
16
|
-
require('react-dom/client');
|
|
17
17
|
|
|
18
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
19
19
|
|
|
@@ -32,12 +32,12 @@ var __export = (target, all) => {
|
|
|
32
32
|
for (var name in all)
|
|
33
33
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
34
34
|
};
|
|
35
|
-
function IotaContextProvider({ children }) {
|
|
36
|
-
const
|
|
37
|
-
if (!
|
|
38
|
-
throw new Error("
|
|
35
|
+
function IotaContextProvider({ context, children }) {
|
|
36
|
+
const resolved = context ?? (typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0);
|
|
37
|
+
if (!resolved) {
|
|
38
|
+
throw new Error("APPLET_CONTEXT not found. Pass a `context` prop or ensure the server injected context into window.__APPLET_CONTEXT__.");
|
|
39
39
|
}
|
|
40
|
-
return /* @__PURE__ */ jsxRuntime.jsx(IotaContext.Provider, { value:
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(IotaContext.Provider, { value: resolved, children });
|
|
41
41
|
}
|
|
42
42
|
function useIotaContext() {
|
|
43
43
|
const context = React.useContext(IotaContext);
|
|
@@ -47,7 +47,7 @@ function useIotaContext() {
|
|
|
47
47
|
return context;
|
|
48
48
|
}
|
|
49
49
|
function hasPermission(permission) {
|
|
50
|
-
const context = window.
|
|
50
|
+
const context = typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0;
|
|
51
51
|
if (!context) {
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
@@ -304,12 +304,16 @@ var init_chartSpec = __esm({
|
|
|
304
304
|
exports.TableExportButton = void 0;
|
|
305
305
|
var init_TableExportButton = __esm({
|
|
306
306
|
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
307
|
+
init_useTranslation();
|
|
307
308
|
exports.TableExportButton = React.memo(function TableExportButton2({
|
|
308
309
|
onClick,
|
|
309
310
|
disabled = false,
|
|
310
|
-
label
|
|
311
|
-
disabledTooltip
|
|
311
|
+
label,
|
|
312
|
+
disabledTooltip
|
|
312
313
|
}) {
|
|
314
|
+
const { t } = useTranslation();
|
|
315
|
+
const resolvedLabel = label ?? t("BiChat.Export");
|
|
316
|
+
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
313
317
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
314
318
|
"button",
|
|
315
319
|
{
|
|
@@ -317,35 +321,38 @@ var init_TableExportButton = __esm({
|
|
|
317
321
|
onClick,
|
|
318
322
|
disabled,
|
|
319
323
|
className: "cursor-pointer inline-flex items-center gap-1 px-2 py-1 text-xs font-medium text-green-700 dark:text-green-400 hover:text-green-800 dark:hover:text-green-300 disabled:text-gray-400 disabled:cursor-not-allowed transition-colors rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50",
|
|
320
|
-
"aria-label":
|
|
321
|
-
title: disabled ?
|
|
324
|
+
"aria-label": resolvedLabel,
|
|
325
|
+
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
322
326
|
children: [
|
|
323
327
|
/* @__PURE__ */ jsxRuntime.jsx(react.FileXls, { size: 16, weight: "fill" }),
|
|
324
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children:
|
|
328
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedLabel })
|
|
325
329
|
]
|
|
326
330
|
}
|
|
327
331
|
);
|
|
328
332
|
});
|
|
329
333
|
}
|
|
330
334
|
});
|
|
331
|
-
|
|
335
|
+
exports.TableWithExport = void 0;
|
|
332
336
|
var init_TableWithExport = __esm({
|
|
333
337
|
"ui/src/bichat/components/TableWithExport.tsx"() {
|
|
334
338
|
init_TableExportButton();
|
|
335
|
-
|
|
339
|
+
init_useTranslation();
|
|
336
340
|
exports.TableWithExport = React.memo(function TableWithExport2({
|
|
337
341
|
children,
|
|
338
342
|
sendMessage,
|
|
339
343
|
disabled = false,
|
|
340
|
-
exportMessage
|
|
341
|
-
exportLabel
|
|
344
|
+
exportMessage,
|
|
345
|
+
exportLabel
|
|
342
346
|
}) {
|
|
347
|
+
const { t } = useTranslation();
|
|
348
|
+
const resolvedExportMessage = exportMessage ?? t("BiChat.ExportTableToExcel");
|
|
349
|
+
const resolvedExportLabel = exportLabel ?? t("BiChat.Export");
|
|
343
350
|
const handleExport = React.useCallback(() => {
|
|
344
|
-
sendMessage?.(
|
|
345
|
-
}, [sendMessage,
|
|
351
|
+
sendMessage?.(resolvedExportMessage);
|
|
352
|
+
}, [sendMessage, resolvedExportMessage]);
|
|
346
353
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
347
354
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "markdown-table-wrapper overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "markdown-table w-full border-collapse", children }) }),
|
|
348
|
-
sendMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(exports.TableExportButton, { onClick: handleExport, disabled, label:
|
|
355
|
+
sendMessage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsxRuntime.jsx(exports.TableExportButton, { onClick: handleExport, disabled, label: resolvedExportLabel }) })
|
|
349
356
|
] });
|
|
350
357
|
});
|
|
351
358
|
}
|
|
@@ -365,9 +372,12 @@ function CodeBlock({
|
|
|
365
372
|
language,
|
|
366
373
|
value,
|
|
367
374
|
inline,
|
|
368
|
-
copyLabel
|
|
369
|
-
copiedLabel
|
|
375
|
+
copyLabel,
|
|
376
|
+
copiedLabel
|
|
370
377
|
}) {
|
|
378
|
+
const { t } = useTranslation();
|
|
379
|
+
const resolvedCopyLabel = copyLabel ?? t("BiChat.Message.Copy");
|
|
380
|
+
const resolvedCopiedLabel = copiedLabel ?? t("BiChat.Message.Copied");
|
|
371
381
|
const [copied, setCopied] = React.useState(false);
|
|
372
382
|
const [copyFailed, setCopyFailed] = React.useState(false);
|
|
373
383
|
const [isDarkMode, setIsDarkMode] = React.useState(getInitialDarkMode);
|
|
@@ -443,14 +453,14 @@ function CodeBlock({
|
|
|
443
453
|
{
|
|
444
454
|
onClick: handleCopy,
|
|
445
455
|
className: `text-xs transition-colors flex items-center gap-1.5 ${copyFailed ? "text-red-500 dark:text-red-400" : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"}`,
|
|
446
|
-
title:
|
|
456
|
+
title: resolvedCopyLabel,
|
|
447
457
|
"aria-live": "polite",
|
|
448
458
|
children: copied ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
449
459
|
/* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 16, className: "w-4 h-4" }),
|
|
450
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children:
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedCopiedLabel })
|
|
451
461
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
452
462
|
/* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 16, className: "w-4 h-4" }),
|
|
453
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: copyFailed ? "
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: copyFailed ? t("BiChat.Message.CopyFailed") : resolvedCopyLabel })
|
|
454
464
|
] })
|
|
455
465
|
}
|
|
456
466
|
)
|
|
@@ -482,6 +492,7 @@ function CodeBlock({
|
|
|
482
492
|
var getInitialDarkMode, languageMap; exports.CodeBlock = void 0; var CodeBlock_default;
|
|
483
493
|
var init_CodeBlock = __esm({
|
|
484
494
|
"ui/src/bichat/components/CodeBlock.tsx"() {
|
|
495
|
+
init_useTranslation();
|
|
485
496
|
getInitialDarkMode = () => {
|
|
486
497
|
if (typeof document === "undefined") return false;
|
|
487
498
|
return document.documentElement.classList.contains("dark");
|
|
@@ -540,10 +551,14 @@ function MarkdownRenderer({
|
|
|
540
551
|
citations,
|
|
541
552
|
sendMessage,
|
|
542
553
|
sendDisabled = false,
|
|
543
|
-
copyLabel
|
|
544
|
-
copiedLabel
|
|
545
|
-
exportLabel
|
|
554
|
+
copyLabel,
|
|
555
|
+
copiedLabel,
|
|
556
|
+
exportLabel
|
|
546
557
|
}) {
|
|
558
|
+
const { t } = useTranslation();
|
|
559
|
+
const resolvedCopyLabel = copyLabel ?? t("BiChat.Message.Copy");
|
|
560
|
+
const resolvedCopiedLabel = copiedLabel ?? t("BiChat.Message.Copied");
|
|
561
|
+
const resolvedExportLabel = exportLabel ?? t("BiChat.Export");
|
|
547
562
|
const processed = React.useMemo(() => {
|
|
548
563
|
return processCitations(content, citations);
|
|
549
564
|
}, [content, citations]);
|
|
@@ -578,8 +593,8 @@ function MarkdownRenderer({
|
|
|
578
593
|
language,
|
|
579
594
|
value,
|
|
580
595
|
inline: false,
|
|
581
|
-
copyLabel,
|
|
582
|
-
copiedLabel
|
|
596
|
+
copyLabel: resolvedCopyLabel,
|
|
597
|
+
copiedLabel: resolvedCopiedLabel
|
|
583
598
|
}
|
|
584
599
|
)
|
|
585
600
|
}
|
|
@@ -616,7 +631,7 @@ function MarkdownRenderer({
|
|
|
616
631
|
{
|
|
617
632
|
sendMessage,
|
|
618
633
|
disabled: sendDisabled,
|
|
619
|
-
exportLabel,
|
|
634
|
+
exportLabel: resolvedExportLabel,
|
|
620
635
|
children
|
|
621
636
|
}
|
|
622
637
|
),
|
|
@@ -638,6 +653,7 @@ var init_MarkdownRenderer = __esm({
|
|
|
638
653
|
init_chartSpec();
|
|
639
654
|
init_TableWithExport();
|
|
640
655
|
init_ChartCard();
|
|
656
|
+
init_useTranslation();
|
|
641
657
|
CodeBlock2 = React.lazy(() => Promise.resolve().then(() => (init_CodeBlock(), CodeBlock_exports)).then((module) => ({ default: module.CodeBlock })));
|
|
642
658
|
INLINE_TAGS = /* @__PURE__ */ new Set([
|
|
643
659
|
"a",
|
|
@@ -706,43 +722,300 @@ var RateLimiter = class {
|
|
|
706
722
|
}
|
|
707
723
|
};
|
|
708
724
|
|
|
709
|
-
// ui/src/
|
|
710
|
-
function
|
|
711
|
-
if (
|
|
712
|
-
|
|
725
|
+
// ui/src/applet-devtools/enabled.ts
|
|
726
|
+
function shouldEnableAppletDevtools() {
|
|
727
|
+
if (typeof window === "undefined") return false;
|
|
728
|
+
const url = new URL(window.location.href);
|
|
729
|
+
if (url.searchParams.get("appletDebug") === "1") return true;
|
|
730
|
+
try {
|
|
731
|
+
return window.localStorage.getItem("iotaAppletDevtools") === "1";
|
|
732
|
+
} catch {
|
|
733
|
+
return false;
|
|
734
|
+
}
|
|
713
735
|
}
|
|
714
|
-
|
|
715
|
-
|
|
736
|
+
|
|
737
|
+
// ui/src/applet-host/rpc.ts
|
|
738
|
+
var AppletRPCException = class extends Error {
|
|
739
|
+
constructor(args) {
|
|
740
|
+
super(args.message);
|
|
741
|
+
this.name = "AppletRPCException";
|
|
742
|
+
this.code = args.code;
|
|
743
|
+
this.details = args.details;
|
|
744
|
+
this.cause = args.cause;
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
function createAppletRPCClient(options) {
|
|
748
|
+
const fetcher = options.fetcher ?? fetch;
|
|
749
|
+
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : 0;
|
|
750
|
+
async function call(method, params) {
|
|
751
|
+
const req = { id: crypto.randomUUID(), method, params };
|
|
752
|
+
const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
753
|
+
const abortController = timeoutMs > 0 ? new AbortController() : void 0;
|
|
754
|
+
let timeoutHandle;
|
|
755
|
+
let timedOut = false;
|
|
756
|
+
maybeDispatchRPCEvent({
|
|
757
|
+
id: req.id,
|
|
758
|
+
method: req.method,
|
|
759
|
+
status: "start"
|
|
760
|
+
});
|
|
761
|
+
try {
|
|
762
|
+
if (abortController) {
|
|
763
|
+
timeoutHandle = setTimeout(() => {
|
|
764
|
+
timedOut = true;
|
|
765
|
+
abortController.abort();
|
|
766
|
+
}, timeoutMs);
|
|
767
|
+
}
|
|
768
|
+
const resp = await fetcher(options.endpoint, {
|
|
769
|
+
method: "POST",
|
|
770
|
+
headers: { "Content-Type": "application/json" },
|
|
771
|
+
body: JSON.stringify(req),
|
|
772
|
+
signal: abortController?.signal
|
|
773
|
+
});
|
|
774
|
+
if (!resp.ok) {
|
|
775
|
+
throw new AppletRPCException({
|
|
776
|
+
code: "http_error",
|
|
777
|
+
message: `HTTP ${resp.status}`,
|
|
778
|
+
details: { status: resp.status }
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
const json = await resp.json();
|
|
782
|
+
if (json.error) {
|
|
783
|
+
throw new AppletRPCException({
|
|
784
|
+
code: json.error.code,
|
|
785
|
+
message: json.error.message,
|
|
786
|
+
details: json.error.details
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
if (json.result === void 0) {
|
|
790
|
+
throw new AppletRPCException({
|
|
791
|
+
code: "invalid_response",
|
|
792
|
+
message: "Missing result in successful response"
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
maybeDispatchRPCEvent({
|
|
796
|
+
id: req.id,
|
|
797
|
+
method: req.method,
|
|
798
|
+
status: "success",
|
|
799
|
+
durationMs: elapsedMs(startedAt)
|
|
800
|
+
});
|
|
801
|
+
return json.result;
|
|
802
|
+
} catch (err) {
|
|
803
|
+
let rpcErr = err;
|
|
804
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
805
|
+
rpcErr = new AppletRPCException({
|
|
806
|
+
code: timedOut ? "timeout" : "aborted",
|
|
807
|
+
message: timedOut ? `RPC request timed out after ${timeoutMs}ms` : "RPC request was aborted",
|
|
808
|
+
cause: err
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
maybeDispatchRPCEvent({
|
|
812
|
+
id: req.id,
|
|
813
|
+
method: req.method,
|
|
814
|
+
status: "error",
|
|
815
|
+
durationMs: elapsedMs(startedAt),
|
|
816
|
+
error: rpcErr
|
|
817
|
+
});
|
|
818
|
+
throw rpcErr;
|
|
819
|
+
} finally {
|
|
820
|
+
if (timeoutHandle !== void 0) {
|
|
821
|
+
clearTimeout(timeoutHandle);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
async function callTyped(method, params) {
|
|
826
|
+
return call(method, params);
|
|
827
|
+
}
|
|
828
|
+
return { call, callTyped };
|
|
716
829
|
}
|
|
717
|
-
function
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
830
|
+
function maybeDispatchRPCEvent(detail) {
|
|
831
|
+
if (typeof window === "undefined") return;
|
|
832
|
+
if (!shouldEnableAppletDevtools()) return;
|
|
833
|
+
window.dispatchEvent(new CustomEvent("iota:applet-rpc", { detail }));
|
|
834
|
+
}
|
|
835
|
+
function elapsedMs(startedAt) {
|
|
836
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
837
|
+
return Math.max(0, Math.round(now - startedAt));
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// ui/src/bichat/utils/errorDisplay.ts
|
|
841
|
+
function isPermissionDeniedError(error) {
|
|
842
|
+
if (!error) return false;
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
const msg = error.message.toLowerCase();
|
|
845
|
+
if (msg.includes("forbidden") || msg.includes("permission denied")) return true;
|
|
846
|
+
}
|
|
847
|
+
if (typeof error === "object" && error !== null) {
|
|
848
|
+
const obj = error;
|
|
849
|
+
if (obj.code === "forbidden" || obj.code === 403) return true;
|
|
850
|
+
if (obj.status === 403) return true;
|
|
851
|
+
if (obj.statusCode === 403) return true;
|
|
852
|
+
if (typeof obj.response === "object" && obj.response !== null) {
|
|
853
|
+
const resp = obj.response;
|
|
854
|
+
if (resp.status === 403) return true;
|
|
729
855
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
856
|
+
}
|
|
857
|
+
if (typeof error === "string") {
|
|
858
|
+
const lower = error.toLowerCase();
|
|
859
|
+
if (lower.includes("forbidden") || lower.includes("permission denied")) return true;
|
|
860
|
+
}
|
|
861
|
+
return false;
|
|
862
|
+
}
|
|
863
|
+
function extractStatus(error) {
|
|
864
|
+
if (!error || typeof error !== "object") return void 0;
|
|
865
|
+
const obj = error;
|
|
866
|
+
if (typeof obj.status === "number") return obj.status;
|
|
867
|
+
if (typeof obj.statusCode === "number") return obj.statusCode;
|
|
868
|
+
if (typeof obj.details === "object" && obj.details !== null) {
|
|
869
|
+
const details = obj.details;
|
|
870
|
+
if (typeof details.status === "number") return details.status;
|
|
871
|
+
if (typeof details.statusCode === "number") return details.statusCode;
|
|
872
|
+
}
|
|
873
|
+
if (typeof obj.response === "object" && obj.response !== null) {
|
|
874
|
+
const response = obj.response;
|
|
875
|
+
if (typeof response.status === "number") return response.status;
|
|
876
|
+
}
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
879
|
+
function isOfflineNow() {
|
|
880
|
+
return typeof navigator !== "undefined" && navigator.onLine === false;
|
|
881
|
+
}
|
|
882
|
+
function inferErrorCode(error) {
|
|
883
|
+
if (isOfflineNow()) return "offline";
|
|
884
|
+
if (error instanceof AppletRPCException) {
|
|
885
|
+
const code = String(error.code || "").toLowerCase().trim();
|
|
886
|
+
if (code) return code;
|
|
887
|
+
}
|
|
888
|
+
if (typeof error === "object" && error !== null) {
|
|
889
|
+
const obj = error;
|
|
890
|
+
if (typeof obj.code === "string" && obj.code.trim() !== "") {
|
|
891
|
+
return obj.code.toLowerCase();
|
|
892
|
+
}
|
|
893
|
+
if (typeof obj.error === "string" && obj.error.trim() !== "") {
|
|
894
|
+
return obj.error.toLowerCase();
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
const status = extractStatus(error);
|
|
898
|
+
if (status === 401 || status === 403) return "forbidden";
|
|
899
|
+
if (status === 404) return "not_found";
|
|
900
|
+
if (status === 408) return "timeout";
|
|
901
|
+
if (status === 413) return "payload_too_large";
|
|
902
|
+
if (status === 429) return "rate_limited";
|
|
903
|
+
if (status && status >= 500) return "server_error";
|
|
904
|
+
if (status && status >= 400) return "bad_request";
|
|
905
|
+
let message = "";
|
|
906
|
+
if (error instanceof Error) message = error.message;
|
|
907
|
+
if (!message && typeof error === "string") message = error;
|
|
908
|
+
const lower = message.toLowerCase();
|
|
909
|
+
if (lower.includes("timeout") || lower.includes("timed out")) return "timeout";
|
|
910
|
+
if (lower.includes("abort") || lower.includes("cancel")) return "aborted";
|
|
911
|
+
if (lower.includes("forbidden") || lower.includes("permission denied")) return "forbidden";
|
|
912
|
+
if (lower.includes("not found") || lower.includes("session not found")) return "not_found";
|
|
913
|
+
if (lower.includes("payload too large") || lower.includes("request too large") || lower.includes("413")) return "payload_too_large";
|
|
914
|
+
if (lower.includes("network") || lower.includes("failed to fetch")) return "network_error";
|
|
915
|
+
return "unknown";
|
|
916
|
+
}
|
|
917
|
+
function describeCode(code, fallbackTitle) {
|
|
918
|
+
switch (code) {
|
|
919
|
+
case "offline":
|
|
920
|
+
return {
|
|
921
|
+
title: "You are offline",
|
|
922
|
+
description: "Check your internet connection and try again.",
|
|
923
|
+
retryable: true
|
|
924
|
+
};
|
|
925
|
+
case "timeout":
|
|
926
|
+
return {
|
|
927
|
+
title: "Request timed out",
|
|
928
|
+
description: "The request took too long. Please try again.",
|
|
929
|
+
retryable: true
|
|
930
|
+
};
|
|
931
|
+
case "aborted":
|
|
932
|
+
return {
|
|
933
|
+
title: "Request canceled",
|
|
934
|
+
description: "The request was canceled before completion.",
|
|
935
|
+
retryable: true
|
|
936
|
+
};
|
|
937
|
+
case "forbidden":
|
|
938
|
+
return {
|
|
939
|
+
title: "Access denied",
|
|
940
|
+
description: "Your account does not have permission for this action.",
|
|
941
|
+
retryable: false
|
|
942
|
+
};
|
|
943
|
+
case "not_found":
|
|
944
|
+
return {
|
|
945
|
+
title: "Not found",
|
|
946
|
+
description: "The requested resource could not be found.",
|
|
947
|
+
retryable: false
|
|
948
|
+
};
|
|
949
|
+
case "payload_too_large":
|
|
950
|
+
return {
|
|
951
|
+
title: "Attachment too large",
|
|
952
|
+
description: "The uploaded payload exceeds allowed limits. Reduce file size and retry.",
|
|
953
|
+
retryable: false
|
|
954
|
+
};
|
|
955
|
+
case "invalid_request":
|
|
956
|
+
case "validation":
|
|
957
|
+
case "bad_request":
|
|
958
|
+
return {
|
|
959
|
+
title: "Invalid request",
|
|
960
|
+
description: "The request could not be processed. Review the input and try again.",
|
|
961
|
+
retryable: false
|
|
962
|
+
};
|
|
963
|
+
case "rate_limited":
|
|
964
|
+
return {
|
|
965
|
+
title: "Too many requests",
|
|
966
|
+
description: "Please wait a moment before trying again.",
|
|
967
|
+
retryable: true
|
|
968
|
+
};
|
|
969
|
+
case "http_error":
|
|
970
|
+
case "server_error":
|
|
971
|
+
return {
|
|
972
|
+
title: "Server error",
|
|
973
|
+
description: "The server failed to process this request. Please retry shortly.",
|
|
974
|
+
retryable: true
|
|
975
|
+
};
|
|
976
|
+
case "network_error":
|
|
977
|
+
return {
|
|
978
|
+
title: "Network error",
|
|
979
|
+
description: "A network issue interrupted the request. Please try again.",
|
|
980
|
+
retryable: true
|
|
981
|
+
};
|
|
982
|
+
default:
|
|
983
|
+
return {
|
|
984
|
+
title: fallbackTitle,
|
|
985
|
+
description: "Something went wrong. Please try again.",
|
|
986
|
+
retryable: true
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
function normalizeRPCError(error, fallbackTitle) {
|
|
991
|
+
const code = inferErrorCode(error);
|
|
992
|
+
const base = describeCode(code, fallbackTitle);
|
|
993
|
+
const permissionDenied = code === "forbidden" || isPermissionDeniedError(error);
|
|
994
|
+
let description = base.description;
|
|
995
|
+
if (error instanceof AppletRPCException && typeof error.message === "string" && error.message.trim() !== "" && base.title === fallbackTitle) {
|
|
996
|
+
description = error.message;
|
|
997
|
+
} else if (error instanceof Error && error.message && code === "unknown") {
|
|
998
|
+
description = error.message;
|
|
737
999
|
}
|
|
738
1000
|
return {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1001
|
+
code,
|
|
1002
|
+
title: base.title,
|
|
1003
|
+
description,
|
|
1004
|
+
userMessage: description || base.title,
|
|
1005
|
+
retryable: base.retryable,
|
|
1006
|
+
isPermissionDenied: permissionDenied,
|
|
1007
|
+
isTimeout: code === "timeout",
|
|
1008
|
+
isOffline: code === "offline",
|
|
1009
|
+
isCanceled: code === "aborted",
|
|
1010
|
+
isNotFound: code === "not_found"
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
function toErrorDisplay(error, fallbackTitle) {
|
|
1014
|
+
const normalized = normalizeRPCError(error, fallbackTitle);
|
|
1015
|
+
return {
|
|
1016
|
+
title: normalized.title,
|
|
1017
|
+
description: normalized.description,
|
|
1018
|
+
isPermissionDenied: normalized.isPermissionDenied
|
|
746
1019
|
};
|
|
747
1020
|
}
|
|
748
1021
|
|
|
@@ -795,8 +1068,45 @@ function loadQueue(sessionId) {
|
|
|
795
1068
|
}
|
|
796
1069
|
}
|
|
797
1070
|
|
|
798
|
-
// ui/src/bichat/
|
|
799
|
-
|
|
1071
|
+
// ui/src/bichat/utils/debugTrace.ts
|
|
1072
|
+
function hasMeaningfulUsage(trace) {
|
|
1073
|
+
if (!trace) return false;
|
|
1074
|
+
return trace.promptTokens > 0 || trace.completionTokens > 0 || trace.totalTokens > 0 || (trace.cachedTokens ?? 0) > 0 || (trace.cost ?? 0) > 0;
|
|
1075
|
+
}
|
|
1076
|
+
function hasDebugTrace(trace) {
|
|
1077
|
+
return trace.tools.length > 0 || hasMeaningfulUsage(trace.usage) || !!trace.generationMs;
|
|
1078
|
+
}
|
|
1079
|
+
function getSessionDebugUsage(turns) {
|
|
1080
|
+
let promptTokens = 0;
|
|
1081
|
+
let completionTokens = 0;
|
|
1082
|
+
let totalTokens = 0;
|
|
1083
|
+
let turnsWithUsage = 0;
|
|
1084
|
+
let latestPromptTokens = 0;
|
|
1085
|
+
let latestCompletionTokens = 0;
|
|
1086
|
+
let latestTotalTokens = 0;
|
|
1087
|
+
for (const turn of turns) {
|
|
1088
|
+
const usage = turn.assistantTurn?.debug?.usage;
|
|
1089
|
+
if (!hasMeaningfulUsage(usage) || !usage) {
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
turnsWithUsage++;
|
|
1093
|
+
promptTokens += usage.promptTokens;
|
|
1094
|
+
completionTokens += usage.completionTokens;
|
|
1095
|
+
totalTokens += usage.totalTokens;
|
|
1096
|
+
latestPromptTokens = usage.promptTokens;
|
|
1097
|
+
latestCompletionTokens = usage.completionTokens;
|
|
1098
|
+
latestTotalTokens = usage.totalTokens;
|
|
1099
|
+
}
|
|
1100
|
+
return {
|
|
1101
|
+
promptTokens,
|
|
1102
|
+
completionTokens,
|
|
1103
|
+
totalTokens,
|
|
1104
|
+
turnsWithUsage,
|
|
1105
|
+
latestPromptTokens,
|
|
1106
|
+
latestCompletionTokens,
|
|
1107
|
+
latestTotalTokens
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
800
1110
|
|
|
801
1111
|
// ui/src/bichat/types/index.ts
|
|
802
1112
|
var MessageRole = /* @__PURE__ */ ((MessageRole2) => {
|
|
@@ -873,7 +1183,7 @@ function readDebugLimitsFromGlobalContext() {
|
|
|
873
1183
|
if (typeof window === "undefined") {
|
|
874
1184
|
return null;
|
|
875
1185
|
}
|
|
876
|
-
const limits = window.
|
|
1186
|
+
const limits = window.__APPLET_CONTEXT__?.extensions?.debug?.limits;
|
|
877
1187
|
if (!limits) {
|
|
878
1188
|
return null;
|
|
879
1189
|
}
|
|
@@ -893,580 +1203,924 @@ function readDebugLimitsFromGlobalContext() {
|
|
|
893
1203
|
completionReserveTokens
|
|
894
1204
|
};
|
|
895
1205
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1206
|
+
|
|
1207
|
+
// ui/src/bichat/machine/selectors.ts
|
|
1208
|
+
function deriveDebugMode(state) {
|
|
1209
|
+
const key2 = state.session.currentSessionId || "new";
|
|
1210
|
+
return state.session.debugModeBySession[key2] ?? false;
|
|
1211
|
+
}
|
|
1212
|
+
function deriveSessionSnapshot(state, methods) {
|
|
1213
|
+
return {
|
|
1214
|
+
session: state.session.session,
|
|
1215
|
+
currentSessionId: state.session.currentSessionId,
|
|
1216
|
+
fetching: state.session.fetching,
|
|
1217
|
+
error: state.session.error,
|
|
1218
|
+
errorRetryable: state.session.errorRetryable,
|
|
1219
|
+
debugMode: deriveDebugMode(state),
|
|
1220
|
+
sessionDebugUsage: getSessionDebugUsage(state.messaging.turns),
|
|
1221
|
+
debugLimits: state.session.debugLimits,
|
|
1222
|
+
setError: methods.setError,
|
|
1223
|
+
retryFetchSession: methods.retryFetchSession
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
function deriveMessagingSnapshot(state, methods) {
|
|
1227
|
+
return {
|
|
1228
|
+
turns: state.messaging.turns,
|
|
1229
|
+
streamingContent: state.messaging.streamingContent,
|
|
1230
|
+
isStreaming: state.messaging.isStreaming,
|
|
1231
|
+
streamError: state.messaging.streamError,
|
|
1232
|
+
streamErrorRetryable: state.messaging.streamErrorRetryable,
|
|
1233
|
+
loading: state.messaging.loading,
|
|
1234
|
+
pendingQuestion: state.messaging.pendingQuestion,
|
|
1235
|
+
codeOutputs: state.messaging.codeOutputs,
|
|
1236
|
+
isCompacting: state.messaging.isCompacting,
|
|
1237
|
+
compactionSummary: null,
|
|
1238
|
+
artifactsInvalidationTrigger: state.messaging.artifactsInvalidationTrigger,
|
|
1239
|
+
...methods
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
function deriveInputSnapshot(state, methods) {
|
|
1243
|
+
return {
|
|
1244
|
+
message: state.input.message,
|
|
1245
|
+
inputError: state.input.inputError,
|
|
1246
|
+
messageQueue: state.input.messageQueue,
|
|
1247
|
+
...methods
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// ui/src/bichat/machine/ChatMachine.ts
|
|
1252
|
+
var MAX_QUEUE_SIZE = 5;
|
|
1253
|
+
var ChatMachine = class {
|
|
1254
|
+
constructor(config) {
|
|
1255
|
+
// ── Refs (mutable, no subscription) ─────────────────────────────────────
|
|
1256
|
+
this.abortController = null;
|
|
1257
|
+
this.lastSendAttempt = null;
|
|
1258
|
+
/** Prevents fetchSession effect from clobbering state while stream is active. */
|
|
1259
|
+
this.sendingSessionId = null;
|
|
1260
|
+
this.fetchCancelled = false;
|
|
1261
|
+
this.disposed = false;
|
|
1262
|
+
/** Memoized sessionDebugUsage — avoids unnecessary session re-renders during streaming. */
|
|
1263
|
+
this.lastSessionDebugUsage = null;
|
|
1264
|
+
// ── Listeners ───────────────────────────────────────────────────────────
|
|
1265
|
+
this.sessionListeners = /* @__PURE__ */ new Set();
|
|
1266
|
+
this.messagingListeners = /* @__PURE__ */ new Set();
|
|
1267
|
+
this.inputListeners = /* @__PURE__ */ new Set();
|
|
1268
|
+
// ── Snapshot caches (for useSyncExternalStore identity stability) ───────
|
|
1269
|
+
this.cachedSessionSnapshot = null;
|
|
1270
|
+
this.cachedMessagingSnapshot = null;
|
|
1271
|
+
this.cachedInputSnapshot = null;
|
|
1272
|
+
this.sessionSnapshotVersion = 0;
|
|
1273
|
+
this.messagingSnapshotVersion = 0;
|
|
1274
|
+
this.inputSnapshotVersion = 0;
|
|
1275
|
+
this.lastSessionSnapshotVersion = -1;
|
|
1276
|
+
this.lastMessagingSnapshotVersion = -1;
|
|
1277
|
+
this.lastInputSnapshotVersion = -1;
|
|
1278
|
+
// =====================================================================
|
|
1279
|
+
// Subscribe / getSnapshot (for useSyncExternalStore)
|
|
1280
|
+
// =====================================================================
|
|
1281
|
+
this.subscribeSession = (listener) => {
|
|
1282
|
+
this.sessionListeners.add(listener);
|
|
1283
|
+
return () => {
|
|
1284
|
+
this.sessionListeners.delete(listener);
|
|
1285
|
+
};
|
|
1286
|
+
};
|
|
1287
|
+
this.getSessionSnapshot = () => {
|
|
1288
|
+
if (this.lastSessionSnapshotVersion !== this.sessionSnapshotVersion) {
|
|
1289
|
+
this.cachedSessionSnapshot = deriveSessionSnapshot(this.state, {
|
|
1290
|
+
setError: this.setError,
|
|
1291
|
+
retryFetchSession: this.retryFetchSession
|
|
1292
|
+
});
|
|
1293
|
+
this.lastSessionSnapshotVersion = this.sessionSnapshotVersion;
|
|
1294
|
+
}
|
|
1295
|
+
return this.cachedSessionSnapshot;
|
|
1296
|
+
};
|
|
1297
|
+
this.subscribeMessaging = (listener) => {
|
|
1298
|
+
this.messagingListeners.add(listener);
|
|
1299
|
+
return () => {
|
|
1300
|
+
this.messagingListeners.delete(listener);
|
|
1301
|
+
};
|
|
1302
|
+
};
|
|
1303
|
+
this.getMessagingSnapshot = () => {
|
|
1304
|
+
if (this.lastMessagingSnapshotVersion !== this.messagingSnapshotVersion) {
|
|
1305
|
+
this.cachedMessagingSnapshot = deriveMessagingSnapshot(this.state, {
|
|
1306
|
+
sendMessage: this.sendMessage,
|
|
1307
|
+
handleRegenerate: this.handleRegenerate,
|
|
1308
|
+
handleEdit: this.handleEdit,
|
|
1309
|
+
handleCopy: this.handleCopy,
|
|
1310
|
+
handleSubmitQuestionAnswers: this.handleSubmitQuestionAnswers,
|
|
1311
|
+
handleRejectPendingQuestion: this.handleRejectPendingQuestion,
|
|
1312
|
+
retryLastMessage: this.retryLastMessage,
|
|
1313
|
+
clearStreamError: this.clearStreamError,
|
|
1314
|
+
cancel: this.cancel,
|
|
1315
|
+
setCodeOutputs: this.setCodeOutputs
|
|
1316
|
+
});
|
|
1317
|
+
this.lastMessagingSnapshotVersion = this.messagingSnapshotVersion;
|
|
1318
|
+
}
|
|
1319
|
+
return this.cachedMessagingSnapshot;
|
|
1320
|
+
};
|
|
1321
|
+
this.subscribeInput = (listener) => {
|
|
1322
|
+
this.inputListeners.add(listener);
|
|
1323
|
+
return () => {
|
|
1324
|
+
this.inputListeners.delete(listener);
|
|
1325
|
+
};
|
|
1326
|
+
};
|
|
1327
|
+
this.getInputSnapshot = () => {
|
|
1328
|
+
if (this.lastInputSnapshotVersion !== this.inputSnapshotVersion) {
|
|
1329
|
+
this.cachedInputSnapshot = deriveInputSnapshot(this.state, {
|
|
1330
|
+
setMessage: this.setMessage,
|
|
1331
|
+
setInputError: this.setInputError,
|
|
1332
|
+
handleSubmit: this.handleSubmit,
|
|
1333
|
+
handleUnqueue: this.handleUnqueue,
|
|
1334
|
+
enqueueMessage: this.enqueueMessage,
|
|
1335
|
+
removeQueueItem: this.removeQueueItem,
|
|
1336
|
+
updateQueueItem: this.updateQueueItem
|
|
1337
|
+
});
|
|
1338
|
+
this.lastInputSnapshotVersion = this.inputSnapshotVersion;
|
|
1339
|
+
}
|
|
1340
|
+
return this.cachedInputSnapshot;
|
|
1341
|
+
};
|
|
1342
|
+
this.dataSource = config.dataSource;
|
|
1343
|
+
this.rateLimiter = config.rateLimiter;
|
|
1344
|
+
this.onSessionCreated = config.onSessionCreated;
|
|
1345
|
+
this.state = {
|
|
1346
|
+
session: {
|
|
1347
|
+
currentSessionId: void 0,
|
|
1348
|
+
session: null,
|
|
1349
|
+
fetching: false,
|
|
1350
|
+
error: null,
|
|
1351
|
+
errorRetryable: false,
|
|
1352
|
+
debugModeBySession: {},
|
|
1353
|
+
debugLimits: readDebugLimitsFromGlobalContext()
|
|
1354
|
+
},
|
|
1355
|
+
messaging: {
|
|
1356
|
+
turns: [],
|
|
1357
|
+
streamingContent: "",
|
|
1358
|
+
isStreaming: false,
|
|
1359
|
+
streamError: null,
|
|
1360
|
+
streamErrorRetryable: false,
|
|
1361
|
+
loading: false,
|
|
1362
|
+
pendingQuestion: null,
|
|
1363
|
+
codeOutputs: [],
|
|
1364
|
+
isCompacting: false,
|
|
1365
|
+
artifactsInvalidationTrigger: 0
|
|
1366
|
+
},
|
|
1367
|
+
input: {
|
|
1368
|
+
message: "",
|
|
1369
|
+
inputError: null,
|
|
1370
|
+
messageQueue: []
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
this.setError = this._setError.bind(this);
|
|
1374
|
+
this.retryFetchSession = this._retryFetchSession.bind(this);
|
|
1375
|
+
this.sendMessage = this._sendMessage.bind(this);
|
|
1376
|
+
this.handleRegenerate = this._handleRegenerate.bind(this);
|
|
1377
|
+
this.handleEdit = this._handleEdit.bind(this);
|
|
1378
|
+
this.handleCopy = this._handleCopy.bind(this);
|
|
1379
|
+
this.handleSubmitQuestionAnswers = this._handleSubmitQuestionAnswers.bind(this);
|
|
1380
|
+
this.handleRejectPendingQuestion = this._handleRejectPendingQuestion.bind(this);
|
|
1381
|
+
this.retryLastMessage = this._retryLastMessage.bind(this);
|
|
1382
|
+
this.clearStreamError = this._clearStreamError.bind(this);
|
|
1383
|
+
this.cancel = this._cancel.bind(this);
|
|
1384
|
+
this.setCodeOutputs = this._setCodeOutputs.bind(this);
|
|
1385
|
+
this.setMessage = this._setMessage.bind(this);
|
|
1386
|
+
this.setInputError = this._setInputError.bind(this);
|
|
1387
|
+
this.handleSubmit = this._handleSubmit.bind(this);
|
|
1388
|
+
this.handleUnqueue = this._handleUnqueue.bind(this);
|
|
1389
|
+
this.enqueueMessage = this._enqueueMessage.bind(this);
|
|
1390
|
+
this.removeQueueItem = this._removeQueueItem.bind(this);
|
|
1391
|
+
this.updateQueueItem = this._updateQueueItem.bind(this);
|
|
1392
|
+
}
|
|
1393
|
+
// =====================================================================
|
|
1394
|
+
// Lifecycle
|
|
1395
|
+
// =====================================================================
|
|
1396
|
+
/**
|
|
1397
|
+
* Set the active session ID. Triggers fetch when transitioning to a real
|
|
1398
|
+
* session, or resets state for 'new'/undefined.
|
|
1399
|
+
*/
|
|
1400
|
+
setSessionId(id) {
|
|
1401
|
+
if (this.disposed) return;
|
|
1402
|
+
const prev = this.state.session.currentSessionId;
|
|
1403
|
+
if (id === prev) return;
|
|
1404
|
+
this.state.session.currentSessionId = id;
|
|
1405
|
+
this._notifySession();
|
|
1406
|
+
if (!id || id === "new") {
|
|
1407
|
+
this._updateInput({ messageQueue: [] });
|
|
1408
|
+
} else {
|
|
1409
|
+
this._updateInput({ messageQueue: loadQueue(id) });
|
|
1410
|
+
}
|
|
1411
|
+
this._fetchSessionIfNeeded();
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Update mutable config that may change across parent re-renders.
|
|
1415
|
+
* Called from the React provider's useEffect to keep the machine in sync.
|
|
1416
|
+
*/
|
|
1417
|
+
updateConfig(config) {
|
|
1418
|
+
this.dataSource = config.dataSource;
|
|
1419
|
+
this.onSessionCreated = config.onSessionCreated;
|
|
1420
|
+
}
|
|
1421
|
+
dispose() {
|
|
1422
|
+
this.disposed = true;
|
|
1423
|
+
this.fetchCancelled = true;
|
|
1424
|
+
this.abortController?.abort();
|
|
1425
|
+
this.sessionListeners.clear();
|
|
1426
|
+
this.messagingListeners.clear();
|
|
1427
|
+
this.inputListeners.clear();
|
|
1428
|
+
}
|
|
1429
|
+
// =====================================================================
|
|
1430
|
+
// Private — state updates + notification
|
|
1431
|
+
// =====================================================================
|
|
1432
|
+
_updateSession(patch) {
|
|
1433
|
+
Object.assign(this.state.session, patch);
|
|
1434
|
+
this._notifySession();
|
|
1435
|
+
}
|
|
1436
|
+
_notifySession() {
|
|
1437
|
+
this.sessionSnapshotVersion++;
|
|
1438
|
+
for (const fn of this.sessionListeners) fn();
|
|
1439
|
+
}
|
|
1440
|
+
_updateMessaging(patch) {
|
|
1441
|
+
Object.assign(this.state.messaging, patch);
|
|
1442
|
+
this._notifyMessaging();
|
|
1443
|
+
if ("turns" in patch) {
|
|
1444
|
+
const newUsage = getSessionDebugUsage(this.state.messaging.turns);
|
|
1445
|
+
if (!sessionDebugUsageEqual(this.lastSessionDebugUsage, newUsage)) {
|
|
1446
|
+
this.lastSessionDebugUsage = newUsage;
|
|
1447
|
+
this._notifySession();
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
_notifyMessaging() {
|
|
1452
|
+
this.messagingSnapshotVersion++;
|
|
1453
|
+
for (const fn of this.messagingListeners) fn();
|
|
1454
|
+
}
|
|
1455
|
+
_updateInput(patch) {
|
|
1456
|
+
Object.assign(this.state.input, patch);
|
|
1457
|
+
if ("messageQueue" in patch) {
|
|
1458
|
+
this._persistQueue();
|
|
1459
|
+
}
|
|
1460
|
+
this._notifyInput();
|
|
1461
|
+
}
|
|
1462
|
+
_notifyInput() {
|
|
1463
|
+
this.inputSnapshotVersion++;
|
|
1464
|
+
for (const fn of this.inputListeners) fn();
|
|
1465
|
+
}
|
|
1466
|
+
_persistQueue() {
|
|
1467
|
+
const sid = this.state.session.currentSessionId;
|
|
1468
|
+
if (!sid || sid === "new") return;
|
|
1469
|
+
saveQueue(sid, this.state.input.messageQueue);
|
|
1470
|
+
}
|
|
1471
|
+
// =====================================================================
|
|
1472
|
+
// Private — session fetch
|
|
1473
|
+
// =====================================================================
|
|
1474
|
+
_fetchSessionIfNeeded() {
|
|
1475
|
+
const id = this.state.session.currentSessionId;
|
|
1476
|
+
if (!id || id === "new") {
|
|
1477
|
+
this._updateSession({
|
|
1478
|
+
session: null,
|
|
1479
|
+
fetching: false,
|
|
1480
|
+
error: null,
|
|
1481
|
+
errorRetryable: false
|
|
1482
|
+
});
|
|
1483
|
+
this._updateMessaging({
|
|
1484
|
+
turns: [],
|
|
1485
|
+
pendingQuestion: null
|
|
1486
|
+
});
|
|
1487
|
+
this._updateInput({ inputError: null });
|
|
962
1488
|
return;
|
|
963
1489
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
if (
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
return state.turns ?? prev;
|
|
978
|
-
});
|
|
979
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1490
|
+
if (this.sendingSessionId === id) return;
|
|
1491
|
+
this.fetchCancelled = false;
|
|
1492
|
+
this._updateSession({ fetching: true, error: null, errorRetryable: false });
|
|
1493
|
+
this._updateInput({ inputError: null });
|
|
1494
|
+
const fetchId = id;
|
|
1495
|
+
this.dataSource.fetchSession(fetchId).then((result) => {
|
|
1496
|
+
if (this.fetchCancelled || this.disposed) return;
|
|
1497
|
+
if (this.state.session.currentSessionId !== fetchId) return;
|
|
1498
|
+
if (this.sendingSessionId === fetchId) return;
|
|
1499
|
+
if (result) {
|
|
1500
|
+
this._updateSession({ session: result.session, fetching: false });
|
|
1501
|
+
this._setTurnsFromFetch(result.turns);
|
|
1502
|
+
this._updateMessaging({ pendingQuestion: result.pendingQuestion || null });
|
|
980
1503
|
} else {
|
|
981
|
-
|
|
1504
|
+
this._updateSession({ error: "Session not found", fetching: false });
|
|
982
1505
|
}
|
|
983
|
-
setFetching(false);
|
|
984
1506
|
}).catch((err) => {
|
|
985
|
-
if (
|
|
986
|
-
|
|
987
|
-
|
|
1507
|
+
if (this.fetchCancelled || this.disposed) return;
|
|
1508
|
+
if (this.state.session.currentSessionId !== fetchId) return;
|
|
1509
|
+
if (this.sendingSessionId === fetchId) return;
|
|
1510
|
+
const normalized = normalizeRPCError(err, "Failed to load session");
|
|
1511
|
+
this._updateSession({
|
|
1512
|
+
error: normalized.userMessage,
|
|
1513
|
+
errorRetryable: normalized.retryable,
|
|
1514
|
+
fetching: false
|
|
1515
|
+
});
|
|
988
1516
|
});
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1517
|
+
}
|
|
1518
|
+
/** Sets turns from fetch, preserving pending user-only turns if server hasn't caught up. */
|
|
1519
|
+
_setTurnsFromFetch(fetchedTurns) {
|
|
1520
|
+
const prev = this.state.messaging.turns;
|
|
1521
|
+
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1522
|
+
if (hasPendingUserOnly && (!fetchedTurns || fetchedTurns.length === 0)) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
this._updateMessaging({ turns: fetchedTurns ?? prev });
|
|
1526
|
+
}
|
|
1527
|
+
// =====================================================================
|
|
1528
|
+
// Private — actions
|
|
1529
|
+
// =====================================================================
|
|
1530
|
+
_setError(error) {
|
|
1531
|
+
this._updateSession({
|
|
1532
|
+
error,
|
|
1533
|
+
errorRetryable: false
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
_retryFetchSession() {
|
|
1537
|
+
this._fetchSessionIfNeeded();
|
|
1538
|
+
}
|
|
1539
|
+
_clearStreamError() {
|
|
1540
|
+
this._updateMessaging({
|
|
1541
|
+
streamError: null,
|
|
1542
|
+
streamErrorRetryable: false
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
_cancel() {
|
|
1546
|
+
if (this.abortController) {
|
|
1547
|
+
this.abortController.abort();
|
|
1548
|
+
this.abortController = null;
|
|
1549
|
+
this._updateMessaging({ isStreaming: false, loading: false });
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
_setCodeOutputs(outputs) {
|
|
1553
|
+
this._updateMessaging({ codeOutputs: outputs });
|
|
1554
|
+
}
|
|
1555
|
+
_setMessage(message) {
|
|
1556
|
+
this._updateInput({ message });
|
|
1557
|
+
}
|
|
1558
|
+
_setInputError(error) {
|
|
1559
|
+
this._updateInput({ inputError: error });
|
|
1560
|
+
}
|
|
1561
|
+
// ── Slash commands ──────────────────────────────────────────────────────
|
|
1562
|
+
async _executeSlashCommand(command) {
|
|
1563
|
+
if (command.hasArgs) {
|
|
1564
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorNoArguments" });
|
|
1565
|
+
return true;
|
|
1566
|
+
}
|
|
1567
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1568
|
+
this._updateInput({ inputError: null });
|
|
1569
|
+
this._clearStreamError();
|
|
1570
|
+
if (command.name === "/debug") {
|
|
1571
|
+
const debugMode = deriveDebugMode(this.state);
|
|
1572
|
+
const key2 = this.state.session.currentSessionId || "new";
|
|
1573
|
+
const nextDebugMode = !debugMode;
|
|
1574
|
+
this._updateSession({
|
|
1575
|
+
debugModeBySession: {
|
|
1576
|
+
...this.state.session.debugModeBySession,
|
|
1577
|
+
[key2]: nextDebugMode
|
|
1028
1578
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
const curSessionId = sessionRef.current.currentSessionId;
|
|
1033
|
-
if (!curSessionId || curSessionId === "new") {
|
|
1034
|
-
setInputError("slash.error.sessionRequired");
|
|
1035
|
-
return true;
|
|
1036
|
-
}
|
|
1037
|
-
if (command.name === "/clear") {
|
|
1038
|
-
setLoading(true);
|
|
1039
|
-
setStreamingContent("");
|
|
1579
|
+
});
|
|
1580
|
+
if (nextDebugMode && this.state.session.currentSessionId && this.state.session.currentSessionId !== "new") {
|
|
1040
1581
|
try {
|
|
1041
|
-
await dataSource.
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
setTurns([]);
|
|
1582
|
+
const result = await this.dataSource.fetchSession(this.state.session.currentSessionId);
|
|
1583
|
+
if (result) {
|
|
1584
|
+
this._updateSession({ session: result.session });
|
|
1585
|
+
this._updateMessaging({
|
|
1586
|
+
turns: result.turns,
|
|
1587
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1588
|
+
});
|
|
1049
1589
|
}
|
|
1050
|
-
setCompactionSummary(null);
|
|
1051
|
-
setCodeOutputs([]);
|
|
1052
|
-
setMessage("");
|
|
1053
1590
|
} catch (err) {
|
|
1054
|
-
|
|
1055
|
-
} finally {
|
|
1056
|
-
setLoading(false);
|
|
1057
|
-
setIsStreaming(false);
|
|
1591
|
+
console.error("Failed to refresh session for debug mode:", err);
|
|
1058
1592
|
}
|
|
1059
|
-
return true;
|
|
1060
1593
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
} finally {
|
|
1084
|
-
setIsCompacting(false);
|
|
1085
|
-
setLoading(false);
|
|
1086
|
-
setIsStreaming(false);
|
|
1594
|
+
this._updateInput({ message: "" });
|
|
1595
|
+
return true;
|
|
1596
|
+
}
|
|
1597
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1598
|
+
if (!curSessionId || curSessionId === "new") {
|
|
1599
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorSessionRequired" });
|
|
1600
|
+
return true;
|
|
1601
|
+
}
|
|
1602
|
+
if (command.name === "/clear") {
|
|
1603
|
+
this._updateInput({ message: "" });
|
|
1604
|
+
this._updateMessaging({ loading: true, streamingContent: "" });
|
|
1605
|
+
try {
|
|
1606
|
+
await this.dataSource.clearSessionHistory(curSessionId);
|
|
1607
|
+
const result = await this.dataSource.fetchSession(curSessionId);
|
|
1608
|
+
if (result) {
|
|
1609
|
+
this._updateSession({ session: result.session });
|
|
1610
|
+
this._updateMessaging({
|
|
1611
|
+
turns: result.turns,
|
|
1612
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1613
|
+
});
|
|
1614
|
+
} else {
|
|
1615
|
+
this._updateMessaging({ turns: [] });
|
|
1087
1616
|
}
|
|
1088
|
-
|
|
1617
|
+
this._updateMessaging({ codeOutputs: [] });
|
|
1618
|
+
} catch (err) {
|
|
1619
|
+
const normalized = normalizeRPCError(err, "Failed to clear session history");
|
|
1620
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1621
|
+
} finally {
|
|
1622
|
+
this._updateMessaging({ loading: false, isStreaming: false });
|
|
1089
1623
|
}
|
|
1090
|
-
setInputError("slash.error.unknownCommand");
|
|
1091
1624
|
return true;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1625
|
+
}
|
|
1626
|
+
if (command.name === "/compact") {
|
|
1627
|
+
this._updateInput({ message: "" });
|
|
1628
|
+
this._updateMessaging({
|
|
1629
|
+
loading: true,
|
|
1630
|
+
isCompacting: true,
|
|
1631
|
+
streamingContent: ""
|
|
1632
|
+
});
|
|
1633
|
+
try {
|
|
1634
|
+
const compactResult = await this.dataSource.compactSessionHistory(curSessionId);
|
|
1635
|
+
const summary = compactResult.summary || "";
|
|
1636
|
+
this._updateMessaging({
|
|
1637
|
+
turns: [createCompactedSystemTurn(curSessionId, summary)]
|
|
1638
|
+
});
|
|
1639
|
+
const result = await this.dataSource.fetchSession(curSessionId);
|
|
1640
|
+
if (result) {
|
|
1641
|
+
this._updateSession({ session: result.session });
|
|
1642
|
+
this._updateMessaging({
|
|
1643
|
+
turns: result.turns,
|
|
1644
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1645
|
+
});
|
|
1646
|
+
} else {
|
|
1647
|
+
this._updateMessaging({ turns: [] });
|
|
1108
1648
|
}
|
|
1109
|
-
|
|
1649
|
+
this._updateMessaging({ codeOutputs: [] });
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
const normalized = normalizeRPCError(err, "Failed to compact session history");
|
|
1652
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1653
|
+
} finally {
|
|
1654
|
+
this._updateMessaging({ isCompacting: false, loading: false, isStreaming: false });
|
|
1655
|
+
}
|
|
1656
|
+
return true;
|
|
1657
|
+
}
|
|
1658
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorUnknownCommand" });
|
|
1659
|
+
return true;
|
|
1660
|
+
}
|
|
1661
|
+
// ── Send message ────────────────────────────────────────────────────────
|
|
1662
|
+
/**
|
|
1663
|
+
* Public entry point (no options). Calls _sendMessageCore internally.
|
|
1664
|
+
*/
|
|
1665
|
+
async _sendMessage(content, attachments = []) {
|
|
1666
|
+
return this._sendMessageCore(content, attachments);
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Internal entry point with options (for regenerate/edit).
|
|
1670
|
+
*/
|
|
1671
|
+
async _sendMessageDirect(content, attachments, options) {
|
|
1672
|
+
return this._sendMessageCore(content, attachments, options);
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Core send-message logic. Handles slash commands, rate limiting, streaming,
|
|
1676
|
+
* session creation, optimistic turns, and auto-queue-drain.
|
|
1677
|
+
*/
|
|
1678
|
+
async _sendMessageCore(content, attachments = [], options) {
|
|
1679
|
+
if (this.disposed) return;
|
|
1680
|
+
if (!content.trim() || this.state.messaging.loading) return;
|
|
1681
|
+
const trimmedContent = content.trim();
|
|
1682
|
+
if (trimmedContent.startsWith("/")) {
|
|
1683
|
+
const maybeCommand = parseSlashCommand(content);
|
|
1684
|
+
if (!maybeCommand) {
|
|
1685
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorUnknownCommand" });
|
|
1110
1686
|
return;
|
|
1111
1687
|
}
|
|
1112
|
-
if (
|
|
1113
|
-
|
|
1114
|
-
const seconds = Math.ceil(timeUntilNext / 1e3);
|
|
1115
|
-
setError(`Rate limit exceeded. Please wait ${seconds} seconds before sending another message.`);
|
|
1688
|
+
if (attachments.length > 0) {
|
|
1689
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorNoAttachments" });
|
|
1116
1690
|
return;
|
|
1117
1691
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
const curDebugMode = sessionRef.current.debugMode;
|
|
1127
|
-
const tempTurn = createPendingTurn(curSessionId || "new", content, attachments);
|
|
1128
|
-
const replaceFromMessageID = options?.replaceFromMessageID;
|
|
1129
|
-
setTurns((prev) => {
|
|
1130
|
-
if (!replaceFromMessageID) {
|
|
1131
|
-
return [...prev, tempTurn];
|
|
1132
|
-
}
|
|
1133
|
-
const replaceIndex = prev.findIndex((turn) => turn.userTurn.id === replaceFromMessageID);
|
|
1134
|
-
if (replaceIndex === -1) {
|
|
1135
|
-
console.warn(
|
|
1136
|
-
`[ChatContext] replaceFromMessageID "${replaceFromMessageID}" not found in turns; appending as new turn`
|
|
1137
|
-
);
|
|
1138
|
-
return [...prev, tempTurn];
|
|
1139
|
-
}
|
|
1140
|
-
return [...prev.slice(0, replaceIndex), tempTurn];
|
|
1692
|
+
await this._executeSlashCommand(maybeCommand);
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
if (!this.rateLimiter.canMakeRequest()) {
|
|
1696
|
+
const timeUntilNext = this.rateLimiter.getTimeUntilNextRequest();
|
|
1697
|
+
const seconds = Math.ceil(timeUntilNext / 1e3);
|
|
1698
|
+
this._updateInput({
|
|
1699
|
+
inputError: `Rate limit exceeded. Please wait ${seconds} seconds before sending another message.`
|
|
1141
1700
|
});
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
this._updateInput({ message: "", inputError: null });
|
|
1704
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1705
|
+
this._clearStreamError();
|
|
1706
|
+
this._updateMessaging({
|
|
1707
|
+
loading: true,
|
|
1708
|
+
streamingContent: ""
|
|
1709
|
+
});
|
|
1710
|
+
this.abortController = new AbortController();
|
|
1711
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1712
|
+
const curDebugMode = deriveDebugMode(this.state);
|
|
1713
|
+
const replaceFromMessageID = options?.replaceFromMessageID;
|
|
1714
|
+
const tempTurn = createPendingTurn(curSessionId || "new", content, attachments);
|
|
1715
|
+
this.lastSendAttempt = { content, attachments, options };
|
|
1716
|
+
const prevTurns = this.state.messaging.turns;
|
|
1717
|
+
if (!replaceFromMessageID) {
|
|
1718
|
+
this._updateMessaging({ turns: [...prevTurns, tempTurn] });
|
|
1719
|
+
} else {
|
|
1720
|
+
const idx = prevTurns.findIndex((t) => t.userTurn.id === replaceFromMessageID);
|
|
1721
|
+
if (idx === -1) {
|
|
1722
|
+
console.warn(`[ChatMachine] replaceFromMessageID "${replaceFromMessageID}" not found; appending as new turn`);
|
|
1723
|
+
this._updateMessaging({ turns: [...prevTurns, tempTurn] });
|
|
1724
|
+
} else {
|
|
1725
|
+
this._updateMessaging({ turns: [...prevTurns.slice(0, idx), tempTurn] });
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
let shouldDrainQueue = true;
|
|
1729
|
+
try {
|
|
1730
|
+
let activeSessionId = curSessionId;
|
|
1731
|
+
let shouldNavigateAfter = false;
|
|
1732
|
+
if (!activeSessionId || activeSessionId === "new") {
|
|
1733
|
+
const result = await this.dataSource.createSession();
|
|
1734
|
+
if (result) {
|
|
1735
|
+
const createdSessionID = result.id;
|
|
1736
|
+
activeSessionId = createdSessionID;
|
|
1737
|
+
this._updateSession({ currentSessionId: createdSessionID });
|
|
1738
|
+
if (curDebugMode) {
|
|
1739
|
+
this._updateSession({
|
|
1740
|
+
debugModeBySession: {
|
|
1741
|
+
...this.state.session.debugModeBySession,
|
|
1742
|
+
[createdSessionID]: true
|
|
1743
|
+
}
|
|
1154
1744
|
});
|
|
1155
|
-
shouldNavigateAfter = true;
|
|
1156
1745
|
}
|
|
1746
|
+
shouldNavigateAfter = true;
|
|
1157
1747
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1748
|
+
}
|
|
1749
|
+
this.sendingSessionId = activeSessionId || null;
|
|
1750
|
+
let accumulatedContent = "";
|
|
1751
|
+
let createdSessionId;
|
|
1752
|
+
let sessionFetched = false;
|
|
1753
|
+
this._updateMessaging({ isStreaming: true });
|
|
1754
|
+
for await (const chunk of this.dataSource.sendMessage(
|
|
1755
|
+
activeSessionId || "new",
|
|
1756
|
+
content,
|
|
1757
|
+
attachments,
|
|
1758
|
+
this.abortController?.signal,
|
|
1759
|
+
{
|
|
1760
|
+
debugMode: curDebugMode,
|
|
1761
|
+
replaceFromMessageID
|
|
1762
|
+
}
|
|
1763
|
+
)) {
|
|
1764
|
+
if (this.abortController?.signal.aborted) break;
|
|
1765
|
+
if ((chunk.type === "chunk" || chunk.type === "content") && chunk.content) {
|
|
1766
|
+
accumulatedContent += chunk.content;
|
|
1767
|
+
this._updateMessaging({ streamingContent: accumulatedContent });
|
|
1768
|
+
} else if (chunk.type === "error") {
|
|
1769
|
+
throw new Error(chunk.error || "Stream error");
|
|
1770
|
+
} else if (chunk.type === "interrupt" || chunk.type === "done") {
|
|
1771
|
+
if (chunk.sessionId) {
|
|
1772
|
+
createdSessionId = chunk.sessionId;
|
|
1174
1773
|
}
|
|
1175
|
-
if (
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
if (!sessionFetched) {
|
|
1185
|
-
sessionFetched = true;
|
|
1186
|
-
const finalSessionId = createdSessionId || activeSessionId;
|
|
1187
|
-
if (finalSessionId && finalSessionId !== "new") {
|
|
1188
|
-
const state = await dataSource.fetchSession(finalSessionId);
|
|
1189
|
-
if (state) {
|
|
1190
|
-
setSession(state.session);
|
|
1191
|
-
setTurns((prev) => {
|
|
1192
|
-
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1193
|
-
if (hasPendingUserOnly && (!state.turns || state.turns.length === 0)) {
|
|
1194
|
-
return prev;
|
|
1195
|
-
}
|
|
1196
|
-
return state.turns ?? prev;
|
|
1197
|
-
});
|
|
1198
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1199
|
-
}
|
|
1774
|
+
if (!sessionFetched) {
|
|
1775
|
+
sessionFetched = true;
|
|
1776
|
+
const finalSessionId = createdSessionId || activeSessionId;
|
|
1777
|
+
if (finalSessionId && finalSessionId !== "new") {
|
|
1778
|
+
const fetchResult = await this.dataSource.fetchSession(finalSessionId);
|
|
1779
|
+
if (fetchResult) {
|
|
1780
|
+
this._updateSession({ session: fetchResult.session });
|
|
1781
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1782
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1200
1783
|
}
|
|
1201
1784
|
}
|
|
1202
|
-
} else if (chunk.type === "user_message" && chunk.sessionId) {
|
|
1203
|
-
createdSessionId = chunk.sessionId;
|
|
1204
|
-
} else if (chunk.type === "tool_end" && chunk.tool?.name && ARTIFACT_TOOL_NAMES.has(chunk.tool.name)) {
|
|
1205
|
-
setArtifactsInvalidationTrigger((n) => n + 1);
|
|
1206
1785
|
}
|
|
1786
|
+
} else if (chunk.type === "user_message" && chunk.sessionId) {
|
|
1787
|
+
createdSessionId = chunk.sessionId;
|
|
1788
|
+
} else if (chunk.type === "tool_end" && chunk.tool?.name && ARTIFACT_TOOL_NAMES.has(chunk.tool.name)) {
|
|
1789
|
+
this._updateMessaging({
|
|
1790
|
+
artifactsInvalidationTrigger: this.state.messaging.artifactsInvalidationTrigger + 1
|
|
1791
|
+
});
|
|
1207
1792
|
}
|
|
1208
|
-
const targetSessionId = createdSessionId || activeSessionId;
|
|
1209
|
-
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1210
|
-
dataSource.navigateToSession?.(targetSessionId);
|
|
1211
|
-
}
|
|
1212
|
-
} catch (err) {
|
|
1213
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
1214
|
-
setMessage(content);
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
setTurns((prev) => prev.filter((t) => t.id !== tempTurn.id));
|
|
1218
|
-
const errorMessage = err instanceof Error ? err.message : "Error.NetworkError";
|
|
1219
|
-
setInputError(errorMessage);
|
|
1220
|
-
console.error("Send message error:", err);
|
|
1221
|
-
} finally {
|
|
1222
|
-
setLoading(false);
|
|
1223
|
-
setStreamingContent("");
|
|
1224
|
-
setIsStreaming(false);
|
|
1225
|
-
abortControllerRef.current = null;
|
|
1226
1793
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
const convertedAttachments = attachments.map((att) => ({
|
|
1244
|
-
clientKey: att.clientKey || crypto.randomUUID(),
|
|
1245
|
-
filename: att.filename,
|
|
1246
|
-
mimeType: att.mimeType,
|
|
1247
|
-
sizeBytes: att.sizeBytes,
|
|
1248
|
-
base64Data: att.base64Data,
|
|
1249
|
-
url: att.url,
|
|
1250
|
-
preview: att.preview
|
|
1251
|
-
}));
|
|
1252
|
-
sendMessageDirect(message, convertedAttachments);
|
|
1253
|
-
},
|
|
1254
|
-
[message, sendMessageDirect]
|
|
1255
|
-
);
|
|
1256
|
-
const handleUnqueue = React.useCallback(() => {
|
|
1257
|
-
if (messageQueue.length === 0) {
|
|
1258
|
-
return null;
|
|
1259
|
-
}
|
|
1260
|
-
const lastQueued = messageQueue[messageQueue.length - 1];
|
|
1261
|
-
setMessageQueue((prev) => prev.slice(0, -1));
|
|
1262
|
-
return {
|
|
1263
|
-
content: lastQueued.content,
|
|
1264
|
-
attachments: lastQueued.attachments
|
|
1265
|
-
};
|
|
1266
|
-
}, [messageQueue]);
|
|
1267
|
-
const handleRegenerate = React.useCallback(
|
|
1268
|
-
async (turnId) => {
|
|
1269
|
-
const curSessionId = sessionRef.current.currentSessionId;
|
|
1270
|
-
if (!curSessionId || curSessionId === "new") return;
|
|
1271
|
-
const turn = messagingRef.current.turns.find((t) => t.id === turnId);
|
|
1272
|
-
if (!turn) return;
|
|
1273
|
-
setError(null);
|
|
1274
|
-
try {
|
|
1275
|
-
await sendMessageDirect(turn.userTurn.content, turn.userTurn.attachments, {
|
|
1276
|
-
replaceFromMessageID: turn.userTurn.id
|
|
1277
|
-
});
|
|
1278
|
-
} catch (err) {
|
|
1279
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to regenerate response";
|
|
1280
|
-
setError(errorMessage);
|
|
1281
|
-
console.error("Regenerate error:", err);
|
|
1794
|
+
if (!sessionFetched) {
|
|
1795
|
+
const finalSessionId = createdSessionId || activeSessionId;
|
|
1796
|
+
if (finalSessionId && finalSessionId !== "new") {
|
|
1797
|
+
try {
|
|
1798
|
+
const fetchResult = await this.dataSource.fetchSession(finalSessionId);
|
|
1799
|
+
if (fetchResult) {
|
|
1800
|
+
this._updateSession({ session: fetchResult.session });
|
|
1801
|
+
this._updateMessaging({
|
|
1802
|
+
turns: fetchResult.turns ?? [],
|
|
1803
|
+
pendingQuestion: fetchResult.pendingQuestion || null
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
} catch (fetchErr) {
|
|
1807
|
+
console.error("Failed to fetch session after stream:", fetchErr);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1282
1810
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
setMessage(newContent);
|
|
1291
|
-
setTurns((prev) => prev.filter((t) => t.id !== turnId));
|
|
1292
|
-
return;
|
|
1811
|
+
const targetSessionId = createdSessionId || activeSessionId;
|
|
1812
|
+
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1813
|
+
if (this.onSessionCreated) {
|
|
1814
|
+
this.onSessionCreated(targetSessionId);
|
|
1815
|
+
} else {
|
|
1816
|
+
this.dataSource.navigateToSession?.(targetSessionId);
|
|
1817
|
+
}
|
|
1293
1818
|
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1819
|
+
this._clearStreamError();
|
|
1820
|
+
this.lastSendAttempt = null;
|
|
1821
|
+
} catch (err) {
|
|
1822
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1823
|
+
this._updateInput({ message: content });
|
|
1824
|
+
this._clearStreamError();
|
|
1825
|
+
shouldDrainQueue = false;
|
|
1297
1826
|
return;
|
|
1298
1827
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1828
|
+
this._updateMessaging({
|
|
1829
|
+
turns: this.state.messaging.turns.filter((t) => t.id !== tempTurn.id)
|
|
1830
|
+
});
|
|
1831
|
+
const normalized = normalizeRPCError(err, "Failed to send message");
|
|
1832
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1833
|
+
this._updateMessaging({
|
|
1834
|
+
streamError: normalized.userMessage,
|
|
1835
|
+
streamErrorRetryable: normalized.retryable
|
|
1836
|
+
});
|
|
1837
|
+
console.error("Send message error:", err);
|
|
1838
|
+
shouldDrainQueue = false;
|
|
1839
|
+
} finally {
|
|
1840
|
+
this._updateMessaging({
|
|
1841
|
+
loading: false,
|
|
1842
|
+
streamingContent: "",
|
|
1843
|
+
isStreaming: false
|
|
1844
|
+
});
|
|
1845
|
+
this.abortController = null;
|
|
1846
|
+
this.sendingSessionId = null;
|
|
1847
|
+
if (shouldDrainQueue) {
|
|
1848
|
+
const queue = this.state.input.messageQueue;
|
|
1849
|
+
if (queue.length > 0) {
|
|
1850
|
+
const next = queue[0];
|
|
1851
|
+
this._updateInput({ messageQueue: queue.slice(1) });
|
|
1852
|
+
setTimeout(() => {
|
|
1853
|
+
this._sendMessageCore(next.content, next.attachments);
|
|
1854
|
+
}, 0);
|
|
1855
|
+
}
|
|
1308
1856
|
}
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
// ── Retry ───────────────────────────────────────────────────────────────
|
|
1860
|
+
async _retryLastMessage() {
|
|
1861
|
+
const lastAttempt = this.lastSendAttempt;
|
|
1862
|
+
if (!lastAttempt || this.state.messaging.loading) return;
|
|
1863
|
+
this._clearStreamError();
|
|
1864
|
+
this._updateInput({ inputError: null });
|
|
1865
|
+
await this._sendMessageDirect(lastAttempt.content, lastAttempt.attachments, lastAttempt.options);
|
|
1866
|
+
}
|
|
1867
|
+
// ── Regenerate / Edit ───────────────────────────────────────────────────
|
|
1868
|
+
async _handleRegenerate(turnId) {
|
|
1869
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1870
|
+
if (!curSessionId || curSessionId === "new") return;
|
|
1871
|
+
const turn = this.state.messaging.turns.find((t) => t.id === turnId);
|
|
1872
|
+
if (!turn) return;
|
|
1873
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1874
|
+
await this._sendMessageDirect(turn.userTurn.content, turn.userTurn.attachments, {
|
|
1875
|
+
replaceFromMessageID: turn.userTurn.id
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
async _handleEdit(turnId, newContent) {
|
|
1879
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1880
|
+
if (!curSessionId || curSessionId === "new") {
|
|
1881
|
+
this._updateInput({ message: newContent });
|
|
1882
|
+
this._updateMessaging({
|
|
1883
|
+
turns: this.state.messaging.turns.filter((t) => t.id !== turnId)
|
|
1884
|
+
});
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
const turn = this.state.messaging.turns.find((t) => t.id === turnId);
|
|
1888
|
+
if (!turn) {
|
|
1889
|
+
this._updateSession({ error: "Failed to edit message", errorRetryable: false });
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1893
|
+
await this._sendMessageDirect(newContent, turn.userTurn.attachments, {
|
|
1894
|
+
replaceFromMessageID: turn.userTurn.id
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
async _handleCopy(text) {
|
|
1898
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) return;
|
|
1899
|
+
try {
|
|
1900
|
+
await navigator.clipboard.writeText(text);
|
|
1901
|
+
} catch {
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
// ── HITL ────────────────────────────────────────────────────────────────
|
|
1905
|
+
async _handleSubmitQuestionAnswers(answers) {
|
|
1906
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1907
|
+
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1908
|
+
if (!curSessionId || !curPendingQuestion) return;
|
|
1909
|
+
this._updateMessaging({ loading: true });
|
|
1910
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1911
|
+
const previousPendingQuestion = curPendingQuestion;
|
|
1912
|
+
this._updateMessaging({ pendingQuestion: null });
|
|
1913
|
+
try {
|
|
1914
|
+
const result = await this.dataSource.submitQuestionAnswers(
|
|
1915
|
+
curSessionId,
|
|
1916
|
+
previousPendingQuestion.id,
|
|
1917
|
+
answers
|
|
1918
|
+
);
|
|
1919
|
+
if (this.disposed) return;
|
|
1920
|
+
if (result.success) {
|
|
1921
|
+
if (curSessionId !== "new") {
|
|
1922
|
+
try {
|
|
1923
|
+
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1924
|
+
if (this.disposed) return;
|
|
1925
|
+
if (fetchResult) {
|
|
1926
|
+
this._updateMessaging({
|
|
1927
|
+
turns: fetchResult.turns,
|
|
1928
|
+
pendingQuestion: fetchResult.pendingQuestion || null
|
|
1929
|
+
});
|
|
1930
|
+
} else {
|
|
1931
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1932
|
+
this._updateSession({ error: "Failed to load updated session", errorRetryable: false });
|
|
1344
1933
|
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1934
|
+
} catch (fetchErr) {
|
|
1935
|
+
if (this.disposed) return;
|
|
1936
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1937
|
+
const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
|
|
1938
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1348
1939
|
}
|
|
1349
|
-
} catch (err) {
|
|
1350
|
-
setPendingQuestion(previousPendingQuestion);
|
|
1351
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to submit answers";
|
|
1352
|
-
setError(errorMessage);
|
|
1353
|
-
} finally {
|
|
1354
|
-
setLoading(false);
|
|
1355
1940
|
}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1941
|
+
} else {
|
|
1942
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1943
|
+
this._updateSession({ error: result.error || "Failed to submit answers", errorRetryable: false });
|
|
1944
|
+
}
|
|
1945
|
+
} catch (err) {
|
|
1946
|
+
if (this.disposed) return;
|
|
1947
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1948
|
+
const normalized = normalizeRPCError(err, "Failed to submit answers");
|
|
1949
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1950
|
+
} finally {
|
|
1951
|
+
if (!this.disposed) {
|
|
1952
|
+
this._updateMessaging({ loading: false });
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
async _handleRejectPendingQuestion() {
|
|
1957
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1958
|
+
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1363
1959
|
if (!curSessionId || !curPendingQuestion) return;
|
|
1364
1960
|
try {
|
|
1365
|
-
const result = await dataSource.rejectPendingQuestion(curSessionId);
|
|
1961
|
+
const result = await this.dataSource.rejectPendingQuestion(curSessionId);
|
|
1962
|
+
if (this.disposed) return;
|
|
1366
1963
|
if (result.success) {
|
|
1367
|
-
|
|
1964
|
+
this._updateMessaging({ pendingQuestion: null });
|
|
1368
1965
|
if (curSessionId !== "new") {
|
|
1369
|
-
const
|
|
1370
|
-
if (
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
return prev;
|
|
1376
|
-
}
|
|
1377
|
-
return state.turns ?? prev;
|
|
1378
|
-
});
|
|
1379
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1966
|
+
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1967
|
+
if (this.disposed) return;
|
|
1968
|
+
if (fetchResult) {
|
|
1969
|
+
this._updateSession({ session: fetchResult.session });
|
|
1970
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1971
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1380
1972
|
}
|
|
1381
1973
|
}
|
|
1382
1974
|
} else {
|
|
1383
|
-
|
|
1975
|
+
this._updateSession({ error: result.error || "Failed to reject question", errorRetryable: false });
|
|
1384
1976
|
}
|
|
1385
1977
|
} catch (err) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1978
|
+
if (this.disposed) return;
|
|
1979
|
+
const normalized = normalizeRPCError(err, "Failed to reject question");
|
|
1980
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1388
1981
|
}
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1982
|
+
}
|
|
1983
|
+
// ── Input / queue ───────────────────────────────────────────────────────
|
|
1984
|
+
_handleSubmit(e, attachments = []) {
|
|
1985
|
+
e.preventDefault();
|
|
1986
|
+
const msg = this.state.input.message;
|
|
1987
|
+
if (!msg.trim() && attachments.length === 0) return;
|
|
1988
|
+
this._updateInput({ inputError: null });
|
|
1989
|
+
this._clearStreamError();
|
|
1990
|
+
const convertedAttachments = attachments.map((att) => ({
|
|
1991
|
+
clientKey: att.clientKey || crypto.randomUUID(),
|
|
1992
|
+
filename: att.filename,
|
|
1993
|
+
mimeType: att.mimeType,
|
|
1994
|
+
sizeBytes: att.sizeBytes,
|
|
1995
|
+
base64Data: att.base64Data,
|
|
1996
|
+
url: att.url,
|
|
1997
|
+
preview: att.preview
|
|
1998
|
+
}));
|
|
1999
|
+
if (this.state.messaging.loading) {
|
|
2000
|
+
const ok = this._enqueueMessage(msg.trim(), convertedAttachments);
|
|
2001
|
+
if (ok) {
|
|
2002
|
+
this._updateInput({ message: "" });
|
|
2003
|
+
}
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
this._sendMessage(msg.trim(), convertedAttachments);
|
|
2007
|
+
}
|
|
2008
|
+
_handleUnqueue() {
|
|
2009
|
+
const queue = this.state.input.messageQueue;
|
|
2010
|
+
if (queue.length === 0) return null;
|
|
2011
|
+
const last = queue[queue.length - 1];
|
|
2012
|
+
this._updateInput({ messageQueue: queue.slice(0, -1) });
|
|
2013
|
+
return { content: last.content, attachments: last.attachments };
|
|
2014
|
+
}
|
|
2015
|
+
_enqueueMessage(content, attachments) {
|
|
2016
|
+
if (this.state.input.messageQueue.length >= MAX_QUEUE_SIZE) {
|
|
2017
|
+
this._updateInput({ inputError: "BiChat.Input.QueueFull" });
|
|
2018
|
+
return false;
|
|
2019
|
+
}
|
|
2020
|
+
this._updateInput({
|
|
2021
|
+
messageQueue: [...this.state.input.messageQueue, { content, attachments }]
|
|
2022
|
+
});
|
|
2023
|
+
return true;
|
|
2024
|
+
}
|
|
2025
|
+
_removeQueueItem(index) {
|
|
2026
|
+
this._updateInput({
|
|
2027
|
+
messageQueue: this.state.input.messageQueue.filter((_, i) => i !== index)
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
_updateQueueItem(index, content) {
|
|
2031
|
+
this._updateInput({
|
|
2032
|
+
messageQueue: this.state.input.messageQueue.map(
|
|
2033
|
+
(item, i) => i === index ? { ...item, content } : item
|
|
2034
|
+
)
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2038
|
+
function sessionDebugUsageEqual(a, b) {
|
|
2039
|
+
if (!a) return false;
|
|
2040
|
+
return a.promptTokens === b.promptTokens && a.completionTokens === b.completionTokens && a.totalTokens === b.totalTokens && a.turnsWithUsage === b.turnsWithUsage && a.latestPromptTokens === b.latestPromptTokens && a.latestCompletionTokens === b.latestCompletionTokens && a.latestTotalTokens === b.latestTotalTokens;
|
|
1446
2041
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
2042
|
+
var MachineCtx = React.createContext(null);
|
|
2043
|
+
var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
2044
|
+
maxRequests: 20,
|
|
2045
|
+
windowMs: 6e4
|
|
2046
|
+
};
|
|
2047
|
+
function ChatSessionProvider({
|
|
2048
|
+
dataSource,
|
|
2049
|
+
sessionId,
|
|
2050
|
+
rateLimiter: externalRateLimiter,
|
|
2051
|
+
rateLimitConfig,
|
|
2052
|
+
onSessionCreated,
|
|
2053
|
+
children
|
|
2054
|
+
}) {
|
|
2055
|
+
const machineRef = React.useRef(null);
|
|
2056
|
+
if (!machineRef.current) {
|
|
2057
|
+
machineRef.current = new ChatMachine({
|
|
2058
|
+
dataSource,
|
|
2059
|
+
rateLimiter: externalRateLimiter || new RateLimiter(rateLimitConfig || DEFAULT_RATE_LIMIT_CONFIG),
|
|
2060
|
+
onSessionCreated
|
|
2061
|
+
});
|
|
1451
2062
|
}
|
|
1452
|
-
|
|
2063
|
+
const machine = machineRef.current;
|
|
2064
|
+
React.useEffect(() => {
|
|
2065
|
+
machine.updateConfig({ dataSource, onSessionCreated });
|
|
2066
|
+
}, [machine, dataSource, onSessionCreated]);
|
|
2067
|
+
React.useEffect(() => {
|
|
2068
|
+
machine.setSessionId(sessionId);
|
|
2069
|
+
}, [machine, sessionId]);
|
|
2070
|
+
React.useEffect(() => {
|
|
2071
|
+
return () => {
|
|
2072
|
+
machine.dispose();
|
|
2073
|
+
};
|
|
2074
|
+
}, [machine]);
|
|
2075
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MachineCtx.Provider, { value: machine, children });
|
|
1453
2076
|
}
|
|
1454
|
-
function
|
|
1455
|
-
const
|
|
1456
|
-
if (!
|
|
1457
|
-
throw new Error("
|
|
2077
|
+
function useMachine() {
|
|
2078
|
+
const machine = React.useContext(MachineCtx);
|
|
2079
|
+
if (!machine) {
|
|
2080
|
+
throw new Error("Chat hooks must be used within ChatSessionProvider");
|
|
1458
2081
|
}
|
|
1459
|
-
return
|
|
2082
|
+
return machine;
|
|
2083
|
+
}
|
|
2084
|
+
function useChatSession() {
|
|
2085
|
+
const machine = useMachine();
|
|
2086
|
+
return React.useSyncExternalStore(
|
|
2087
|
+
machine.subscribeSession,
|
|
2088
|
+
machine.getSessionSnapshot,
|
|
2089
|
+
machine.getSessionSnapshot
|
|
2090
|
+
// SSR fallback
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
function useChatMessaging() {
|
|
2094
|
+
const machine = useMachine();
|
|
2095
|
+
return React.useSyncExternalStore(
|
|
2096
|
+
machine.subscribeMessaging,
|
|
2097
|
+
machine.getMessagingSnapshot,
|
|
2098
|
+
machine.getMessagingSnapshot
|
|
2099
|
+
);
|
|
1460
2100
|
}
|
|
1461
2101
|
function useOptionalChatMessaging() {
|
|
1462
|
-
|
|
2102
|
+
const machine = React.useContext(MachineCtx);
|
|
2103
|
+
const snapshot = React.useSyncExternalStore(
|
|
2104
|
+
machine ? machine.subscribeMessaging : noopSubscribe,
|
|
2105
|
+
machine ? machine.getMessagingSnapshot : nullSnapshot,
|
|
2106
|
+
machine ? machine.getMessagingSnapshot : nullSnapshot
|
|
2107
|
+
);
|
|
2108
|
+
return machine ? snapshot : null;
|
|
2109
|
+
}
|
|
2110
|
+
function useChatInput() {
|
|
2111
|
+
const machine = useMachine();
|
|
2112
|
+
return React.useSyncExternalStore(
|
|
2113
|
+
machine.subscribeInput,
|
|
2114
|
+
machine.getInputSnapshot,
|
|
2115
|
+
machine.getInputSnapshot
|
|
2116
|
+
);
|
|
2117
|
+
}
|
|
2118
|
+
function noopSubscribe() {
|
|
2119
|
+
return () => {
|
|
2120
|
+
};
|
|
1463
2121
|
}
|
|
1464
|
-
function
|
|
1465
|
-
|
|
1466
|
-
if (!context) {
|
|
1467
|
-
throw new Error("useChatInput must be used within ChatSessionProvider");
|
|
1468
|
-
}
|
|
1469
|
-
return context;
|
|
2122
|
+
function nullSnapshot() {
|
|
2123
|
+
return null;
|
|
1470
2124
|
}
|
|
1471
2125
|
|
|
1472
2126
|
// ui/src/bichat/components/ChatHeader.tsx
|
|
@@ -1563,6 +2217,26 @@ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
|
|
|
1563
2217
|
] })
|
|
1564
2218
|
] }) });
|
|
1565
2219
|
}
|
|
2220
|
+
function formatRelativeTime(date, t) {
|
|
2221
|
+
const messageDate = new Date(date);
|
|
2222
|
+
const now = /* @__PURE__ */ new Date();
|
|
2223
|
+
const diffMins = dateFns.differenceInMinutes(now, messageDate);
|
|
2224
|
+
const diffHours = dateFns.differenceInHours(now, messageDate);
|
|
2225
|
+
const diffDays = dateFns.differenceInDays(now, messageDate);
|
|
2226
|
+
if (diffMins < 1) {
|
|
2227
|
+
return t ? t("BiChat.RelativeTime.JustNow") : "Just now";
|
|
2228
|
+
}
|
|
2229
|
+
if (diffMins < 60) {
|
|
2230
|
+
return t ? t("BiChat.RelativeTime.MinutesAgo", { count: diffMins }) : `${diffMins}m ago`;
|
|
2231
|
+
}
|
|
2232
|
+
if (diffHours < 24) {
|
|
2233
|
+
return t ? t("BiChat.RelativeTime.HoursAgo", { count: diffHours }) : `${diffHours}h ago`;
|
|
2234
|
+
}
|
|
2235
|
+
if (diffDays <= 7) {
|
|
2236
|
+
return t ? t("BiChat.RelativeTime.DaysAgo", { count: diffDays }) : `${diffDays}d ago`;
|
|
2237
|
+
}
|
|
2238
|
+
return dateFns.format(messageDate, "HH:mm");
|
|
2239
|
+
}
|
|
1566
2240
|
var MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1567
2241
|
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
1568
2242
|
"image/jpeg",
|
|
@@ -2284,7 +2958,7 @@ function UserMessage({
|
|
|
2284
2958
|
[selectedImageIndex, imageAttachments.length]
|
|
2285
2959
|
);
|
|
2286
2960
|
const currentAttachment = selectedImageIndex !== null ? imageAttachments[selectedImageIndex] : null;
|
|
2287
|
-
const timestamp =
|
|
2961
|
+
const timestamp = formatRelativeTime(turn.createdAt, t);
|
|
2288
2962
|
const avatarSlotProps = { initials };
|
|
2289
2963
|
const contentSlotProps = { content: turn.content };
|
|
2290
2964
|
const attachmentsSlotProps = {
|
|
@@ -2329,7 +3003,7 @@ function UserMessage({
|
|
|
2329
3003
|
value: draftContent,
|
|
2330
3004
|
onChange: (e) => setDraftContent(e.target.value),
|
|
2331
3005
|
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",
|
|
2332
|
-
"aria-label": "
|
|
3006
|
+
"aria-label": t("BiChat.Message.EditMessage")
|
|
2333
3007
|
}
|
|
2334
3008
|
),
|
|
2335
3009
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
@@ -2339,7 +3013,7 @@ function UserMessage({
|
|
|
2339
3013
|
type: "button",
|
|
2340
3014
|
onClick: handleEditCancel,
|
|
2341
3015
|
className: "cursor-pointer px-3 py-1.5 rounded-lg bg-white/10 hover:bg-white/15 transition-colors text-sm font-medium",
|
|
2342
|
-
children: "Cancel"
|
|
3016
|
+
children: t("BiChat.Message.Cancel")
|
|
2343
3017
|
}
|
|
2344
3018
|
),
|
|
2345
3019
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2349,7 +3023,7 @@ function UserMessage({
|
|
|
2349
3023
|
onClick: handleEditSave,
|
|
2350
3024
|
className: "cursor-pointer 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",
|
|
2351
3025
|
disabled: !draftContent.trim() || draftContent === turn.content,
|
|
2352
|
-
children: "Save"
|
|
3026
|
+
children: t("BiChat.Message.Save")
|
|
2353
3027
|
}
|
|
2354
3028
|
)
|
|
2355
3029
|
] })
|
|
@@ -2364,7 +3038,7 @@ function UserMessage({
|
|
|
2364
3038
|
{
|
|
2365
3039
|
onClick: handleCopyClick,
|
|
2366
3040
|
className: `cursor-pointer ${classes.actionButton} ${isCopied ? "text-green-600 dark:text-green-400" : ""}`,
|
|
2367
|
-
"aria-label": "
|
|
3041
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
2368
3042
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
2369
3043
|
children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 14, weight: "regular" })
|
|
2370
3044
|
}
|
|
@@ -2374,8 +3048,8 @@ function UserMessage({
|
|
|
2374
3048
|
{
|
|
2375
3049
|
onClick: handleEditClick,
|
|
2376
3050
|
className: `cursor-pointer ${classes.actionButton}`,
|
|
2377
|
-
"aria-label": "
|
|
2378
|
-
title: "
|
|
3051
|
+
"aria-label": t("BiChat.Message.EditMessage"),
|
|
3052
|
+
title: t("BiChat.Message.EditMessage"),
|
|
2379
3053
|
disabled: isEditing,
|
|
2380
3054
|
children: /* @__PURE__ */ jsxRuntime.jsx(react.PencilSimple, { size: 14, weight: "regular" })
|
|
2381
3055
|
}
|
|
@@ -2425,6 +3099,7 @@ function UserTurnView({
|
|
|
2425
3099
|
}
|
|
2426
3100
|
);
|
|
2427
3101
|
}
|
|
3102
|
+
init_useTranslation();
|
|
2428
3103
|
function toBase64(str) {
|
|
2429
3104
|
const bytes = new TextEncoder().encode(str);
|
|
2430
3105
|
let binary = "";
|
|
@@ -2434,16 +3109,17 @@ function toBase64(str) {
|
|
|
2434
3109
|
return btoa(binary);
|
|
2435
3110
|
}
|
|
2436
3111
|
function CodeOutputsPanel({ outputs }) {
|
|
3112
|
+
const { t } = useTranslation();
|
|
2437
3113
|
if (!outputs || outputs.length === 0) return null;
|
|
2438
3114
|
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: [
|
|
2439
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold text-gray-600 dark:text-gray-400 mb-2", children: "
|
|
3115
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold text-gray-600 dark:text-gray-400 mb-2", children: t("BiChat.CodeOutput.Title") }),
|
|
2440
3116
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: outputs.map((output, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2441
3117
|
output.type === "image" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative group", children: [
|
|
2442
3118
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2443
3119
|
"img",
|
|
2444
3120
|
{
|
|
2445
3121
|
src: output.content.startsWith("data:") ? output.content : `data:${output.mimeType || "image/png"};base64,${output.content}`,
|
|
2446
|
-
alt: output.filename || "
|
|
3122
|
+
alt: output.filename || t("BiChat.CodeOutput.CodeOutput"),
|
|
2447
3123
|
className: "max-w-full rounded border border-gray-300 dark:border-gray-600"
|
|
2448
3124
|
}
|
|
2449
3125
|
),
|
|
@@ -2475,19 +3151,23 @@ function CodeOutputsPanel({ outputs }) {
|
|
|
2475
3151
|
] })
|
|
2476
3152
|
] }),
|
|
2477
3153
|
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: [
|
|
2478
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Error" }),
|
|
3154
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: t("BiChat.Error.Label") }),
|
|
2479
3155
|
/* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap", children: output.content })
|
|
2480
3156
|
] })
|
|
2481
3157
|
] }, index)) })
|
|
2482
3158
|
] });
|
|
2483
3159
|
}
|
|
2484
3160
|
var CodeOutputsPanel_default = CodeOutputsPanel;
|
|
3161
|
+
|
|
3162
|
+
// ui/src/bichat/components/StreamingCursor.tsx
|
|
3163
|
+
init_useTranslation();
|
|
2485
3164
|
function StreamingCursor() {
|
|
3165
|
+
const { t } = useTranslation();
|
|
2486
3166
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2487
3167
|
"span",
|
|
2488
3168
|
{
|
|
2489
3169
|
className: "inline-block w-1.5 h-4 ml-0.5 bg-primary-600 dark:bg-primary-500 animate-pulse",
|
|
2490
|
-
"aria-label": "
|
|
3170
|
+
"aria-label": t("BiChat.Common.AITyping")
|
|
2491
3171
|
}
|
|
2492
3172
|
);
|
|
2493
3173
|
}
|
|
@@ -3266,7 +3946,7 @@ function AssistantMessage({
|
|
|
3266
3946
|
await onRegenerate(turnId);
|
|
3267
3947
|
}
|
|
3268
3948
|
}, [onRegenerate, turnId]);
|
|
3269
|
-
const timestamp =
|
|
3949
|
+
const timestamp = formatRelativeTime(turn.createdAt, t);
|
|
3270
3950
|
const avatarSlotProps = { text: isSystemMessage ? "SYS" : "AI" };
|
|
3271
3951
|
const contentSlotProps = {
|
|
3272
3952
|
content: turn.content,
|
|
@@ -3320,7 +4000,7 @@ function AssistantMessage({
|
|
|
3320
4000
|
{
|
|
3321
4001
|
fallback: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-400 dark:text-gray-500", children: [
|
|
3322
4002
|
/* @__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" }),
|
|
3323
|
-
"Loading
|
|
4003
|
+
t("BiChat.Common.Loading")
|
|
3324
4004
|
] }),
|
|
3325
4005
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3326
4006
|
MarkdownRenderer2,
|
|
@@ -3374,7 +4054,7 @@ function AssistantMessage({
|
|
|
3374
4054
|
]
|
|
3375
4055
|
}
|
|
3376
4056
|
),
|
|
3377
|
-
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
|
|
4057
|
+
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: t("BiChat.Common.Loading") }), children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownRenderer2, { content: turn.explanation }) }) })
|
|
3378
4058
|
] })
|
|
3379
4059
|
) }),
|
|
3380
4060
|
showDebug && /* @__PURE__ */ jsxRuntime.jsx(DebugPanel, { trace: turn.debug })
|
|
@@ -3395,7 +4075,7 @@ function AssistantMessage({
|
|
|
3395
4075
|
{
|
|
3396
4076
|
onClick: handleCopyClick,
|
|
3397
4077
|
className: `cursor-pointer ${classes.actionButton} ${isCopied ? "text-green-600 dark:text-green-400" : ""}`,
|
|
3398
|
-
"aria-label": "
|
|
4078
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
3399
4079
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
3400
4080
|
children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 14, weight: "regular" })
|
|
3401
4081
|
}
|
|
@@ -3405,8 +4085,8 @@ function AssistantMessage({
|
|
|
3405
4085
|
{
|
|
3406
4086
|
onClick: handleRegenerateClick,
|
|
3407
4087
|
className: `cursor-pointer ${classes.actionButton}`,
|
|
3408
|
-
"aria-label": "Regenerate
|
|
3409
|
-
title: "Regenerate",
|
|
4088
|
+
"aria-label": t("BiChat.Message.Regenerate"),
|
|
4089
|
+
title: t("BiChat.Message.Regenerate"),
|
|
3410
4090
|
children: /* @__PURE__ */ jsxRuntime.jsx(react.ArrowsClockwise, { size: 14, weight: "regular" })
|
|
3411
4091
|
}
|
|
3412
4092
|
)
|
|
@@ -3415,8 +4095,6 @@ function AssistantMessage({
|
|
|
3415
4095
|
] })
|
|
3416
4096
|
] });
|
|
3417
4097
|
}
|
|
3418
|
-
|
|
3419
|
-
// ui/src/bichat/components/SystemMessage.tsx
|
|
3420
4098
|
init_useTranslation();
|
|
3421
4099
|
var MarkdownRenderer3 = React.lazy(
|
|
3422
4100
|
() => Promise.resolve().then(() => (init_MarkdownRenderer(), MarkdownRenderer_exports)).then((module) => ({ default: module.MarkdownRenderer }))
|
|
@@ -3483,7 +4161,7 @@ function SystemMessage({
|
|
|
3483
4161
|
setIsCopied(false);
|
|
3484
4162
|
}
|
|
3485
4163
|
}, [content, onCopy]);
|
|
3486
|
-
const timestamp =
|
|
4164
|
+
const timestamp = formatRelativeTime(createdAt, t);
|
|
3487
4165
|
const resolvedHeight = isExpanded ? contentHeight : COLLAPSED_HEIGHT;
|
|
3488
4166
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full max-w-3xl px-2 sm:px-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative overflow-hidden rounded-xl border border-gray-200/80 dark:border-gray-700/60 bg-gradient-to-b from-gray-50/80 to-gray-100/40 dark:from-gray-800/40 dark:to-gray-900/30 shadow-[0_1px_3px_rgba(0,0,0,0.04)] dark:shadow-[0_1px_3px_rgba(0,0,0,0.2)]", children: [
|
|
3489
4167
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-gray-300/40 dark:via-gray-600/20 to-transparent" }),
|
|
@@ -3503,7 +4181,7 @@ function SystemMessage({
|
|
|
3503
4181
|
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50
|
|
3504
4182
|
${isCopied ? "text-green-600 dark:text-green-400" : "text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-200/50 dark:hover:bg-gray-700/40"}
|
|
3505
4183
|
`,
|
|
3506
|
-
"aria-label": "
|
|
4184
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
3507
4185
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
3508
4186
|
children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 13, weight: "bold" }) : /* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 13, weight: "regular" })
|
|
3509
4187
|
}
|
|
@@ -3843,23 +4521,6 @@ var dropdownVariants = {
|
|
|
3843
4521
|
transition: { duration: 0.1 }
|
|
3844
4522
|
}
|
|
3845
4523
|
};
|
|
3846
|
-
var toastVariants = {
|
|
3847
|
-
initial: { opacity: 0, y: -8 },
|
|
3848
|
-
animate: {
|
|
3849
|
-
opacity: 1,
|
|
3850
|
-
y: 0,
|
|
3851
|
-
transition: {
|
|
3852
|
-
duration: prefersReducedMotion() ? 0 : 0.2
|
|
3853
|
-
}
|
|
3854
|
-
},
|
|
3855
|
-
exit: {
|
|
3856
|
-
opacity: 0,
|
|
3857
|
-
y: -8,
|
|
3858
|
-
transition: {
|
|
3859
|
-
duration: prefersReducedMotion() ? 0 : 0.15
|
|
3860
|
-
}
|
|
3861
|
-
}
|
|
3862
|
-
};
|
|
3863
4524
|
var sessionItemVariants = {
|
|
3864
4525
|
initial: { opacity: 0, x: -20 },
|
|
3865
4526
|
animate: {
|
|
@@ -3921,7 +4582,7 @@ var prefersReducedMotion2 = () => {
|
|
|
3921
4582
|
var getRandomVerb = (verbs, current) => {
|
|
3922
4583
|
const available = verbs.filter((v) => v !== current);
|
|
3923
4584
|
if (available.length === 0) {
|
|
3924
|
-
return current || verbs[0] || "
|
|
4585
|
+
return current || verbs[0] || "";
|
|
3925
4586
|
}
|
|
3926
4587
|
return available[Math.floor(Math.random() * available.length)];
|
|
3927
4588
|
};
|
|
@@ -3963,7 +4624,7 @@ function TypingIndicator({
|
|
|
3963
4624
|
animate: "animate",
|
|
3964
4625
|
exit: "exit",
|
|
3965
4626
|
className: "text-sm bichat-thinking-shimmer block",
|
|
3966
|
-
"aria-label":
|
|
4627
|
+
"aria-label": t("BiChat.Thinking.AriaLabel", { verb }),
|
|
3967
4628
|
children: [
|
|
3968
4629
|
verb,
|
|
3969
4630
|
"..."
|
|
@@ -3977,6 +4638,9 @@ function TypingIndicator({
|
|
|
3977
4638
|
}
|
|
3978
4639
|
var MemoizedTypingIndicator = React.memo(TypingIndicator);
|
|
3979
4640
|
MemoizedTypingIndicator.displayName = "TypingIndicator";
|
|
4641
|
+
|
|
4642
|
+
// ui/src/bichat/components/ScrollToBottomButton.tsx
|
|
4643
|
+
init_useTranslation();
|
|
3980
4644
|
function ScrollToBottomButton({
|
|
3981
4645
|
show,
|
|
3982
4646
|
onClick,
|
|
@@ -3984,6 +4648,7 @@ function ScrollToBottomButton({
|
|
|
3984
4648
|
disabled = false,
|
|
3985
4649
|
label
|
|
3986
4650
|
}) {
|
|
4651
|
+
const { t } = useTranslation();
|
|
3987
4652
|
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: show && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3988
4653
|
"div",
|
|
3989
4654
|
{
|
|
@@ -3999,7 +4664,7 @@ function ScrollToBottomButton({
|
|
|
3999
4664
|
onClick: disabled ? void 0 : onClick,
|
|
4000
4665
|
disabled,
|
|
4001
4666
|
className: `pointer-events-auto cursor-pointer 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 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 disabled:opacity-40 disabled:cursor-not-allowed ${label ? "flex items-center gap-1.5 px-4 py-2 rounded-full bg-primary-600 dark:bg-primary-500 border-primary-600 dark:border-primary-500 hover:bg-primary-700 dark:hover:bg-primary-600 active:bg-primary-800 dark:active:bg-primary-700" : "p-2.5 rounded-full bg-white dark:bg-gray-800"}`,
|
|
4002
|
-
"aria-label": label || "
|
|
4667
|
+
"aria-label": label || t("BiChat.Common.ScrollToBottom"),
|
|
4003
4668
|
children: label ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4004
4669
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-white", children: label }),
|
|
4005
4670
|
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowDown, { size: 16, weight: "bold", className: "text-white" })
|
|
@@ -4013,23 +4678,6 @@ function ScrollToBottomButton({
|
|
|
4013
4678
|
) });
|
|
4014
4679
|
}
|
|
4015
4680
|
var ScrollToBottomButton_default = ScrollToBottomButton;
|
|
4016
|
-
function CompactionDoodle({ title, subtitle }) {
|
|
4017
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4018
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-10 h-10", children: [
|
|
4019
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full bg-primary-500/20 animate-pulse motion-reduce:animate-none" }),
|
|
4020
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-1 rounded-full bg-primary-500/40 animate-pulse motion-reduce:animate-none" }),
|
|
4021
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-3 rounded-full bg-primary-600" })
|
|
4022
|
-
] }),
|
|
4023
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
4024
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: title }),
|
|
4025
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: subtitle })
|
|
4026
|
-
] })
|
|
4027
|
-
] }) });
|
|
4028
|
-
}
|
|
4029
|
-
var CompactionDoodle_default = CompactionDoodle;
|
|
4030
|
-
|
|
4031
|
-
// ui/src/bichat/components/MessageList.tsx
|
|
4032
|
-
init_useTranslation();
|
|
4033
4681
|
|
|
4034
4682
|
// ui/src/bichat/utils/markdownStream.ts
|
|
4035
4683
|
function normalizeStreamingMarkdown(text) {
|
|
@@ -4058,7 +4706,6 @@ var MarkdownRenderer4 = React.lazy(
|
|
|
4058
4706
|
() => Promise.resolve().then(() => (init_MarkdownRenderer(), MarkdownRenderer_exports)).then((m) => ({ default: m.MarkdownRenderer }))
|
|
4059
4707
|
);
|
|
4060
4708
|
function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readOnly }) {
|
|
4061
|
-
const { t } = useTranslation();
|
|
4062
4709
|
const { currentSessionId, fetching } = useChatSession();
|
|
4063
4710
|
const { turns, streamingContent, isStreaming, loading, isCompacting } = useChatMessaging();
|
|
4064
4711
|
const messagesEndRef = React.useRef(null);
|
|
@@ -4116,13 +4763,6 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
|
|
|
4116
4763
|
);
|
|
4117
4764
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 min-h-0", children: [
|
|
4118
4765
|
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full overflow-y-auto px-4 py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto space-y-6", children: [
|
|
4119
|
-
isCompacting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4120
|
-
CompactionDoodle_default,
|
|
4121
|
-
{
|
|
4122
|
-
title: t("BiChat.Slash.CompactingTitle"),
|
|
4123
|
-
subtitle: t("BiChat.Slash.CompactingSubtitle")
|
|
4124
|
-
}
|
|
4125
|
-
),
|
|
4126
4766
|
fetching && turns.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", "aria-hidden": "true", children: [
|
|
4127
4767
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-3/5 max-w-md rounded-2xl bg-gray-100 dark:bg-gray-800 p-4 space-y-2", children: [
|
|
4128
4768
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-full rounded bg-gray-200 dark:bg-gray-700 animate-pulse" }),
|
|
@@ -4185,6 +4825,128 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
|
|
|
4185
4825
|
)
|
|
4186
4826
|
] });
|
|
4187
4827
|
}
|
|
4828
|
+
|
|
4829
|
+
// ui/src/bichat/components/MessageQueueList.tsx
|
|
4830
|
+
init_useTranslation();
|
|
4831
|
+
function MessageQueueList({ queue, onRemove, onUpdate }) {
|
|
4832
|
+
const { t } = useTranslation();
|
|
4833
|
+
const [editingIndex, setEditingIndex] = React.useState(null);
|
|
4834
|
+
const [editValue, setEditValue] = React.useState("");
|
|
4835
|
+
const startEdit = React.useCallback((index) => {
|
|
4836
|
+
setEditingIndex(index);
|
|
4837
|
+
setEditValue(queue[index].content);
|
|
4838
|
+
}, [queue]);
|
|
4839
|
+
const saveEdit = React.useCallback(() => {
|
|
4840
|
+
if (editingIndex === null) return;
|
|
4841
|
+
const trimmed = editValue.trim();
|
|
4842
|
+
if (trimmed) {
|
|
4843
|
+
onUpdate(editingIndex, trimmed);
|
|
4844
|
+
}
|
|
4845
|
+
setEditingIndex(null);
|
|
4846
|
+
setEditValue("");
|
|
4847
|
+
}, [editingIndex, editValue, onUpdate]);
|
|
4848
|
+
const cancelEdit = React.useCallback(() => {
|
|
4849
|
+
setEditingIndex(null);
|
|
4850
|
+
setEditValue("");
|
|
4851
|
+
}, []);
|
|
4852
|
+
if (queue.length === 0) return null;
|
|
4853
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 space-y-1.5", children: [
|
|
4854
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: t("BiChat.Input.QueuedMessages", { count: queue.length }) }) }),
|
|
4855
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { initial: false, children: queue.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4856
|
+
framerMotion.motion.div,
|
|
4857
|
+
{
|
|
4858
|
+
initial: { opacity: 0, height: 0 },
|
|
4859
|
+
animate: { opacity: 1, height: "auto" },
|
|
4860
|
+
exit: { opacity: 0, height: 0 },
|
|
4861
|
+
transition: { duration: 0.15 },
|
|
4862
|
+
className: "overflow-hidden",
|
|
4863
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-2 text-sm", children: [
|
|
4864
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 mt-0.5 w-5 h-5 rounded-full bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400 flex items-center justify-center text-[10px] font-bold", children: index + 1 }),
|
|
4865
|
+
editingIndex === index ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-1.5", children: [
|
|
4866
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4867
|
+
"textarea",
|
|
4868
|
+
{
|
|
4869
|
+
value: editValue,
|
|
4870
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
4871
|
+
onKeyDown: (e) => {
|
|
4872
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
4873
|
+
e.preventDefault();
|
|
4874
|
+
saveEdit();
|
|
4875
|
+
}
|
|
4876
|
+
if (e.key === "Escape") cancelEdit();
|
|
4877
|
+
},
|
|
4878
|
+
className: "w-full resize-none rounded border border-primary-300 dark:border-primary-600 bg-transparent px-2 py-1 text-sm text-gray-900 dark:text-white outline-none focus:ring-1 focus:ring-primary-500",
|
|
4879
|
+
rows: 1,
|
|
4880
|
+
autoFocus: true
|
|
4881
|
+
}
|
|
4882
|
+
),
|
|
4883
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1", children: [
|
|
4884
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4885
|
+
"button",
|
|
4886
|
+
{
|
|
4887
|
+
type: "button",
|
|
4888
|
+
onClick: saveEdit,
|
|
4889
|
+
className: "cursor-pointer inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium text-primary-700 dark:text-primary-300 bg-primary-50 dark:bg-primary-900/30 hover:bg-primary-100 dark:hover:bg-primary-900/50 rounded transition-colors",
|
|
4890
|
+
children: [
|
|
4891
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 12, weight: "bold" }),
|
|
4892
|
+
t("BiChat.Message.Save")
|
|
4893
|
+
]
|
|
4894
|
+
}
|
|
4895
|
+
),
|
|
4896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4897
|
+
"button",
|
|
4898
|
+
{
|
|
4899
|
+
type: "button",
|
|
4900
|
+
onClick: cancelEdit,
|
|
4901
|
+
className: "cursor-pointer inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors",
|
|
4902
|
+
children: [
|
|
4903
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowCounterClockwise, { size: 12 }),
|
|
4904
|
+
t("BiChat.Message.Cancel")
|
|
4905
|
+
]
|
|
4906
|
+
}
|
|
4907
|
+
)
|
|
4908
|
+
] })
|
|
4909
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex-1 min-w-0 text-gray-700 dark:text-gray-300 truncate", children: [
|
|
4910
|
+
item.content,
|
|
4911
|
+
item.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1.5 text-gray-400 dark:text-gray-500", children: [
|
|
4912
|
+
"+",
|
|
4913
|
+
item.attachments.length,
|
|
4914
|
+
" ",
|
|
4915
|
+
t("BiChat.Input.AttachFiles").toLowerCase()
|
|
4916
|
+
] })
|
|
4917
|
+
] }),
|
|
4918
|
+
editingIndex !== index && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
|
|
4919
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4920
|
+
"button",
|
|
4921
|
+
{
|
|
4922
|
+
type: "button",
|
|
4923
|
+
onClick: () => startEdit(index),
|
|
4924
|
+
className: "cursor-pointer p-1 text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors",
|
|
4925
|
+
"aria-label": t("BiChat.Input.EditQueueItem"),
|
|
4926
|
+
title: t("BiChat.Input.EditQueueItem"),
|
|
4927
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.PencilSimple, { size: 14 })
|
|
4928
|
+
}
|
|
4929
|
+
),
|
|
4930
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4931
|
+
"button",
|
|
4932
|
+
{
|
|
4933
|
+
type: "button",
|
|
4934
|
+
onClick: () => onRemove(index),
|
|
4935
|
+
className: "cursor-pointer p-1 text-gray-400 dark:text-gray-500 hover:text-red-500 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors",
|
|
4936
|
+
"aria-label": t("BiChat.Input.RemoveQueueItem"),
|
|
4937
|
+
title: t("BiChat.Input.RemoveQueueItem"),
|
|
4938
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 14 })
|
|
4939
|
+
}
|
|
4940
|
+
)
|
|
4941
|
+
] })
|
|
4942
|
+
] })
|
|
4943
|
+
},
|
|
4944
|
+
`queue-${index}-${item.content.slice(0, 20)}`
|
|
4945
|
+
)) })
|
|
4946
|
+
] });
|
|
4947
|
+
}
|
|
4948
|
+
|
|
4949
|
+
// ui/src/bichat/components/MessageInput.tsx
|
|
4188
4950
|
init_useTranslation();
|
|
4189
4951
|
var MAX_FILES_DEFAULT = 10;
|
|
4190
4952
|
var MAX_FILE_SIZE_DEFAULT = 20 * 1024 * 1024;
|
|
@@ -4204,6 +4966,8 @@ var MessageInput = React.forwardRef(
|
|
|
4204
4966
|
onMessageChange,
|
|
4205
4967
|
onSubmit,
|
|
4206
4968
|
onUnqueue,
|
|
4969
|
+
onRemoveQueueItem,
|
|
4970
|
+
onUpdateQueueItem,
|
|
4207
4971
|
placeholder: placeholderOverride,
|
|
4208
4972
|
maxFiles = MAX_FILES_DEFAULT,
|
|
4209
4973
|
maxFileSize = MAX_FILE_SIZE_DEFAULT,
|
|
@@ -4427,14 +5191,22 @@ var MessageInput = React.forwardRef(
|
|
|
4427
5191
|
return;
|
|
4428
5192
|
}
|
|
4429
5193
|
if (isCommandListVisible) {
|
|
4430
|
-
if (e.key === "
|
|
5194
|
+
if (e.key === "Tab") {
|
|
5195
|
+
e.preventDefault();
|
|
5196
|
+
if (filteredCommands.length > 0) {
|
|
5197
|
+
onMessageChange(filteredCommands[activeCommandIndex].name);
|
|
5198
|
+
setCommandListDismissed(true);
|
|
5199
|
+
}
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
if (e.key === "ArrowDown") {
|
|
4431
5203
|
e.preventDefault();
|
|
4432
5204
|
if (filteredCommands.length > 0) {
|
|
4433
5205
|
setActiveCommandIndex((prev) => (prev + 1) % filteredCommands.length);
|
|
4434
5206
|
}
|
|
4435
5207
|
return;
|
|
4436
5208
|
}
|
|
4437
|
-
if (e.key === "ArrowUp"
|
|
5209
|
+
if (e.key === "ArrowUp") {
|
|
4438
5210
|
e.preventDefault();
|
|
4439
5211
|
if (filteredCommands.length > 0) {
|
|
4440
5212
|
setActiveCommandIndex(
|
|
@@ -4460,7 +5232,7 @@ var MessageInput = React.forwardRef(
|
|
|
4460
5232
|
}
|
|
4461
5233
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
4462
5234
|
e.preventDefault();
|
|
4463
|
-
if (
|
|
5235
|
+
if (message.trim() || attachments.length > 0) {
|
|
4464
5236
|
handleFormSubmit(e);
|
|
4465
5237
|
}
|
|
4466
5238
|
}
|
|
@@ -4484,7 +5256,7 @@ var MessageInput = React.forwardRef(
|
|
|
4484
5256
|
const handleFormSubmit = (e) => {
|
|
4485
5257
|
e.preventDefault();
|
|
4486
5258
|
if (isComposing) return;
|
|
4487
|
-
if (
|
|
5259
|
+
if (disabled || !message.trim() && attachments.length === 0) {
|
|
4488
5260
|
return;
|
|
4489
5261
|
}
|
|
4490
5262
|
setCommandListDismissed(true);
|
|
@@ -4492,7 +5264,7 @@ var MessageInput = React.forwardRef(
|
|
|
4492
5264
|
setAttachments([]);
|
|
4493
5265
|
setError(null);
|
|
4494
5266
|
};
|
|
4495
|
-
const canSubmit = !
|
|
5267
|
+
const canSubmit = !disabled && (message.trim() || attachments.length > 0);
|
|
4496
5268
|
const visibleError = error || commandError;
|
|
4497
5269
|
const visibleErrorText = visibleError ? t(visibleError) : "";
|
|
4498
5270
|
const defaultContainerClassName = "shrink-0 px-4 pt-4 pb-6";
|
|
@@ -4513,8 +5285,9 @@ var MessageInput = React.forwardRef(
|
|
|
4513
5285
|
ref: containerRef,
|
|
4514
5286
|
className: containerClassName ?? defaultContainerClassName,
|
|
4515
5287
|
children: /* @__PURE__ */ jsxRuntime.jsxs("form", { ref: formRef, onSubmit: handleFormSubmit, className: formClassName ?? "mx-auto", children: [
|
|
4516
|
-
visibleError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3
|
|
4517
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5288
|
+
visibleError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 flex items-start gap-2.5 px-3 py-2.5 bg-red-50 dark:bg-red-950/40 border border-red-200/80 dark:border-red-900/60 rounded-xl text-sm shadow-sm", children: [
|
|
5289
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 mt-0.5 flex items-center justify-center w-5 h-5 rounded-full bg-red-100 dark:bg-red-900/40", children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 10, className: "text-red-600 dark:text-red-400", weight: "bold" }) }),
|
|
5290
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-red-700 dark:text-red-300 text-xs leading-relaxed", children: visibleErrorText }),
|
|
4518
5291
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4519
5292
|
"button",
|
|
4520
5293
|
{
|
|
@@ -4523,13 +5296,20 @@ var MessageInput = React.forwardRef(
|
|
|
4523
5296
|
setError(null);
|
|
4524
5297
|
onClearCommandError?.();
|
|
4525
5298
|
},
|
|
4526
|
-
className: "cursor-pointer
|
|
5299
|
+
className: "cursor-pointer flex-shrink-0 p-0.5 text-red-400 dark:text-red-500 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/40 rounded-md transition-colors",
|
|
4527
5300
|
"aria-label": t("BiChat.Input.DismissError"),
|
|
4528
5301
|
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 14 })
|
|
4529
5302
|
}
|
|
4530
5303
|
)
|
|
4531
5304
|
] }),
|
|
4532
|
-
messageQueue.length > 0 &&
|
|
5305
|
+
messageQueue.length > 0 && onRemoveQueueItem && onUpdateQueueItem && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5306
|
+
MessageQueueList,
|
|
5307
|
+
{
|
|
5308
|
+
queue: messageQueue,
|
|
5309
|
+
onRemove: onRemoveQueueItem,
|
|
5310
|
+
onUpdate: onUpdateQueueItem
|
|
5311
|
+
}
|
|
5312
|
+
),
|
|
4533
5313
|
debugMode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3 space-y-2 text-xs", children: [
|
|
4534
5314
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 px-2.5 py-1 bg-amber-100 dark:bg-amber-900/40 text-amber-700 dark:text-amber-300 rounded-full font-medium text-[11px]", children: [
|
|
4535
5315
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-1.5 w-1.5", "aria-hidden": "true", children: [
|
|
@@ -4686,7 +5466,7 @@ var MessageInput = React.forwardRef(
|
|
|
4686
5466
|
className: "resize-none bg-transparent border-none outline-none px-1 py-2 w-full text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 text-sm leading-relaxed",
|
|
4687
5467
|
style: { maxHeight: `${MAX_HEIGHT}px` },
|
|
4688
5468
|
rows: 1,
|
|
4689
|
-
disabled
|
|
5469
|
+
disabled,
|
|
4690
5470
|
"aria-busy": loading,
|
|
4691
5471
|
"aria-label": t("BiChat.Input.MessageInput")
|
|
4692
5472
|
}
|
|
@@ -4752,13 +5532,36 @@ var MessageInput = React.forwardRef(
|
|
|
4752
5532
|
}
|
|
4753
5533
|
);
|
|
4754
5534
|
MessageInput.displayName = "MessageInput";
|
|
5535
|
+
function CompactionDoodle({ title, subtitle }) {
|
|
5536
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5537
|
+
framerMotion.motion.div,
|
|
5538
|
+
{
|
|
5539
|
+
initial: { opacity: 0, y: 8, scale: 0.96 },
|
|
5540
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
5541
|
+
exit: { opacity: 0, y: 4, scale: 0.98 },
|
|
5542
|
+
transition: { type: "spring", stiffness: 400, damping: 28 },
|
|
5543
|
+
className: "flex items-center gap-2.5 rounded-xl border border-gray-200/70 bg-white/95 px-3.5 py-2 shadow-sm backdrop-blur-sm dark:border-gray-700/50 dark:bg-gray-800/95",
|
|
5544
|
+
children: [
|
|
5545
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex h-5 w-5 items-center justify-center", children: [
|
|
5546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary-400/30" }),
|
|
5547
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary-500" })
|
|
5548
|
+
] }),
|
|
5549
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
5550
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700 dark:text-gray-200", children: title }),
|
|
5551
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500", children: subtitle })
|
|
5552
|
+
] })
|
|
5553
|
+
]
|
|
5554
|
+
}
|
|
5555
|
+
);
|
|
5556
|
+
}
|
|
5557
|
+
var CompactionDoodle_default = CompactionDoodle;
|
|
4755
5558
|
|
|
4756
5559
|
// ui/src/bichat/components/WelcomeContent.tsx
|
|
4757
5560
|
init_useTranslation();
|
|
4758
5561
|
var PROMPT_DEFS = [
|
|
4759
|
-
{ categoryKey: "Welcome.Prompt1Category", textKey: "Welcome.Prompt1Text", icon: react.ChartBar, defaultCategory: "
|
|
4760
|
-
{ categoryKey: "Welcome.Prompt2Category", textKey: "Welcome.Prompt2Text", icon: react.FileText, defaultCategory: "
|
|
4761
|
-
{ categoryKey: "Welcome.Prompt3Category", textKey: "Welcome.Prompt3Text", icon: react.Lightbulb, defaultCategory: "
|
|
5562
|
+
{ categoryKey: "BiChat.Welcome.Prompt1Category", textKey: "BiChat.Welcome.Prompt1Text", icon: react.ChartBar, defaultCategory: "Data Analysis", defaultText: "Show me a summary of key metrics" },
|
|
5563
|
+
{ categoryKey: "BiChat.Welcome.Prompt2Category", textKey: "BiChat.Welcome.Prompt2Text", icon: react.FileText, defaultCategory: "Reports", defaultText: "Generate a report for the current period" },
|
|
5564
|
+
{ categoryKey: "BiChat.Welcome.Prompt3Category", textKey: "BiChat.Welcome.Prompt3Text", icon: react.Lightbulb, defaultCategory: "Insights", defaultText: "What trends can you identify in the data?" }
|
|
4762
5565
|
];
|
|
4763
5566
|
var PROMPT_STYLES = [
|
|
4764
5567
|
{
|
|
@@ -4908,11 +5711,172 @@ function WelcomeContent({
|
|
|
4908
5711
|
);
|
|
4909
5712
|
}) })
|
|
4910
5713
|
] })
|
|
4911
|
-
]
|
|
4912
|
-
}
|
|
4913
|
-
);
|
|
5714
|
+
]
|
|
5715
|
+
}
|
|
5716
|
+
);
|
|
5717
|
+
}
|
|
5718
|
+
var WelcomeContent_default = WelcomeContent;
|
|
5719
|
+
init_useTranslation();
|
|
5720
|
+
var variantStyles = {
|
|
5721
|
+
error: {
|
|
5722
|
+
container: "border-red-200 bg-red-50 dark:bg-red-900/20",
|
|
5723
|
+
title: "text-red-800 dark:text-red-300",
|
|
5724
|
+
message: "text-red-700 dark:text-red-400",
|
|
5725
|
+
icon: "text-red-600 dark:text-red-400",
|
|
5726
|
+
button: "text-red-400 hover:text-red-600 dark:hover:text-red-300",
|
|
5727
|
+
retryButton: "bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-800 text-white",
|
|
5728
|
+
Icon: react.XCircle
|
|
5729
|
+
},
|
|
5730
|
+
success: {
|
|
5731
|
+
container: "border-green-200 bg-green-50 dark:bg-green-900/20",
|
|
5732
|
+
title: "text-green-800 dark:text-green-300",
|
|
5733
|
+
message: "text-green-700 dark:text-green-400",
|
|
5734
|
+
icon: "text-green-600 dark:text-green-400",
|
|
5735
|
+
button: "text-green-400 hover:text-green-600 dark:hover:text-green-300",
|
|
5736
|
+
retryButton: "bg-green-600 dark:bg-green-700 hover:bg-green-700 dark:hover:bg-green-800 text-white",
|
|
5737
|
+
Icon: react.CheckCircle
|
|
5738
|
+
},
|
|
5739
|
+
warning: {
|
|
5740
|
+
container: "border-yellow-200 bg-yellow-50 dark:bg-yellow-900/20",
|
|
5741
|
+
title: "text-yellow-800 dark:text-yellow-300",
|
|
5742
|
+
message: "text-yellow-700 dark:text-yellow-400",
|
|
5743
|
+
icon: "text-yellow-600 dark:text-yellow-400",
|
|
5744
|
+
button: "text-yellow-400 hover:text-yellow-600 dark:hover:text-yellow-300",
|
|
5745
|
+
retryButton: "bg-yellow-600 dark:bg-yellow-700 hover:bg-yellow-700 dark:hover:bg-yellow-800 text-white",
|
|
5746
|
+
Icon: react.Warning
|
|
5747
|
+
},
|
|
5748
|
+
info: {
|
|
5749
|
+
container: "border-blue-200 bg-blue-50 dark:bg-blue-900/20",
|
|
5750
|
+
title: "text-blue-800 dark:text-blue-300",
|
|
5751
|
+
message: "text-blue-700 dark:text-blue-400",
|
|
5752
|
+
icon: "text-blue-600 dark:text-blue-400",
|
|
5753
|
+
button: "text-blue-400 hover:text-blue-600 dark:hover:text-blue-300",
|
|
5754
|
+
retryButton: "bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 text-white",
|
|
5755
|
+
Icon: react.Info
|
|
5756
|
+
}
|
|
5757
|
+
};
|
|
5758
|
+
function Alert({
|
|
5759
|
+
variant = "info",
|
|
5760
|
+
message,
|
|
5761
|
+
title,
|
|
5762
|
+
onDismiss,
|
|
5763
|
+
onRetry,
|
|
5764
|
+
show = true,
|
|
5765
|
+
dismissible = true
|
|
5766
|
+
}) {
|
|
5767
|
+
const { t } = useTranslation();
|
|
5768
|
+
const styles = variantStyles[variant];
|
|
5769
|
+
const IconComponent = styles.Icon;
|
|
5770
|
+
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: show && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5771
|
+
framerMotion.motion.div,
|
|
5772
|
+
{
|
|
5773
|
+
variants: errorMessageVariants,
|
|
5774
|
+
initial: "initial",
|
|
5775
|
+
animate: "animate",
|
|
5776
|
+
exit: "exit",
|
|
5777
|
+
className: `border-t border ${styles.container} px-4 py-3`,
|
|
5778
|
+
role: "alert",
|
|
5779
|
+
"aria-live": "assertive",
|
|
5780
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
5781
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
5782
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 20, className: `w-5 h-5 ${styles.icon} flex-shrink-0 mt-0.5` }),
|
|
5783
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
5784
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${styles.title} font-medium`, children: title }),
|
|
5785
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${styles.message} ${title ? "mt-1" : ""}`, children: message }),
|
|
5786
|
+
onRetry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5787
|
+
"button",
|
|
5788
|
+
{
|
|
5789
|
+
onClick: onRetry,
|
|
5790
|
+
className: `mt-2 text-xs px-3 py-1.5 rounded ${styles.retryButton} transition-colors font-medium`,
|
|
5791
|
+
children: t("BiChat.Chat.Retry")
|
|
5792
|
+
}
|
|
5793
|
+
)
|
|
5794
|
+
] })
|
|
5795
|
+
] }),
|
|
5796
|
+
dismissible && onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5797
|
+
"button",
|
|
5798
|
+
{
|
|
5799
|
+
onClick: onDismiss,
|
|
5800
|
+
className: `${styles.button} transition-colors flex-shrink-0`,
|
|
5801
|
+
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
5802
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 20, className: "w-5 h-5" })
|
|
5803
|
+
}
|
|
5804
|
+
)
|
|
5805
|
+
] })
|
|
5806
|
+
}
|
|
5807
|
+
) });
|
|
5808
|
+
}
|
|
5809
|
+
var Alert_default = React.memo(Alert);
|
|
5810
|
+
|
|
5811
|
+
// ui/src/bichat/components/ArchiveBanner.tsx
|
|
5812
|
+
init_useTranslation();
|
|
5813
|
+
function ArchiveBanner({
|
|
5814
|
+
show = true,
|
|
5815
|
+
onRestore,
|
|
5816
|
+
restoring = false,
|
|
5817
|
+
onRestoreComplete
|
|
5818
|
+
}) {
|
|
5819
|
+
const { t } = useTranslation();
|
|
5820
|
+
const [error, setError] = React.useState(null);
|
|
5821
|
+
const handleRestore = async () => {
|
|
5822
|
+
try {
|
|
5823
|
+
setError(null);
|
|
5824
|
+
if (onRestore) {
|
|
5825
|
+
await onRestore();
|
|
5826
|
+
}
|
|
5827
|
+
if (onRestoreComplete) {
|
|
5828
|
+
onRestoreComplete();
|
|
5829
|
+
}
|
|
5830
|
+
} catch (err) {
|
|
5831
|
+
const message = err instanceof Error ? err.message : t("BiChat.Archive.RestoreFailed");
|
|
5832
|
+
setError(message);
|
|
5833
|
+
}
|
|
5834
|
+
};
|
|
5835
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5836
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: show && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5837
|
+
framerMotion.motion.div,
|
|
5838
|
+
{
|
|
5839
|
+
variants: errorMessageVariants,
|
|
5840
|
+
initial: "initial",
|
|
5841
|
+
animate: "animate",
|
|
5842
|
+
exit: "exit",
|
|
5843
|
+
className: "border-t border border-blue-200 bg-blue-50 dark:bg-blue-900/20 px-4 py-3",
|
|
5844
|
+
role: "region",
|
|
5845
|
+
"aria-label": t("BiChat.Archive.Banner"),
|
|
5846
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
5847
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
5848
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Archive, { size: 20, className: "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" }),
|
|
5849
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-blue-700 dark:text-blue-400", children: t("BiChat.Archive.Archived") }) })
|
|
5850
|
+
] }),
|
|
5851
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5852
|
+
"button",
|
|
5853
|
+
{
|
|
5854
|
+
onClick: handleRestore,
|
|
5855
|
+
disabled: restoring,
|
|
5856
|
+
className: "ml-2 flex-shrink-0 px-3 py-1.5 text-xs font-medium bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 text-white rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1.5",
|
|
5857
|
+
"aria-label": t("BiChat.Archive.Restore"),
|
|
5858
|
+
children: restoring ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5859
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.Spinner, { size: 16, className: "w-4 h-4 animate-spin" }),
|
|
5860
|
+
t("BiChat.Archive.Restoring")
|
|
5861
|
+
] }) : t("BiChat.Archive.Restore")
|
|
5862
|
+
}
|
|
5863
|
+
)
|
|
5864
|
+
] })
|
|
5865
|
+
}
|
|
5866
|
+
) }),
|
|
5867
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5868
|
+
Alert_default,
|
|
5869
|
+
{
|
|
5870
|
+
variant: "error",
|
|
5871
|
+
message: error,
|
|
5872
|
+
title: t("BiChat.Archive.RestoreFailed"),
|
|
5873
|
+
onDismiss: () => setError(null),
|
|
5874
|
+
dismissible: true
|
|
5875
|
+
}
|
|
5876
|
+
)
|
|
5877
|
+
] });
|
|
4914
5878
|
}
|
|
4915
|
-
var
|
|
5879
|
+
var ArchiveBanner_default = React.memo(ArchiveBanner);
|
|
4916
5880
|
|
|
4917
5881
|
// ui/src/bichat/components/ChatSession.tsx
|
|
4918
5882
|
init_useTranslation();
|
|
@@ -4923,11 +5887,11 @@ init_useTranslation();
|
|
|
4923
5887
|
// ui/src/bichat/components/SessionArtifactList.tsx
|
|
4924
5888
|
init_useTranslation();
|
|
4925
5889
|
var TYPE_LABEL_KEYS = {
|
|
4926
|
-
chart: "Artifacts.GroupCharts",
|
|
4927
|
-
code_output: "Artifacts.GroupCodeOutputs",
|
|
4928
|
-
export: "Artifacts.GroupExports",
|
|
4929
|
-
attachment: "Artifacts.GroupAttachments",
|
|
4930
|
-
other: "Artifacts.GroupOther"
|
|
5890
|
+
chart: "BiChat.Artifacts.GroupCharts",
|
|
5891
|
+
code_output: "BiChat.Artifacts.GroupCodeOutputs",
|
|
5892
|
+
export: "BiChat.Artifacts.GroupExports",
|
|
5893
|
+
attachment: "BiChat.Artifacts.GroupAttachments",
|
|
5894
|
+
other: "BiChat.Artifacts.GroupOther"
|
|
4931
5895
|
};
|
|
4932
5896
|
function getGroupIcon(type) {
|
|
4933
5897
|
const cls = "h-3.5 w-3.5";
|
|
@@ -5003,7 +5967,8 @@ function SessionArtifactList({
|
|
|
5003
5967
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-xl bg-gray-100 dark:bg-gray-800", children: /* @__PURE__ */ jsxRuntime.jsx(react.Package, { className: "h-6 w-6 text-gray-400 dark:text-gray-500", weight: "duotone" }) }),
|
|
5004
5968
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
5005
5969
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: t("BiChat.Artifacts.Empty") }),
|
|
5006
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptySubtitle") })
|
|
5970
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptySubtitle") }),
|
|
5971
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptyHint") })
|
|
5007
5972
|
] })
|
|
5008
5973
|
] });
|
|
5009
5974
|
}
|
|
@@ -5695,6 +6660,29 @@ function SessionArtifactsPanel({
|
|
|
5695
6660
|
},
|
|
5696
6661
|
[canDeleteArtifact, dataSource]
|
|
5697
6662
|
);
|
|
6663
|
+
const fileInputRef = React.useRef(null);
|
|
6664
|
+
const handleAttachClick = React.useCallback(() => {
|
|
6665
|
+
fileInputRef.current?.click();
|
|
6666
|
+
}, []);
|
|
6667
|
+
const handleFileInputChange = React.useCallback(
|
|
6668
|
+
async (e) => {
|
|
6669
|
+
if (!dataSource.uploadSessionArtifacts || !e.target.files?.length) return;
|
|
6670
|
+
const files = Array.from(e.target.files);
|
|
6671
|
+
try {
|
|
6672
|
+
const result = await dataSource.uploadSessionArtifacts(sessionId, files);
|
|
6673
|
+
if ((result.artifacts || []).length > 0) {
|
|
6674
|
+
setDropSuccessState();
|
|
6675
|
+
void fetchArtifacts({ reset: true, manual: false });
|
|
6676
|
+
}
|
|
6677
|
+
setError(null);
|
|
6678
|
+
} catch (err) {
|
|
6679
|
+
setError(err instanceof Error ? err.message : tRef.current("BiChat.Artifacts.FailedToLoad"));
|
|
6680
|
+
} finally {
|
|
6681
|
+
e.target.value = "";
|
|
6682
|
+
}
|
|
6683
|
+
},
|
|
6684
|
+
[dataSource, fetchArtifacts, sessionId, setDropSuccessState]
|
|
6685
|
+
);
|
|
5698
6686
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5699
6687
|
"aside",
|
|
5700
6688
|
{
|
|
@@ -5722,12 +6710,38 @@ function SessionArtifactsPanel({
|
|
|
5722
6710
|
]
|
|
5723
6711
|
}
|
|
5724
6712
|
) }),
|
|
5725
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
6713
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center justify-between border-b border-gray-200 px-3 py-2 dark:border-gray-700/80", children: [
|
|
6714
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: [
|
|
6715
|
+
t("BiChat.Artifacts.Title"),
|
|
6716
|
+
" (",
|
|
6717
|
+
artifacts.length,
|
|
6718
|
+
")"
|
|
6719
|
+
] }) }),
|
|
6720
|
+
canDropFiles && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6721
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6722
|
+
"button",
|
|
6723
|
+
{
|
|
6724
|
+
type: "button",
|
|
6725
|
+
onClick: handleAttachClick,
|
|
6726
|
+
className: "ml-2 flex-shrink-0 rounded-md p-1.5 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 dark:text-gray-500 dark:hover:bg-gray-800 dark:hover:text-gray-300",
|
|
6727
|
+
"aria-label": t("BiChat.Artifacts.AttachFiles"),
|
|
6728
|
+
title: t("BiChat.Artifacts.AttachFiles"),
|
|
6729
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.Plus, { className: "h-4 w-4", weight: "bold" })
|
|
6730
|
+
}
|
|
6731
|
+
),
|
|
6732
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6733
|
+
"input",
|
|
6734
|
+
{
|
|
6735
|
+
ref: fileInputRef,
|
|
6736
|
+
type: "file",
|
|
6737
|
+
multiple: true,
|
|
6738
|
+
className: "hidden",
|
|
6739
|
+
onChange: handleFileInputChange,
|
|
6740
|
+
"aria-label": t("BiChat.Artifacts.AttachFiles")
|
|
6741
|
+
}
|
|
6742
|
+
)
|
|
6743
|
+
] })
|
|
6744
|
+
] }),
|
|
5731
6745
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 py-3", children: fetching ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-sm text-gray-500 dark:text-gray-400", children: t("BiChat.Artifacts.Loading") }) : error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-900/70 dark:bg-red-950/30", children: [
|
|
5732
6746
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-300", children: t("BiChat.Artifacts.FailedToLoad") }),
|
|
5733
6747
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-700 dark:text-red-400", children: error }),
|
|
@@ -5780,6 +6794,77 @@ function SessionArtifactsPanel({
|
|
|
5780
6794
|
}
|
|
5781
6795
|
);
|
|
5782
6796
|
}
|
|
6797
|
+
|
|
6798
|
+
// ui/src/bichat/components/StreamError.tsx
|
|
6799
|
+
init_useTranslation();
|
|
6800
|
+
function StreamError({
|
|
6801
|
+
error,
|
|
6802
|
+
onRetry,
|
|
6803
|
+
onRegenerate,
|
|
6804
|
+
onDismiss,
|
|
6805
|
+
compact = false
|
|
6806
|
+
}) {
|
|
6807
|
+
const { t } = useTranslation();
|
|
6808
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6809
|
+
framerMotion.motion.div,
|
|
6810
|
+
{
|
|
6811
|
+
initial: { opacity: 0, y: 10 },
|
|
6812
|
+
animate: { opacity: 1, y: 0 },
|
|
6813
|
+
exit: { opacity: 0, y: -10 },
|
|
6814
|
+
className: `flex items-start gap-3 ${compact ? "px-3 py-2.5" : "px-4 py-3"} bg-red-50 dark:bg-red-950/40 border border-red-200/80 dark:border-red-900/60 rounded-xl shadow-sm`,
|
|
6815
|
+
role: "alert",
|
|
6816
|
+
children: [
|
|
6817
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 mt-0.5 flex items-center justify-center w-7 h-7 rounded-full bg-red-100 dark:bg-red-900/40", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6818
|
+
react.Warning,
|
|
6819
|
+
{
|
|
6820
|
+
className: "w-4 h-4 text-red-600 dark:text-red-400",
|
|
6821
|
+
weight: "fill"
|
|
6822
|
+
}
|
|
6823
|
+
) }),
|
|
6824
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
6825
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
|
|
6826
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
|
|
6827
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
6828
|
+
onRetry && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6829
|
+
"button",
|
|
6830
|
+
{
|
|
6831
|
+
onClick: onRetry,
|
|
6832
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-white bg-red-600 hover:bg-red-700 active:bg-red-800 dark:bg-red-700 dark:hover:bg-red-600 rounded-lg transition-colors shadow-sm",
|
|
6833
|
+
type: "button",
|
|
6834
|
+
children: [
|
|
6835
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { className: "w-3.5 h-3.5" }),
|
|
6836
|
+
t("BiChat.StreamError.Retry")
|
|
6837
|
+
]
|
|
6838
|
+
}
|
|
6839
|
+
),
|
|
6840
|
+
onRegenerate && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6841
|
+
"button",
|
|
6842
|
+
{
|
|
6843
|
+
onClick: onRegenerate,
|
|
6844
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg transition-colors shadow-sm",
|
|
6845
|
+
type: "button",
|
|
6846
|
+
children: [
|
|
6847
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowsCounterClockwise, { className: "w-3.5 h-3.5" }),
|
|
6848
|
+
t("BiChat.StreamError.Regenerate")
|
|
6849
|
+
]
|
|
6850
|
+
}
|
|
6851
|
+
)
|
|
6852
|
+
] })
|
|
6853
|
+
] }),
|
|
6854
|
+
onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6855
|
+
"button",
|
|
6856
|
+
{
|
|
6857
|
+
onClick: onDismiss,
|
|
6858
|
+
className: "cursor-pointer flex-shrink-0 mt-0.5 inline-flex items-center justify-center w-6 h-6 text-red-400 dark:text-red-500 hover:text-red-600 dark:hover:text-red-300 hover:bg-red-100 dark:hover:bg-red-900/40 rounded-md transition-colors",
|
|
6859
|
+
type: "button",
|
|
6860
|
+
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
6861
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { className: "w-3.5 h-3.5" })
|
|
6862
|
+
}
|
|
6863
|
+
)
|
|
6864
|
+
]
|
|
6865
|
+
}
|
|
6866
|
+
);
|
|
6867
|
+
}
|
|
5783
6868
|
var ARTIFACTS_PANEL_WIDTH_DEFAULT = 352;
|
|
5784
6869
|
var ARTIFACTS_PANEL_WIDTH_MIN = 280;
|
|
5785
6870
|
var ARTIFACTS_PANEL_WIDTH_MAX = 560;
|
|
@@ -5796,13 +6881,34 @@ function ChatSessionCore({
|
|
|
5796
6881
|
actionsSlot,
|
|
5797
6882
|
onBack,
|
|
5798
6883
|
thinkingVerbs,
|
|
6884
|
+
onSessionRestored,
|
|
5799
6885
|
showArtifactsPanel = false,
|
|
5800
6886
|
artifactsPanelDefaultExpanded = false,
|
|
5801
6887
|
artifactsPanelStorageKey = "bichat.artifacts-panel.expanded"
|
|
5802
6888
|
}) {
|
|
5803
6889
|
const { t } = useTranslation();
|
|
5804
|
-
const {
|
|
5805
|
-
|
|
6890
|
+
const {
|
|
6891
|
+
session,
|
|
6892
|
+
fetching,
|
|
6893
|
+
error,
|
|
6894
|
+
errorRetryable,
|
|
6895
|
+
debugMode,
|
|
6896
|
+
sessionDebugUsage,
|
|
6897
|
+
debugLimits,
|
|
6898
|
+
currentSessionId,
|
|
6899
|
+
setError,
|
|
6900
|
+
retryFetchSession
|
|
6901
|
+
} = useChatSession();
|
|
6902
|
+
const {
|
|
6903
|
+
turns,
|
|
6904
|
+
loading,
|
|
6905
|
+
isStreaming,
|
|
6906
|
+
streamError,
|
|
6907
|
+
streamErrorRetryable,
|
|
6908
|
+
isCompacting,
|
|
6909
|
+
retryLastMessage,
|
|
6910
|
+
clearStreamError
|
|
6911
|
+
} = useChatMessaging();
|
|
5806
6912
|
const {
|
|
5807
6913
|
inputError,
|
|
5808
6914
|
message,
|
|
@@ -5810,9 +6916,23 @@ function ChatSessionCore({
|
|
|
5810
6916
|
setInputError,
|
|
5811
6917
|
handleSubmit,
|
|
5812
6918
|
messageQueue,
|
|
5813
|
-
handleUnqueue
|
|
6919
|
+
handleUnqueue,
|
|
6920
|
+
removeQueueItem,
|
|
6921
|
+
updateQueueItem
|
|
5814
6922
|
} = useChatInput();
|
|
5815
|
-
const
|
|
6923
|
+
const isArchived = session?.status === "archived";
|
|
6924
|
+
const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived;
|
|
6925
|
+
const [restoring, setRestoring] = React.useState(false);
|
|
6926
|
+
const handleRestore = React.useCallback(async () => {
|
|
6927
|
+
if (!session?.id) return;
|
|
6928
|
+
setRestoring(true);
|
|
6929
|
+
try {
|
|
6930
|
+
await dataSource.unarchiveSession(session.id);
|
|
6931
|
+
onSessionRestored?.(session.id);
|
|
6932
|
+
} finally {
|
|
6933
|
+
setRestoring(false);
|
|
6934
|
+
}
|
|
6935
|
+
}, [dataSource, session?.id, onSessionRestored]);
|
|
5816
6936
|
const [artifactsPanelExpanded, setArtifactsPanelExpanded] = React.useState(
|
|
5817
6937
|
artifactsPanelDefaultExpanded
|
|
5818
6938
|
);
|
|
@@ -5884,16 +7004,9 @@ function ChatSessionCore({
|
|
|
5884
7004
|
document.body.style.userSelect = "";
|
|
5885
7005
|
};
|
|
5886
7006
|
}, [isResizingArtifactsPanel, artifactsPanelStorageKey]);
|
|
5887
|
-
if (fetching) {
|
|
7007
|
+
if (fetching && turns.length === 0 && !session) {
|
|
5888
7008
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 dark:text-gray-400", children: t("BiChat.Input.Processing") }) });
|
|
5889
7009
|
}
|
|
5890
|
-
if (error) {
|
|
5891
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-red-500 dark:text-red-400", children: [
|
|
5892
|
-
t("BiChat.Error.Generic"),
|
|
5893
|
-
": ",
|
|
5894
|
-
error
|
|
5895
|
-
] }) });
|
|
5896
|
-
}
|
|
5897
7010
|
const showWelcome = !session && turns.length === 0;
|
|
5898
7011
|
const activeSessionId = session?.id || (currentSessionId && currentSessionId !== "new" ? currentSessionId : void 0);
|
|
5899
7012
|
const supportsArtifactsPanel = typeof dataSource.fetchSessionArtifacts === "function";
|
|
@@ -5950,6 +7063,16 @@ function ChatSessionCore({
|
|
|
5950
7063
|
actionsSlot: headerActions
|
|
5951
7064
|
}
|
|
5952
7065
|
),
|
|
7066
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7067
|
+
Alert_default,
|
|
7068
|
+
{
|
|
7069
|
+
variant: errorRetryable ? "warning" : "error",
|
|
7070
|
+
title: t("BiChat.Error.Generic"),
|
|
7071
|
+
message: error,
|
|
7072
|
+
onDismiss: () => setError(null),
|
|
7073
|
+
onRetry: errorRetryable ? retryFetchSession : void 0
|
|
7074
|
+
}
|
|
7075
|
+
),
|
|
5953
7076
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5954
7077
|
"div",
|
|
5955
7078
|
{
|
|
@@ -5958,6 +7081,15 @@ function ChatSessionCore({
|
|
|
5958
7081
|
children: [
|
|
5959
7082
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: showWelcome ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center px-4 py-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-5xl", children: [
|
|
5960
7083
|
welcomeSlot || /* @__PURE__ */ jsxRuntime.jsx(WelcomeContent_default, { onPromptSelect: handlePromptSelect, disabled: loading }),
|
|
7084
|
+
streamError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7085
|
+
StreamError,
|
|
7086
|
+
{
|
|
7087
|
+
error: streamError,
|
|
7088
|
+
compact: true,
|
|
7089
|
+
onRetry: streamErrorRetryable ? () => void retryLastMessage() : void 0,
|
|
7090
|
+
onDismiss: clearStreamError
|
|
7091
|
+
}
|
|
7092
|
+
) }),
|
|
5961
7093
|
!effectiveReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5962
7094
|
MessageInput,
|
|
5963
7095
|
{
|
|
@@ -5973,12 +7105,22 @@ function ChatSessionCore({
|
|
|
5973
7105
|
onSubmit: handleSubmit,
|
|
5974
7106
|
messageQueue,
|
|
5975
7107
|
onUnqueue: handleUnqueue,
|
|
7108
|
+
onRemoveQueueItem: removeQueueItem,
|
|
7109
|
+
onUpdateQueueItem: updateQueueItem,
|
|
5976
7110
|
containerClassName: "pt-6 px-6",
|
|
5977
7111
|
formClassName: "mx-auto"
|
|
5978
7112
|
}
|
|
5979
7113
|
),
|
|
5980
7114
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-4 pb-1 text-center text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Welcome.Disclaimer") })
|
|
5981
7115
|
] }) }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7116
|
+
isArchived && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7117
|
+
ArchiveBanner_default,
|
|
7118
|
+
{
|
|
7119
|
+
show: true,
|
|
7120
|
+
onRestore: handleRestore,
|
|
7121
|
+
restoring
|
|
7122
|
+
}
|
|
7123
|
+
),
|
|
5982
7124
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5983
7125
|
MessageList,
|
|
5984
7126
|
{
|
|
@@ -5988,6 +7130,22 @@ function ChatSessionCore({
|
|
|
5988
7130
|
readOnly: effectiveReadOnly
|
|
5989
7131
|
}
|
|
5990
7132
|
),
|
|
7133
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isCompacting && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center px-4 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7134
|
+
CompactionDoodle_default,
|
|
7135
|
+
{
|
|
7136
|
+
title: t("BiChat.Slash.CompactingTitle"),
|
|
7137
|
+
subtitle: t("BiChat.Slash.CompactingSubtitle")
|
|
7138
|
+
}
|
|
7139
|
+
) }) }),
|
|
7140
|
+
streamError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7141
|
+
StreamError,
|
|
7142
|
+
{
|
|
7143
|
+
error: streamError,
|
|
7144
|
+
compact: true,
|
|
7145
|
+
onRetry: streamErrorRetryable ? () => void retryLastMessage() : void 0,
|
|
7146
|
+
onDismiss: clearStreamError
|
|
7147
|
+
}
|
|
7148
|
+
) }),
|
|
5991
7149
|
!effectiveReadOnly && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5992
7150
|
MessageInput,
|
|
5993
7151
|
{
|
|
@@ -6002,7 +7160,9 @@ function ChatSessionCore({
|
|
|
6002
7160
|
onMessageChange: setMessage,
|
|
6003
7161
|
onSubmit: handleSubmit,
|
|
6004
7162
|
messageQueue,
|
|
6005
|
-
onUnqueue: handleUnqueue
|
|
7163
|
+
onUnqueue: handleUnqueue,
|
|
7164
|
+
onRemoveQueueItem: removeQueueItem,
|
|
7165
|
+
onUpdateQueueItem: updateQueueItem
|
|
6006
7166
|
}
|
|
6007
7167
|
)
|
|
6008
7168
|
] }) }),
|
|
@@ -6093,8 +7253,17 @@ function ChatSessionCore({
|
|
|
6093
7253
|
);
|
|
6094
7254
|
}
|
|
6095
7255
|
function ChatSession(props) {
|
|
6096
|
-
const { dataSource, sessionId, rateLimiter, ...coreProps } = props;
|
|
6097
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7256
|
+
const { dataSource, sessionId, rateLimiter, onSessionCreated, ...coreProps } = props;
|
|
7257
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7258
|
+
ChatSessionProvider,
|
|
7259
|
+
{
|
|
7260
|
+
dataSource,
|
|
7261
|
+
sessionId,
|
|
7262
|
+
rateLimiter,
|
|
7263
|
+
onSessionCreated,
|
|
7264
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ChatSessionCore, { dataSource, ...coreProps })
|
|
7265
|
+
}
|
|
7266
|
+
);
|
|
6098
7267
|
}
|
|
6099
7268
|
|
|
6100
7269
|
// ui/src/bichat/index.ts
|
|
@@ -6196,20 +7365,19 @@ var EditableText = React.forwardRef(
|
|
|
6196
7365
|
onSave,
|
|
6197
7366
|
maxLength = 100,
|
|
6198
7367
|
isLoading = false,
|
|
6199
|
-
placeholder
|
|
7368
|
+
placeholder,
|
|
6200
7369
|
className = "",
|
|
6201
7370
|
inputClassName = "",
|
|
6202
7371
|
size = "sm"
|
|
6203
7372
|
}, ref) => {
|
|
6204
7373
|
const { t } = useTranslation();
|
|
7374
|
+
const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Untitled");
|
|
6205
7375
|
const [isEditing, setIsEditing] = React.useState(false);
|
|
6206
7376
|
const [editValue, setEditValue] = React.useState(value);
|
|
6207
7377
|
const inputRef = React.useRef(null);
|
|
6208
7378
|
React.useImperativeHandle(ref, () => ({
|
|
6209
7379
|
startEditing: () => {
|
|
6210
|
-
|
|
6211
|
-
setIsEditing(true);
|
|
6212
|
-
}
|
|
7380
|
+
setIsEditing(true);
|
|
6213
7381
|
},
|
|
6214
7382
|
cancelEditing: () => {
|
|
6215
7383
|
setEditValue(value);
|
|
@@ -6251,9 +7419,7 @@ var EditableText = React.forwardRef(
|
|
|
6251
7419
|
}
|
|
6252
7420
|
};
|
|
6253
7421
|
const handleDoubleClick = () => {
|
|
6254
|
-
|
|
6255
|
-
setIsEditing(true);
|
|
6256
|
-
}
|
|
7422
|
+
setIsEditing(true);
|
|
6257
7423
|
};
|
|
6258
7424
|
const handleBlur = () => {
|
|
6259
7425
|
handleSave();
|
|
@@ -6275,7 +7441,7 @@ var EditableText = React.forwardRef(
|
|
|
6275
7441
|
onKeyDown: handleKeyDown,
|
|
6276
7442
|
onBlur: handleBlur,
|
|
6277
7443
|
maxLength,
|
|
6278
|
-
placeholder,
|
|
7444
|
+
placeholder: resolvedPlaceholder,
|
|
6279
7445
|
className: `flex-1 px-2 py-1 ${sizeClass} bg-white dark:bg-gray-700 border border-primary-500 dark:border-primary-600 rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 dark:focus-visible:ring-primary-600/30 text-gray-900 dark:text-white ${inputClassName}`,
|
|
6280
7446
|
"aria-label": t("BiChat.EditableText.AriaLabel")
|
|
6281
7447
|
}
|
|
@@ -6283,7 +7449,7 @@ var EditableText = React.forwardRef(
|
|
|
6283
7449
|
}
|
|
6284
7450
|
);
|
|
6285
7451
|
}
|
|
6286
|
-
const displayValue = value ||
|
|
7452
|
+
const displayValue = value || resolvedPlaceholder;
|
|
6287
7453
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6288
7454
|
"span",
|
|
6289
7455
|
{
|
|
@@ -6331,16 +7497,18 @@ var sizeClasses3 = {
|
|
|
6331
7497
|
function SearchInput({
|
|
6332
7498
|
value,
|
|
6333
7499
|
onChange,
|
|
6334
|
-
placeholder
|
|
7500
|
+
placeholder,
|
|
6335
7501
|
autoFocus = false,
|
|
6336
7502
|
onSubmit,
|
|
6337
7503
|
onEscape,
|
|
6338
7504
|
className = "",
|
|
6339
7505
|
size = "md",
|
|
6340
7506
|
disabled = false,
|
|
6341
|
-
ariaLabel
|
|
7507
|
+
ariaLabel
|
|
6342
7508
|
}) {
|
|
6343
7509
|
const { t } = useTranslation();
|
|
7510
|
+
const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Search");
|
|
7511
|
+
const resolvedAriaLabel = ariaLabel ?? t("BiChat.Common.Search");
|
|
6344
7512
|
const inputRef = React.useRef(null);
|
|
6345
7513
|
const sizes = sizeClasses3[size];
|
|
6346
7514
|
React.useEffect(() => {
|
|
@@ -6383,10 +7551,10 @@ function SearchInput({
|
|
|
6383
7551
|
value,
|
|
6384
7552
|
onChange: (e) => onChange(e.target.value),
|
|
6385
7553
|
onKeyDown: handleKeyDown,
|
|
6386
|
-
placeholder,
|
|
7554
|
+
placeholder: resolvedPlaceholder,
|
|
6387
7555
|
disabled,
|
|
6388
7556
|
className: `w-full ${sizes.container} bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700/50 rounded-xl focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50 focus-visible:border-primary-400 dark:focus-visible:border-primary-600 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed`,
|
|
6389
|
-
"aria-label":
|
|
7557
|
+
"aria-label": resolvedAriaLabel
|
|
6390
7558
|
}
|
|
6391
7559
|
),
|
|
6392
7560
|
value && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6586,26 +7754,37 @@ var LoadingSpinner_default = MemoizedLoadingSpinner;
|
|
|
6586
7754
|
// ui/src/bichat/index.ts
|
|
6587
7755
|
init_TableExportButton();
|
|
6588
7756
|
init_TableWithExport();
|
|
7757
|
+
|
|
7758
|
+
// ui/src/bichat/components/Toast.tsx
|
|
7759
|
+
init_useTranslation();
|
|
6589
7760
|
var typeConfig = {
|
|
6590
7761
|
success: {
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
icon:
|
|
7762
|
+
accent: "text-emerald-600 dark:text-emerald-400",
|
|
7763
|
+
bg: "bg-emerald-50 dark:bg-emerald-950/40 border-emerald-200/80 dark:border-emerald-800/50",
|
|
7764
|
+
icon: "bg-emerald-100 dark:bg-emerald-900/50",
|
|
7765
|
+
progress: "bg-emerald-500 dark:bg-emerald-400",
|
|
7766
|
+
iconEl: react.CheckCircle
|
|
6594
7767
|
},
|
|
6595
7768
|
error: {
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
icon:
|
|
7769
|
+
accent: "text-red-600 dark:text-red-400",
|
|
7770
|
+
bg: "bg-red-50 dark:bg-red-950/40 border-red-200/80 dark:border-red-800/50",
|
|
7771
|
+
icon: "bg-red-100 dark:bg-red-900/50",
|
|
7772
|
+
progress: "bg-red-500 dark:bg-red-400",
|
|
7773
|
+
iconEl: react.XCircle
|
|
6599
7774
|
},
|
|
6600
7775
|
info: {
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
icon:
|
|
7776
|
+
accent: "text-blue-600 dark:text-blue-400",
|
|
7777
|
+
bg: "bg-blue-50 dark:bg-blue-950/40 border-blue-200/80 dark:border-blue-800/50",
|
|
7778
|
+
icon: "bg-blue-100 dark:bg-blue-900/50",
|
|
7779
|
+
progress: "bg-blue-500 dark:bg-blue-400",
|
|
7780
|
+
iconEl: react.Info
|
|
6604
7781
|
},
|
|
6605
7782
|
warning: {
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
icon:
|
|
7783
|
+
accent: "text-amber-600 dark:text-amber-400",
|
|
7784
|
+
bg: "bg-amber-50 dark:bg-amber-950/40 border-amber-200/80 dark:border-amber-800/50",
|
|
7785
|
+
icon: "bg-amber-100 dark:bg-amber-900/50",
|
|
7786
|
+
progress: "bg-amber-500 dark:bg-amber-400",
|
|
7787
|
+
iconEl: react.Warning
|
|
6609
7788
|
}
|
|
6610
7789
|
};
|
|
6611
7790
|
function Toast({
|
|
@@ -6614,55 +7793,117 @@ function Toast({
|
|
|
6614
7793
|
message,
|
|
6615
7794
|
duration = 5e3,
|
|
6616
7795
|
onDismiss,
|
|
6617
|
-
dismissLabel
|
|
7796
|
+
dismissLabel
|
|
6618
7797
|
}) {
|
|
7798
|
+
const { t } = useTranslation();
|
|
7799
|
+
const resolvedDismissLabel = dismissLabel ?? t("BiChat.Chat.DismissNotification");
|
|
6619
7800
|
const config = typeConfig[type];
|
|
7801
|
+
const Icon = config.iconEl;
|
|
7802
|
+
const [show, setShow] = React.useState(false);
|
|
7803
|
+
const [paused, setPaused] = React.useState(false);
|
|
7804
|
+
const remainingRef = React.useRef(duration);
|
|
7805
|
+
const startRef = React.useRef(Date.now());
|
|
7806
|
+
React.useEffect(() => {
|
|
7807
|
+
const frame = requestAnimationFrame(() => setShow(true));
|
|
7808
|
+
return () => cancelAnimationFrame(frame);
|
|
7809
|
+
}, []);
|
|
7810
|
+
React.useEffect(() => {
|
|
7811
|
+
if (paused) return;
|
|
7812
|
+
startRef.current = Date.now();
|
|
7813
|
+
const timer = setTimeout(() => {
|
|
7814
|
+
setShow(false);
|
|
7815
|
+
setTimeout(() => onDismiss(id), 200);
|
|
7816
|
+
}, remainingRef.current);
|
|
7817
|
+
return () => {
|
|
7818
|
+
const elapsed = Date.now() - startRef.current;
|
|
7819
|
+
remainingRef.current = Math.max(0, remainingRef.current - elapsed);
|
|
7820
|
+
clearTimeout(timer);
|
|
7821
|
+
};
|
|
7822
|
+
}, [id, paused, onDismiss]);
|
|
7823
|
+
const handleDismiss = React.useCallback(() => {
|
|
7824
|
+
setShow(false);
|
|
7825
|
+
setTimeout(() => onDismiss(id), 200);
|
|
7826
|
+
}, [id, onDismiss]);
|
|
6620
7827
|
const ariaLive = type === "error" ? "assertive" : "polite";
|
|
6621
7828
|
const role = type === "error" || type === "warning" ? "alert" : "status";
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
return () => clearTimeout(timer);
|
|
6625
|
-
}, [id, duration, onDismiss]);
|
|
6626
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6627
|
-
framerMotion.motion.div,
|
|
7829
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7830
|
+
react$1.Transition,
|
|
6628
7831
|
{
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
"
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
"
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
7832
|
+
show,
|
|
7833
|
+
enter: "transition duration-200 ease-out",
|
|
7834
|
+
enterFrom: "-translate-y-2 opacity-0 scale-95",
|
|
7835
|
+
enterTo: "translate-y-0 opacity-100 scale-100",
|
|
7836
|
+
leave: "transition duration-150 ease-in",
|
|
7837
|
+
leaveFrom: "translate-y-0 opacity-100 scale-100",
|
|
7838
|
+
leaveTo: "-translate-y-2 opacity-0 scale-95",
|
|
7839
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7840
|
+
"div",
|
|
7841
|
+
{
|
|
7842
|
+
className: `relative flex items-start gap-3 rounded-xl border px-4 py-3 shadow-lg shadow-black/5 dark:shadow-black/20 backdrop-blur-sm min-w-[320px] max-w-[420px] overflow-hidden ${config.bg}`,
|
|
7843
|
+
role,
|
|
7844
|
+
"aria-live": ariaLive,
|
|
7845
|
+
"aria-atomic": "true",
|
|
7846
|
+
onMouseEnter: () => setPaused(true),
|
|
7847
|
+
onMouseLeave: () => setPaused(false),
|
|
7848
|
+
children: [
|
|
7849
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg ${config.icon}`, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 16, className: config.accent, weight: "fill" }) }),
|
|
7850
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 pt-0.5 text-sm font-medium leading-snug text-gray-800 dark:text-gray-100", children: message }),
|
|
7851
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7852
|
+
"button",
|
|
7853
|
+
{
|
|
7854
|
+
onClick: handleDismiss,
|
|
7855
|
+
className: "mt-0.5 -mr-1 cursor-pointer shrink-0 rounded-lg p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-200/60 dark:text-gray-500 dark:hover:text-gray-300 dark:hover:bg-gray-700/60 transition-colors duration-100",
|
|
7856
|
+
"aria-label": resolvedDismissLabel,
|
|
7857
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 14, weight: "bold" })
|
|
7858
|
+
}
|
|
7859
|
+
),
|
|
7860
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-black/5 dark:bg-white/5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7861
|
+
"div",
|
|
7862
|
+
{
|
|
7863
|
+
className: `h-full ${config.progress} origin-left`,
|
|
7864
|
+
style: {
|
|
7865
|
+
animation: `bichat-toast-progress ${duration}ms linear forwards`,
|
|
7866
|
+
animationPlayState: paused ? "paused" : "running"
|
|
7867
|
+
}
|
|
7868
|
+
}
|
|
7869
|
+
) })
|
|
7870
|
+
]
|
|
7871
|
+
}
|
|
7872
|
+
)
|
|
6650
7873
|
}
|
|
6651
7874
|
);
|
|
6652
7875
|
}
|
|
7876
|
+
|
|
7877
|
+
// ui/src/bichat/components/ToastContainer.tsx
|
|
7878
|
+
init_useTranslation();
|
|
6653
7879
|
function ToastContainer({ toasts, onDismiss, dismissLabel }) {
|
|
6654
|
-
|
|
7880
|
+
const { t } = useTranslation();
|
|
7881
|
+
if (toasts.length === 0) return null;
|
|
7882
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7883
|
+
"div",
|
|
7884
|
+
{
|
|
7885
|
+
"aria-label": t("BiChat.Common.Notifications"),
|
|
7886
|
+
className: "fixed top-6 right-6 z-[var(--bichat-z-toast,60)] flex flex-col gap-2 pointer-events-none",
|
|
7887
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsxRuntime.jsx(Toast, { ...toast, onDismiss, dismissLabel }) }, toast.id))
|
|
7888
|
+
}
|
|
7889
|
+
);
|
|
6655
7890
|
}
|
|
7891
|
+
|
|
7892
|
+
// ui/src/bichat/components/ConfirmModal.tsx
|
|
7893
|
+
init_useTranslation();
|
|
6656
7894
|
function ConfirmModalBase({
|
|
6657
7895
|
isOpen,
|
|
6658
7896
|
title,
|
|
6659
7897
|
message,
|
|
6660
7898
|
onConfirm,
|
|
6661
7899
|
onCancel,
|
|
6662
|
-
confirmText
|
|
6663
|
-
cancelText
|
|
7900
|
+
confirmText,
|
|
7901
|
+
cancelText,
|
|
6664
7902
|
isDanger = false
|
|
6665
7903
|
}) {
|
|
7904
|
+
const { t } = useTranslation();
|
|
7905
|
+
const resolvedConfirmText = confirmText ?? t("BiChat.Common.Confirm");
|
|
7906
|
+
const resolvedCancelText = cancelText ?? t("BiChat.Common.Cancel");
|
|
6666
7907
|
return /* @__PURE__ */ jsxRuntime.jsxs(react$1.Dialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
|
|
6667
7908
|
/* @__PURE__ */ jsxRuntime.jsx(react$1.DialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
|
|
6668
7909
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxRuntime.jsxs(react$1.DialogPanel, { className: "bg-white dark:bg-gray-800 rounded-2xl shadow-xl dark:shadow-2xl dark:shadow-black/30 max-w-sm w-full overflow-hidden", children: [
|
|
@@ -6679,9 +7920,9 @@ function ConfirmModalBase({
|
|
|
6679
7920
|
{
|
|
6680
7921
|
onClick: onCancel,
|
|
6681
7922
|
className: "cursor-pointer px-4 py-2 text-sm font-medium rounded-xl text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700/60 hover:bg-gray-200 dark:hover:bg-gray-700 active:bg-gray-250 dark:active:bg-gray-600 transition-colors 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-800",
|
|
6682
|
-
"aria-label":
|
|
7923
|
+
"aria-label": resolvedCancelText,
|
|
6683
7924
|
"data-testid": "confirm-modal-cancel",
|
|
6684
|
-
children:
|
|
7925
|
+
children: resolvedCancelText
|
|
6685
7926
|
}
|
|
6686
7927
|
),
|
|
6687
7928
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6694,9 +7935,9 @@ function ConfirmModalBase({
|
|
|
6694
7935
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
|
|
6695
7936
|
isDanger ? "bg-red-600 hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500/50" : "bg-primary-600 hover:bg-primary-700 active:bg-primary-800 focus-visible:ring-primary-500/50"
|
|
6696
7937
|
].join(" "),
|
|
6697
|
-
"aria-label":
|
|
7938
|
+
"aria-label": resolvedConfirmText,
|
|
6698
7939
|
"data-testid": "confirm-modal-confirm",
|
|
6699
|
-
children:
|
|
7940
|
+
children: resolvedConfirmText
|
|
6700
7941
|
}
|
|
6701
7942
|
)
|
|
6702
7943
|
] })
|
|
@@ -6786,28 +8027,44 @@ function PermissionGuard({
|
|
|
6786
8027
|
const permitted = mode === "all" ? permissions.every((p) => hasPermission2(p)) : permissions.some((p) => hasPermission2(p));
|
|
6787
8028
|
return permitted ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
|
|
6788
8029
|
}
|
|
8030
|
+
|
|
8031
|
+
// ui/src/bichat/components/ErrorBoundary.tsx
|
|
8032
|
+
init_useTranslation();
|
|
6789
8033
|
function DefaultErrorContent({
|
|
6790
8034
|
error,
|
|
6791
8035
|
onReset,
|
|
6792
|
-
resetLabel
|
|
6793
|
-
errorTitle
|
|
8036
|
+
resetLabel,
|
|
8037
|
+
errorTitle
|
|
6794
8038
|
}) {
|
|
8039
|
+
const { t } = useTranslation();
|
|
8040
|
+
const resolvedResetLabel = resetLabel ?? t("BiChat.Common.TryAgain");
|
|
8041
|
+
const resolvedErrorTitle = errorTitle ?? t("BiChat.Error.SomethingWentWrong");
|
|
6795
8042
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: [
|
|
6796
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
className: "flex items-center
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
8043
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 overflow-hidden pointer-events-none opacity-[0.03] dark:opacity-[0.04]", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "absolute -top-8 -right-8 w-64 h-64 text-red-500", viewBox: "0 0 200 200", fill: "currentColor", children: [
|
|
8044
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "100", cy: "100", r: "80", opacity: "0.5" }),
|
|
8045
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "100", cy: "100", r: "50", opacity: "0.3" }),
|
|
8046
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "100", cy: "100", r: "25", opacity: "0.2" })
|
|
8047
|
+
] }) }),
|
|
8048
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col items-center", children: [
|
|
8049
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mb-5", children: [
|
|
8050
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full bg-red-100 dark:bg-red-900/30 scale-150 blur-md" }),
|
|
8051
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex items-center justify-center w-14 h-14 rounded-full bg-red-50 dark:bg-red-900/20 border border-red-200/60 dark:border-red-800/40", children: /* @__PURE__ */ jsxRuntime.jsx(react.WarningCircle, { size: 28, className: "text-red-500 dark:text-red-400", weight: "fill" }) })
|
|
8052
|
+
] }),
|
|
8053
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-1.5", children: resolvedErrorTitle }),
|
|
8054
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400 mb-5 max-w-md leading-relaxed", children: error?.message || t("BiChat.Error.UnexpectedError") }),
|
|
8055
|
+
onReset && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8056
|
+
"button",
|
|
8057
|
+
{
|
|
8058
|
+
type: "button",
|
|
8059
|
+
onClick: onReset,
|
|
8060
|
+
className: "flex items-center gap-2 px-5 py-2.5 bg-red-600 hover:bg-red-700 active:bg-red-800 text-white rounded-lg transition-colors shadow-sm text-sm font-medium",
|
|
8061
|
+
children: [
|
|
8062
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, weight: "bold" }),
|
|
8063
|
+
resolvedResetLabel
|
|
8064
|
+
]
|
|
8065
|
+
}
|
|
8066
|
+
)
|
|
8067
|
+
] })
|
|
6811
8068
|
] });
|
|
6812
8069
|
}
|
|
6813
8070
|
var ErrorBoundary = class extends React.Component {
|
|
@@ -6944,10 +8201,13 @@ var TouchContextMenu = ({
|
|
|
6944
8201
|
const [focusedIndex, setFocusedIndex] = React.useState(-1);
|
|
6945
8202
|
const menuRef = React.useRef(null);
|
|
6946
8203
|
const itemRefs = React.useRef([]);
|
|
6947
|
-
const enabledIndices =
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
8204
|
+
const enabledIndices = React.useMemo(
|
|
8205
|
+
() => items.reduce((acc, item, i) => {
|
|
8206
|
+
if (!item.disabled) acc.push(i);
|
|
8207
|
+
return acc;
|
|
8208
|
+
}, []),
|
|
8209
|
+
[items]
|
|
8210
|
+
);
|
|
6951
8211
|
const focusItem = React.useCallback((index) => {
|
|
6952
8212
|
setFocusedIndex(index);
|
|
6953
8213
|
itemRefs.current[index]?.focus();
|
|
@@ -6963,7 +8223,7 @@ var TouchContextMenu = ({
|
|
|
6963
8223
|
}
|
|
6964
8224
|
});
|
|
6965
8225
|
return () => cancelAnimationFrame(timer);
|
|
6966
|
-
}, [isOpen, enabledIndices
|
|
8226
|
+
}, [isOpen, enabledIndices, focusItem]);
|
|
6967
8227
|
React.useEffect(() => {
|
|
6968
8228
|
if (!isOpen) return;
|
|
6969
8229
|
const handleKeyDown = (e) => {
|
|
@@ -7261,7 +8521,7 @@ var SessionItem = React.memo(
|
|
|
7261
8521
|
react$1.MenuItems,
|
|
7262
8522
|
{
|
|
7263
8523
|
anchor: "bottom start",
|
|
7264
|
-
className: "w-52 bg-white
|
|
8524
|
+
className: "w-52 bg-white dark:bg-gray-900 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700 z-30 [--anchor-gap:8px] mt-1 p-2 space-y-1",
|
|
7265
8525
|
children: [
|
|
7266
8526
|
mode !== "archived" && onPin && /* @__PURE__ */ jsxRuntime.jsx(react$1.MenuItem, { children: ({ focus }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7267
8527
|
"button",
|
|
@@ -7387,108 +8647,18 @@ var SessionItem = React.memo(
|
|
|
7387
8647
|
onClose: () => setMenuOpen(false),
|
|
7388
8648
|
anchorRect: menuAnchor
|
|
7389
8649
|
}
|
|
7390
|
-
)
|
|
7391
|
-
] });
|
|
7392
|
-
}
|
|
7393
|
-
);
|
|
7394
|
-
SessionItem.displayName = "SessionItem";
|
|
7395
|
-
var SessionItem_default = SessionItem;
|
|
7396
|
-
function DateGroupHeader({ groupName, count }) {
|
|
7397
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 bg-surface-300 dark:bg-gray-900 px-4 py-2 text-sm font-medium z-10 border-b border-gray-100 dark:border-gray-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
7398
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-700 dark:text-gray-300 font-semibold", children: groupName }),
|
|
7399
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded-full", children: count })
|
|
7400
|
-
] }) });
|
|
7401
|
-
}
|
|
7402
|
-
function TabBar({ tabs, activeTab, onTabChange }) {
|
|
7403
|
-
const tablistRef = React.useRef(null);
|
|
7404
|
-
const handleKeyDown = React.useCallback(
|
|
7405
|
-
(e) => {
|
|
7406
|
-
const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);
|
|
7407
|
-
if (currentIndex < 0) return;
|
|
7408
|
-
let nextIndex = null;
|
|
7409
|
-
switch (e.key) {
|
|
7410
|
-
case "ArrowRight":
|
|
7411
|
-
e.preventDefault();
|
|
7412
|
-
nextIndex = (currentIndex + 1) % tabs.length;
|
|
7413
|
-
break;
|
|
7414
|
-
case "ArrowLeft":
|
|
7415
|
-
e.preventDefault();
|
|
7416
|
-
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
7417
|
-
break;
|
|
7418
|
-
case "Home":
|
|
7419
|
-
e.preventDefault();
|
|
7420
|
-
nextIndex = 0;
|
|
7421
|
-
break;
|
|
7422
|
-
case "End":
|
|
7423
|
-
e.preventDefault();
|
|
7424
|
-
nextIndex = tabs.length - 1;
|
|
7425
|
-
break;
|
|
7426
|
-
}
|
|
7427
|
-
if (nextIndex !== null) {
|
|
7428
|
-
onTabChange(tabs[nextIndex].id);
|
|
7429
|
-
const tablist = tablistRef.current;
|
|
7430
|
-
if (tablist) {
|
|
7431
|
-
const buttons = tablist.querySelectorAll('[role="tab"]');
|
|
7432
|
-
buttons[nextIndex]?.focus();
|
|
7433
|
-
}
|
|
7434
|
-
}
|
|
7435
|
-
},
|
|
7436
|
-
[tabs, activeTab, onTabChange]
|
|
7437
|
-
);
|
|
7438
|
-
if (tabs.length === 0) {
|
|
7439
|
-
return null;
|
|
8650
|
+
)
|
|
8651
|
+
] });
|
|
7440
8652
|
}
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
TabButton,
|
|
7450
|
-
{
|
|
7451
|
-
id: tab.id,
|
|
7452
|
-
label: tab.label,
|
|
7453
|
-
isActive: activeTab === tab.id,
|
|
7454
|
-
onClick: () => onTabChange(tab.id)
|
|
7455
|
-
},
|
|
7456
|
-
tab.id
|
|
7457
|
-
))
|
|
7458
|
-
}
|
|
7459
|
-
);
|
|
7460
|
-
}
|
|
7461
|
-
function TabButton({ id, label, isActive, onClick }) {
|
|
7462
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7463
|
-
"button",
|
|
7464
|
-
{
|
|
7465
|
-
id,
|
|
7466
|
-
role: "tab",
|
|
7467
|
-
"aria-selected": isActive,
|
|
7468
|
-
"aria-controls": `${id}-panel`,
|
|
7469
|
-
tabIndex: isActive ? 0 : -1,
|
|
7470
|
-
onClick,
|
|
7471
|
-
className: `
|
|
7472
|
-
cursor-pointer relative px-4 py-2 rounded-t-lg text-sm font-medium transition-smooth focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50
|
|
7473
|
-
${isActive ? "text-primary-700 dark:text-primary-400" : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}
|
|
7474
|
-
`,
|
|
7475
|
-
children: [
|
|
7476
|
-
label,
|
|
7477
|
-
isActive && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7478
|
-
framerMotion.motion.div,
|
|
7479
|
-
{
|
|
7480
|
-
layoutId: "activeTab",
|
|
7481
|
-
className: "absolute bottom-0 left-0 right-0 h-0.5 bg-primary-600 dark:bg-primary-500",
|
|
7482
|
-
transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] }
|
|
7483
|
-
}
|
|
7484
|
-
)
|
|
7485
|
-
]
|
|
7486
|
-
}
|
|
7487
|
-
);
|
|
8653
|
+
);
|
|
8654
|
+
SessionItem.displayName = "SessionItem";
|
|
8655
|
+
var SessionItem_default = SessionItem;
|
|
8656
|
+
function DateGroupHeader({ groupName, count }) {
|
|
8657
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 bg-surface-300 dark:bg-gray-900 px-4 py-2 text-sm font-medium z-10 border-b border-gray-100 dark:border-gray-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
8658
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-700 dark:text-gray-300 font-semibold", children: groupName }),
|
|
8659
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-800 px-2 py-0.5 rounded-full", children: count })
|
|
8660
|
+
] }) });
|
|
7488
8661
|
}
|
|
7489
|
-
var MemoizedTabBar = React.memo(TabBar);
|
|
7490
|
-
MemoizedTabBar.displayName = "TabBar";
|
|
7491
|
-
var TabBar_default = MemoizedTabBar;
|
|
7492
8662
|
init_useTranslation();
|
|
7493
8663
|
function UserFilter({ users, selectedUser, onUserChange, loading }) {
|
|
7494
8664
|
const { t } = useTranslation();
|
|
@@ -7824,12 +8994,32 @@ init_useTranslation();
|
|
|
7824
8994
|
function generateId() {
|
|
7825
8995
|
return Math.random().toString(36).substring(7);
|
|
7826
8996
|
}
|
|
8997
|
+
var DEDUPE_WINDOW_MS = 2500;
|
|
8998
|
+
var MAX_ACTIVE_TOASTS = 5;
|
|
7827
8999
|
function useToast() {
|
|
7828
9000
|
const [toasts, setToasts] = React.useState([]);
|
|
9001
|
+
const recentToastMapRef = React.useRef(/* @__PURE__ */ new Map());
|
|
7829
9002
|
const showToast = React.useCallback(
|
|
7830
9003
|
(type, message, duration) => {
|
|
9004
|
+
const normalizedMessage = message.trim().toLowerCase();
|
|
9005
|
+
const key2 = `${type}:${normalizedMessage}`;
|
|
9006
|
+
const now = Date.now();
|
|
9007
|
+
const lastShownAt = recentToastMapRef.current.get(key2);
|
|
9008
|
+
if (lastShownAt && now - lastShownAt < DEDUPE_WINDOW_MS) {
|
|
9009
|
+
return;
|
|
9010
|
+
}
|
|
9011
|
+
for (const [mapKey, ts] of recentToastMapRef.current.entries()) {
|
|
9012
|
+
if (now - ts > DEDUPE_WINDOW_MS * 4) {
|
|
9013
|
+
recentToastMapRef.current.delete(mapKey);
|
|
9014
|
+
}
|
|
9015
|
+
}
|
|
9016
|
+
recentToastMapRef.current.set(key2, now);
|
|
7831
9017
|
const id = generateId();
|
|
7832
|
-
setToasts((prev) =>
|
|
9018
|
+
setToasts((prev) => {
|
|
9019
|
+
const next = [...prev, { id, type, message, duration }];
|
|
9020
|
+
if (next.length <= MAX_ACTIVE_TOASTS) return next;
|
|
9021
|
+
return next.slice(next.length - MAX_ACTIVE_TOASTS);
|
|
9022
|
+
});
|
|
7833
9023
|
},
|
|
7834
9024
|
[]
|
|
7835
9025
|
);
|
|
@@ -7838,6 +9028,7 @@ function useToast() {
|
|
|
7838
9028
|
}, []);
|
|
7839
9029
|
const dismissAll = React.useCallback(() => {
|
|
7840
9030
|
setToasts([]);
|
|
9031
|
+
recentToastMapRef.current.clear();
|
|
7841
9032
|
}, []);
|
|
7842
9033
|
return {
|
|
7843
9034
|
toasts,
|
|
@@ -7896,49 +9087,6 @@ function groupSessionsByDate(sessions, t) {
|
|
|
7896
9087
|
});
|
|
7897
9088
|
return groups;
|
|
7898
9089
|
}
|
|
7899
|
-
|
|
7900
|
-
// ui/src/bichat/utils/errorDisplay.ts
|
|
7901
|
-
function isPermissionDeniedError(error) {
|
|
7902
|
-
if (!error) return false;
|
|
7903
|
-
if (error instanceof Error) {
|
|
7904
|
-
const msg = error.message.toLowerCase();
|
|
7905
|
-
if (msg.includes("forbidden") || msg.includes("permission denied")) return true;
|
|
7906
|
-
}
|
|
7907
|
-
if (typeof error === "object" && error !== null) {
|
|
7908
|
-
const obj = error;
|
|
7909
|
-
if (obj.code === "forbidden" || obj.code === 403) return true;
|
|
7910
|
-
if (obj.status === 403) return true;
|
|
7911
|
-
if (obj.statusCode === 403) return true;
|
|
7912
|
-
if (typeof obj.response === "object" && obj.response !== null) {
|
|
7913
|
-
const resp = obj.response;
|
|
7914
|
-
if (resp.status === 403) return true;
|
|
7915
|
-
}
|
|
7916
|
-
}
|
|
7917
|
-
if (typeof error === "string") {
|
|
7918
|
-
const lower = error.toLowerCase();
|
|
7919
|
-
if (lower.includes("forbidden") || lower.includes("permission denied")) return true;
|
|
7920
|
-
}
|
|
7921
|
-
return false;
|
|
7922
|
-
}
|
|
7923
|
-
function toErrorDisplay(error, fallbackTitle) {
|
|
7924
|
-
const permDenied = isPermissionDeniedError(error);
|
|
7925
|
-
let title = fallbackTitle;
|
|
7926
|
-
let description = "";
|
|
7927
|
-
if (error instanceof Error) {
|
|
7928
|
-
description = error.message;
|
|
7929
|
-
} else if (typeof error === "object" && error !== null) {
|
|
7930
|
-
const obj = error;
|
|
7931
|
-
if (typeof obj.message === "string" && obj.message) description = obj.message;
|
|
7932
|
-
if (typeof obj.title === "string" && obj.title) title = obj.title;
|
|
7933
|
-
if (typeof obj.detail === "string" && obj.detail) description = obj.detail;
|
|
7934
|
-
} else if (typeof error === "string") {
|
|
7935
|
-
description = error;
|
|
7936
|
-
}
|
|
7937
|
-
if (permDenied && !description) {
|
|
7938
|
-
description = "Your account does not have permission for this action.";
|
|
7939
|
-
}
|
|
7940
|
-
return { title, description, isPermissionDenied: permDenied };
|
|
7941
|
-
}
|
|
7942
9090
|
function ErrorAlert({ error }) {
|
|
7943
9091
|
const amber = error.isPermissionDenied;
|
|
7944
9092
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -8022,7 +9170,7 @@ function Sidebar2({
|
|
|
8022
9170
|
const shouldReduceMotion = framerMotion.useReducedMotion();
|
|
8023
9171
|
const sessionListRef = React.useRef(null);
|
|
8024
9172
|
const searchContainerRef = React.useRef(null);
|
|
8025
|
-
const { isCollapsed,
|
|
9173
|
+
const { isCollapsed, toggle, collapse } = useSidebarCollapse();
|
|
8026
9174
|
const collapsible = !onClose;
|
|
8027
9175
|
const handleSidebarClick = React.useCallback(
|
|
8028
9176
|
(e) => {
|
|
@@ -8033,17 +9181,6 @@ function Sidebar2({
|
|
|
8033
9181
|
},
|
|
8034
9182
|
[collapsible, toggle]
|
|
8035
9183
|
);
|
|
8036
|
-
const focusSearch = React.useCallback(() => {
|
|
8037
|
-
if (!collapsible) return;
|
|
8038
|
-
if (isCollapsedRef.current) {
|
|
8039
|
-
expand();
|
|
8040
|
-
setTimeout(() => {
|
|
8041
|
-
searchContainerRef.current?.querySelector("input")?.focus();
|
|
8042
|
-
}, 250);
|
|
8043
|
-
} else {
|
|
8044
|
-
searchContainerRef.current?.querySelector("input")?.focus();
|
|
8045
|
-
}
|
|
8046
|
-
}, [collapsible, expand, isCollapsedRef]);
|
|
8047
9184
|
React.useEffect(() => {
|
|
8048
9185
|
if (!collapsible) return;
|
|
8049
9186
|
const handleKeyDown = (e) => {
|
|
@@ -8052,14 +9189,10 @@ function Sidebar2({
|
|
|
8052
9189
|
e.preventDefault();
|
|
8053
9190
|
toggle();
|
|
8054
9191
|
}
|
|
8055
|
-
if (isMod && e.key === "k") {
|
|
8056
|
-
e.preventDefault();
|
|
8057
|
-
focusSearch();
|
|
8058
|
-
}
|
|
8059
9192
|
};
|
|
8060
9193
|
document.addEventListener("keydown", handleKeyDown);
|
|
8061
9194
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
8062
|
-
}, [collapsible, toggle
|
|
9195
|
+
}, [collapsible, toggle]);
|
|
8063
9196
|
React.useEffect(() => {
|
|
8064
9197
|
if (!collapsible) return;
|
|
8065
9198
|
const handler = (e) => {
|
|
@@ -8082,13 +9215,6 @@ function Sidebar2({
|
|
|
8082
9215
|
const [refreshKey, setRefreshKey] = React.useState(0);
|
|
8083
9216
|
const [showConfirm, setShowConfirm] = React.useState(false);
|
|
8084
9217
|
const [sessionToArchive, setSessionToArchive] = React.useState(null);
|
|
8085
|
-
const tabs = React.useMemo(() => {
|
|
8086
|
-
const items = [{ id: "my-chats", label: t("BiChat.Sidebar.MyChats") }];
|
|
8087
|
-
if (showAllChatsTab) {
|
|
8088
|
-
items.push({ id: "all-chats", label: t("BiChat.Sidebar.AllChats") });
|
|
8089
|
-
}
|
|
8090
|
-
return items;
|
|
8091
|
-
}, [showAllChatsTab, t]);
|
|
8092
9218
|
const fetchSessions = React.useCallback(async () => {
|
|
8093
9219
|
try {
|
|
8094
9220
|
setLoading(true);
|
|
@@ -8301,14 +9427,6 @@ function Sidebar2({
|
|
|
8301
9427
|
}
|
|
8302
9428
|
)
|
|
8303
9429
|
] }),
|
|
8304
|
-
showAllChatsTab && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8305
|
-
TabBar_default,
|
|
8306
|
-
{
|
|
8307
|
-
tabs,
|
|
8308
|
-
activeTab,
|
|
8309
|
-
onTabChange: (id) => setActiveTab(id)
|
|
8310
|
-
}
|
|
8311
|
-
),
|
|
8312
9430
|
activeTab === "all-chats" && showAllChatsTab ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8313
9431
|
AllChatsList,
|
|
8314
9432
|
{
|
|
@@ -8929,232 +10047,73 @@ function BiChatLayout({
|
|
|
8929
10047
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
8930
10048
|
}, [closeMobile, isMobile, isMobileOpen]);
|
|
8931
10049
|
const handleDrawerDragEnd = (_, info) => {
|
|
8932
|
-
if (info.offset.x < -80) {
|
|
8933
|
-
closeMobile();
|
|
8934
|
-
}
|
|
8935
|
-
};
|
|
8936
|
-
const content = routeKey ? /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8937
|
-
framerMotion.motion.div,
|
|
8938
|
-
{
|
|
8939
|
-
className: "flex flex-1 min-h-0",
|
|
8940
|
-
initial: { opacity: 0, y: 4 },
|
|
8941
|
-
animate: { opacity: 1, y: 0 },
|
|
8942
|
-
exit: { opacity: 0, y: -4 },
|
|
8943
|
-
transition: { duration: 0.15, ease: "easeOut" },
|
|
8944
|
-
children
|
|
8945
|
-
},
|
|
8946
|
-
routeKey
|
|
8947
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 min-h-0", children });
|
|
8948
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative flex flex-1 w-full h-full min-h-0 overflow-hidden ${className}`, children: [
|
|
8949
|
-
/* @__PURE__ */ jsxRuntime.jsx(SkipLink, {}),
|
|
8950
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:block", children: renderSidebar({}) }),
|
|
8951
|
-
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isMobile && isMobileOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8952
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8953
|
-
framerMotion.motion.div,
|
|
8954
|
-
{
|
|
8955
|
-
className: "fixed inset-0 z-40 bg-black/40",
|
|
8956
|
-
initial: { opacity: 0 },
|
|
8957
|
-
animate: { opacity: 1 },
|
|
8958
|
-
exit: { opacity: 0 },
|
|
8959
|
-
onClick: closeMobile,
|
|
8960
|
-
"aria-hidden": "true"
|
|
8961
|
-
},
|
|
8962
|
-
"sidebar-backdrop"
|
|
8963
|
-
),
|
|
8964
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8965
|
-
framerMotion.motion.div,
|
|
8966
|
-
{
|
|
8967
|
-
className: "fixed inset-y-0 left-0 z-50 w-[18rem] max-w-[85vw] shadow-2xl",
|
|
8968
|
-
initial: { x: "-100%" },
|
|
8969
|
-
animate: { x: 0 },
|
|
8970
|
-
exit: { x: "-100%" },
|
|
8971
|
-
transition: { type: "spring", stiffness: 320, damping: 32 },
|
|
8972
|
-
drag: "x",
|
|
8973
|
-
dragDirectionLock: true,
|
|
8974
|
-
dragConstraints: { left: -120, right: 0 },
|
|
8975
|
-
dragElastic: { left: 0.2, right: 0 },
|
|
8976
|
-
onDragEnd: handleDrawerDragEnd,
|
|
8977
|
-
onClick: (e) => e.stopPropagation(),
|
|
8978
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: drawerRef, className: "h-full bg-white dark:bg-gray-900", children: renderSidebar({ onClose: closeMobile }) })
|
|
8979
|
-
},
|
|
8980
|
-
"sidebar-drawer"
|
|
8981
|
-
)
|
|
8982
|
-
] }) }),
|
|
8983
|
-
/* @__PURE__ */ jsxRuntime.jsxs("main", { id: "main-content", className: "relative flex-1 flex flex-col min-h-0 overflow-hidden", children: [
|
|
8984
|
-
isMobile && !isMobileOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8985
|
-
"button",
|
|
8986
|
-
{
|
|
8987
|
-
ref: menuButtonRef,
|
|
8988
|
-
onClick: openMobile,
|
|
8989
|
-
className: "md:hidden absolute top-3 left-3 z-30 w-10 h-10 rounded-xl bg-white/90 dark:bg-gray-900/90 text-gray-700 dark:text-gray-200 border border-gray-200/60 dark:border-gray-800/80 shadow-sm flex items-center justify-center hover:bg-white dark:hover:bg-gray-900 transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-primary-400/50",
|
|
8990
|
-
"aria-label": t("BiChat.Layout.OpenSidebar"),
|
|
8991
|
-
title: t("BiChat.Layout.OpenSidebar"),
|
|
8992
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(react.List, { size: 20, weight: "bold" })
|
|
8993
|
-
}
|
|
8994
|
-
),
|
|
8995
|
-
content
|
|
8996
|
-
] })
|
|
8997
|
-
] });
|
|
8998
|
-
}
|
|
8999
|
-
init_useTranslation();
|
|
9000
|
-
var variantStyles = {
|
|
9001
|
-
error: {
|
|
9002
|
-
container: "border-red-200 bg-red-50 dark:bg-red-900/20",
|
|
9003
|
-
title: "text-red-800 dark:text-red-300",
|
|
9004
|
-
message: "text-red-700 dark:text-red-400",
|
|
9005
|
-
icon: "text-red-600 dark:text-red-400",
|
|
9006
|
-
button: "text-red-400 hover:text-red-600 dark:hover:text-red-300",
|
|
9007
|
-
retryButton: "bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-800 text-white",
|
|
9008
|
-
Icon: react.XCircle
|
|
9009
|
-
},
|
|
9010
|
-
success: {
|
|
9011
|
-
container: "border-green-200 bg-green-50 dark:bg-green-900/20",
|
|
9012
|
-
title: "text-green-800 dark:text-green-300",
|
|
9013
|
-
message: "text-green-700 dark:text-green-400",
|
|
9014
|
-
icon: "text-green-600 dark:text-green-400",
|
|
9015
|
-
button: "text-green-400 hover:text-green-600 dark:hover:text-green-300",
|
|
9016
|
-
retryButton: "bg-green-600 dark:bg-green-700 hover:bg-green-700 dark:hover:bg-green-800 text-white",
|
|
9017
|
-
Icon: react.CheckCircle
|
|
9018
|
-
},
|
|
9019
|
-
warning: {
|
|
9020
|
-
container: "border-yellow-200 bg-yellow-50 dark:bg-yellow-900/20",
|
|
9021
|
-
title: "text-yellow-800 dark:text-yellow-300",
|
|
9022
|
-
message: "text-yellow-700 dark:text-yellow-400",
|
|
9023
|
-
icon: "text-yellow-600 dark:text-yellow-400",
|
|
9024
|
-
button: "text-yellow-400 hover:text-yellow-600 dark:hover:text-yellow-300",
|
|
9025
|
-
retryButton: "bg-yellow-600 dark:bg-yellow-700 hover:bg-yellow-700 dark:hover:bg-yellow-800 text-white",
|
|
9026
|
-
Icon: react.Warning
|
|
9027
|
-
},
|
|
9028
|
-
info: {
|
|
9029
|
-
container: "border-blue-200 bg-blue-50 dark:bg-blue-900/20",
|
|
9030
|
-
title: "text-blue-800 dark:text-blue-300",
|
|
9031
|
-
message: "text-blue-700 dark:text-blue-400",
|
|
9032
|
-
icon: "text-blue-600 dark:text-blue-400",
|
|
9033
|
-
button: "text-blue-400 hover:text-blue-600 dark:hover:text-blue-300",
|
|
9034
|
-
retryButton: "bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 text-white",
|
|
9035
|
-
Icon: react.Info
|
|
9036
|
-
}
|
|
9037
|
-
};
|
|
9038
|
-
function Alert({
|
|
9039
|
-
variant = "info",
|
|
9040
|
-
message,
|
|
9041
|
-
title,
|
|
9042
|
-
onDismiss,
|
|
9043
|
-
onRetry,
|
|
9044
|
-
show = true,
|
|
9045
|
-
dismissible = true
|
|
9046
|
-
}) {
|
|
9047
|
-
const { t } = useTranslation();
|
|
9048
|
-
const styles = variantStyles[variant];
|
|
9049
|
-
const IconComponent = styles.Icon;
|
|
9050
|
-
return /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: show && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9051
|
-
framerMotion.motion.div,
|
|
9052
|
-
{
|
|
9053
|
-
variants: errorMessageVariants,
|
|
9054
|
-
initial: "initial",
|
|
9055
|
-
animate: "animate",
|
|
9056
|
-
exit: "exit",
|
|
9057
|
-
className: `border-t border ${styles.container} px-4 py-3`,
|
|
9058
|
-
role: "alert",
|
|
9059
|
-
"aria-live": "assertive",
|
|
9060
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
9061
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
9062
|
-
/* @__PURE__ */ jsxRuntime.jsx(IconComponent, { size: 20, className: `w-5 h-5 ${styles.icon} flex-shrink-0 mt-0.5` }),
|
|
9063
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
9064
|
-
title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${styles.title} font-medium`, children: title }),
|
|
9065
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${styles.message} ${title ? "mt-1" : ""}`, children: message }),
|
|
9066
|
-
onRetry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9067
|
-
"button",
|
|
9068
|
-
{
|
|
9069
|
-
onClick: onRetry,
|
|
9070
|
-
className: `mt-2 text-xs px-3 py-1.5 rounded ${styles.retryButton} transition-colors font-medium`,
|
|
9071
|
-
children: t("BiChat.Chat.Retry")
|
|
9072
|
-
}
|
|
9073
|
-
)
|
|
9074
|
-
] })
|
|
9075
|
-
] }),
|
|
9076
|
-
dismissible && onDismiss && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9077
|
-
"button",
|
|
9078
|
-
{
|
|
9079
|
-
onClick: onDismiss,
|
|
9080
|
-
className: `${styles.button} transition-colors flex-shrink-0`,
|
|
9081
|
-
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
9082
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 20, className: "w-5 h-5" })
|
|
9083
|
-
}
|
|
9084
|
-
)
|
|
9085
|
-
] })
|
|
9086
|
-
}
|
|
9087
|
-
) });
|
|
9088
|
-
}
|
|
9089
|
-
var Alert_default = React.memo(Alert);
|
|
9090
|
-
init_useTranslation();
|
|
9091
|
-
function ArchiveBanner({
|
|
9092
|
-
show = true,
|
|
9093
|
-
onRestore,
|
|
9094
|
-
restoring = false,
|
|
9095
|
-
onRestoreComplete
|
|
9096
|
-
}) {
|
|
9097
|
-
const { t } = useTranslation();
|
|
9098
|
-
const [error, setError] = React.useState(null);
|
|
9099
|
-
const handleRestore = async () => {
|
|
9100
|
-
try {
|
|
9101
|
-
setError(null);
|
|
9102
|
-
if (onRestore) {
|
|
9103
|
-
await onRestore();
|
|
9104
|
-
}
|
|
9105
|
-
if (onRestoreComplete) {
|
|
9106
|
-
onRestoreComplete();
|
|
9107
|
-
}
|
|
9108
|
-
} catch (err) {
|
|
9109
|
-
const message = err instanceof Error ? err.message : t("BiChat.Archive.RestoreFailed");
|
|
9110
|
-
setError(message);
|
|
10050
|
+
if (info.offset.x < -80) {
|
|
10051
|
+
closeMobile();
|
|
9111
10052
|
}
|
|
9112
10053
|
};
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
|
|
9151
|
-
|
|
9152
|
-
|
|
9153
|
-
|
|
9154
|
-
|
|
10054
|
+
const content = routeKey ? /* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10055
|
+
framerMotion.motion.div,
|
|
10056
|
+
{
|
|
10057
|
+
className: "flex flex-1 min-h-0",
|
|
10058
|
+
initial: { opacity: 0, y: 4 },
|
|
10059
|
+
animate: { opacity: 1, y: 0 },
|
|
10060
|
+
exit: { opacity: 0, y: -4 },
|
|
10061
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
10062
|
+
children
|
|
10063
|
+
},
|
|
10064
|
+
routeKey
|
|
10065
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 min-h-0", children });
|
|
10066
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative flex flex-1 w-full h-full min-h-0 overflow-hidden ${className}`, children: [
|
|
10067
|
+
/* @__PURE__ */ jsxRuntime.jsx(SkipLink, {}),
|
|
10068
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:block", children: renderSidebar({}) }),
|
|
10069
|
+
/* @__PURE__ */ jsxRuntime.jsx(framerMotion.AnimatePresence, { children: isMobile && isMobileOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10070
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10071
|
+
framerMotion.motion.div,
|
|
10072
|
+
{
|
|
10073
|
+
className: "fixed inset-0 z-40 bg-black/40",
|
|
10074
|
+
initial: { opacity: 0 },
|
|
10075
|
+
animate: { opacity: 1 },
|
|
10076
|
+
exit: { opacity: 0 },
|
|
10077
|
+
onClick: closeMobile,
|
|
10078
|
+
"aria-hidden": "true"
|
|
10079
|
+
},
|
|
10080
|
+
"sidebar-backdrop"
|
|
10081
|
+
),
|
|
10082
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10083
|
+
framerMotion.motion.div,
|
|
10084
|
+
{
|
|
10085
|
+
className: "fixed inset-y-0 left-0 z-50 w-[18rem] max-w-[85vw] shadow-2xl",
|
|
10086
|
+
initial: { x: "-100%" },
|
|
10087
|
+
animate: { x: 0 },
|
|
10088
|
+
exit: { x: "-100%" },
|
|
10089
|
+
transition: { type: "spring", stiffness: 320, damping: 32 },
|
|
10090
|
+
drag: "x",
|
|
10091
|
+
dragDirectionLock: true,
|
|
10092
|
+
dragConstraints: { left: -120, right: 0 },
|
|
10093
|
+
dragElastic: { left: 0.2, right: 0 },
|
|
10094
|
+
onDragEnd: handleDrawerDragEnd,
|
|
10095
|
+
onClick: (e) => e.stopPropagation(),
|
|
10096
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: drawerRef, className: "h-full bg-white dark:bg-gray-900", children: renderSidebar({ onClose: closeMobile }) })
|
|
10097
|
+
},
|
|
10098
|
+
"sidebar-drawer"
|
|
10099
|
+
)
|
|
10100
|
+
] }) }),
|
|
10101
|
+
/* @__PURE__ */ jsxRuntime.jsxs("main", { id: "main-content", className: "relative flex-1 flex flex-col min-h-0 overflow-hidden", children: [
|
|
10102
|
+
isMobile && !isMobileOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10103
|
+
"button",
|
|
10104
|
+
{
|
|
10105
|
+
ref: menuButtonRef,
|
|
10106
|
+
onClick: openMobile,
|
|
10107
|
+
className: "md:hidden absolute top-3 left-3 z-30 w-10 h-10 rounded-xl bg-white/90 dark:bg-gray-900/90 text-gray-700 dark:text-gray-200 border border-gray-200/60 dark:border-gray-800/80 shadow-sm flex items-center justify-center hover:bg-white dark:hover:bg-gray-900 transition-colors cursor-pointer focus-visible:ring-2 focus-visible:ring-primary-400/50",
|
|
10108
|
+
"aria-label": t("BiChat.Layout.OpenSidebar"),
|
|
10109
|
+
title: t("BiChat.Layout.OpenSidebar"),
|
|
10110
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.List, { size: 20, weight: "bold" })
|
|
10111
|
+
}
|
|
10112
|
+
),
|
|
10113
|
+
content
|
|
10114
|
+
] })
|
|
9155
10115
|
] });
|
|
9156
10116
|
}
|
|
9157
|
-
var ArchiveBanner_default = React.memo(ArchiveBanner);
|
|
9158
10117
|
|
|
9159
10118
|
// ui/src/bichat/components/RetryActionArea.tsx
|
|
9160
10119
|
init_useTranslation();
|
|
@@ -9208,66 +10167,6 @@ var RetryActionArea = React.memo(function RetryActionArea2({
|
|
|
9208
10167
|
)
|
|
9209
10168
|
);
|
|
9210
10169
|
});
|
|
9211
|
-
|
|
9212
|
-
// ui/src/bichat/components/StreamError.tsx
|
|
9213
|
-
init_useTranslation();
|
|
9214
|
-
function StreamError({
|
|
9215
|
-
error,
|
|
9216
|
-
onRetry,
|
|
9217
|
-
onRegenerate,
|
|
9218
|
-
compact = false
|
|
9219
|
-
}) {
|
|
9220
|
-
const { t } = useTranslation();
|
|
9221
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9222
|
-
framerMotion.motion.div,
|
|
9223
|
-
{
|
|
9224
|
-
initial: { opacity: 0, y: 10 },
|
|
9225
|
-
animate: { opacity: 1, y: 0 },
|
|
9226
|
-
exit: { opacity: 0, y: -10 },
|
|
9227
|
-
className: `flex items-center gap-3 ${compact ? "p-3" : "p-4"} bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg`,
|
|
9228
|
-
role: "alert",
|
|
9229
|
-
children: [
|
|
9230
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9231
|
-
react.Warning,
|
|
9232
|
-
{
|
|
9233
|
-
className: "w-5 h-5 text-red-500 dark:text-red-400 flex-shrink-0",
|
|
9234
|
-
weight: "fill"
|
|
9235
|
-
}
|
|
9236
|
-
),
|
|
9237
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
9238
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200", children: t("BiChat.Error.Generic") }),
|
|
9239
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-red-600 dark:text-red-300 break-words", children: error })
|
|
9240
|
-
] }),
|
|
9241
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
|
|
9242
|
-
onRetry && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9243
|
-
"button",
|
|
9244
|
-
{
|
|
9245
|
-
onClick: onRetry,
|
|
9246
|
-
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-red-700 dark:text-red-200 bg-red-100 dark:bg-red-800/50 hover:bg-red-200 dark:hover:bg-red-800 rounded-md transition-colors",
|
|
9247
|
-
type: "button",
|
|
9248
|
-
children: [
|
|
9249
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { className: "w-4 h-4" }),
|
|
9250
|
-
t("BiChat.StreamError.Retry")
|
|
9251
|
-
]
|
|
9252
|
-
}
|
|
9253
|
-
),
|
|
9254
|
-
onRegenerate && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9255
|
-
"button",
|
|
9256
|
-
{
|
|
9257
|
-
onClick: onRegenerate,
|
|
9258
|
-
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-md transition-colors",
|
|
9259
|
-
type: "button",
|
|
9260
|
-
children: [
|
|
9261
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowsCounterClockwise, { className: "w-4 h-4" }),
|
|
9262
|
-
t("BiChat.StreamError.Regenerate")
|
|
9263
|
-
]
|
|
9264
|
-
}
|
|
9265
|
-
)
|
|
9266
|
-
] })
|
|
9267
|
-
]
|
|
9268
|
-
}
|
|
9269
|
-
);
|
|
9270
|
-
}
|
|
9271
10170
|
init_useTranslation();
|
|
9272
10171
|
function MessageActions({
|
|
9273
10172
|
message,
|
|
@@ -10666,9 +11565,10 @@ function useMarkdownCopy(options = {}) {
|
|
|
10666
11565
|
const [copiedStates, setCopiedStates] = React.useState(/* @__PURE__ */ new Map());
|
|
10667
11566
|
const timeoutsRef = React.useRef(/* @__PURE__ */ new Map());
|
|
10668
11567
|
React.useEffect(() => {
|
|
11568
|
+
const timeouts = timeoutsRef.current;
|
|
10669
11569
|
return () => {
|
|
10670
|
-
|
|
10671
|
-
|
|
11570
|
+
timeouts.forEach((timeout) => clearTimeout(timeout));
|
|
11571
|
+
timeouts.clear();
|
|
10672
11572
|
};
|
|
10673
11573
|
}, []);
|
|
10674
11574
|
const isCopied = React.useCallback(
|
|
@@ -10791,8 +11691,9 @@ function ConfigProvider({ config, useGlobalConfig = false, children }) {
|
|
|
10791
11691
|
if (config) {
|
|
10792
11692
|
resolvedConfig = config;
|
|
10793
11693
|
} else if (useGlobalConfig && typeof window !== "undefined") {
|
|
10794
|
-
const
|
|
10795
|
-
const
|
|
11694
|
+
const w = window;
|
|
11695
|
+
const globalContext = w.__APPLET_CONTEXT__;
|
|
11696
|
+
const globalCSRF = w.__CSRF_TOKEN__;
|
|
10796
11697
|
if (globalContext) {
|
|
10797
11698
|
resolvedConfig = {
|
|
10798
11699
|
user: {
|
|
@@ -10989,98 +11890,6 @@ function createHeadersWithCSRF(init) {
|
|
|
10989
11890
|
return addCSRFHeader(headers);
|
|
10990
11891
|
}
|
|
10991
11892
|
|
|
10992
|
-
// ui/src/applet-devtools/enabled.ts
|
|
10993
|
-
function shouldEnableAppletDevtools() {
|
|
10994
|
-
if (typeof window === "undefined") return false;
|
|
10995
|
-
const url = new URL(window.location.href);
|
|
10996
|
-
if (url.searchParams.get("appletDebug") === "1") return true;
|
|
10997
|
-
try {
|
|
10998
|
-
return window.localStorage.getItem("iotaAppletDevtools") === "1";
|
|
10999
|
-
} catch {
|
|
11000
|
-
return false;
|
|
11001
|
-
}
|
|
11002
|
-
}
|
|
11003
|
-
|
|
11004
|
-
// ui/src/applet-host/rpc.ts
|
|
11005
|
-
var AppletRPCException = class extends Error {
|
|
11006
|
-
constructor(args) {
|
|
11007
|
-
super(args.message);
|
|
11008
|
-
this.name = "AppletRPCException";
|
|
11009
|
-
this.code = args.code;
|
|
11010
|
-
this.details = args.details;
|
|
11011
|
-
this.cause = args.cause;
|
|
11012
|
-
}
|
|
11013
|
-
};
|
|
11014
|
-
function createAppletRPCClient(options) {
|
|
11015
|
-
const fetcher = options.fetcher ?? fetch;
|
|
11016
|
-
async function call(method, params) {
|
|
11017
|
-
const req = { id: crypto.randomUUID(), method, params };
|
|
11018
|
-
const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
11019
|
-
maybeDispatchRPCEvent({
|
|
11020
|
-
id: req.id,
|
|
11021
|
-
method: req.method,
|
|
11022
|
-
status: "start"
|
|
11023
|
-
});
|
|
11024
|
-
try {
|
|
11025
|
-
const resp = await fetcher(options.endpoint, {
|
|
11026
|
-
method: "POST",
|
|
11027
|
-
headers: { "Content-Type": "application/json" },
|
|
11028
|
-
body: JSON.stringify(req)
|
|
11029
|
-
});
|
|
11030
|
-
if (!resp.ok) {
|
|
11031
|
-
throw new AppletRPCException({
|
|
11032
|
-
code: "http_error",
|
|
11033
|
-
message: `HTTP ${resp.status}`,
|
|
11034
|
-
details: { status: resp.status }
|
|
11035
|
-
});
|
|
11036
|
-
}
|
|
11037
|
-
const json = await resp.json();
|
|
11038
|
-
if (json.error) {
|
|
11039
|
-
throw new AppletRPCException({
|
|
11040
|
-
code: json.error.code,
|
|
11041
|
-
message: json.error.message,
|
|
11042
|
-
details: json.error.details
|
|
11043
|
-
});
|
|
11044
|
-
}
|
|
11045
|
-
if (json.result === void 0) {
|
|
11046
|
-
throw new AppletRPCException({
|
|
11047
|
-
code: "invalid_response",
|
|
11048
|
-
message: "Missing result in successful response"
|
|
11049
|
-
});
|
|
11050
|
-
}
|
|
11051
|
-
maybeDispatchRPCEvent({
|
|
11052
|
-
id: req.id,
|
|
11053
|
-
method: req.method,
|
|
11054
|
-
status: "success",
|
|
11055
|
-
durationMs: elapsedMs(startedAt)
|
|
11056
|
-
});
|
|
11057
|
-
return json.result;
|
|
11058
|
-
} catch (err) {
|
|
11059
|
-
maybeDispatchRPCEvent({
|
|
11060
|
-
id: req.id,
|
|
11061
|
-
method: req.method,
|
|
11062
|
-
status: "error",
|
|
11063
|
-
durationMs: elapsedMs(startedAt),
|
|
11064
|
-
error: err
|
|
11065
|
-
});
|
|
11066
|
-
throw err;
|
|
11067
|
-
}
|
|
11068
|
-
}
|
|
11069
|
-
async function callTyped(method, params) {
|
|
11070
|
-
return call(method, params);
|
|
11071
|
-
}
|
|
11072
|
-
return { call, callTyped };
|
|
11073
|
-
}
|
|
11074
|
-
function maybeDispatchRPCEvent(detail) {
|
|
11075
|
-
if (typeof window === "undefined") return;
|
|
11076
|
-
if (!shouldEnableAppletDevtools()) return;
|
|
11077
|
-
window.dispatchEvent(new CustomEvent("iota:applet-rpc", { detail }));
|
|
11078
|
-
}
|
|
11079
|
-
function elapsedMs(startedAt) {
|
|
11080
|
-
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
11081
|
-
return Math.max(0, Math.round(now - startedAt));
|
|
11082
|
-
}
|
|
11083
|
-
|
|
11084
11893
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
11085
11894
|
init_chartSpec();
|
|
11086
11895
|
|
|
@@ -11126,7 +11935,9 @@ async function* parseSSEStream(reader) {
|
|
|
11126
11935
|
reader.releaseLock();
|
|
11127
11936
|
}
|
|
11128
11937
|
}
|
|
11938
|
+
var TERMINAL_TYPES = /* @__PURE__ */ new Set(["done", "error"]);
|
|
11129
11939
|
async function* parseBichatStream(reader) {
|
|
11940
|
+
let yieldedTerminal = false;
|
|
11130
11941
|
for await (const event of parseSSEStream(reader)) {
|
|
11131
11942
|
const parsed = event;
|
|
11132
11943
|
const inferredType = parsed.type || (parsed.content ? "content" : "error");
|
|
@@ -11134,11 +11945,50 @@ async function* parseBichatStream(reader) {
|
|
|
11134
11945
|
...parsed,
|
|
11135
11946
|
type: inferredType
|
|
11136
11947
|
};
|
|
11948
|
+
if (TERMINAL_TYPES.has(inferredType)) {
|
|
11949
|
+
yieldedTerminal = true;
|
|
11950
|
+
}
|
|
11137
11951
|
yield normalized;
|
|
11138
11952
|
}
|
|
11953
|
+
if (!yieldedTerminal) {
|
|
11954
|
+
yield { type: "done" };
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11957
|
+
async function* parseBichatStreamEvents(reader) {
|
|
11958
|
+
for await (const chunk of parseBichatStream(reader)) {
|
|
11959
|
+
const event = toStreamEvent(chunk);
|
|
11960
|
+
if (event) yield event;
|
|
11961
|
+
}
|
|
11962
|
+
}
|
|
11963
|
+
function toStreamEvent(chunk) {
|
|
11964
|
+
switch (chunk.type) {
|
|
11965
|
+
case "chunk":
|
|
11966
|
+
case "content":
|
|
11967
|
+
return { type: "content", content: chunk.content ?? "" };
|
|
11968
|
+
case "tool_start":
|
|
11969
|
+
return chunk.tool ? { type: "tool_start", tool: chunk.tool } : null;
|
|
11970
|
+
case "tool_end":
|
|
11971
|
+
return chunk.tool ? { type: "tool_end", tool: chunk.tool } : null;
|
|
11972
|
+
case "usage":
|
|
11973
|
+
return chunk.usage ? { type: "usage", usage: chunk.usage } : null;
|
|
11974
|
+
case "user_message":
|
|
11975
|
+
return chunk.sessionId ? { type: "user_message", sessionId: chunk.sessionId } : null;
|
|
11976
|
+
case "interrupt":
|
|
11977
|
+
return chunk.interrupt ? { type: "interrupt", interrupt: chunk.interrupt, sessionId: chunk.sessionId } : null;
|
|
11978
|
+
case "done":
|
|
11979
|
+
return { type: "done", sessionId: chunk.sessionId, generationMs: chunk.generationMs };
|
|
11980
|
+
case "error":
|
|
11981
|
+
return { type: "error", error: chunk.error ?? "Unknown error" };
|
|
11982
|
+
default:
|
|
11983
|
+
return null;
|
|
11984
|
+
}
|
|
11139
11985
|
}
|
|
11140
11986
|
|
|
11141
11987
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
11988
|
+
function isSessionNotFoundError(err) {
|
|
11989
|
+
if (!(err instanceof AppletRPCException)) return false;
|
|
11990
|
+
return err.code === "not_found" || err.code === "session_not_found";
|
|
11991
|
+
}
|
|
11142
11992
|
function toSession(session) {
|
|
11143
11993
|
return {
|
|
11144
11994
|
...session,
|
|
@@ -11326,34 +12176,18 @@ function attachArtifactsToTurns(turns, artifacts) {
|
|
|
11326
12176
|
}
|
|
11327
12177
|
};
|
|
11328
12178
|
});
|
|
11329
|
-
const assistantPositions = [];
|
|
11330
12179
|
const turnIndexByMessageID = /* @__PURE__ */ new Map();
|
|
11331
12180
|
nextTurns.forEach((turn, index) => {
|
|
11332
12181
|
turnIndexByMessageID.set(turn.userTurn.id, index);
|
|
11333
12182
|
const assistantTurn = turn.assistantTurn;
|
|
11334
12183
|
if (!assistantTurn) return;
|
|
11335
12184
|
turnIndexByMessageID.set(assistantTurn.id, index);
|
|
11336
|
-
assistantPositions.push({
|
|
11337
|
-
index,
|
|
11338
|
-
createdAtMs: toMillis(assistantTurn.createdAt || turn.createdAt)
|
|
11339
|
-
});
|
|
11340
12185
|
});
|
|
11341
|
-
if (assistantPositions.length === 0) return turns;
|
|
11342
|
-
const findFallbackAssistantIndex = (artifactCreatedAt) => {
|
|
11343
|
-
const artifactMs = toMillis(artifactCreatedAt);
|
|
11344
|
-
if (!Number.isFinite(artifactMs)) {
|
|
11345
|
-
return assistantPositions[assistantPositions.length - 1].index;
|
|
11346
|
-
}
|
|
11347
|
-
for (const pos of assistantPositions) {
|
|
11348
|
-
if (Number.isFinite(pos.createdAtMs) && pos.createdAtMs >= artifactMs) {
|
|
11349
|
-
return pos.index;
|
|
11350
|
-
}
|
|
11351
|
-
}
|
|
11352
|
-
return assistantPositions[assistantPositions.length - 1].index;
|
|
11353
|
-
};
|
|
11354
12186
|
for (const entry of downloadArtifacts) {
|
|
11355
12187
|
const messageID = entry.raw.messageId;
|
|
11356
|
-
|
|
12188
|
+
if (!messageID) continue;
|
|
12189
|
+
const targetIndex = turnIndexByMessageID.get(messageID);
|
|
12190
|
+
if (targetIndex === void 0) continue;
|
|
11357
12191
|
const assistantTurn = nextTurns[targetIndex]?.assistantTurn;
|
|
11358
12192
|
if (!assistantTurn) continue;
|
|
11359
12193
|
const exists = assistantTurn.artifacts.some(
|
|
@@ -11365,7 +12199,9 @@ function attachArtifactsToTurns(turns, artifacts) {
|
|
|
11365
12199
|
}
|
|
11366
12200
|
for (const raw of chartArtifacts) {
|
|
11367
12201
|
const messageID = raw.messageId;
|
|
11368
|
-
|
|
12202
|
+
if (!messageID) continue;
|
|
12203
|
+
const targetIndex = turnIndexByMessageID.get(messageID);
|
|
12204
|
+
if (targetIndex === void 0) continue;
|
|
11369
12205
|
const assistantTurn = nextTurns[targetIndex]?.assistantTurn;
|
|
11370
12206
|
if (!assistantTurn) continue;
|
|
11371
12207
|
if (assistantTurn.chartData) continue;
|
|
@@ -11391,7 +12227,8 @@ var HttpDataSource = class {
|
|
|
11391
12227
|
this.navigateToSession = config.navigateToSession;
|
|
11392
12228
|
}
|
|
11393
12229
|
this.rpc = createAppletRPCClient({
|
|
11394
|
-
endpoint: `${this.config.baseUrl}${this.config.rpcEndpoint}
|
|
12230
|
+
endpoint: `${this.config.baseUrl}${this.config.rpcEndpoint}`,
|
|
12231
|
+
timeoutMs: this.config.timeout
|
|
11395
12232
|
});
|
|
11396
12233
|
}
|
|
11397
12234
|
/**
|
|
@@ -11447,8 +12284,11 @@ var HttpDataSource = class {
|
|
|
11447
12284
|
pendingQuestion: toPendingQuestion(data.pendingQuestion)
|
|
11448
12285
|
};
|
|
11449
12286
|
} catch (err) {
|
|
12287
|
+
if (isSessionNotFoundError(err)) {
|
|
12288
|
+
return null;
|
|
12289
|
+
}
|
|
11450
12290
|
console.error("Failed to fetch session:", err);
|
|
11451
|
-
|
|
12291
|
+
throw err instanceof Error ? err : new Error("Failed to fetch session");
|
|
11452
12292
|
}
|
|
11453
12293
|
}
|
|
11454
12294
|
async fetchSessionArtifacts(sessionId, options) {
|
|
@@ -11537,15 +12377,28 @@ var HttpDataSource = class {
|
|
|
11537
12377
|
url: a.url
|
|
11538
12378
|
}))
|
|
11539
12379
|
};
|
|
12380
|
+
let connectionTimeoutID;
|
|
12381
|
+
let connectionTimedOut = false;
|
|
11540
12382
|
try {
|
|
12383
|
+
const timeoutMs = this.config.timeout ?? 0;
|
|
12384
|
+
if (timeoutMs > 0) {
|
|
12385
|
+
connectionTimeoutID = setTimeout(() => {
|
|
12386
|
+
connectionTimedOut = true;
|
|
12387
|
+
this.abortController?.abort();
|
|
12388
|
+
}, timeoutMs);
|
|
12389
|
+
}
|
|
11541
12390
|
const response = await fetch(url, {
|
|
11542
12391
|
method: "POST",
|
|
11543
12392
|
headers: this.createHeaders(),
|
|
11544
12393
|
body: JSON.stringify(payload),
|
|
11545
12394
|
signal: this.abortController.signal
|
|
11546
12395
|
});
|
|
12396
|
+
if (connectionTimeoutID !== void 0) {
|
|
12397
|
+
clearTimeout(connectionTimeoutID);
|
|
12398
|
+
connectionTimeoutID = void 0;
|
|
12399
|
+
}
|
|
11547
12400
|
if (!response.ok) {
|
|
11548
|
-
throw new Error(`Stream request failed: ${response.
|
|
12401
|
+
throw new Error(`Stream request failed: HTTP ${response.status}`);
|
|
11549
12402
|
}
|
|
11550
12403
|
if (!response.body) {
|
|
11551
12404
|
throw new Error("Response body is null");
|
|
@@ -11562,7 +12415,7 @@ var HttpDataSource = class {
|
|
|
11562
12415
|
if (err.name === "AbortError") {
|
|
11563
12416
|
yield {
|
|
11564
12417
|
type: "error",
|
|
11565
|
-
error: "Stream cancelled"
|
|
12418
|
+
error: connectionTimedOut ? `Stream request timed out after ${this.config.timeout}ms` : "Stream cancelled"
|
|
11566
12419
|
};
|
|
11567
12420
|
} else {
|
|
11568
12421
|
yield {
|
|
@@ -11577,6 +12430,9 @@ var HttpDataSource = class {
|
|
|
11577
12430
|
};
|
|
11578
12431
|
}
|
|
11579
12432
|
} finally {
|
|
12433
|
+
if (connectionTimeoutID !== void 0) {
|
|
12434
|
+
clearTimeout(connectionTimeoutID);
|
|
12435
|
+
}
|
|
11580
12436
|
if (signal && onExternalAbort) {
|
|
11581
12437
|
signal.removeEventListener("abort", onExternalAbort);
|
|
11582
12438
|
}
|
|
@@ -11708,6 +12564,7 @@ exports.Bubble = Bubble;
|
|
|
11708
12564
|
exports.CHART_VISUAL = CHART_VISUAL;
|
|
11709
12565
|
exports.ChartCard = ChartCard;
|
|
11710
12566
|
exports.ChatHeader = ChatHeader;
|
|
12567
|
+
exports.ChatMachine = ChatMachine;
|
|
11711
12568
|
exports.ChatSession = ChatSession;
|
|
11712
12569
|
exports.ChatSessionProvider = ChatSessionProvider;
|
|
11713
12570
|
exports.CodeOutputsPanel = CodeOutputsPanel;
|
|
@@ -11757,7 +12614,6 @@ exports.SourcesPanel = SourcesPanel;
|
|
|
11757
12614
|
exports.StreamError = StreamError;
|
|
11758
12615
|
exports.StreamingCursor = StreamingCursor;
|
|
11759
12616
|
exports.SystemMessage = SystemMessage;
|
|
11760
|
-
exports.TabBar = MemoizedTabBar;
|
|
11761
12617
|
exports.ThemeProvider = ThemeProvider;
|
|
11762
12618
|
exports.Toast = Toast;
|
|
11763
12619
|
exports.ToastContainer = ToastContainer;
|
|
@@ -11795,11 +12651,13 @@ exports.lightTheme = lightTheme;
|
|
|
11795
12651
|
exports.listItemVariants = listItemVariants;
|
|
11796
12652
|
exports.messageContainerVariants = messageContainerVariants;
|
|
11797
12653
|
exports.messageVariants = messageVariants;
|
|
12654
|
+
exports.parseBichatStream = parseBichatStream;
|
|
12655
|
+
exports.parseBichatStreamEvents = parseBichatStreamEvents;
|
|
12656
|
+
exports.parseSSEStream = parseSSEStream;
|
|
11798
12657
|
exports.scaleFadeVariants = scaleFadeVariants;
|
|
11799
12658
|
exports.sessionItemVariants = sessionItemVariants;
|
|
11800
12659
|
exports.staggerContainerVariants = staggerContainerVariants;
|
|
11801
12660
|
exports.toErrorDisplay = toErrorDisplay;
|
|
11802
|
-
exports.toastVariants = toastVariants;
|
|
11803
12661
|
exports.typingDotVariants = typingDotVariants;
|
|
11804
12662
|
exports.useActionButtonContext = useActionButtonContext;
|
|
11805
12663
|
exports.useAttachments = useAttachments;
|