@iota-uz/sdk 0.4.12 → 0.4.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -2
- 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/applet-runtime/index.cjs +890 -0
- package/dist/applet-runtime/index.cjs.map +1 -0
- package/dist/applet-runtime/index.d.cts +191 -0
- package/dist/applet-runtime/index.d.ts +191 -0
- package/dist/applet-runtime/index.mjs +876 -0
- package/dist/applet-runtime/index.mjs.map +1 -0
- 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 +8 -2
- package/tailwind/compiled.css +1 -1
package/dist/bichat/index.mjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import React, { createContext, lazy, memo, forwardRef, useState, useRef, useMemo, useEffect, useImperativeHandle, useCallback, isValidElement, cloneElement, useContext, useId, Suspense, Component, Children } from 'react';
|
|
1
|
+
import React, { createContext, lazy, memo, forwardRef, useState, useRef, useMemo, useEffect, useImperativeHandle, useCallback, isValidElement, cloneElement, useContext, useId, useSyncExternalStore, Suspense, Component, Children } from 'react';
|
|
2
2
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import ReactApexChart from 'react-apexcharts';
|
|
4
4
|
import ApexCharts from 'apexcharts';
|
|
5
|
-
import { X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Warning, ArrowClockwise, Image, ImageBroken, CaretLeft, CaretRight,
|
|
5
|
+
import { X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Warning, ArrowClockwise, Image, ArrowCounterClockwise, ImageBroken, CaretLeft, CaretRight, Info, CheckCircle, XCircle, Spinner, MagnifyingGlass, WarningCircle, CaretDown, Copy, FilePdf, FileXls, FileCsv, FileDoc, FileCode, FileText, File, ChartBar, DownloadSimple, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, Wrench, ClockCounterClockwise, Lightbulb, Package, Plus, ArrowsCounterClockwise, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, ArrowSquareOut, SpinnerGap, FloppyDisk, Sidebar } from '@phosphor-icons/react';
|
|
6
6
|
import { Prism } from 'react-syntax-highlighter';
|
|
7
7
|
import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
8
8
|
import ReactMarkdown from 'react-markdown';
|
|
9
9
|
import remarkGfm from 'remark-gfm';
|
|
10
10
|
import { useMotionValue, useTransform, motion, AnimatePresence, useReducedMotion } from 'framer-motion';
|
|
11
|
-
import { formatDistanceToNow, startOfDay, differenceInDays } from 'date-fns';
|
|
12
|
-
import { Menu, MenuButton, MenuItems, MenuItem, Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
|
|
13
|
-
import { createPortal } from 'react-dom';
|
|
14
11
|
import 'react-dom/client';
|
|
12
|
+
import { startOfDay, differenceInDays, differenceInMinutes, differenceInHours, format } from 'date-fns';
|
|
13
|
+
import { Menu, MenuButton, MenuItems, MenuItem, Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Disclosure, DisclosureButton, DisclosurePanel, Transition } from '@headlessui/react';
|
|
14
|
+
import { createPortal } from 'react-dom';
|
|
15
15
|
|
|
16
16
|
var __defProp = Object.defineProperty;
|
|
17
17
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -22,12 +22,12 @@ var __export = (target, all) => {
|
|
|
22
22
|
for (var name in all)
|
|
23
23
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
24
24
|
};
|
|
25
|
-
function IotaContextProvider({ children }) {
|
|
26
|
-
const
|
|
27
|
-
if (!
|
|
28
|
-
throw new Error("
|
|
25
|
+
function IotaContextProvider({ context, children }) {
|
|
26
|
+
const resolved = context ?? (typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0);
|
|
27
|
+
if (!resolved) {
|
|
28
|
+
throw new Error("APPLET_CONTEXT not found. Pass a `context` prop or ensure the server injected context into window.__APPLET_CONTEXT__.");
|
|
29
29
|
}
|
|
30
|
-
return /* @__PURE__ */ jsx(IotaContext.Provider, { value:
|
|
30
|
+
return /* @__PURE__ */ jsx(IotaContext.Provider, { value: resolved, children });
|
|
31
31
|
}
|
|
32
32
|
function useIotaContext() {
|
|
33
33
|
const context = useContext(IotaContext);
|
|
@@ -37,7 +37,7 @@ function useIotaContext() {
|
|
|
37
37
|
return context;
|
|
38
38
|
}
|
|
39
39
|
function hasPermission(permission) {
|
|
40
|
-
const context = window.
|
|
40
|
+
const context = typeof window !== "undefined" ? window.__APPLET_CONTEXT__ : void 0;
|
|
41
41
|
if (!context) {
|
|
42
42
|
return false;
|
|
43
43
|
}
|
|
@@ -294,12 +294,16 @@ var init_chartSpec = __esm({
|
|
|
294
294
|
var TableExportButton;
|
|
295
295
|
var init_TableExportButton = __esm({
|
|
296
296
|
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
297
|
+
init_useTranslation();
|
|
297
298
|
TableExportButton = memo(function TableExportButton2({
|
|
298
299
|
onClick,
|
|
299
300
|
disabled = false,
|
|
300
|
-
label
|
|
301
|
-
disabledTooltip
|
|
301
|
+
label,
|
|
302
|
+
disabledTooltip
|
|
302
303
|
}) {
|
|
304
|
+
const { t } = useTranslation();
|
|
305
|
+
const resolvedLabel = label ?? t("BiChat.Export");
|
|
306
|
+
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
303
307
|
return /* @__PURE__ */ jsxs(
|
|
304
308
|
"button",
|
|
305
309
|
{
|
|
@@ -307,35 +311,38 @@ var init_TableExportButton = __esm({
|
|
|
307
311
|
onClick,
|
|
308
312
|
disabled,
|
|
309
313
|
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",
|
|
310
|
-
"aria-label":
|
|
311
|
-
title: disabled ?
|
|
314
|
+
"aria-label": resolvedLabel,
|
|
315
|
+
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
312
316
|
children: [
|
|
313
317
|
/* @__PURE__ */ jsx(FileXls, { size: 16, weight: "fill" }),
|
|
314
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
318
|
+
/* @__PURE__ */ jsx("span", { children: resolvedLabel })
|
|
315
319
|
]
|
|
316
320
|
}
|
|
317
321
|
);
|
|
318
322
|
});
|
|
319
323
|
}
|
|
320
324
|
});
|
|
321
|
-
var
|
|
325
|
+
var TableWithExport;
|
|
322
326
|
var init_TableWithExport = __esm({
|
|
323
327
|
"ui/src/bichat/components/TableWithExport.tsx"() {
|
|
324
328
|
init_TableExportButton();
|
|
325
|
-
|
|
329
|
+
init_useTranslation();
|
|
326
330
|
TableWithExport = memo(function TableWithExport2({
|
|
327
331
|
children,
|
|
328
332
|
sendMessage,
|
|
329
333
|
disabled = false,
|
|
330
|
-
exportMessage
|
|
331
|
-
exportLabel
|
|
334
|
+
exportMessage,
|
|
335
|
+
exportLabel
|
|
332
336
|
}) {
|
|
337
|
+
const { t } = useTranslation();
|
|
338
|
+
const resolvedExportMessage = exportMessage ?? t("BiChat.ExportTableToExcel");
|
|
339
|
+
const resolvedExportLabel = exportLabel ?? t("BiChat.Export");
|
|
333
340
|
const handleExport = useCallback(() => {
|
|
334
|
-
sendMessage?.(
|
|
335
|
-
}, [sendMessage,
|
|
341
|
+
sendMessage?.(resolvedExportMessage);
|
|
342
|
+
}, [sendMessage, resolvedExportMessage]);
|
|
336
343
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
337
344
|
/* @__PURE__ */ jsx("div", { className: "markdown-table-wrapper overflow-x-auto", children: /* @__PURE__ */ jsx("table", { className: "markdown-table w-full border-collapse", children }) }),
|
|
338
|
-
sendMessage && /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsx(TableExportButton, { onClick: handleExport, disabled, label:
|
|
345
|
+
sendMessage && /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsx(TableExportButton, { onClick: handleExport, disabled, label: resolvedExportLabel }) })
|
|
339
346
|
] });
|
|
340
347
|
});
|
|
341
348
|
}
|
|
@@ -355,9 +362,12 @@ function CodeBlock({
|
|
|
355
362
|
language,
|
|
356
363
|
value,
|
|
357
364
|
inline,
|
|
358
|
-
copyLabel
|
|
359
|
-
copiedLabel
|
|
365
|
+
copyLabel,
|
|
366
|
+
copiedLabel
|
|
360
367
|
}) {
|
|
368
|
+
const { t } = useTranslation();
|
|
369
|
+
const resolvedCopyLabel = copyLabel ?? t("BiChat.Message.Copy");
|
|
370
|
+
const resolvedCopiedLabel = copiedLabel ?? t("BiChat.Message.Copied");
|
|
361
371
|
const [copied, setCopied] = useState(false);
|
|
362
372
|
const [copyFailed, setCopyFailed] = useState(false);
|
|
363
373
|
const [isDarkMode, setIsDarkMode] = useState(getInitialDarkMode);
|
|
@@ -433,14 +443,14 @@ function CodeBlock({
|
|
|
433
443
|
{
|
|
434
444
|
onClick: handleCopy,
|
|
435
445
|
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"}`,
|
|
436
|
-
title:
|
|
446
|
+
title: resolvedCopyLabel,
|
|
437
447
|
"aria-live": "polite",
|
|
438
448
|
children: copied ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
439
449
|
/* @__PURE__ */ jsx(Check, { size: 16, className: "w-4 h-4" }),
|
|
440
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
450
|
+
/* @__PURE__ */ jsx("span", { children: resolvedCopiedLabel })
|
|
441
451
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
442
452
|
/* @__PURE__ */ jsx(Copy, { size: 16, className: "w-4 h-4" }),
|
|
443
|
-
/* @__PURE__ */ jsx("span", { children: copyFailed ? "
|
|
453
|
+
/* @__PURE__ */ jsx("span", { children: copyFailed ? t("BiChat.Message.CopyFailed") : resolvedCopyLabel })
|
|
444
454
|
] })
|
|
445
455
|
}
|
|
446
456
|
)
|
|
@@ -472,6 +482,7 @@ function CodeBlock({
|
|
|
472
482
|
var getInitialDarkMode, languageMap, MemoizedCodeBlock, CodeBlock_default;
|
|
473
483
|
var init_CodeBlock = __esm({
|
|
474
484
|
"ui/src/bichat/components/CodeBlock.tsx"() {
|
|
485
|
+
init_useTranslation();
|
|
475
486
|
getInitialDarkMode = () => {
|
|
476
487
|
if (typeof document === "undefined") return false;
|
|
477
488
|
return document.documentElement.classList.contains("dark");
|
|
@@ -530,10 +541,14 @@ function MarkdownRenderer({
|
|
|
530
541
|
citations,
|
|
531
542
|
sendMessage,
|
|
532
543
|
sendDisabled = false,
|
|
533
|
-
copyLabel
|
|
534
|
-
copiedLabel
|
|
535
|
-
exportLabel
|
|
544
|
+
copyLabel,
|
|
545
|
+
copiedLabel,
|
|
546
|
+
exportLabel
|
|
536
547
|
}) {
|
|
548
|
+
const { t } = useTranslation();
|
|
549
|
+
const resolvedCopyLabel = copyLabel ?? t("BiChat.Message.Copy");
|
|
550
|
+
const resolvedCopiedLabel = copiedLabel ?? t("BiChat.Message.Copied");
|
|
551
|
+
const resolvedExportLabel = exportLabel ?? t("BiChat.Export");
|
|
537
552
|
const processed = useMemo(() => {
|
|
538
553
|
return processCitations(content, citations);
|
|
539
554
|
}, [content, citations]);
|
|
@@ -568,8 +583,8 @@ function MarkdownRenderer({
|
|
|
568
583
|
language,
|
|
569
584
|
value,
|
|
570
585
|
inline: false,
|
|
571
|
-
copyLabel,
|
|
572
|
-
copiedLabel
|
|
586
|
+
copyLabel: resolvedCopyLabel,
|
|
587
|
+
copiedLabel: resolvedCopiedLabel
|
|
573
588
|
}
|
|
574
589
|
)
|
|
575
590
|
}
|
|
@@ -606,7 +621,7 @@ function MarkdownRenderer({
|
|
|
606
621
|
{
|
|
607
622
|
sendMessage,
|
|
608
623
|
disabled: sendDisabled,
|
|
609
|
-
exportLabel,
|
|
624
|
+
exportLabel: resolvedExportLabel,
|
|
610
625
|
children
|
|
611
626
|
}
|
|
612
627
|
),
|
|
@@ -628,6 +643,7 @@ var init_MarkdownRenderer = __esm({
|
|
|
628
643
|
init_chartSpec();
|
|
629
644
|
init_TableWithExport();
|
|
630
645
|
init_ChartCard();
|
|
646
|
+
init_useTranslation();
|
|
631
647
|
CodeBlock2 = lazy(() => Promise.resolve().then(() => (init_CodeBlock(), CodeBlock_exports)).then((module) => ({ default: module.CodeBlock })));
|
|
632
648
|
INLINE_TAGS = /* @__PURE__ */ new Set([
|
|
633
649
|
"a",
|
|
@@ -696,43 +712,300 @@ var RateLimiter = class {
|
|
|
696
712
|
}
|
|
697
713
|
};
|
|
698
714
|
|
|
699
|
-
// ui/src/
|
|
700
|
-
function
|
|
701
|
-
if (
|
|
702
|
-
|
|
715
|
+
// ui/src/applet-devtools/enabled.ts
|
|
716
|
+
function shouldEnableAppletDevtools() {
|
|
717
|
+
if (typeof window === "undefined") return false;
|
|
718
|
+
const url = new URL(window.location.href);
|
|
719
|
+
if (url.searchParams.get("appletDebug") === "1") return true;
|
|
720
|
+
try {
|
|
721
|
+
return window.localStorage.getItem("iotaAppletDevtools") === "1";
|
|
722
|
+
} catch {
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
703
725
|
}
|
|
704
|
-
|
|
705
|
-
|
|
726
|
+
|
|
727
|
+
// ui/src/applet-host/rpc.ts
|
|
728
|
+
var AppletRPCException = class extends Error {
|
|
729
|
+
constructor(args) {
|
|
730
|
+
super(args.message);
|
|
731
|
+
this.name = "AppletRPCException";
|
|
732
|
+
this.code = args.code;
|
|
733
|
+
this.details = args.details;
|
|
734
|
+
this.cause = args.cause;
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
function createAppletRPCClient(options) {
|
|
738
|
+
const fetcher = options.fetcher ?? fetch;
|
|
739
|
+
const timeoutMs = typeof options.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : 0;
|
|
740
|
+
async function call(method, params) {
|
|
741
|
+
const req = { id: crypto.randomUUID(), method, params };
|
|
742
|
+
const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
743
|
+
const abortController = timeoutMs > 0 ? new AbortController() : void 0;
|
|
744
|
+
let timeoutHandle;
|
|
745
|
+
let timedOut = false;
|
|
746
|
+
maybeDispatchRPCEvent({
|
|
747
|
+
id: req.id,
|
|
748
|
+
method: req.method,
|
|
749
|
+
status: "start"
|
|
750
|
+
});
|
|
751
|
+
try {
|
|
752
|
+
if (abortController) {
|
|
753
|
+
timeoutHandle = setTimeout(() => {
|
|
754
|
+
timedOut = true;
|
|
755
|
+
abortController.abort();
|
|
756
|
+
}, timeoutMs);
|
|
757
|
+
}
|
|
758
|
+
const resp = await fetcher(options.endpoint, {
|
|
759
|
+
method: "POST",
|
|
760
|
+
headers: { "Content-Type": "application/json" },
|
|
761
|
+
body: JSON.stringify(req),
|
|
762
|
+
signal: abortController?.signal
|
|
763
|
+
});
|
|
764
|
+
if (!resp.ok) {
|
|
765
|
+
throw new AppletRPCException({
|
|
766
|
+
code: "http_error",
|
|
767
|
+
message: `HTTP ${resp.status}`,
|
|
768
|
+
details: { status: resp.status }
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
const json = await resp.json();
|
|
772
|
+
if (json.error) {
|
|
773
|
+
throw new AppletRPCException({
|
|
774
|
+
code: json.error.code,
|
|
775
|
+
message: json.error.message,
|
|
776
|
+
details: json.error.details
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
if (json.result === void 0) {
|
|
780
|
+
throw new AppletRPCException({
|
|
781
|
+
code: "invalid_response",
|
|
782
|
+
message: "Missing result in successful response"
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
maybeDispatchRPCEvent({
|
|
786
|
+
id: req.id,
|
|
787
|
+
method: req.method,
|
|
788
|
+
status: "success",
|
|
789
|
+
durationMs: elapsedMs(startedAt)
|
|
790
|
+
});
|
|
791
|
+
return json.result;
|
|
792
|
+
} catch (err) {
|
|
793
|
+
let rpcErr = err;
|
|
794
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
795
|
+
rpcErr = new AppletRPCException({
|
|
796
|
+
code: timedOut ? "timeout" : "aborted",
|
|
797
|
+
message: timedOut ? `RPC request timed out after ${timeoutMs}ms` : "RPC request was aborted",
|
|
798
|
+
cause: err
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
maybeDispatchRPCEvent({
|
|
802
|
+
id: req.id,
|
|
803
|
+
method: req.method,
|
|
804
|
+
status: "error",
|
|
805
|
+
durationMs: elapsedMs(startedAt),
|
|
806
|
+
error: rpcErr
|
|
807
|
+
});
|
|
808
|
+
throw rpcErr;
|
|
809
|
+
} finally {
|
|
810
|
+
if (timeoutHandle !== void 0) {
|
|
811
|
+
clearTimeout(timeoutHandle);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
async function callTyped(method, params) {
|
|
816
|
+
return call(method, params);
|
|
817
|
+
}
|
|
818
|
+
return { call, callTyped };
|
|
706
819
|
}
|
|
707
|
-
function
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
820
|
+
function maybeDispatchRPCEvent(detail) {
|
|
821
|
+
if (typeof window === "undefined") return;
|
|
822
|
+
if (!shouldEnableAppletDevtools()) return;
|
|
823
|
+
window.dispatchEvent(new CustomEvent("iota:applet-rpc", { detail }));
|
|
824
|
+
}
|
|
825
|
+
function elapsedMs(startedAt) {
|
|
826
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
827
|
+
return Math.max(0, Math.round(now - startedAt));
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// ui/src/bichat/utils/errorDisplay.ts
|
|
831
|
+
function isPermissionDeniedError(error) {
|
|
832
|
+
if (!error) return false;
|
|
833
|
+
if (error instanceof Error) {
|
|
834
|
+
const msg = error.message.toLowerCase();
|
|
835
|
+
if (msg.includes("forbidden") || msg.includes("permission denied")) return true;
|
|
836
|
+
}
|
|
837
|
+
if (typeof error === "object" && error !== null) {
|
|
838
|
+
const obj = error;
|
|
839
|
+
if (obj.code === "forbidden" || obj.code === 403) return true;
|
|
840
|
+
if (obj.status === 403) return true;
|
|
841
|
+
if (obj.statusCode === 403) return true;
|
|
842
|
+
if (typeof obj.response === "object" && obj.response !== null) {
|
|
843
|
+
const resp = obj.response;
|
|
844
|
+
if (resp.status === 403) return true;
|
|
719
845
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
846
|
+
}
|
|
847
|
+
if (typeof error === "string") {
|
|
848
|
+
const lower = error.toLowerCase();
|
|
849
|
+
if (lower.includes("forbidden") || lower.includes("permission denied")) return true;
|
|
850
|
+
}
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
function extractStatus(error) {
|
|
854
|
+
if (!error || typeof error !== "object") return void 0;
|
|
855
|
+
const obj = error;
|
|
856
|
+
if (typeof obj.status === "number") return obj.status;
|
|
857
|
+
if (typeof obj.statusCode === "number") return obj.statusCode;
|
|
858
|
+
if (typeof obj.details === "object" && obj.details !== null) {
|
|
859
|
+
const details = obj.details;
|
|
860
|
+
if (typeof details.status === "number") return details.status;
|
|
861
|
+
if (typeof details.statusCode === "number") return details.statusCode;
|
|
862
|
+
}
|
|
863
|
+
if (typeof obj.response === "object" && obj.response !== null) {
|
|
864
|
+
const response = obj.response;
|
|
865
|
+
if (typeof response.status === "number") return response.status;
|
|
866
|
+
}
|
|
867
|
+
return void 0;
|
|
868
|
+
}
|
|
869
|
+
function isOfflineNow() {
|
|
870
|
+
return typeof navigator !== "undefined" && navigator.onLine === false;
|
|
871
|
+
}
|
|
872
|
+
function inferErrorCode(error) {
|
|
873
|
+
if (isOfflineNow()) return "offline";
|
|
874
|
+
if (error instanceof AppletRPCException) {
|
|
875
|
+
const code = String(error.code || "").toLowerCase().trim();
|
|
876
|
+
if (code) return code;
|
|
877
|
+
}
|
|
878
|
+
if (typeof error === "object" && error !== null) {
|
|
879
|
+
const obj = error;
|
|
880
|
+
if (typeof obj.code === "string" && obj.code.trim() !== "") {
|
|
881
|
+
return obj.code.toLowerCase();
|
|
882
|
+
}
|
|
883
|
+
if (typeof obj.error === "string" && obj.error.trim() !== "") {
|
|
884
|
+
return obj.error.toLowerCase();
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
const status = extractStatus(error);
|
|
888
|
+
if (status === 401 || status === 403) return "forbidden";
|
|
889
|
+
if (status === 404) return "not_found";
|
|
890
|
+
if (status === 408) return "timeout";
|
|
891
|
+
if (status === 413) return "payload_too_large";
|
|
892
|
+
if (status === 429) return "rate_limited";
|
|
893
|
+
if (status && status >= 500) return "server_error";
|
|
894
|
+
if (status && status >= 400) return "bad_request";
|
|
895
|
+
let message = "";
|
|
896
|
+
if (error instanceof Error) message = error.message;
|
|
897
|
+
if (!message && typeof error === "string") message = error;
|
|
898
|
+
const lower = message.toLowerCase();
|
|
899
|
+
if (lower.includes("timeout") || lower.includes("timed out")) return "timeout";
|
|
900
|
+
if (lower.includes("abort") || lower.includes("cancel")) return "aborted";
|
|
901
|
+
if (lower.includes("forbidden") || lower.includes("permission denied")) return "forbidden";
|
|
902
|
+
if (lower.includes("not found") || lower.includes("session not found")) return "not_found";
|
|
903
|
+
if (lower.includes("payload too large") || lower.includes("request too large") || lower.includes("413")) return "payload_too_large";
|
|
904
|
+
if (lower.includes("network") || lower.includes("failed to fetch")) return "network_error";
|
|
905
|
+
return "unknown";
|
|
906
|
+
}
|
|
907
|
+
function describeCode(code, fallbackTitle) {
|
|
908
|
+
switch (code) {
|
|
909
|
+
case "offline":
|
|
910
|
+
return {
|
|
911
|
+
title: "You are offline",
|
|
912
|
+
description: "Check your internet connection and try again.",
|
|
913
|
+
retryable: true
|
|
914
|
+
};
|
|
915
|
+
case "timeout":
|
|
916
|
+
return {
|
|
917
|
+
title: "Request timed out",
|
|
918
|
+
description: "The request took too long. Please try again.",
|
|
919
|
+
retryable: true
|
|
920
|
+
};
|
|
921
|
+
case "aborted":
|
|
922
|
+
return {
|
|
923
|
+
title: "Request canceled",
|
|
924
|
+
description: "The request was canceled before completion.",
|
|
925
|
+
retryable: true
|
|
926
|
+
};
|
|
927
|
+
case "forbidden":
|
|
928
|
+
return {
|
|
929
|
+
title: "Access denied",
|
|
930
|
+
description: "Your account does not have permission for this action.",
|
|
931
|
+
retryable: false
|
|
932
|
+
};
|
|
933
|
+
case "not_found":
|
|
934
|
+
return {
|
|
935
|
+
title: "Not found",
|
|
936
|
+
description: "The requested resource could not be found.",
|
|
937
|
+
retryable: false
|
|
938
|
+
};
|
|
939
|
+
case "payload_too_large":
|
|
940
|
+
return {
|
|
941
|
+
title: "Attachment too large",
|
|
942
|
+
description: "The uploaded payload exceeds allowed limits. Reduce file size and retry.",
|
|
943
|
+
retryable: false
|
|
944
|
+
};
|
|
945
|
+
case "invalid_request":
|
|
946
|
+
case "validation":
|
|
947
|
+
case "bad_request":
|
|
948
|
+
return {
|
|
949
|
+
title: "Invalid request",
|
|
950
|
+
description: "The request could not be processed. Review the input and try again.",
|
|
951
|
+
retryable: false
|
|
952
|
+
};
|
|
953
|
+
case "rate_limited":
|
|
954
|
+
return {
|
|
955
|
+
title: "Too many requests",
|
|
956
|
+
description: "Please wait a moment before trying again.",
|
|
957
|
+
retryable: true
|
|
958
|
+
};
|
|
959
|
+
case "http_error":
|
|
960
|
+
case "server_error":
|
|
961
|
+
return {
|
|
962
|
+
title: "Server error",
|
|
963
|
+
description: "The server failed to process this request. Please retry shortly.",
|
|
964
|
+
retryable: true
|
|
965
|
+
};
|
|
966
|
+
case "network_error":
|
|
967
|
+
return {
|
|
968
|
+
title: "Network error",
|
|
969
|
+
description: "A network issue interrupted the request. Please try again.",
|
|
970
|
+
retryable: true
|
|
971
|
+
};
|
|
972
|
+
default:
|
|
973
|
+
return {
|
|
974
|
+
title: fallbackTitle,
|
|
975
|
+
description: "Something went wrong. Please try again.",
|
|
976
|
+
retryable: true
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
function normalizeRPCError(error, fallbackTitle) {
|
|
981
|
+
const code = inferErrorCode(error);
|
|
982
|
+
const base = describeCode(code, fallbackTitle);
|
|
983
|
+
const permissionDenied = code === "forbidden" || isPermissionDeniedError(error);
|
|
984
|
+
let description = base.description;
|
|
985
|
+
if (error instanceof AppletRPCException && typeof error.message === "string" && error.message.trim() !== "" && base.title === fallbackTitle) {
|
|
986
|
+
description = error.message;
|
|
987
|
+
} else if (error instanceof Error && error.message && code === "unknown") {
|
|
988
|
+
description = error.message;
|
|
727
989
|
}
|
|
728
990
|
return {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
991
|
+
code,
|
|
992
|
+
title: base.title,
|
|
993
|
+
description,
|
|
994
|
+
userMessage: description || base.title,
|
|
995
|
+
retryable: base.retryable,
|
|
996
|
+
isPermissionDenied: permissionDenied,
|
|
997
|
+
isTimeout: code === "timeout",
|
|
998
|
+
isOffline: code === "offline",
|
|
999
|
+
isCanceled: code === "aborted",
|
|
1000
|
+
isNotFound: code === "not_found"
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
function toErrorDisplay(error, fallbackTitle) {
|
|
1004
|
+
const normalized = normalizeRPCError(error, fallbackTitle);
|
|
1005
|
+
return {
|
|
1006
|
+
title: normalized.title,
|
|
1007
|
+
description: normalized.description,
|
|
1008
|
+
isPermissionDenied: normalized.isPermissionDenied
|
|
736
1009
|
};
|
|
737
1010
|
}
|
|
738
1011
|
|
|
@@ -785,8 +1058,45 @@ function loadQueue(sessionId) {
|
|
|
785
1058
|
}
|
|
786
1059
|
}
|
|
787
1060
|
|
|
788
|
-
// ui/src/bichat/
|
|
789
|
-
|
|
1061
|
+
// ui/src/bichat/utils/debugTrace.ts
|
|
1062
|
+
function hasMeaningfulUsage(trace) {
|
|
1063
|
+
if (!trace) return false;
|
|
1064
|
+
return trace.promptTokens > 0 || trace.completionTokens > 0 || trace.totalTokens > 0 || (trace.cachedTokens ?? 0) > 0 || (trace.cost ?? 0) > 0;
|
|
1065
|
+
}
|
|
1066
|
+
function hasDebugTrace(trace) {
|
|
1067
|
+
return trace.tools.length > 0 || hasMeaningfulUsage(trace.usage) || !!trace.generationMs;
|
|
1068
|
+
}
|
|
1069
|
+
function getSessionDebugUsage(turns) {
|
|
1070
|
+
let promptTokens = 0;
|
|
1071
|
+
let completionTokens = 0;
|
|
1072
|
+
let totalTokens = 0;
|
|
1073
|
+
let turnsWithUsage = 0;
|
|
1074
|
+
let latestPromptTokens = 0;
|
|
1075
|
+
let latestCompletionTokens = 0;
|
|
1076
|
+
let latestTotalTokens = 0;
|
|
1077
|
+
for (const turn of turns) {
|
|
1078
|
+
const usage = turn.assistantTurn?.debug?.usage;
|
|
1079
|
+
if (!hasMeaningfulUsage(usage) || !usage) {
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
turnsWithUsage++;
|
|
1083
|
+
promptTokens += usage.promptTokens;
|
|
1084
|
+
completionTokens += usage.completionTokens;
|
|
1085
|
+
totalTokens += usage.totalTokens;
|
|
1086
|
+
latestPromptTokens = usage.promptTokens;
|
|
1087
|
+
latestCompletionTokens = usage.completionTokens;
|
|
1088
|
+
latestTotalTokens = usage.totalTokens;
|
|
1089
|
+
}
|
|
1090
|
+
return {
|
|
1091
|
+
promptTokens,
|
|
1092
|
+
completionTokens,
|
|
1093
|
+
totalTokens,
|
|
1094
|
+
turnsWithUsage,
|
|
1095
|
+
latestPromptTokens,
|
|
1096
|
+
latestCompletionTokens,
|
|
1097
|
+
latestTotalTokens
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
790
1100
|
|
|
791
1101
|
// ui/src/bichat/types/index.ts
|
|
792
1102
|
var MessageRole = /* @__PURE__ */ ((MessageRole2) => {
|
|
@@ -863,7 +1173,7 @@ function readDebugLimitsFromGlobalContext() {
|
|
|
863
1173
|
if (typeof window === "undefined") {
|
|
864
1174
|
return null;
|
|
865
1175
|
}
|
|
866
|
-
const limits = window.
|
|
1176
|
+
const limits = window.__APPLET_CONTEXT__?.extensions?.debug?.limits;
|
|
867
1177
|
if (!limits) {
|
|
868
1178
|
return null;
|
|
869
1179
|
}
|
|
@@ -883,580 +1193,924 @@ function readDebugLimitsFromGlobalContext() {
|
|
|
883
1193
|
completionReserveTokens
|
|
884
1194
|
};
|
|
885
1195
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
|
|
1196
|
+
|
|
1197
|
+
// ui/src/bichat/machine/selectors.ts
|
|
1198
|
+
function deriveDebugMode(state) {
|
|
1199
|
+
const key2 = state.session.currentSessionId || "new";
|
|
1200
|
+
return state.session.debugModeBySession[key2] ?? false;
|
|
1201
|
+
}
|
|
1202
|
+
function deriveSessionSnapshot(state, methods) {
|
|
1203
|
+
return {
|
|
1204
|
+
session: state.session.session,
|
|
1205
|
+
currentSessionId: state.session.currentSessionId,
|
|
1206
|
+
fetching: state.session.fetching,
|
|
1207
|
+
error: state.session.error,
|
|
1208
|
+
errorRetryable: state.session.errorRetryable,
|
|
1209
|
+
debugMode: deriveDebugMode(state),
|
|
1210
|
+
sessionDebugUsage: getSessionDebugUsage(state.messaging.turns),
|
|
1211
|
+
debugLimits: state.session.debugLimits,
|
|
1212
|
+
setError: methods.setError,
|
|
1213
|
+
retryFetchSession: methods.retryFetchSession
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
function deriveMessagingSnapshot(state, methods) {
|
|
1217
|
+
return {
|
|
1218
|
+
turns: state.messaging.turns,
|
|
1219
|
+
streamingContent: state.messaging.streamingContent,
|
|
1220
|
+
isStreaming: state.messaging.isStreaming,
|
|
1221
|
+
streamError: state.messaging.streamError,
|
|
1222
|
+
streamErrorRetryable: state.messaging.streamErrorRetryable,
|
|
1223
|
+
loading: state.messaging.loading,
|
|
1224
|
+
pendingQuestion: state.messaging.pendingQuestion,
|
|
1225
|
+
codeOutputs: state.messaging.codeOutputs,
|
|
1226
|
+
isCompacting: state.messaging.isCompacting,
|
|
1227
|
+
compactionSummary: null,
|
|
1228
|
+
artifactsInvalidationTrigger: state.messaging.artifactsInvalidationTrigger,
|
|
1229
|
+
...methods
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
function deriveInputSnapshot(state, methods) {
|
|
1233
|
+
return {
|
|
1234
|
+
message: state.input.message,
|
|
1235
|
+
inputError: state.input.inputError,
|
|
1236
|
+
messageQueue: state.input.messageQueue,
|
|
1237
|
+
...methods
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// ui/src/bichat/machine/ChatMachine.ts
|
|
1242
|
+
var MAX_QUEUE_SIZE = 5;
|
|
1243
|
+
var ChatMachine = class {
|
|
1244
|
+
constructor(config) {
|
|
1245
|
+
// ── Refs (mutable, no subscription) ─────────────────────────────────────
|
|
1246
|
+
this.abortController = null;
|
|
1247
|
+
this.lastSendAttempt = null;
|
|
1248
|
+
/** Prevents fetchSession effect from clobbering state while stream is active. */
|
|
1249
|
+
this.sendingSessionId = null;
|
|
1250
|
+
this.fetchCancelled = false;
|
|
1251
|
+
this.disposed = false;
|
|
1252
|
+
/** Memoized sessionDebugUsage — avoids unnecessary session re-renders during streaming. */
|
|
1253
|
+
this.lastSessionDebugUsage = null;
|
|
1254
|
+
// ── Listeners ───────────────────────────────────────────────────────────
|
|
1255
|
+
this.sessionListeners = /* @__PURE__ */ new Set();
|
|
1256
|
+
this.messagingListeners = /* @__PURE__ */ new Set();
|
|
1257
|
+
this.inputListeners = /* @__PURE__ */ new Set();
|
|
1258
|
+
// ── Snapshot caches (for useSyncExternalStore identity stability) ───────
|
|
1259
|
+
this.cachedSessionSnapshot = null;
|
|
1260
|
+
this.cachedMessagingSnapshot = null;
|
|
1261
|
+
this.cachedInputSnapshot = null;
|
|
1262
|
+
this.sessionSnapshotVersion = 0;
|
|
1263
|
+
this.messagingSnapshotVersion = 0;
|
|
1264
|
+
this.inputSnapshotVersion = 0;
|
|
1265
|
+
this.lastSessionSnapshotVersion = -1;
|
|
1266
|
+
this.lastMessagingSnapshotVersion = -1;
|
|
1267
|
+
this.lastInputSnapshotVersion = -1;
|
|
1268
|
+
// =====================================================================
|
|
1269
|
+
// Subscribe / getSnapshot (for useSyncExternalStore)
|
|
1270
|
+
// =====================================================================
|
|
1271
|
+
this.subscribeSession = (listener) => {
|
|
1272
|
+
this.sessionListeners.add(listener);
|
|
1273
|
+
return () => {
|
|
1274
|
+
this.sessionListeners.delete(listener);
|
|
1275
|
+
};
|
|
1276
|
+
};
|
|
1277
|
+
this.getSessionSnapshot = () => {
|
|
1278
|
+
if (this.lastSessionSnapshotVersion !== this.sessionSnapshotVersion) {
|
|
1279
|
+
this.cachedSessionSnapshot = deriveSessionSnapshot(this.state, {
|
|
1280
|
+
setError: this.setError,
|
|
1281
|
+
retryFetchSession: this.retryFetchSession
|
|
1282
|
+
});
|
|
1283
|
+
this.lastSessionSnapshotVersion = this.sessionSnapshotVersion;
|
|
1284
|
+
}
|
|
1285
|
+
return this.cachedSessionSnapshot;
|
|
1286
|
+
};
|
|
1287
|
+
this.subscribeMessaging = (listener) => {
|
|
1288
|
+
this.messagingListeners.add(listener);
|
|
1289
|
+
return () => {
|
|
1290
|
+
this.messagingListeners.delete(listener);
|
|
1291
|
+
};
|
|
1292
|
+
};
|
|
1293
|
+
this.getMessagingSnapshot = () => {
|
|
1294
|
+
if (this.lastMessagingSnapshotVersion !== this.messagingSnapshotVersion) {
|
|
1295
|
+
this.cachedMessagingSnapshot = deriveMessagingSnapshot(this.state, {
|
|
1296
|
+
sendMessage: this.sendMessage,
|
|
1297
|
+
handleRegenerate: this.handleRegenerate,
|
|
1298
|
+
handleEdit: this.handleEdit,
|
|
1299
|
+
handleCopy: this.handleCopy,
|
|
1300
|
+
handleSubmitQuestionAnswers: this.handleSubmitQuestionAnswers,
|
|
1301
|
+
handleRejectPendingQuestion: this.handleRejectPendingQuestion,
|
|
1302
|
+
retryLastMessage: this.retryLastMessage,
|
|
1303
|
+
clearStreamError: this.clearStreamError,
|
|
1304
|
+
cancel: this.cancel,
|
|
1305
|
+
setCodeOutputs: this.setCodeOutputs
|
|
1306
|
+
});
|
|
1307
|
+
this.lastMessagingSnapshotVersion = this.messagingSnapshotVersion;
|
|
1308
|
+
}
|
|
1309
|
+
return this.cachedMessagingSnapshot;
|
|
1310
|
+
};
|
|
1311
|
+
this.subscribeInput = (listener) => {
|
|
1312
|
+
this.inputListeners.add(listener);
|
|
1313
|
+
return () => {
|
|
1314
|
+
this.inputListeners.delete(listener);
|
|
1315
|
+
};
|
|
1316
|
+
};
|
|
1317
|
+
this.getInputSnapshot = () => {
|
|
1318
|
+
if (this.lastInputSnapshotVersion !== this.inputSnapshotVersion) {
|
|
1319
|
+
this.cachedInputSnapshot = deriveInputSnapshot(this.state, {
|
|
1320
|
+
setMessage: this.setMessage,
|
|
1321
|
+
setInputError: this.setInputError,
|
|
1322
|
+
handleSubmit: this.handleSubmit,
|
|
1323
|
+
handleUnqueue: this.handleUnqueue,
|
|
1324
|
+
enqueueMessage: this.enqueueMessage,
|
|
1325
|
+
removeQueueItem: this.removeQueueItem,
|
|
1326
|
+
updateQueueItem: this.updateQueueItem
|
|
1327
|
+
});
|
|
1328
|
+
this.lastInputSnapshotVersion = this.inputSnapshotVersion;
|
|
1329
|
+
}
|
|
1330
|
+
return this.cachedInputSnapshot;
|
|
1331
|
+
};
|
|
1332
|
+
this.dataSource = config.dataSource;
|
|
1333
|
+
this.rateLimiter = config.rateLimiter;
|
|
1334
|
+
this.onSessionCreated = config.onSessionCreated;
|
|
1335
|
+
this.state = {
|
|
1336
|
+
session: {
|
|
1337
|
+
currentSessionId: void 0,
|
|
1338
|
+
session: null,
|
|
1339
|
+
fetching: false,
|
|
1340
|
+
error: null,
|
|
1341
|
+
errorRetryable: false,
|
|
1342
|
+
debugModeBySession: {},
|
|
1343
|
+
debugLimits: readDebugLimitsFromGlobalContext()
|
|
1344
|
+
},
|
|
1345
|
+
messaging: {
|
|
1346
|
+
turns: [],
|
|
1347
|
+
streamingContent: "",
|
|
1348
|
+
isStreaming: false,
|
|
1349
|
+
streamError: null,
|
|
1350
|
+
streamErrorRetryable: false,
|
|
1351
|
+
loading: false,
|
|
1352
|
+
pendingQuestion: null,
|
|
1353
|
+
codeOutputs: [],
|
|
1354
|
+
isCompacting: false,
|
|
1355
|
+
artifactsInvalidationTrigger: 0
|
|
1356
|
+
},
|
|
1357
|
+
input: {
|
|
1358
|
+
message: "",
|
|
1359
|
+
inputError: null,
|
|
1360
|
+
messageQueue: []
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
this.setError = this._setError.bind(this);
|
|
1364
|
+
this.retryFetchSession = this._retryFetchSession.bind(this);
|
|
1365
|
+
this.sendMessage = this._sendMessage.bind(this);
|
|
1366
|
+
this.handleRegenerate = this._handleRegenerate.bind(this);
|
|
1367
|
+
this.handleEdit = this._handleEdit.bind(this);
|
|
1368
|
+
this.handleCopy = this._handleCopy.bind(this);
|
|
1369
|
+
this.handleSubmitQuestionAnswers = this._handleSubmitQuestionAnswers.bind(this);
|
|
1370
|
+
this.handleRejectPendingQuestion = this._handleRejectPendingQuestion.bind(this);
|
|
1371
|
+
this.retryLastMessage = this._retryLastMessage.bind(this);
|
|
1372
|
+
this.clearStreamError = this._clearStreamError.bind(this);
|
|
1373
|
+
this.cancel = this._cancel.bind(this);
|
|
1374
|
+
this.setCodeOutputs = this._setCodeOutputs.bind(this);
|
|
1375
|
+
this.setMessage = this._setMessage.bind(this);
|
|
1376
|
+
this.setInputError = this._setInputError.bind(this);
|
|
1377
|
+
this.handleSubmit = this._handleSubmit.bind(this);
|
|
1378
|
+
this.handleUnqueue = this._handleUnqueue.bind(this);
|
|
1379
|
+
this.enqueueMessage = this._enqueueMessage.bind(this);
|
|
1380
|
+
this.removeQueueItem = this._removeQueueItem.bind(this);
|
|
1381
|
+
this.updateQueueItem = this._updateQueueItem.bind(this);
|
|
1382
|
+
}
|
|
1383
|
+
// =====================================================================
|
|
1384
|
+
// Lifecycle
|
|
1385
|
+
// =====================================================================
|
|
1386
|
+
/**
|
|
1387
|
+
* Set the active session ID. Triggers fetch when transitioning to a real
|
|
1388
|
+
* session, or resets state for 'new'/undefined.
|
|
1389
|
+
*/
|
|
1390
|
+
setSessionId(id) {
|
|
1391
|
+
if (this.disposed) return;
|
|
1392
|
+
const prev = this.state.session.currentSessionId;
|
|
1393
|
+
if (id === prev) return;
|
|
1394
|
+
this.state.session.currentSessionId = id;
|
|
1395
|
+
this._notifySession();
|
|
1396
|
+
if (!id || id === "new") {
|
|
1397
|
+
this._updateInput({ messageQueue: [] });
|
|
1398
|
+
} else {
|
|
1399
|
+
this._updateInput({ messageQueue: loadQueue(id) });
|
|
1400
|
+
}
|
|
1401
|
+
this._fetchSessionIfNeeded();
|
|
1402
|
+
}
|
|
1403
|
+
/**
|
|
1404
|
+
* Update mutable config that may change across parent re-renders.
|
|
1405
|
+
* Called from the React provider's useEffect to keep the machine in sync.
|
|
1406
|
+
*/
|
|
1407
|
+
updateConfig(config) {
|
|
1408
|
+
this.dataSource = config.dataSource;
|
|
1409
|
+
this.onSessionCreated = config.onSessionCreated;
|
|
1410
|
+
}
|
|
1411
|
+
dispose() {
|
|
1412
|
+
this.disposed = true;
|
|
1413
|
+
this.fetchCancelled = true;
|
|
1414
|
+
this.abortController?.abort();
|
|
1415
|
+
this.sessionListeners.clear();
|
|
1416
|
+
this.messagingListeners.clear();
|
|
1417
|
+
this.inputListeners.clear();
|
|
1418
|
+
}
|
|
1419
|
+
// =====================================================================
|
|
1420
|
+
// Private — state updates + notification
|
|
1421
|
+
// =====================================================================
|
|
1422
|
+
_updateSession(patch) {
|
|
1423
|
+
Object.assign(this.state.session, patch);
|
|
1424
|
+
this._notifySession();
|
|
1425
|
+
}
|
|
1426
|
+
_notifySession() {
|
|
1427
|
+
this.sessionSnapshotVersion++;
|
|
1428
|
+
for (const fn of this.sessionListeners) fn();
|
|
1429
|
+
}
|
|
1430
|
+
_updateMessaging(patch) {
|
|
1431
|
+
Object.assign(this.state.messaging, patch);
|
|
1432
|
+
this._notifyMessaging();
|
|
1433
|
+
if ("turns" in patch) {
|
|
1434
|
+
const newUsage = getSessionDebugUsage(this.state.messaging.turns);
|
|
1435
|
+
if (!sessionDebugUsageEqual(this.lastSessionDebugUsage, newUsage)) {
|
|
1436
|
+
this.lastSessionDebugUsage = newUsage;
|
|
1437
|
+
this._notifySession();
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
_notifyMessaging() {
|
|
1442
|
+
this.messagingSnapshotVersion++;
|
|
1443
|
+
for (const fn of this.messagingListeners) fn();
|
|
1444
|
+
}
|
|
1445
|
+
_updateInput(patch) {
|
|
1446
|
+
Object.assign(this.state.input, patch);
|
|
1447
|
+
if ("messageQueue" in patch) {
|
|
1448
|
+
this._persistQueue();
|
|
1449
|
+
}
|
|
1450
|
+
this._notifyInput();
|
|
1451
|
+
}
|
|
1452
|
+
_notifyInput() {
|
|
1453
|
+
this.inputSnapshotVersion++;
|
|
1454
|
+
for (const fn of this.inputListeners) fn();
|
|
1455
|
+
}
|
|
1456
|
+
_persistQueue() {
|
|
1457
|
+
const sid = this.state.session.currentSessionId;
|
|
1458
|
+
if (!sid || sid === "new") return;
|
|
1459
|
+
saveQueue(sid, this.state.input.messageQueue);
|
|
1460
|
+
}
|
|
1461
|
+
// =====================================================================
|
|
1462
|
+
// Private — session fetch
|
|
1463
|
+
// =====================================================================
|
|
1464
|
+
_fetchSessionIfNeeded() {
|
|
1465
|
+
const id = this.state.session.currentSessionId;
|
|
1466
|
+
if (!id || id === "new") {
|
|
1467
|
+
this._updateSession({
|
|
1468
|
+
session: null,
|
|
1469
|
+
fetching: false,
|
|
1470
|
+
error: null,
|
|
1471
|
+
errorRetryable: false
|
|
1472
|
+
});
|
|
1473
|
+
this._updateMessaging({
|
|
1474
|
+
turns: [],
|
|
1475
|
+
pendingQuestion: null
|
|
1476
|
+
});
|
|
1477
|
+
this._updateInput({ inputError: null });
|
|
952
1478
|
return;
|
|
953
1479
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
return state.turns ?? prev;
|
|
968
|
-
});
|
|
969
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1480
|
+
if (this.sendingSessionId === id) return;
|
|
1481
|
+
this.fetchCancelled = false;
|
|
1482
|
+
this._updateSession({ fetching: true, error: null, errorRetryable: false });
|
|
1483
|
+
this._updateInput({ inputError: null });
|
|
1484
|
+
const fetchId = id;
|
|
1485
|
+
this.dataSource.fetchSession(fetchId).then((result) => {
|
|
1486
|
+
if (this.fetchCancelled || this.disposed) return;
|
|
1487
|
+
if (this.state.session.currentSessionId !== fetchId) return;
|
|
1488
|
+
if (this.sendingSessionId === fetchId) return;
|
|
1489
|
+
if (result) {
|
|
1490
|
+
this._updateSession({ session: result.session, fetching: false });
|
|
1491
|
+
this._setTurnsFromFetch(result.turns);
|
|
1492
|
+
this._updateMessaging({ pendingQuestion: result.pendingQuestion || null });
|
|
970
1493
|
} else {
|
|
971
|
-
|
|
1494
|
+
this._updateSession({ error: "Session not found", fetching: false });
|
|
972
1495
|
}
|
|
973
|
-
setFetching(false);
|
|
974
1496
|
}).catch((err) => {
|
|
975
|
-
if (
|
|
976
|
-
|
|
977
|
-
|
|
1497
|
+
if (this.fetchCancelled || this.disposed) return;
|
|
1498
|
+
if (this.state.session.currentSessionId !== fetchId) return;
|
|
1499
|
+
if (this.sendingSessionId === fetchId) return;
|
|
1500
|
+
const normalized = normalizeRPCError(err, "Failed to load session");
|
|
1501
|
+
this._updateSession({
|
|
1502
|
+
error: normalized.userMessage,
|
|
1503
|
+
errorRetryable: normalized.retryable,
|
|
1504
|
+
fetching: false
|
|
1505
|
+
});
|
|
978
1506
|
});
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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
|
-
|
|
1507
|
+
}
|
|
1508
|
+
/** Sets turns from fetch, preserving pending user-only turns if server hasn't caught up. */
|
|
1509
|
+
_setTurnsFromFetch(fetchedTurns) {
|
|
1510
|
+
const prev = this.state.messaging.turns;
|
|
1511
|
+
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1512
|
+
if (hasPendingUserOnly && (!fetchedTurns || fetchedTurns.length === 0)) {
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
this._updateMessaging({ turns: fetchedTurns ?? prev });
|
|
1516
|
+
}
|
|
1517
|
+
// =====================================================================
|
|
1518
|
+
// Private — actions
|
|
1519
|
+
// =====================================================================
|
|
1520
|
+
_setError(error) {
|
|
1521
|
+
this._updateSession({
|
|
1522
|
+
error,
|
|
1523
|
+
errorRetryable: false
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
_retryFetchSession() {
|
|
1527
|
+
this._fetchSessionIfNeeded();
|
|
1528
|
+
}
|
|
1529
|
+
_clearStreamError() {
|
|
1530
|
+
this._updateMessaging({
|
|
1531
|
+
streamError: null,
|
|
1532
|
+
streamErrorRetryable: false
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
_cancel() {
|
|
1536
|
+
if (this.abortController) {
|
|
1537
|
+
this.abortController.abort();
|
|
1538
|
+
this.abortController = null;
|
|
1539
|
+
this._updateMessaging({ isStreaming: false, loading: false });
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
_setCodeOutputs(outputs) {
|
|
1543
|
+
this._updateMessaging({ codeOutputs: outputs });
|
|
1544
|
+
}
|
|
1545
|
+
_setMessage(message) {
|
|
1546
|
+
this._updateInput({ message });
|
|
1547
|
+
}
|
|
1548
|
+
_setInputError(error) {
|
|
1549
|
+
this._updateInput({ inputError: error });
|
|
1550
|
+
}
|
|
1551
|
+
// ── Slash commands ──────────────────────────────────────────────────────
|
|
1552
|
+
async _executeSlashCommand(command) {
|
|
1553
|
+
if (command.hasArgs) {
|
|
1554
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorNoArguments" });
|
|
1555
|
+
return true;
|
|
1556
|
+
}
|
|
1557
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1558
|
+
this._updateInput({ inputError: null });
|
|
1559
|
+
this._clearStreamError();
|
|
1560
|
+
if (command.name === "/debug") {
|
|
1561
|
+
const debugMode = deriveDebugMode(this.state);
|
|
1562
|
+
const key2 = this.state.session.currentSessionId || "new";
|
|
1563
|
+
const nextDebugMode = !debugMode;
|
|
1564
|
+
this._updateSession({
|
|
1565
|
+
debugModeBySession: {
|
|
1566
|
+
...this.state.session.debugModeBySession,
|
|
1567
|
+
[key2]: nextDebugMode
|
|
1018
1568
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
}
|
|
1022
|
-
const curSessionId = sessionRef.current.currentSessionId;
|
|
1023
|
-
if (!curSessionId || curSessionId === "new") {
|
|
1024
|
-
setInputError("slash.error.sessionRequired");
|
|
1025
|
-
return true;
|
|
1026
|
-
}
|
|
1027
|
-
if (command.name === "/clear") {
|
|
1028
|
-
setLoading(true);
|
|
1029
|
-
setStreamingContent("");
|
|
1569
|
+
});
|
|
1570
|
+
if (nextDebugMode && this.state.session.currentSessionId && this.state.session.currentSessionId !== "new") {
|
|
1030
1571
|
try {
|
|
1031
|
-
await dataSource.
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
setTurns([]);
|
|
1572
|
+
const result = await this.dataSource.fetchSession(this.state.session.currentSessionId);
|
|
1573
|
+
if (result) {
|
|
1574
|
+
this._updateSession({ session: result.session });
|
|
1575
|
+
this._updateMessaging({
|
|
1576
|
+
turns: result.turns,
|
|
1577
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1578
|
+
});
|
|
1039
1579
|
}
|
|
1040
|
-
setCompactionSummary(null);
|
|
1041
|
-
setCodeOutputs([]);
|
|
1042
|
-
setMessage("");
|
|
1043
1580
|
} catch (err) {
|
|
1044
|
-
|
|
1045
|
-
} finally {
|
|
1046
|
-
setLoading(false);
|
|
1047
|
-
setIsStreaming(false);
|
|
1581
|
+
console.error("Failed to refresh session for debug mode:", err);
|
|
1048
1582
|
}
|
|
1049
|
-
return true;
|
|
1050
1583
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
} finally {
|
|
1074
|
-
setIsCompacting(false);
|
|
1075
|
-
setLoading(false);
|
|
1076
|
-
setIsStreaming(false);
|
|
1584
|
+
this._updateInput({ message: "" });
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1587
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1588
|
+
if (!curSessionId || curSessionId === "new") {
|
|
1589
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorSessionRequired" });
|
|
1590
|
+
return true;
|
|
1591
|
+
}
|
|
1592
|
+
if (command.name === "/clear") {
|
|
1593
|
+
this._updateInput({ message: "" });
|
|
1594
|
+
this._updateMessaging({ loading: true, streamingContent: "" });
|
|
1595
|
+
try {
|
|
1596
|
+
await this.dataSource.clearSessionHistory(curSessionId);
|
|
1597
|
+
const result = await this.dataSource.fetchSession(curSessionId);
|
|
1598
|
+
if (result) {
|
|
1599
|
+
this._updateSession({ session: result.session });
|
|
1600
|
+
this._updateMessaging({
|
|
1601
|
+
turns: result.turns,
|
|
1602
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1603
|
+
});
|
|
1604
|
+
} else {
|
|
1605
|
+
this._updateMessaging({ turns: [] });
|
|
1077
1606
|
}
|
|
1078
|
-
|
|
1607
|
+
this._updateMessaging({ codeOutputs: [] });
|
|
1608
|
+
} catch (err) {
|
|
1609
|
+
const normalized = normalizeRPCError(err, "Failed to clear session history");
|
|
1610
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1611
|
+
} finally {
|
|
1612
|
+
this._updateMessaging({ loading: false, isStreaming: false });
|
|
1079
1613
|
}
|
|
1080
|
-
setInputError("slash.error.unknownCommand");
|
|
1081
1614
|
return true;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1615
|
+
}
|
|
1616
|
+
if (command.name === "/compact") {
|
|
1617
|
+
this._updateInput({ message: "" });
|
|
1618
|
+
this._updateMessaging({
|
|
1619
|
+
loading: true,
|
|
1620
|
+
isCompacting: true,
|
|
1621
|
+
streamingContent: ""
|
|
1622
|
+
});
|
|
1623
|
+
try {
|
|
1624
|
+
const compactResult = await this.dataSource.compactSessionHistory(curSessionId);
|
|
1625
|
+
const summary = compactResult.summary || "";
|
|
1626
|
+
this._updateMessaging({
|
|
1627
|
+
turns: [createCompactedSystemTurn(curSessionId, summary)]
|
|
1628
|
+
});
|
|
1629
|
+
const result = await this.dataSource.fetchSession(curSessionId);
|
|
1630
|
+
if (result) {
|
|
1631
|
+
this._updateSession({ session: result.session });
|
|
1632
|
+
this._updateMessaging({
|
|
1633
|
+
turns: result.turns,
|
|
1634
|
+
pendingQuestion: result.pendingQuestion || null
|
|
1635
|
+
});
|
|
1636
|
+
} else {
|
|
1637
|
+
this._updateMessaging({ turns: [] });
|
|
1098
1638
|
}
|
|
1099
|
-
|
|
1639
|
+
this._updateMessaging({ codeOutputs: [] });
|
|
1640
|
+
} catch (err) {
|
|
1641
|
+
const normalized = normalizeRPCError(err, "Failed to compact session history");
|
|
1642
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1643
|
+
} finally {
|
|
1644
|
+
this._updateMessaging({ isCompacting: false, loading: false, isStreaming: false });
|
|
1645
|
+
}
|
|
1646
|
+
return true;
|
|
1647
|
+
}
|
|
1648
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorUnknownCommand" });
|
|
1649
|
+
return true;
|
|
1650
|
+
}
|
|
1651
|
+
// ── Send message ────────────────────────────────────────────────────────
|
|
1652
|
+
/**
|
|
1653
|
+
* Public entry point (no options). Calls _sendMessageCore internally.
|
|
1654
|
+
*/
|
|
1655
|
+
async _sendMessage(content, attachments = []) {
|
|
1656
|
+
return this._sendMessageCore(content, attachments);
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Internal entry point with options (for regenerate/edit).
|
|
1660
|
+
*/
|
|
1661
|
+
async _sendMessageDirect(content, attachments, options) {
|
|
1662
|
+
return this._sendMessageCore(content, attachments, options);
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Core send-message logic. Handles slash commands, rate limiting, streaming,
|
|
1666
|
+
* session creation, optimistic turns, and auto-queue-drain.
|
|
1667
|
+
*/
|
|
1668
|
+
async _sendMessageCore(content, attachments = [], options) {
|
|
1669
|
+
if (this.disposed) return;
|
|
1670
|
+
if (!content.trim() || this.state.messaging.loading) return;
|
|
1671
|
+
const trimmedContent = content.trim();
|
|
1672
|
+
if (trimmedContent.startsWith("/")) {
|
|
1673
|
+
const maybeCommand = parseSlashCommand(content);
|
|
1674
|
+
if (!maybeCommand) {
|
|
1675
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorUnknownCommand" });
|
|
1100
1676
|
return;
|
|
1101
1677
|
}
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1104
|
-
const seconds = Math.ceil(timeUntilNext / 1e3);
|
|
1105
|
-
setError(`Rate limit exceeded. Please wait ${seconds} seconds before sending another message.`);
|
|
1678
|
+
if (attachments.length > 0) {
|
|
1679
|
+
this._updateInput({ inputError: "BiChat.Slash.ErrorNoAttachments" });
|
|
1106
1680
|
return;
|
|
1107
1681
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
const curDebugMode = sessionRef.current.debugMode;
|
|
1117
|
-
const tempTurn = createPendingTurn(curSessionId || "new", content, attachments);
|
|
1118
|
-
const replaceFromMessageID = options?.replaceFromMessageID;
|
|
1119
|
-
setTurns((prev) => {
|
|
1120
|
-
if (!replaceFromMessageID) {
|
|
1121
|
-
return [...prev, tempTurn];
|
|
1122
|
-
}
|
|
1123
|
-
const replaceIndex = prev.findIndex((turn) => turn.userTurn.id === replaceFromMessageID);
|
|
1124
|
-
if (replaceIndex === -1) {
|
|
1125
|
-
console.warn(
|
|
1126
|
-
`[ChatContext] replaceFromMessageID "${replaceFromMessageID}" not found in turns; appending as new turn`
|
|
1127
|
-
);
|
|
1128
|
-
return [...prev, tempTurn];
|
|
1129
|
-
}
|
|
1130
|
-
return [...prev.slice(0, replaceIndex), tempTurn];
|
|
1682
|
+
await this._executeSlashCommand(maybeCommand);
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
if (!this.rateLimiter.canMakeRequest()) {
|
|
1686
|
+
const timeUntilNext = this.rateLimiter.getTimeUntilNextRequest();
|
|
1687
|
+
const seconds = Math.ceil(timeUntilNext / 1e3);
|
|
1688
|
+
this._updateInput({
|
|
1689
|
+
inputError: `Rate limit exceeded. Please wait ${seconds} seconds before sending another message.`
|
|
1131
1690
|
});
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
this._updateInput({ message: "", inputError: null });
|
|
1694
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1695
|
+
this._clearStreamError();
|
|
1696
|
+
this._updateMessaging({
|
|
1697
|
+
loading: true,
|
|
1698
|
+
streamingContent: ""
|
|
1699
|
+
});
|
|
1700
|
+
this.abortController = new AbortController();
|
|
1701
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1702
|
+
const curDebugMode = deriveDebugMode(this.state);
|
|
1703
|
+
const replaceFromMessageID = options?.replaceFromMessageID;
|
|
1704
|
+
const tempTurn = createPendingTurn(curSessionId || "new", content, attachments);
|
|
1705
|
+
this.lastSendAttempt = { content, attachments, options };
|
|
1706
|
+
const prevTurns = this.state.messaging.turns;
|
|
1707
|
+
if (!replaceFromMessageID) {
|
|
1708
|
+
this._updateMessaging({ turns: [...prevTurns, tempTurn] });
|
|
1709
|
+
} else {
|
|
1710
|
+
const idx = prevTurns.findIndex((t) => t.userTurn.id === replaceFromMessageID);
|
|
1711
|
+
if (idx === -1) {
|
|
1712
|
+
console.warn(`[ChatMachine] replaceFromMessageID "${replaceFromMessageID}" not found; appending as new turn`);
|
|
1713
|
+
this._updateMessaging({ turns: [...prevTurns, tempTurn] });
|
|
1714
|
+
} else {
|
|
1715
|
+
this._updateMessaging({ turns: [...prevTurns.slice(0, idx), tempTurn] });
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
let shouldDrainQueue = true;
|
|
1719
|
+
try {
|
|
1720
|
+
let activeSessionId = curSessionId;
|
|
1721
|
+
let shouldNavigateAfter = false;
|
|
1722
|
+
if (!activeSessionId || activeSessionId === "new") {
|
|
1723
|
+
const result = await this.dataSource.createSession();
|
|
1724
|
+
if (result) {
|
|
1725
|
+
const createdSessionID = result.id;
|
|
1726
|
+
activeSessionId = createdSessionID;
|
|
1727
|
+
this._updateSession({ currentSessionId: createdSessionID });
|
|
1728
|
+
if (curDebugMode) {
|
|
1729
|
+
this._updateSession({
|
|
1730
|
+
debugModeBySession: {
|
|
1731
|
+
...this.state.session.debugModeBySession,
|
|
1732
|
+
[createdSessionID]: true
|
|
1733
|
+
}
|
|
1144
1734
|
});
|
|
1145
|
-
shouldNavigateAfter = true;
|
|
1146
1735
|
}
|
|
1736
|
+
shouldNavigateAfter = true;
|
|
1147
1737
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1738
|
+
}
|
|
1739
|
+
this.sendingSessionId = activeSessionId || null;
|
|
1740
|
+
let accumulatedContent = "";
|
|
1741
|
+
let createdSessionId;
|
|
1742
|
+
let sessionFetched = false;
|
|
1743
|
+
this._updateMessaging({ isStreaming: true });
|
|
1744
|
+
for await (const chunk of this.dataSource.sendMessage(
|
|
1745
|
+
activeSessionId || "new",
|
|
1746
|
+
content,
|
|
1747
|
+
attachments,
|
|
1748
|
+
this.abortController?.signal,
|
|
1749
|
+
{
|
|
1750
|
+
debugMode: curDebugMode,
|
|
1751
|
+
replaceFromMessageID
|
|
1752
|
+
}
|
|
1753
|
+
)) {
|
|
1754
|
+
if (this.abortController?.signal.aborted) break;
|
|
1755
|
+
if ((chunk.type === "chunk" || chunk.type === "content") && chunk.content) {
|
|
1756
|
+
accumulatedContent += chunk.content;
|
|
1757
|
+
this._updateMessaging({ streamingContent: accumulatedContent });
|
|
1758
|
+
} else if (chunk.type === "error") {
|
|
1759
|
+
throw new Error(chunk.error || "Stream error");
|
|
1760
|
+
} else if (chunk.type === "interrupt" || chunk.type === "done") {
|
|
1761
|
+
if (chunk.sessionId) {
|
|
1762
|
+
createdSessionId = chunk.sessionId;
|
|
1164
1763
|
}
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
if (!sessionFetched) {
|
|
1175
|
-
sessionFetched = true;
|
|
1176
|
-
const finalSessionId = createdSessionId || activeSessionId;
|
|
1177
|
-
if (finalSessionId && finalSessionId !== "new") {
|
|
1178
|
-
const state = await dataSource.fetchSession(finalSessionId);
|
|
1179
|
-
if (state) {
|
|
1180
|
-
setSession(state.session);
|
|
1181
|
-
setTurns((prev) => {
|
|
1182
|
-
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1183
|
-
if (hasPendingUserOnly && (!state.turns || state.turns.length === 0)) {
|
|
1184
|
-
return prev;
|
|
1185
|
-
}
|
|
1186
|
-
return state.turns ?? prev;
|
|
1187
|
-
});
|
|
1188
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1189
|
-
}
|
|
1764
|
+
if (!sessionFetched) {
|
|
1765
|
+
sessionFetched = true;
|
|
1766
|
+
const finalSessionId = createdSessionId || activeSessionId;
|
|
1767
|
+
if (finalSessionId && finalSessionId !== "new") {
|
|
1768
|
+
const fetchResult = await this.dataSource.fetchSession(finalSessionId);
|
|
1769
|
+
if (fetchResult) {
|
|
1770
|
+
this._updateSession({ session: fetchResult.session });
|
|
1771
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1772
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1190
1773
|
}
|
|
1191
1774
|
}
|
|
1192
|
-
} else if (chunk.type === "user_message" && chunk.sessionId) {
|
|
1193
|
-
createdSessionId = chunk.sessionId;
|
|
1194
|
-
} else if (chunk.type === "tool_end" && chunk.tool?.name && ARTIFACT_TOOL_NAMES.has(chunk.tool.name)) {
|
|
1195
|
-
setArtifactsInvalidationTrigger((n) => n + 1);
|
|
1196
1775
|
}
|
|
1776
|
+
} else if (chunk.type === "user_message" && chunk.sessionId) {
|
|
1777
|
+
createdSessionId = chunk.sessionId;
|
|
1778
|
+
} else if (chunk.type === "tool_end" && chunk.tool?.name && ARTIFACT_TOOL_NAMES.has(chunk.tool.name)) {
|
|
1779
|
+
this._updateMessaging({
|
|
1780
|
+
artifactsInvalidationTrigger: this.state.messaging.artifactsInvalidationTrigger + 1
|
|
1781
|
+
});
|
|
1197
1782
|
}
|
|
1198
|
-
const targetSessionId = createdSessionId || activeSessionId;
|
|
1199
|
-
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1200
|
-
dataSource.navigateToSession?.(targetSessionId);
|
|
1201
|
-
}
|
|
1202
|
-
} catch (err) {
|
|
1203
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
1204
|
-
setMessage(content);
|
|
1205
|
-
return;
|
|
1206
|
-
}
|
|
1207
|
-
setTurns((prev) => prev.filter((t) => t.id !== tempTurn.id));
|
|
1208
|
-
const errorMessage = err instanceof Error ? err.message : "Error.NetworkError";
|
|
1209
|
-
setInputError(errorMessage);
|
|
1210
|
-
console.error("Send message error:", err);
|
|
1211
|
-
} finally {
|
|
1212
|
-
setLoading(false);
|
|
1213
|
-
setStreamingContent("");
|
|
1214
|
-
setIsStreaming(false);
|
|
1215
|
-
abortControllerRef.current = null;
|
|
1216
1783
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
const convertedAttachments = attachments.map((att) => ({
|
|
1234
|
-
clientKey: att.clientKey || crypto.randomUUID(),
|
|
1235
|
-
filename: att.filename,
|
|
1236
|
-
mimeType: att.mimeType,
|
|
1237
|
-
sizeBytes: att.sizeBytes,
|
|
1238
|
-
base64Data: att.base64Data,
|
|
1239
|
-
url: att.url,
|
|
1240
|
-
preview: att.preview
|
|
1241
|
-
}));
|
|
1242
|
-
sendMessageDirect(message, convertedAttachments);
|
|
1243
|
-
},
|
|
1244
|
-
[message, sendMessageDirect]
|
|
1245
|
-
);
|
|
1246
|
-
const handleUnqueue = useCallback(() => {
|
|
1247
|
-
if (messageQueue.length === 0) {
|
|
1248
|
-
return null;
|
|
1249
|
-
}
|
|
1250
|
-
const lastQueued = messageQueue[messageQueue.length - 1];
|
|
1251
|
-
setMessageQueue((prev) => prev.slice(0, -1));
|
|
1252
|
-
return {
|
|
1253
|
-
content: lastQueued.content,
|
|
1254
|
-
attachments: lastQueued.attachments
|
|
1255
|
-
};
|
|
1256
|
-
}, [messageQueue]);
|
|
1257
|
-
const handleRegenerate = useCallback(
|
|
1258
|
-
async (turnId) => {
|
|
1259
|
-
const curSessionId = sessionRef.current.currentSessionId;
|
|
1260
|
-
if (!curSessionId || curSessionId === "new") return;
|
|
1261
|
-
const turn = messagingRef.current.turns.find((t) => t.id === turnId);
|
|
1262
|
-
if (!turn) return;
|
|
1263
|
-
setError(null);
|
|
1264
|
-
try {
|
|
1265
|
-
await sendMessageDirect(turn.userTurn.content, turn.userTurn.attachments, {
|
|
1266
|
-
replaceFromMessageID: turn.userTurn.id
|
|
1267
|
-
});
|
|
1268
|
-
} catch (err) {
|
|
1269
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to regenerate response";
|
|
1270
|
-
setError(errorMessage);
|
|
1271
|
-
console.error("Regenerate error:", err);
|
|
1784
|
+
if (!sessionFetched) {
|
|
1785
|
+
const finalSessionId = createdSessionId || activeSessionId;
|
|
1786
|
+
if (finalSessionId && finalSessionId !== "new") {
|
|
1787
|
+
try {
|
|
1788
|
+
const fetchResult = await this.dataSource.fetchSession(finalSessionId);
|
|
1789
|
+
if (fetchResult) {
|
|
1790
|
+
this._updateSession({ session: fetchResult.session });
|
|
1791
|
+
this._updateMessaging({
|
|
1792
|
+
turns: fetchResult.turns ?? [],
|
|
1793
|
+
pendingQuestion: fetchResult.pendingQuestion || null
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
} catch (fetchErr) {
|
|
1797
|
+
console.error("Failed to fetch session after stream:", fetchErr);
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1272
1800
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
setMessage(newContent);
|
|
1281
|
-
setTurns((prev) => prev.filter((t) => t.id !== turnId));
|
|
1282
|
-
return;
|
|
1801
|
+
const targetSessionId = createdSessionId || activeSessionId;
|
|
1802
|
+
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1803
|
+
if (this.onSessionCreated) {
|
|
1804
|
+
this.onSessionCreated(targetSessionId);
|
|
1805
|
+
} else {
|
|
1806
|
+
this.dataSource.navigateToSession?.(targetSessionId);
|
|
1807
|
+
}
|
|
1283
1808
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1809
|
+
this._clearStreamError();
|
|
1810
|
+
this.lastSendAttempt = null;
|
|
1811
|
+
} catch (err) {
|
|
1812
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
1813
|
+
this._updateInput({ message: content });
|
|
1814
|
+
this._clearStreamError();
|
|
1815
|
+
shouldDrainQueue = false;
|
|
1287
1816
|
return;
|
|
1288
1817
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1818
|
+
this._updateMessaging({
|
|
1819
|
+
turns: this.state.messaging.turns.filter((t) => t.id !== tempTurn.id)
|
|
1820
|
+
});
|
|
1821
|
+
const normalized = normalizeRPCError(err, "Failed to send message");
|
|
1822
|
+
this._updateInput({ inputError: normalized.userMessage });
|
|
1823
|
+
this._updateMessaging({
|
|
1824
|
+
streamError: normalized.userMessage,
|
|
1825
|
+
streamErrorRetryable: normalized.retryable
|
|
1826
|
+
});
|
|
1827
|
+
console.error("Send message error:", err);
|
|
1828
|
+
shouldDrainQueue = false;
|
|
1829
|
+
} finally {
|
|
1830
|
+
this._updateMessaging({
|
|
1831
|
+
loading: false,
|
|
1832
|
+
streamingContent: "",
|
|
1833
|
+
isStreaming: false
|
|
1834
|
+
});
|
|
1835
|
+
this.abortController = null;
|
|
1836
|
+
this.sendingSessionId = null;
|
|
1837
|
+
if (shouldDrainQueue) {
|
|
1838
|
+
const queue = this.state.input.messageQueue;
|
|
1839
|
+
if (queue.length > 0) {
|
|
1840
|
+
const next = queue[0];
|
|
1841
|
+
this._updateInput({ messageQueue: queue.slice(1) });
|
|
1842
|
+
setTimeout(() => {
|
|
1843
|
+
this._sendMessageCore(next.content, next.attachments);
|
|
1844
|
+
}, 0);
|
|
1845
|
+
}
|
|
1298
1846
|
}
|
|
1299
|
-
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
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
|
-
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
// ── Retry ───────────────────────────────────────────────────────────────
|
|
1850
|
+
async _retryLastMessage() {
|
|
1851
|
+
const lastAttempt = this.lastSendAttempt;
|
|
1852
|
+
if (!lastAttempt || this.state.messaging.loading) return;
|
|
1853
|
+
this._clearStreamError();
|
|
1854
|
+
this._updateInput({ inputError: null });
|
|
1855
|
+
await this._sendMessageDirect(lastAttempt.content, lastAttempt.attachments, lastAttempt.options);
|
|
1856
|
+
}
|
|
1857
|
+
// ── Regenerate / Edit ───────────────────────────────────────────────────
|
|
1858
|
+
async _handleRegenerate(turnId) {
|
|
1859
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1860
|
+
if (!curSessionId || curSessionId === "new") return;
|
|
1861
|
+
const turn = this.state.messaging.turns.find((t) => t.id === turnId);
|
|
1862
|
+
if (!turn) return;
|
|
1863
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1864
|
+
await this._sendMessageDirect(turn.userTurn.content, turn.userTurn.attachments, {
|
|
1865
|
+
replaceFromMessageID: turn.userTurn.id
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
async _handleEdit(turnId, newContent) {
|
|
1869
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1870
|
+
if (!curSessionId || curSessionId === "new") {
|
|
1871
|
+
this._updateInput({ message: newContent });
|
|
1872
|
+
this._updateMessaging({
|
|
1873
|
+
turns: this.state.messaging.turns.filter((t) => t.id !== turnId)
|
|
1874
|
+
});
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
const turn = this.state.messaging.turns.find((t) => t.id === turnId);
|
|
1878
|
+
if (!turn) {
|
|
1879
|
+
this._updateSession({ error: "Failed to edit message", errorRetryable: false });
|
|
1880
|
+
return;
|
|
1881
|
+
}
|
|
1882
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1883
|
+
await this._sendMessageDirect(newContent, turn.userTurn.attachments, {
|
|
1884
|
+
replaceFromMessageID: turn.userTurn.id
|
|
1885
|
+
});
|
|
1886
|
+
}
|
|
1887
|
+
async _handleCopy(text) {
|
|
1888
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) return;
|
|
1889
|
+
try {
|
|
1890
|
+
await navigator.clipboard.writeText(text);
|
|
1891
|
+
} catch {
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
// ── HITL ────────────────────────────────────────────────────────────────
|
|
1895
|
+
async _handleSubmitQuestionAnswers(answers) {
|
|
1896
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1897
|
+
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1898
|
+
if (!curSessionId || !curPendingQuestion) return;
|
|
1899
|
+
this._updateMessaging({ loading: true });
|
|
1900
|
+
this._updateSession({ error: null, errorRetryable: false });
|
|
1901
|
+
const previousPendingQuestion = curPendingQuestion;
|
|
1902
|
+
this._updateMessaging({ pendingQuestion: null });
|
|
1903
|
+
try {
|
|
1904
|
+
const result = await this.dataSource.submitQuestionAnswers(
|
|
1905
|
+
curSessionId,
|
|
1906
|
+
previousPendingQuestion.id,
|
|
1907
|
+
answers
|
|
1908
|
+
);
|
|
1909
|
+
if (this.disposed) return;
|
|
1910
|
+
if (result.success) {
|
|
1911
|
+
if (curSessionId !== "new") {
|
|
1912
|
+
try {
|
|
1913
|
+
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1914
|
+
if (this.disposed) return;
|
|
1915
|
+
if (fetchResult) {
|
|
1916
|
+
this._updateMessaging({
|
|
1917
|
+
turns: fetchResult.turns,
|
|
1918
|
+
pendingQuestion: fetchResult.pendingQuestion || null
|
|
1919
|
+
});
|
|
1920
|
+
} else {
|
|
1921
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1922
|
+
this._updateSession({ error: "Failed to load updated session", errorRetryable: false });
|
|
1334
1923
|
}
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1924
|
+
} catch (fetchErr) {
|
|
1925
|
+
if (this.disposed) return;
|
|
1926
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1927
|
+
const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
|
|
1928
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1338
1929
|
}
|
|
1339
|
-
} catch (err) {
|
|
1340
|
-
setPendingQuestion(previousPendingQuestion);
|
|
1341
|
-
const errorMessage = err instanceof Error ? err.message : "Failed to submit answers";
|
|
1342
|
-
setError(errorMessage);
|
|
1343
|
-
} finally {
|
|
1344
|
-
setLoading(false);
|
|
1345
1930
|
}
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1931
|
+
} else {
|
|
1932
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1933
|
+
this._updateSession({ error: result.error || "Failed to submit answers", errorRetryable: false });
|
|
1934
|
+
}
|
|
1935
|
+
} catch (err) {
|
|
1936
|
+
if (this.disposed) return;
|
|
1937
|
+
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1938
|
+
const normalized = normalizeRPCError(err, "Failed to submit answers");
|
|
1939
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1940
|
+
} finally {
|
|
1941
|
+
if (!this.disposed) {
|
|
1942
|
+
this._updateMessaging({ loading: false });
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
async _handleRejectPendingQuestion() {
|
|
1947
|
+
const curSessionId = this.state.session.currentSessionId;
|
|
1948
|
+
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1353
1949
|
if (!curSessionId || !curPendingQuestion) return;
|
|
1354
1950
|
try {
|
|
1355
|
-
const result = await dataSource.rejectPendingQuestion(curSessionId);
|
|
1951
|
+
const result = await this.dataSource.rejectPendingQuestion(curSessionId);
|
|
1952
|
+
if (this.disposed) return;
|
|
1356
1953
|
if (result.success) {
|
|
1357
|
-
|
|
1954
|
+
this._updateMessaging({ pendingQuestion: null });
|
|
1358
1955
|
if (curSessionId !== "new") {
|
|
1359
|
-
const
|
|
1360
|
-
if (
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
return prev;
|
|
1366
|
-
}
|
|
1367
|
-
return state.turns ?? prev;
|
|
1368
|
-
});
|
|
1369
|
-
setPendingQuestion(state.pendingQuestion || null);
|
|
1956
|
+
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1957
|
+
if (this.disposed) return;
|
|
1958
|
+
if (fetchResult) {
|
|
1959
|
+
this._updateSession({ session: fetchResult.session });
|
|
1960
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1961
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1370
1962
|
}
|
|
1371
1963
|
}
|
|
1372
1964
|
} else {
|
|
1373
|
-
|
|
1965
|
+
this._updateSession({ error: result.error || "Failed to reject question", errorRetryable: false });
|
|
1374
1966
|
}
|
|
1375
1967
|
} catch (err) {
|
|
1376
|
-
|
|
1377
|
-
|
|
1968
|
+
if (this.disposed) return;
|
|
1969
|
+
const normalized = normalizeRPCError(err, "Failed to reject question");
|
|
1970
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: normalized.retryable });
|
|
1378
1971
|
}
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
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
|
-
|
|
1972
|
+
}
|
|
1973
|
+
// ── Input / queue ───────────────────────────────────────────────────────
|
|
1974
|
+
_handleSubmit(e, attachments = []) {
|
|
1975
|
+
e.preventDefault();
|
|
1976
|
+
const msg = this.state.input.message;
|
|
1977
|
+
if (!msg.trim() && attachments.length === 0) return;
|
|
1978
|
+
this._updateInput({ inputError: null });
|
|
1979
|
+
this._clearStreamError();
|
|
1980
|
+
const convertedAttachments = attachments.map((att) => ({
|
|
1981
|
+
clientKey: att.clientKey || crypto.randomUUID(),
|
|
1982
|
+
filename: att.filename,
|
|
1983
|
+
mimeType: att.mimeType,
|
|
1984
|
+
sizeBytes: att.sizeBytes,
|
|
1985
|
+
base64Data: att.base64Data,
|
|
1986
|
+
url: att.url,
|
|
1987
|
+
preview: att.preview
|
|
1988
|
+
}));
|
|
1989
|
+
if (this.state.messaging.loading) {
|
|
1990
|
+
const ok = this._enqueueMessage(msg.trim(), convertedAttachments);
|
|
1991
|
+
if (ok) {
|
|
1992
|
+
this._updateInput({ message: "" });
|
|
1993
|
+
}
|
|
1994
|
+
return;
|
|
1995
|
+
}
|
|
1996
|
+
this._sendMessage(msg.trim(), convertedAttachments);
|
|
1997
|
+
}
|
|
1998
|
+
_handleUnqueue() {
|
|
1999
|
+
const queue = this.state.input.messageQueue;
|
|
2000
|
+
if (queue.length === 0) return null;
|
|
2001
|
+
const last = queue[queue.length - 1];
|
|
2002
|
+
this._updateInput({ messageQueue: queue.slice(0, -1) });
|
|
2003
|
+
return { content: last.content, attachments: last.attachments };
|
|
2004
|
+
}
|
|
2005
|
+
_enqueueMessage(content, attachments) {
|
|
2006
|
+
if (this.state.input.messageQueue.length >= MAX_QUEUE_SIZE) {
|
|
2007
|
+
this._updateInput({ inputError: "BiChat.Input.QueueFull" });
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
this._updateInput({
|
|
2011
|
+
messageQueue: [...this.state.input.messageQueue, { content, attachments }]
|
|
2012
|
+
});
|
|
2013
|
+
return true;
|
|
2014
|
+
}
|
|
2015
|
+
_removeQueueItem(index) {
|
|
2016
|
+
this._updateInput({
|
|
2017
|
+
messageQueue: this.state.input.messageQueue.filter((_, i) => i !== index)
|
|
2018
|
+
});
|
|
2019
|
+
}
|
|
2020
|
+
_updateQueueItem(index, content) {
|
|
2021
|
+
this._updateInput({
|
|
2022
|
+
messageQueue: this.state.input.messageQueue.map(
|
|
2023
|
+
(item, i) => i === index ? { ...item, content } : item
|
|
2024
|
+
)
|
|
2025
|
+
});
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
function sessionDebugUsageEqual(a, b) {
|
|
2029
|
+
if (!a) return false;
|
|
2030
|
+
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;
|
|
1436
2031
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
2032
|
+
var MachineCtx = createContext(null);
|
|
2033
|
+
var DEFAULT_RATE_LIMIT_CONFIG = {
|
|
2034
|
+
maxRequests: 20,
|
|
2035
|
+
windowMs: 6e4
|
|
2036
|
+
};
|
|
2037
|
+
function ChatSessionProvider({
|
|
2038
|
+
dataSource,
|
|
2039
|
+
sessionId,
|
|
2040
|
+
rateLimiter: externalRateLimiter,
|
|
2041
|
+
rateLimitConfig,
|
|
2042
|
+
onSessionCreated,
|
|
2043
|
+
children
|
|
2044
|
+
}) {
|
|
2045
|
+
const machineRef = useRef(null);
|
|
2046
|
+
if (!machineRef.current) {
|
|
2047
|
+
machineRef.current = new ChatMachine({
|
|
2048
|
+
dataSource,
|
|
2049
|
+
rateLimiter: externalRateLimiter || new RateLimiter(rateLimitConfig || DEFAULT_RATE_LIMIT_CONFIG),
|
|
2050
|
+
onSessionCreated
|
|
2051
|
+
});
|
|
1441
2052
|
}
|
|
1442
|
-
|
|
2053
|
+
const machine = machineRef.current;
|
|
2054
|
+
useEffect(() => {
|
|
2055
|
+
machine.updateConfig({ dataSource, onSessionCreated });
|
|
2056
|
+
}, [machine, dataSource, onSessionCreated]);
|
|
2057
|
+
useEffect(() => {
|
|
2058
|
+
machine.setSessionId(sessionId);
|
|
2059
|
+
}, [machine, sessionId]);
|
|
2060
|
+
useEffect(() => {
|
|
2061
|
+
return () => {
|
|
2062
|
+
machine.dispose();
|
|
2063
|
+
};
|
|
2064
|
+
}, [machine]);
|
|
2065
|
+
return /* @__PURE__ */ jsx(MachineCtx.Provider, { value: machine, children });
|
|
1443
2066
|
}
|
|
1444
|
-
function
|
|
1445
|
-
const
|
|
1446
|
-
if (!
|
|
1447
|
-
throw new Error("
|
|
2067
|
+
function useMachine() {
|
|
2068
|
+
const machine = useContext(MachineCtx);
|
|
2069
|
+
if (!machine) {
|
|
2070
|
+
throw new Error("Chat hooks must be used within ChatSessionProvider");
|
|
1448
2071
|
}
|
|
1449
|
-
return
|
|
2072
|
+
return machine;
|
|
2073
|
+
}
|
|
2074
|
+
function useChatSession() {
|
|
2075
|
+
const machine = useMachine();
|
|
2076
|
+
return useSyncExternalStore(
|
|
2077
|
+
machine.subscribeSession,
|
|
2078
|
+
machine.getSessionSnapshot,
|
|
2079
|
+
machine.getSessionSnapshot
|
|
2080
|
+
// SSR fallback
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
function useChatMessaging() {
|
|
2084
|
+
const machine = useMachine();
|
|
2085
|
+
return useSyncExternalStore(
|
|
2086
|
+
machine.subscribeMessaging,
|
|
2087
|
+
machine.getMessagingSnapshot,
|
|
2088
|
+
machine.getMessagingSnapshot
|
|
2089
|
+
);
|
|
1450
2090
|
}
|
|
1451
2091
|
function useOptionalChatMessaging() {
|
|
1452
|
-
|
|
2092
|
+
const machine = useContext(MachineCtx);
|
|
2093
|
+
const snapshot = useSyncExternalStore(
|
|
2094
|
+
machine ? machine.subscribeMessaging : noopSubscribe,
|
|
2095
|
+
machine ? machine.getMessagingSnapshot : nullSnapshot,
|
|
2096
|
+
machine ? machine.getMessagingSnapshot : nullSnapshot
|
|
2097
|
+
);
|
|
2098
|
+
return machine ? snapshot : null;
|
|
2099
|
+
}
|
|
2100
|
+
function useChatInput() {
|
|
2101
|
+
const machine = useMachine();
|
|
2102
|
+
return useSyncExternalStore(
|
|
2103
|
+
machine.subscribeInput,
|
|
2104
|
+
machine.getInputSnapshot,
|
|
2105
|
+
machine.getInputSnapshot
|
|
2106
|
+
);
|
|
2107
|
+
}
|
|
2108
|
+
function noopSubscribe() {
|
|
2109
|
+
return () => {
|
|
2110
|
+
};
|
|
1453
2111
|
}
|
|
1454
|
-
function
|
|
1455
|
-
|
|
1456
|
-
if (!context) {
|
|
1457
|
-
throw new Error("useChatInput must be used within ChatSessionProvider");
|
|
1458
|
-
}
|
|
1459
|
-
return context;
|
|
2112
|
+
function nullSnapshot() {
|
|
2113
|
+
return null;
|
|
1460
2114
|
}
|
|
1461
2115
|
|
|
1462
2116
|
// ui/src/bichat/components/ChatHeader.tsx
|
|
@@ -1553,6 +2207,26 @@ function ChatHeader({ session, onBack, readOnly, logoSlot, actionsSlot }) {
|
|
|
1553
2207
|
] })
|
|
1554
2208
|
] }) });
|
|
1555
2209
|
}
|
|
2210
|
+
function formatRelativeTime(date, t) {
|
|
2211
|
+
const messageDate = new Date(date);
|
|
2212
|
+
const now = /* @__PURE__ */ new Date();
|
|
2213
|
+
const diffMins = differenceInMinutes(now, messageDate);
|
|
2214
|
+
const diffHours = differenceInHours(now, messageDate);
|
|
2215
|
+
const diffDays = differenceInDays(now, messageDate);
|
|
2216
|
+
if (diffMins < 1) {
|
|
2217
|
+
return t ? t("BiChat.RelativeTime.JustNow") : "Just now";
|
|
2218
|
+
}
|
|
2219
|
+
if (diffMins < 60) {
|
|
2220
|
+
return t ? t("BiChat.RelativeTime.MinutesAgo", { count: diffMins }) : `${diffMins}m ago`;
|
|
2221
|
+
}
|
|
2222
|
+
if (diffHours < 24) {
|
|
2223
|
+
return t ? t("BiChat.RelativeTime.HoursAgo", { count: diffHours }) : `${diffHours}h ago`;
|
|
2224
|
+
}
|
|
2225
|
+
if (diffDays <= 7) {
|
|
2226
|
+
return t ? t("BiChat.RelativeTime.DaysAgo", { count: diffDays }) : `${diffDays}d ago`;
|
|
2227
|
+
}
|
|
2228
|
+
return format(messageDate, "HH:mm");
|
|
2229
|
+
}
|
|
1556
2230
|
var MAX_FILE_SIZE_BYTES = 20 * 1024 * 1024;
|
|
1557
2231
|
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
1558
2232
|
"image/jpeg",
|
|
@@ -2274,7 +2948,7 @@ function UserMessage({
|
|
|
2274
2948
|
[selectedImageIndex, imageAttachments.length]
|
|
2275
2949
|
);
|
|
2276
2950
|
const currentAttachment = selectedImageIndex !== null ? imageAttachments[selectedImageIndex] : null;
|
|
2277
|
-
const timestamp =
|
|
2951
|
+
const timestamp = formatRelativeTime(turn.createdAt, t);
|
|
2278
2952
|
const avatarSlotProps = { initials };
|
|
2279
2953
|
const contentSlotProps = { content: turn.content };
|
|
2280
2954
|
const attachmentsSlotProps = {
|
|
@@ -2319,7 +2993,7 @@ function UserMessage({
|
|
|
2319
2993
|
value: draftContent,
|
|
2320
2994
|
onChange: (e) => setDraftContent(e.target.value),
|
|
2321
2995
|
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",
|
|
2322
|
-
"aria-label": "
|
|
2996
|
+
"aria-label": t("BiChat.Message.EditMessage")
|
|
2323
2997
|
}
|
|
2324
2998
|
),
|
|
2325
2999
|
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
@@ -2329,7 +3003,7 @@ function UserMessage({
|
|
|
2329
3003
|
type: "button",
|
|
2330
3004
|
onClick: handleEditCancel,
|
|
2331
3005
|
className: "cursor-pointer px-3 py-1.5 rounded-lg bg-white/10 hover:bg-white/15 transition-colors text-sm font-medium",
|
|
2332
|
-
children: "Cancel"
|
|
3006
|
+
children: t("BiChat.Message.Cancel")
|
|
2333
3007
|
}
|
|
2334
3008
|
),
|
|
2335
3009
|
/* @__PURE__ */ jsx(
|
|
@@ -2339,7 +3013,7 @@ function UserMessage({
|
|
|
2339
3013
|
onClick: handleEditSave,
|
|
2340
3014
|
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",
|
|
2341
3015
|
disabled: !draftContent.trim() || draftContent === turn.content,
|
|
2342
|
-
children: "Save"
|
|
3016
|
+
children: t("BiChat.Message.Save")
|
|
2343
3017
|
}
|
|
2344
3018
|
)
|
|
2345
3019
|
] })
|
|
@@ -2354,7 +3028,7 @@ function UserMessage({
|
|
|
2354
3028
|
{
|
|
2355
3029
|
onClick: handleCopyClick,
|
|
2356
3030
|
className: `cursor-pointer ${classes.actionButton} ${isCopied ? "text-green-600 dark:text-green-400" : ""}`,
|
|
2357
|
-
"aria-label": "
|
|
3031
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
2358
3032
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
2359
3033
|
children: isCopied ? /* @__PURE__ */ jsx(Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
2360
3034
|
}
|
|
@@ -2364,8 +3038,8 @@ function UserMessage({
|
|
|
2364
3038
|
{
|
|
2365
3039
|
onClick: handleEditClick,
|
|
2366
3040
|
className: `cursor-pointer ${classes.actionButton}`,
|
|
2367
|
-
"aria-label": "
|
|
2368
|
-
title: "
|
|
3041
|
+
"aria-label": t("BiChat.Message.EditMessage"),
|
|
3042
|
+
title: t("BiChat.Message.EditMessage"),
|
|
2369
3043
|
disabled: isEditing,
|
|
2370
3044
|
children: /* @__PURE__ */ jsx(PencilSimple, { size: 14, weight: "regular" })
|
|
2371
3045
|
}
|
|
@@ -2415,6 +3089,7 @@ function UserTurnView({
|
|
|
2415
3089
|
}
|
|
2416
3090
|
);
|
|
2417
3091
|
}
|
|
3092
|
+
init_useTranslation();
|
|
2418
3093
|
function toBase64(str) {
|
|
2419
3094
|
const bytes = new TextEncoder().encode(str);
|
|
2420
3095
|
let binary = "";
|
|
@@ -2424,16 +3099,17 @@ function toBase64(str) {
|
|
|
2424
3099
|
return btoa(binary);
|
|
2425
3100
|
}
|
|
2426
3101
|
function CodeOutputsPanel({ outputs }) {
|
|
3102
|
+
const { t } = useTranslation();
|
|
2427
3103
|
if (!outputs || outputs.length === 0) return null;
|
|
2428
3104
|
return /* @__PURE__ */ jsxs("div", { className: "mb-2 p-3 bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700", children: [
|
|
2429
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs font-semibold text-gray-600 dark:text-gray-400 mb-2", children: "
|
|
3105
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-semibold text-gray-600 dark:text-gray-400 mb-2", children: t("BiChat.CodeOutput.Title") }),
|
|
2430
3106
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: outputs.map((output, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
2431
3107
|
output.type === "image" && /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
2432
3108
|
/* @__PURE__ */ jsx(
|
|
2433
3109
|
"img",
|
|
2434
3110
|
{
|
|
2435
3111
|
src: output.content.startsWith("data:") ? output.content : `data:${output.mimeType || "image/png"};base64,${output.content}`,
|
|
2436
|
-
alt: output.filename || "
|
|
3112
|
+
alt: output.filename || t("BiChat.CodeOutput.CodeOutput"),
|
|
2437
3113
|
className: "max-w-full rounded border border-gray-300 dark:border-gray-600"
|
|
2438
3114
|
}
|
|
2439
3115
|
),
|
|
@@ -2465,19 +3141,23 @@ function CodeOutputsPanel({ outputs }) {
|
|
|
2465
3141
|
] })
|
|
2466
3142
|
] }),
|
|
2467
3143
|
output.type === "error" && /* @__PURE__ */ jsxs("div", { className: "text-xs text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/20 p-2 rounded border border-red-200 dark:border-red-800", children: [
|
|
2468
|
-
/* @__PURE__ */ jsx("div", { className: "font-semibold mb-1", children: "Error" }),
|
|
3144
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold mb-1", children: t("BiChat.Error.Label") }),
|
|
2469
3145
|
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap", children: output.content })
|
|
2470
3146
|
] })
|
|
2471
3147
|
] }, index)) })
|
|
2472
3148
|
] });
|
|
2473
3149
|
}
|
|
2474
3150
|
var CodeOutputsPanel_default = CodeOutputsPanel;
|
|
3151
|
+
|
|
3152
|
+
// ui/src/bichat/components/StreamingCursor.tsx
|
|
3153
|
+
init_useTranslation();
|
|
2475
3154
|
function StreamingCursor() {
|
|
3155
|
+
const { t } = useTranslation();
|
|
2476
3156
|
return /* @__PURE__ */ jsx(
|
|
2477
3157
|
"span",
|
|
2478
3158
|
{
|
|
2479
3159
|
className: "inline-block w-1.5 h-4 ml-0.5 bg-primary-600 dark:bg-primary-500 animate-pulse",
|
|
2480
|
-
"aria-label": "
|
|
3160
|
+
"aria-label": t("BiChat.Common.AITyping")
|
|
2481
3161
|
}
|
|
2482
3162
|
);
|
|
2483
3163
|
}
|
|
@@ -3256,7 +3936,7 @@ function AssistantMessage({
|
|
|
3256
3936
|
await onRegenerate(turnId);
|
|
3257
3937
|
}
|
|
3258
3938
|
}, [onRegenerate, turnId]);
|
|
3259
|
-
const timestamp =
|
|
3939
|
+
const timestamp = formatRelativeTime(turn.createdAt, t);
|
|
3260
3940
|
const avatarSlotProps = { text: isSystemMessage ? "SYS" : "AI" };
|
|
3261
3941
|
const contentSlotProps = {
|
|
3262
3942
|
content: turn.content,
|
|
@@ -3310,7 +3990,7 @@ function AssistantMessage({
|
|
|
3310
3990
|
{
|
|
3311
3991
|
fallback: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm text-gray-400 dark:text-gray-500", children: [
|
|
3312
3992
|
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-gray-300 dark:border-gray-600 border-t-transparent rounded-full animate-spin" }),
|
|
3313
|
-
"Loading
|
|
3993
|
+
t("BiChat.Common.Loading")
|
|
3314
3994
|
] }),
|
|
3315
3995
|
children: /* @__PURE__ */ jsx(
|
|
3316
3996
|
MarkdownRenderer2,
|
|
@@ -3364,7 +4044,7 @@ function AssistantMessage({
|
|
|
3364
4044
|
]
|
|
3365
4045
|
}
|
|
3366
4046
|
),
|
|
3367
|
-
explanationExpanded && /* @__PURE__ */ jsx("div", { className: "pt-3 text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { children: "Loading
|
|
4047
|
+
explanationExpanded && /* @__PURE__ */ jsx("div", { className: "pt-3 text-sm text-gray-600 dark:text-gray-400", children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx("div", { children: t("BiChat.Common.Loading") }), children: /* @__PURE__ */ jsx(MarkdownRenderer2, { content: turn.explanation }) }) })
|
|
3368
4048
|
] })
|
|
3369
4049
|
) }),
|
|
3370
4050
|
showDebug && /* @__PURE__ */ jsx(DebugPanel, { trace: turn.debug })
|
|
@@ -3385,7 +4065,7 @@ function AssistantMessage({
|
|
|
3385
4065
|
{
|
|
3386
4066
|
onClick: handleCopyClick,
|
|
3387
4067
|
className: `cursor-pointer ${classes.actionButton} ${isCopied ? "text-green-600 dark:text-green-400" : ""}`,
|
|
3388
|
-
"aria-label": "
|
|
4068
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
3389
4069
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
3390
4070
|
children: isCopied ? /* @__PURE__ */ jsx(Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
3391
4071
|
}
|
|
@@ -3395,8 +4075,8 @@ function AssistantMessage({
|
|
|
3395
4075
|
{
|
|
3396
4076
|
onClick: handleRegenerateClick,
|
|
3397
4077
|
className: `cursor-pointer ${classes.actionButton}`,
|
|
3398
|
-
"aria-label": "Regenerate
|
|
3399
|
-
title: "Regenerate",
|
|
4078
|
+
"aria-label": t("BiChat.Message.Regenerate"),
|
|
4079
|
+
title: t("BiChat.Message.Regenerate"),
|
|
3400
4080
|
children: /* @__PURE__ */ jsx(ArrowsClockwise, { size: 14, weight: "regular" })
|
|
3401
4081
|
}
|
|
3402
4082
|
)
|
|
@@ -3405,8 +4085,6 @@ function AssistantMessage({
|
|
|
3405
4085
|
] })
|
|
3406
4086
|
] });
|
|
3407
4087
|
}
|
|
3408
|
-
|
|
3409
|
-
// ui/src/bichat/components/SystemMessage.tsx
|
|
3410
4088
|
init_useTranslation();
|
|
3411
4089
|
var MarkdownRenderer3 = lazy(
|
|
3412
4090
|
() => Promise.resolve().then(() => (init_MarkdownRenderer(), MarkdownRenderer_exports)).then((module) => ({ default: module.MarkdownRenderer }))
|
|
@@ -3473,7 +4151,7 @@ function SystemMessage({
|
|
|
3473
4151
|
setIsCopied(false);
|
|
3474
4152
|
}
|
|
3475
4153
|
}, [content, onCopy]);
|
|
3476
|
-
const timestamp =
|
|
4154
|
+
const timestamp = formatRelativeTime(createdAt, t);
|
|
3477
4155
|
const resolvedHeight = isExpanded ? contentHeight : COLLAPSED_HEIGHT;
|
|
3478
4156
|
return /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx("div", { className: "w-full max-w-3xl px-2 sm:px-4", children: /* @__PURE__ */ 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: [
|
|
3479
4157
|
/* @__PURE__ */ 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" }),
|
|
@@ -3493,7 +4171,7 @@ function SystemMessage({
|
|
|
3493
4171
|
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/50
|
|
3494
4172
|
${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"}
|
|
3495
4173
|
`,
|
|
3496
|
-
"aria-label": "
|
|
4174
|
+
"aria-label": t("BiChat.Message.CopyMessage"),
|
|
3497
4175
|
title: isCopied ? t("BiChat.Message.Copied") : t("BiChat.Message.Copy"),
|
|
3498
4176
|
children: isCopied ? /* @__PURE__ */ jsx(Check, { size: 13, weight: "bold" }) : /* @__PURE__ */ jsx(Copy, { size: 13, weight: "regular" })
|
|
3499
4177
|
}
|
|
@@ -3833,23 +4511,6 @@ var dropdownVariants = {
|
|
|
3833
4511
|
transition: { duration: 0.1 }
|
|
3834
4512
|
}
|
|
3835
4513
|
};
|
|
3836
|
-
var toastVariants = {
|
|
3837
|
-
initial: { opacity: 0, y: -8 },
|
|
3838
|
-
animate: {
|
|
3839
|
-
opacity: 1,
|
|
3840
|
-
y: 0,
|
|
3841
|
-
transition: {
|
|
3842
|
-
duration: prefersReducedMotion() ? 0 : 0.2
|
|
3843
|
-
}
|
|
3844
|
-
},
|
|
3845
|
-
exit: {
|
|
3846
|
-
opacity: 0,
|
|
3847
|
-
y: -8,
|
|
3848
|
-
transition: {
|
|
3849
|
-
duration: prefersReducedMotion() ? 0 : 0.15
|
|
3850
|
-
}
|
|
3851
|
-
}
|
|
3852
|
-
};
|
|
3853
4514
|
var sessionItemVariants = {
|
|
3854
4515
|
initial: { opacity: 0, x: -20 },
|
|
3855
4516
|
animate: {
|
|
@@ -3911,7 +4572,7 @@ var prefersReducedMotion2 = () => {
|
|
|
3911
4572
|
var getRandomVerb = (verbs, current) => {
|
|
3912
4573
|
const available = verbs.filter((v) => v !== current);
|
|
3913
4574
|
if (available.length === 0) {
|
|
3914
|
-
return current || verbs[0] || "
|
|
4575
|
+
return current || verbs[0] || "";
|
|
3915
4576
|
}
|
|
3916
4577
|
return available[Math.floor(Math.random() * available.length)];
|
|
3917
4578
|
};
|
|
@@ -3953,7 +4614,7 @@ function TypingIndicator({
|
|
|
3953
4614
|
animate: "animate",
|
|
3954
4615
|
exit: "exit",
|
|
3955
4616
|
className: "text-sm bichat-thinking-shimmer block",
|
|
3956
|
-
"aria-label":
|
|
4617
|
+
"aria-label": t("BiChat.Thinking.AriaLabel", { verb }),
|
|
3957
4618
|
children: [
|
|
3958
4619
|
verb,
|
|
3959
4620
|
"..."
|
|
@@ -3967,6 +4628,9 @@ function TypingIndicator({
|
|
|
3967
4628
|
}
|
|
3968
4629
|
var MemoizedTypingIndicator = memo(TypingIndicator);
|
|
3969
4630
|
MemoizedTypingIndicator.displayName = "TypingIndicator";
|
|
4631
|
+
|
|
4632
|
+
// ui/src/bichat/components/ScrollToBottomButton.tsx
|
|
4633
|
+
init_useTranslation();
|
|
3970
4634
|
function ScrollToBottomButton({
|
|
3971
4635
|
show,
|
|
3972
4636
|
onClick,
|
|
@@ -3974,6 +4638,7 @@ function ScrollToBottomButton({
|
|
|
3974
4638
|
disabled = false,
|
|
3975
4639
|
label
|
|
3976
4640
|
}) {
|
|
4641
|
+
const { t } = useTranslation();
|
|
3977
4642
|
return /* @__PURE__ */ jsx(AnimatePresence, { children: show && /* @__PURE__ */ jsx(
|
|
3978
4643
|
"div",
|
|
3979
4644
|
{
|
|
@@ -3989,7 +4654,7 @@ function ScrollToBottomButton({
|
|
|
3989
4654
|
onClick: disabled ? void 0 : onClick,
|
|
3990
4655
|
disabled,
|
|
3991
4656
|
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"}`,
|
|
3992
|
-
"aria-label": label || "
|
|
4657
|
+
"aria-label": label || t("BiChat.Common.ScrollToBottom"),
|
|
3993
4658
|
children: label ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3994
4659
|
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-white", children: label }),
|
|
3995
4660
|
/* @__PURE__ */ jsx(ArrowDown, { size: 16, weight: "bold", className: "text-white" })
|
|
@@ -4003,23 +4668,6 @@ function ScrollToBottomButton({
|
|
|
4003
4668
|
) });
|
|
4004
4669
|
}
|
|
4005
4670
|
var ScrollToBottomButton_default = ScrollToBottomButton;
|
|
4006
|
-
function CompactionDoodle({ title, subtitle }) {
|
|
4007
|
-
return /* @__PURE__ */ 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__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4008
|
-
/* @__PURE__ */ jsxs("div", { className: "relative w-10 h-10", children: [
|
|
4009
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-primary-500/20 animate-pulse motion-reduce:animate-none" }),
|
|
4010
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-1 rounded-full bg-primary-500/40 animate-pulse motion-reduce:animate-none" }),
|
|
4011
|
-
/* @__PURE__ */ jsx("div", { className: "absolute inset-3 rounded-full bg-primary-600" })
|
|
4012
|
-
] }),
|
|
4013
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
4014
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: title }),
|
|
4015
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: subtitle })
|
|
4016
|
-
] })
|
|
4017
|
-
] }) });
|
|
4018
|
-
}
|
|
4019
|
-
var CompactionDoodle_default = CompactionDoodle;
|
|
4020
|
-
|
|
4021
|
-
// ui/src/bichat/components/MessageList.tsx
|
|
4022
|
-
init_useTranslation();
|
|
4023
4671
|
|
|
4024
4672
|
// ui/src/bichat/utils/markdownStream.ts
|
|
4025
4673
|
function normalizeStreamingMarkdown(text) {
|
|
@@ -4048,7 +4696,6 @@ var MarkdownRenderer4 = lazy(
|
|
|
4048
4696
|
() => Promise.resolve().then(() => (init_MarkdownRenderer(), MarkdownRenderer_exports)).then((m) => ({ default: m.MarkdownRenderer }))
|
|
4049
4697
|
);
|
|
4050
4698
|
function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readOnly }) {
|
|
4051
|
-
const { t } = useTranslation();
|
|
4052
4699
|
const { currentSessionId, fetching } = useChatSession();
|
|
4053
4700
|
const { turns, streamingContent, isStreaming, loading, isCompacting } = useChatMessaging();
|
|
4054
4701
|
const messagesEndRef = useRef(null);
|
|
@@ -4106,13 +4753,6 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
|
|
|
4106
4753
|
);
|
|
4107
4754
|
return /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0", children: [
|
|
4108
4755
|
/* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full overflow-y-auto px-4 py-6", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto space-y-6", children: [
|
|
4109
|
-
isCompacting && /* @__PURE__ */ jsx(
|
|
4110
|
-
CompactionDoodle_default,
|
|
4111
|
-
{
|
|
4112
|
-
title: t("BiChat.Slash.CompactingTitle"),
|
|
4113
|
-
subtitle: t("BiChat.Slash.CompactingSubtitle")
|
|
4114
|
-
}
|
|
4115
|
-
),
|
|
4116
4756
|
fetching && turns.length === 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-6", "aria-hidden": "true", children: [
|
|
4117
4757
|
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs("div", { className: "w-3/5 max-w-md rounded-2xl bg-gray-100 dark:bg-gray-800 p-4 space-y-2", children: [
|
|
4118
4758
|
/* @__PURE__ */ jsx("div", { className: "h-3 w-full rounded bg-gray-200 dark:bg-gray-700 animate-pulse" }),
|
|
@@ -4175,6 +4815,128 @@ function MessageList({ renderUserTurn, renderAssistantTurn, thinkingVerbs, readO
|
|
|
4175
4815
|
)
|
|
4176
4816
|
] });
|
|
4177
4817
|
}
|
|
4818
|
+
|
|
4819
|
+
// ui/src/bichat/components/MessageQueueList.tsx
|
|
4820
|
+
init_useTranslation();
|
|
4821
|
+
function MessageQueueList({ queue, onRemove, onUpdate }) {
|
|
4822
|
+
const { t } = useTranslation();
|
|
4823
|
+
const [editingIndex, setEditingIndex] = useState(null);
|
|
4824
|
+
const [editValue, setEditValue] = useState("");
|
|
4825
|
+
const startEdit = useCallback((index) => {
|
|
4826
|
+
setEditingIndex(index);
|
|
4827
|
+
setEditValue(queue[index].content);
|
|
4828
|
+
}, [queue]);
|
|
4829
|
+
const saveEdit = useCallback(() => {
|
|
4830
|
+
if (editingIndex === null) return;
|
|
4831
|
+
const trimmed = editValue.trim();
|
|
4832
|
+
if (trimmed) {
|
|
4833
|
+
onUpdate(editingIndex, trimmed);
|
|
4834
|
+
}
|
|
4835
|
+
setEditingIndex(null);
|
|
4836
|
+
setEditValue("");
|
|
4837
|
+
}, [editingIndex, editValue, onUpdate]);
|
|
4838
|
+
const cancelEdit = useCallback(() => {
|
|
4839
|
+
setEditingIndex(null);
|
|
4840
|
+
setEditValue("");
|
|
4841
|
+
}, []);
|
|
4842
|
+
if (queue.length === 0) return null;
|
|
4843
|
+
return /* @__PURE__ */ jsxs("div", { className: "mb-3 space-y-1.5", children: [
|
|
4844
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: t("BiChat.Input.QueuedMessages", { count: queue.length }) }) }),
|
|
4845
|
+
/* @__PURE__ */ jsx(AnimatePresence, { initial: false, children: queue.map((item, index) => /* @__PURE__ */ jsx(
|
|
4846
|
+
motion.div,
|
|
4847
|
+
{
|
|
4848
|
+
initial: { opacity: 0, height: 0 },
|
|
4849
|
+
animate: { opacity: 1, height: "auto" },
|
|
4850
|
+
exit: { opacity: 0, height: 0 },
|
|
4851
|
+
transition: { duration: 0.15 },
|
|
4852
|
+
className: "overflow-hidden",
|
|
4853
|
+
children: /* @__PURE__ */ 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: [
|
|
4854
|
+
/* @__PURE__ */ 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 }),
|
|
4855
|
+
editingIndex === index ? /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex flex-col gap-1.5", children: [
|
|
4856
|
+
/* @__PURE__ */ jsx(
|
|
4857
|
+
"textarea",
|
|
4858
|
+
{
|
|
4859
|
+
value: editValue,
|
|
4860
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
4861
|
+
onKeyDown: (e) => {
|
|
4862
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
4863
|
+
e.preventDefault();
|
|
4864
|
+
saveEdit();
|
|
4865
|
+
}
|
|
4866
|
+
if (e.key === "Escape") cancelEdit();
|
|
4867
|
+
},
|
|
4868
|
+
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",
|
|
4869
|
+
rows: 1,
|
|
4870
|
+
autoFocus: true
|
|
4871
|
+
}
|
|
4872
|
+
),
|
|
4873
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
4874
|
+
/* @__PURE__ */ jsxs(
|
|
4875
|
+
"button",
|
|
4876
|
+
{
|
|
4877
|
+
type: "button",
|
|
4878
|
+
onClick: saveEdit,
|
|
4879
|
+
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",
|
|
4880
|
+
children: [
|
|
4881
|
+
/* @__PURE__ */ jsx(Check, { size: 12, weight: "bold" }),
|
|
4882
|
+
t("BiChat.Message.Save")
|
|
4883
|
+
]
|
|
4884
|
+
}
|
|
4885
|
+
),
|
|
4886
|
+
/* @__PURE__ */ jsxs(
|
|
4887
|
+
"button",
|
|
4888
|
+
{
|
|
4889
|
+
type: "button",
|
|
4890
|
+
onClick: cancelEdit,
|
|
4891
|
+
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",
|
|
4892
|
+
children: [
|
|
4893
|
+
/* @__PURE__ */ jsx(ArrowCounterClockwise, { size: 12 }),
|
|
4894
|
+
t("BiChat.Message.Cancel")
|
|
4895
|
+
]
|
|
4896
|
+
}
|
|
4897
|
+
)
|
|
4898
|
+
] })
|
|
4899
|
+
] }) : /* @__PURE__ */ jsxs("p", { className: "flex-1 min-w-0 text-gray-700 dark:text-gray-300 truncate", children: [
|
|
4900
|
+
item.content,
|
|
4901
|
+
item.attachments.length > 0 && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-gray-400 dark:text-gray-500", children: [
|
|
4902
|
+
"+",
|
|
4903
|
+
item.attachments.length,
|
|
4904
|
+
" ",
|
|
4905
|
+
t("BiChat.Input.AttachFiles").toLowerCase()
|
|
4906
|
+
] })
|
|
4907
|
+
] }),
|
|
4908
|
+
editingIndex !== index && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
|
|
4909
|
+
/* @__PURE__ */ jsx(
|
|
4910
|
+
"button",
|
|
4911
|
+
{
|
|
4912
|
+
type: "button",
|
|
4913
|
+
onClick: () => startEdit(index),
|
|
4914
|
+
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",
|
|
4915
|
+
"aria-label": t("BiChat.Input.EditQueueItem"),
|
|
4916
|
+
title: t("BiChat.Input.EditQueueItem"),
|
|
4917
|
+
children: /* @__PURE__ */ jsx(PencilSimple, { size: 14 })
|
|
4918
|
+
}
|
|
4919
|
+
),
|
|
4920
|
+
/* @__PURE__ */ jsx(
|
|
4921
|
+
"button",
|
|
4922
|
+
{
|
|
4923
|
+
type: "button",
|
|
4924
|
+
onClick: () => onRemove(index),
|
|
4925
|
+
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",
|
|
4926
|
+
"aria-label": t("BiChat.Input.RemoveQueueItem"),
|
|
4927
|
+
title: t("BiChat.Input.RemoveQueueItem"),
|
|
4928
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
4929
|
+
}
|
|
4930
|
+
)
|
|
4931
|
+
] })
|
|
4932
|
+
] })
|
|
4933
|
+
},
|
|
4934
|
+
`queue-${index}-${item.content.slice(0, 20)}`
|
|
4935
|
+
)) })
|
|
4936
|
+
] });
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
// ui/src/bichat/components/MessageInput.tsx
|
|
4178
4940
|
init_useTranslation();
|
|
4179
4941
|
var MAX_FILES_DEFAULT = 10;
|
|
4180
4942
|
var MAX_FILE_SIZE_DEFAULT = 20 * 1024 * 1024;
|
|
@@ -4194,6 +4956,8 @@ var MessageInput = forwardRef(
|
|
|
4194
4956
|
onMessageChange,
|
|
4195
4957
|
onSubmit,
|
|
4196
4958
|
onUnqueue,
|
|
4959
|
+
onRemoveQueueItem,
|
|
4960
|
+
onUpdateQueueItem,
|
|
4197
4961
|
placeholder: placeholderOverride,
|
|
4198
4962
|
maxFiles = MAX_FILES_DEFAULT,
|
|
4199
4963
|
maxFileSize = MAX_FILE_SIZE_DEFAULT,
|
|
@@ -4417,14 +5181,22 @@ var MessageInput = forwardRef(
|
|
|
4417
5181
|
return;
|
|
4418
5182
|
}
|
|
4419
5183
|
if (isCommandListVisible) {
|
|
4420
|
-
if (e.key === "
|
|
5184
|
+
if (e.key === "Tab") {
|
|
5185
|
+
e.preventDefault();
|
|
5186
|
+
if (filteredCommands.length > 0) {
|
|
5187
|
+
onMessageChange(filteredCommands[activeCommandIndex].name);
|
|
5188
|
+
setCommandListDismissed(true);
|
|
5189
|
+
}
|
|
5190
|
+
return;
|
|
5191
|
+
}
|
|
5192
|
+
if (e.key === "ArrowDown") {
|
|
4421
5193
|
e.preventDefault();
|
|
4422
5194
|
if (filteredCommands.length > 0) {
|
|
4423
5195
|
setActiveCommandIndex((prev) => (prev + 1) % filteredCommands.length);
|
|
4424
5196
|
}
|
|
4425
5197
|
return;
|
|
4426
5198
|
}
|
|
4427
|
-
if (e.key === "ArrowUp"
|
|
5199
|
+
if (e.key === "ArrowUp") {
|
|
4428
5200
|
e.preventDefault();
|
|
4429
5201
|
if (filteredCommands.length > 0) {
|
|
4430
5202
|
setActiveCommandIndex(
|
|
@@ -4450,7 +5222,7 @@ var MessageInput = forwardRef(
|
|
|
4450
5222
|
}
|
|
4451
5223
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
4452
5224
|
e.preventDefault();
|
|
4453
|
-
if (
|
|
5225
|
+
if (message.trim() || attachments.length > 0) {
|
|
4454
5226
|
handleFormSubmit(e);
|
|
4455
5227
|
}
|
|
4456
5228
|
}
|
|
@@ -4474,7 +5246,7 @@ var MessageInput = forwardRef(
|
|
|
4474
5246
|
const handleFormSubmit = (e) => {
|
|
4475
5247
|
e.preventDefault();
|
|
4476
5248
|
if (isComposing) return;
|
|
4477
|
-
if (
|
|
5249
|
+
if (disabled || !message.trim() && attachments.length === 0) {
|
|
4478
5250
|
return;
|
|
4479
5251
|
}
|
|
4480
5252
|
setCommandListDismissed(true);
|
|
@@ -4482,7 +5254,7 @@ var MessageInput = forwardRef(
|
|
|
4482
5254
|
setAttachments([]);
|
|
4483
5255
|
setError(null);
|
|
4484
5256
|
};
|
|
4485
|
-
const canSubmit = !
|
|
5257
|
+
const canSubmit = !disabled && (message.trim() || attachments.length > 0);
|
|
4486
5258
|
const visibleError = error || commandError;
|
|
4487
5259
|
const visibleErrorText = visibleError ? t(visibleError) : "";
|
|
4488
5260
|
const defaultContainerClassName = "shrink-0 px-4 pt-4 pb-6";
|
|
@@ -4503,8 +5275,9 @@ var MessageInput = forwardRef(
|
|
|
4503
5275
|
ref: containerRef,
|
|
4504
5276
|
className: containerClassName ?? defaultContainerClassName,
|
|
4505
5277
|
children: /* @__PURE__ */ jsxs("form", { ref: formRef, onSubmit: handleFormSubmit, className: formClassName ?? "mx-auto", children: [
|
|
4506
|
-
visibleError && /* @__PURE__ */ jsxs("div", { className: "mb-3
|
|
4507
|
-
/* @__PURE__ */ jsx("
|
|
5278
|
+
visibleError && /* @__PURE__ */ 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: [
|
|
5279
|
+
/* @__PURE__ */ 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__ */ jsx(X, { size: 10, className: "text-red-600 dark:text-red-400", weight: "bold" }) }),
|
|
5280
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-red-700 dark:text-red-300 text-xs leading-relaxed", children: visibleErrorText }),
|
|
4508
5281
|
/* @__PURE__ */ jsx(
|
|
4509
5282
|
"button",
|
|
4510
5283
|
{
|
|
@@ -4513,13 +5286,20 @@ var MessageInput = forwardRef(
|
|
|
4513
5286
|
setError(null);
|
|
4514
5287
|
onClearCommandError?.();
|
|
4515
5288
|
},
|
|
4516
|
-
className: "cursor-pointer
|
|
5289
|
+
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",
|
|
4517
5290
|
"aria-label": t("BiChat.Input.DismissError"),
|
|
4518
5291
|
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
4519
5292
|
}
|
|
4520
5293
|
)
|
|
4521
5294
|
] }),
|
|
4522
|
-
messageQueue.length > 0 &&
|
|
5295
|
+
messageQueue.length > 0 && onRemoveQueueItem && onUpdateQueueItem && /* @__PURE__ */ jsx(
|
|
5296
|
+
MessageQueueList,
|
|
5297
|
+
{
|
|
5298
|
+
queue: messageQueue,
|
|
5299
|
+
onRemove: onRemoveQueueItem,
|
|
5300
|
+
onUpdate: onUpdateQueueItem
|
|
5301
|
+
}
|
|
5302
|
+
),
|
|
4523
5303
|
debugMode && /* @__PURE__ */ jsxs("div", { className: "mb-3 space-y-2 text-xs", children: [
|
|
4524
5304
|
/* @__PURE__ */ 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: [
|
|
4525
5305
|
/* @__PURE__ */ jsxs("span", { className: "relative flex h-1.5 w-1.5", "aria-hidden": "true", children: [
|
|
@@ -4676,7 +5456,7 @@ var MessageInput = forwardRef(
|
|
|
4676
5456
|
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",
|
|
4677
5457
|
style: { maxHeight: `${MAX_HEIGHT}px` },
|
|
4678
5458
|
rows: 1,
|
|
4679
|
-
disabled
|
|
5459
|
+
disabled,
|
|
4680
5460
|
"aria-busy": loading,
|
|
4681
5461
|
"aria-label": t("BiChat.Input.MessageInput")
|
|
4682
5462
|
}
|
|
@@ -4742,13 +5522,36 @@ var MessageInput = forwardRef(
|
|
|
4742
5522
|
}
|
|
4743
5523
|
);
|
|
4744
5524
|
MessageInput.displayName = "MessageInput";
|
|
5525
|
+
function CompactionDoodle({ title, subtitle }) {
|
|
5526
|
+
return /* @__PURE__ */ jsxs(
|
|
5527
|
+
motion.div,
|
|
5528
|
+
{
|
|
5529
|
+
initial: { opacity: 0, y: 8, scale: 0.96 },
|
|
5530
|
+
animate: { opacity: 1, y: 0, scale: 1 },
|
|
5531
|
+
exit: { opacity: 0, y: 4, scale: 0.98 },
|
|
5532
|
+
transition: { type: "spring", stiffness: 400, damping: 28 },
|
|
5533
|
+
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",
|
|
5534
|
+
children: [
|
|
5535
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex h-5 w-5 items-center justify-center", children: [
|
|
5536
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary-400/30" }),
|
|
5537
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary-500" })
|
|
5538
|
+
] }),
|
|
5539
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
|
|
5540
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-700 dark:text-gray-200", children: title }),
|
|
5541
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500", children: subtitle })
|
|
5542
|
+
] })
|
|
5543
|
+
]
|
|
5544
|
+
}
|
|
5545
|
+
);
|
|
5546
|
+
}
|
|
5547
|
+
var CompactionDoodle_default = CompactionDoodle;
|
|
4745
5548
|
|
|
4746
5549
|
// ui/src/bichat/components/WelcomeContent.tsx
|
|
4747
5550
|
init_useTranslation();
|
|
4748
5551
|
var PROMPT_DEFS = [
|
|
4749
|
-
{ categoryKey: "Welcome.Prompt1Category", textKey: "Welcome.Prompt1Text", icon: ChartBar, defaultCategory: "
|
|
4750
|
-
{ categoryKey: "Welcome.Prompt2Category", textKey: "Welcome.Prompt2Text", icon: FileText, defaultCategory: "
|
|
4751
|
-
{ categoryKey: "Welcome.Prompt3Category", textKey: "Welcome.Prompt3Text", icon: Lightbulb, defaultCategory: "
|
|
5552
|
+
{ categoryKey: "BiChat.Welcome.Prompt1Category", textKey: "BiChat.Welcome.Prompt1Text", icon: ChartBar, defaultCategory: "Data Analysis", defaultText: "Show me a summary of key metrics" },
|
|
5553
|
+
{ categoryKey: "BiChat.Welcome.Prompt2Category", textKey: "BiChat.Welcome.Prompt2Text", icon: FileText, defaultCategory: "Reports", defaultText: "Generate a report for the current period" },
|
|
5554
|
+
{ categoryKey: "BiChat.Welcome.Prompt3Category", textKey: "BiChat.Welcome.Prompt3Text", icon: Lightbulb, defaultCategory: "Insights", defaultText: "What trends can you identify in the data?" }
|
|
4752
5555
|
];
|
|
4753
5556
|
var PROMPT_STYLES = [
|
|
4754
5557
|
{
|
|
@@ -4898,11 +5701,172 @@ function WelcomeContent({
|
|
|
4898
5701
|
);
|
|
4899
5702
|
}) })
|
|
4900
5703
|
] })
|
|
4901
|
-
]
|
|
4902
|
-
}
|
|
4903
|
-
);
|
|
5704
|
+
]
|
|
5705
|
+
}
|
|
5706
|
+
);
|
|
5707
|
+
}
|
|
5708
|
+
var WelcomeContent_default = WelcomeContent;
|
|
5709
|
+
init_useTranslation();
|
|
5710
|
+
var variantStyles = {
|
|
5711
|
+
error: {
|
|
5712
|
+
container: "border-red-200 bg-red-50 dark:bg-red-900/20",
|
|
5713
|
+
title: "text-red-800 dark:text-red-300",
|
|
5714
|
+
message: "text-red-700 dark:text-red-400",
|
|
5715
|
+
icon: "text-red-600 dark:text-red-400",
|
|
5716
|
+
button: "text-red-400 hover:text-red-600 dark:hover:text-red-300",
|
|
5717
|
+
retryButton: "bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-800 text-white",
|
|
5718
|
+
Icon: XCircle
|
|
5719
|
+
},
|
|
5720
|
+
success: {
|
|
5721
|
+
container: "border-green-200 bg-green-50 dark:bg-green-900/20",
|
|
5722
|
+
title: "text-green-800 dark:text-green-300",
|
|
5723
|
+
message: "text-green-700 dark:text-green-400",
|
|
5724
|
+
icon: "text-green-600 dark:text-green-400",
|
|
5725
|
+
button: "text-green-400 hover:text-green-600 dark:hover:text-green-300",
|
|
5726
|
+
retryButton: "bg-green-600 dark:bg-green-700 hover:bg-green-700 dark:hover:bg-green-800 text-white",
|
|
5727
|
+
Icon: CheckCircle
|
|
5728
|
+
},
|
|
5729
|
+
warning: {
|
|
5730
|
+
container: "border-yellow-200 bg-yellow-50 dark:bg-yellow-900/20",
|
|
5731
|
+
title: "text-yellow-800 dark:text-yellow-300",
|
|
5732
|
+
message: "text-yellow-700 dark:text-yellow-400",
|
|
5733
|
+
icon: "text-yellow-600 dark:text-yellow-400",
|
|
5734
|
+
button: "text-yellow-400 hover:text-yellow-600 dark:hover:text-yellow-300",
|
|
5735
|
+
retryButton: "bg-yellow-600 dark:bg-yellow-700 hover:bg-yellow-700 dark:hover:bg-yellow-800 text-white",
|
|
5736
|
+
Icon: Warning
|
|
5737
|
+
},
|
|
5738
|
+
info: {
|
|
5739
|
+
container: "border-blue-200 bg-blue-50 dark:bg-blue-900/20",
|
|
5740
|
+
title: "text-blue-800 dark:text-blue-300",
|
|
5741
|
+
message: "text-blue-700 dark:text-blue-400",
|
|
5742
|
+
icon: "text-blue-600 dark:text-blue-400",
|
|
5743
|
+
button: "text-blue-400 hover:text-blue-600 dark:hover:text-blue-300",
|
|
5744
|
+
retryButton: "bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 text-white",
|
|
5745
|
+
Icon: Info
|
|
5746
|
+
}
|
|
5747
|
+
};
|
|
5748
|
+
function Alert({
|
|
5749
|
+
variant = "info",
|
|
5750
|
+
message,
|
|
5751
|
+
title,
|
|
5752
|
+
onDismiss,
|
|
5753
|
+
onRetry,
|
|
5754
|
+
show = true,
|
|
5755
|
+
dismissible = true
|
|
5756
|
+
}) {
|
|
5757
|
+
const { t } = useTranslation();
|
|
5758
|
+
const styles = variantStyles[variant];
|
|
5759
|
+
const IconComponent = styles.Icon;
|
|
5760
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { children: show && /* @__PURE__ */ jsx(
|
|
5761
|
+
motion.div,
|
|
5762
|
+
{
|
|
5763
|
+
variants: errorMessageVariants,
|
|
5764
|
+
initial: "initial",
|
|
5765
|
+
animate: "animate",
|
|
5766
|
+
exit: "exit",
|
|
5767
|
+
className: `border-t border ${styles.container} px-4 py-3`,
|
|
5768
|
+
role: "alert",
|
|
5769
|
+
"aria-live": "assertive",
|
|
5770
|
+
children: /* @__PURE__ */ jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
5771
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
5772
|
+
/* @__PURE__ */ jsx(IconComponent, { size: 20, className: `w-5 h-5 ${styles.icon} flex-shrink-0 mt-0.5` }),
|
|
5773
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
5774
|
+
title && /* @__PURE__ */ jsx("p", { className: `text-sm ${styles.title} font-medium`, children: title }),
|
|
5775
|
+
/* @__PURE__ */ jsx("p", { className: `text-sm ${styles.message} ${title ? "mt-1" : ""}`, children: message }),
|
|
5776
|
+
onRetry && /* @__PURE__ */ jsx(
|
|
5777
|
+
"button",
|
|
5778
|
+
{
|
|
5779
|
+
onClick: onRetry,
|
|
5780
|
+
className: `mt-2 text-xs px-3 py-1.5 rounded ${styles.retryButton} transition-colors font-medium`,
|
|
5781
|
+
children: t("BiChat.Chat.Retry")
|
|
5782
|
+
}
|
|
5783
|
+
)
|
|
5784
|
+
] })
|
|
5785
|
+
] }),
|
|
5786
|
+
dismissible && onDismiss && /* @__PURE__ */ jsx(
|
|
5787
|
+
"button",
|
|
5788
|
+
{
|
|
5789
|
+
onClick: onDismiss,
|
|
5790
|
+
className: `${styles.button} transition-colors flex-shrink-0`,
|
|
5791
|
+
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
5792
|
+
children: /* @__PURE__ */ jsx(X, { size: 20, className: "w-5 h-5" })
|
|
5793
|
+
}
|
|
5794
|
+
)
|
|
5795
|
+
] })
|
|
5796
|
+
}
|
|
5797
|
+
) });
|
|
5798
|
+
}
|
|
5799
|
+
var Alert_default = memo(Alert);
|
|
5800
|
+
|
|
5801
|
+
// ui/src/bichat/components/ArchiveBanner.tsx
|
|
5802
|
+
init_useTranslation();
|
|
5803
|
+
function ArchiveBanner({
|
|
5804
|
+
show = true,
|
|
5805
|
+
onRestore,
|
|
5806
|
+
restoring = false,
|
|
5807
|
+
onRestoreComplete
|
|
5808
|
+
}) {
|
|
5809
|
+
const { t } = useTranslation();
|
|
5810
|
+
const [error, setError] = useState(null);
|
|
5811
|
+
const handleRestore = async () => {
|
|
5812
|
+
try {
|
|
5813
|
+
setError(null);
|
|
5814
|
+
if (onRestore) {
|
|
5815
|
+
await onRestore();
|
|
5816
|
+
}
|
|
5817
|
+
if (onRestoreComplete) {
|
|
5818
|
+
onRestoreComplete();
|
|
5819
|
+
}
|
|
5820
|
+
} catch (err) {
|
|
5821
|
+
const message = err instanceof Error ? err.message : t("BiChat.Archive.RestoreFailed");
|
|
5822
|
+
setError(message);
|
|
5823
|
+
}
|
|
5824
|
+
};
|
|
5825
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5826
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: show && /* @__PURE__ */ jsx(
|
|
5827
|
+
motion.div,
|
|
5828
|
+
{
|
|
5829
|
+
variants: errorMessageVariants,
|
|
5830
|
+
initial: "initial",
|
|
5831
|
+
animate: "animate",
|
|
5832
|
+
exit: "exit",
|
|
5833
|
+
className: "border-t border border-blue-200 bg-blue-50 dark:bg-blue-900/20 px-4 py-3",
|
|
5834
|
+
role: "region",
|
|
5835
|
+
"aria-label": t("BiChat.Archive.Banner"),
|
|
5836
|
+
children: /* @__PURE__ */ jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
5837
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
5838
|
+
/* @__PURE__ */ jsx(Archive, { size: 20, className: "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" }),
|
|
5839
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-blue-700 dark:text-blue-400", children: t("BiChat.Archive.Archived") }) })
|
|
5840
|
+
] }),
|
|
5841
|
+
/* @__PURE__ */ jsx(
|
|
5842
|
+
"button",
|
|
5843
|
+
{
|
|
5844
|
+
onClick: handleRestore,
|
|
5845
|
+
disabled: restoring,
|
|
5846
|
+
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",
|
|
5847
|
+
"aria-label": t("BiChat.Archive.Restore"),
|
|
5848
|
+
children: restoring ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5849
|
+
/* @__PURE__ */ jsx(Spinner, { size: 16, className: "w-4 h-4 animate-spin" }),
|
|
5850
|
+
t("BiChat.Archive.Restoring")
|
|
5851
|
+
] }) : t("BiChat.Archive.Restore")
|
|
5852
|
+
}
|
|
5853
|
+
)
|
|
5854
|
+
] })
|
|
5855
|
+
}
|
|
5856
|
+
) }),
|
|
5857
|
+
error && /* @__PURE__ */ jsx(
|
|
5858
|
+
Alert_default,
|
|
5859
|
+
{
|
|
5860
|
+
variant: "error",
|
|
5861
|
+
message: error,
|
|
5862
|
+
title: t("BiChat.Archive.RestoreFailed"),
|
|
5863
|
+
onDismiss: () => setError(null),
|
|
5864
|
+
dismissible: true
|
|
5865
|
+
}
|
|
5866
|
+
)
|
|
5867
|
+
] });
|
|
4904
5868
|
}
|
|
4905
|
-
var
|
|
5869
|
+
var ArchiveBanner_default = memo(ArchiveBanner);
|
|
4906
5870
|
|
|
4907
5871
|
// ui/src/bichat/components/ChatSession.tsx
|
|
4908
5872
|
init_useTranslation();
|
|
@@ -4913,11 +5877,11 @@ init_useTranslation();
|
|
|
4913
5877
|
// ui/src/bichat/components/SessionArtifactList.tsx
|
|
4914
5878
|
init_useTranslation();
|
|
4915
5879
|
var TYPE_LABEL_KEYS = {
|
|
4916
|
-
chart: "Artifacts.GroupCharts",
|
|
4917
|
-
code_output: "Artifacts.GroupCodeOutputs",
|
|
4918
|
-
export: "Artifacts.GroupExports",
|
|
4919
|
-
attachment: "Artifacts.GroupAttachments",
|
|
4920
|
-
other: "Artifacts.GroupOther"
|
|
5880
|
+
chart: "BiChat.Artifacts.GroupCharts",
|
|
5881
|
+
code_output: "BiChat.Artifacts.GroupCodeOutputs",
|
|
5882
|
+
export: "BiChat.Artifacts.GroupExports",
|
|
5883
|
+
attachment: "BiChat.Artifacts.GroupAttachments",
|
|
5884
|
+
other: "BiChat.Artifacts.GroupOther"
|
|
4921
5885
|
};
|
|
4922
5886
|
function getGroupIcon(type) {
|
|
4923
5887
|
const cls = "h-3.5 w-3.5";
|
|
@@ -4993,7 +5957,8 @@ function SessionArtifactList({
|
|
|
4993
5957
|
/* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-xl bg-gray-100 dark:bg-gray-800", children: /* @__PURE__ */ jsx(Package, { className: "h-6 w-6 text-gray-400 dark:text-gray-500", weight: "duotone" }) }),
|
|
4994
5958
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4995
5959
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-500 dark:text-gray-400", children: t("BiChat.Artifacts.Empty") }),
|
|
4996
|
-
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptySubtitle") })
|
|
5960
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptySubtitle") }),
|
|
5961
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Artifacts.EmptyHint") })
|
|
4997
5962
|
] })
|
|
4998
5963
|
] });
|
|
4999
5964
|
}
|
|
@@ -5685,6 +6650,29 @@ function SessionArtifactsPanel({
|
|
|
5685
6650
|
},
|
|
5686
6651
|
[canDeleteArtifact, dataSource]
|
|
5687
6652
|
);
|
|
6653
|
+
const fileInputRef = useRef(null);
|
|
6654
|
+
const handleAttachClick = useCallback(() => {
|
|
6655
|
+
fileInputRef.current?.click();
|
|
6656
|
+
}, []);
|
|
6657
|
+
const handleFileInputChange = useCallback(
|
|
6658
|
+
async (e) => {
|
|
6659
|
+
if (!dataSource.uploadSessionArtifacts || !e.target.files?.length) return;
|
|
6660
|
+
const files = Array.from(e.target.files);
|
|
6661
|
+
try {
|
|
6662
|
+
const result = await dataSource.uploadSessionArtifacts(sessionId, files);
|
|
6663
|
+
if ((result.artifacts || []).length > 0) {
|
|
6664
|
+
setDropSuccessState();
|
|
6665
|
+
void fetchArtifacts({ reset: true, manual: false });
|
|
6666
|
+
}
|
|
6667
|
+
setError(null);
|
|
6668
|
+
} catch (err) {
|
|
6669
|
+
setError(err instanceof Error ? err.message : tRef.current("BiChat.Artifacts.FailedToLoad"));
|
|
6670
|
+
} finally {
|
|
6671
|
+
e.target.value = "";
|
|
6672
|
+
}
|
|
6673
|
+
},
|
|
6674
|
+
[dataSource, fetchArtifacts, sessionId, setDropSuccessState]
|
|
6675
|
+
);
|
|
5688
6676
|
return /* @__PURE__ */ jsxs(
|
|
5689
6677
|
"aside",
|
|
5690
6678
|
{
|
|
@@ -5712,12 +6700,38 @@ function SessionArtifactsPanel({
|
|
|
5712
6700
|
]
|
|
5713
6701
|
}
|
|
5714
6702
|
) }),
|
|
5715
|
-
/* @__PURE__ */
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
6703
|
+
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between border-b border-gray-200 px-3 py-2 dark:border-gray-700/80", children: [
|
|
6704
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs("h2", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: [
|
|
6705
|
+
t("BiChat.Artifacts.Title"),
|
|
6706
|
+
" (",
|
|
6707
|
+
artifacts.length,
|
|
6708
|
+
")"
|
|
6709
|
+
] }) }),
|
|
6710
|
+
canDropFiles && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6711
|
+
/* @__PURE__ */ jsx(
|
|
6712
|
+
"button",
|
|
6713
|
+
{
|
|
6714
|
+
type: "button",
|
|
6715
|
+
onClick: handleAttachClick,
|
|
6716
|
+
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",
|
|
6717
|
+
"aria-label": t("BiChat.Artifacts.AttachFiles"),
|
|
6718
|
+
title: t("BiChat.Artifacts.AttachFiles"),
|
|
6719
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4", weight: "bold" })
|
|
6720
|
+
}
|
|
6721
|
+
),
|
|
6722
|
+
/* @__PURE__ */ jsx(
|
|
6723
|
+
"input",
|
|
6724
|
+
{
|
|
6725
|
+
ref: fileInputRef,
|
|
6726
|
+
type: "file",
|
|
6727
|
+
multiple: true,
|
|
6728
|
+
className: "hidden",
|
|
6729
|
+
onChange: handleFileInputChange,
|
|
6730
|
+
"aria-label": t("BiChat.Artifacts.AttachFiles")
|
|
6731
|
+
}
|
|
6732
|
+
)
|
|
6733
|
+
] })
|
|
6734
|
+
] }),
|
|
5721
6735
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-3 py-3", children: fetching ? /* @__PURE__ */ 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__ */ 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: [
|
|
5722
6736
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-300", children: t("BiChat.Artifacts.FailedToLoad") }),
|
|
5723
6737
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-red-700 dark:text-red-400", children: error }),
|
|
@@ -5770,6 +6784,77 @@ function SessionArtifactsPanel({
|
|
|
5770
6784
|
}
|
|
5771
6785
|
);
|
|
5772
6786
|
}
|
|
6787
|
+
|
|
6788
|
+
// ui/src/bichat/components/StreamError.tsx
|
|
6789
|
+
init_useTranslation();
|
|
6790
|
+
function StreamError({
|
|
6791
|
+
error,
|
|
6792
|
+
onRetry,
|
|
6793
|
+
onRegenerate,
|
|
6794
|
+
onDismiss,
|
|
6795
|
+
compact = false
|
|
6796
|
+
}) {
|
|
6797
|
+
const { t } = useTranslation();
|
|
6798
|
+
return /* @__PURE__ */ jsxs(
|
|
6799
|
+
motion.div,
|
|
6800
|
+
{
|
|
6801
|
+
initial: { opacity: 0, y: 10 },
|
|
6802
|
+
animate: { opacity: 1, y: 0 },
|
|
6803
|
+
exit: { opacity: 0, y: -10 },
|
|
6804
|
+
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`,
|
|
6805
|
+
role: "alert",
|
|
6806
|
+
children: [
|
|
6807
|
+
/* @__PURE__ */ 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__ */ jsx(
|
|
6808
|
+
Warning,
|
|
6809
|
+
{
|
|
6810
|
+
className: "w-4 h-4 text-red-600 dark:text-red-400",
|
|
6811
|
+
weight: "fill"
|
|
6812
|
+
}
|
|
6813
|
+
) }),
|
|
6814
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
6815
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200 leading-snug", children: t("BiChat.Error.Generic") }),
|
|
6816
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-red-600/80 dark:text-red-400/70 break-words leading-relaxed", children: error }),
|
|
6817
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
|
|
6818
|
+
onRetry && /* @__PURE__ */ jsxs(
|
|
6819
|
+
"button",
|
|
6820
|
+
{
|
|
6821
|
+
onClick: onRetry,
|
|
6822
|
+
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",
|
|
6823
|
+
type: "button",
|
|
6824
|
+
children: [
|
|
6825
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { className: "w-3.5 h-3.5" }),
|
|
6826
|
+
t("BiChat.StreamError.Retry")
|
|
6827
|
+
]
|
|
6828
|
+
}
|
|
6829
|
+
),
|
|
6830
|
+
onRegenerate && /* @__PURE__ */ jsxs(
|
|
6831
|
+
"button",
|
|
6832
|
+
{
|
|
6833
|
+
onClick: onRegenerate,
|
|
6834
|
+
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",
|
|
6835
|
+
type: "button",
|
|
6836
|
+
children: [
|
|
6837
|
+
/* @__PURE__ */ jsx(ArrowsCounterClockwise, { className: "w-3.5 h-3.5" }),
|
|
6838
|
+
t("BiChat.StreamError.Regenerate")
|
|
6839
|
+
]
|
|
6840
|
+
}
|
|
6841
|
+
)
|
|
6842
|
+
] })
|
|
6843
|
+
] }),
|
|
6844
|
+
onDismiss && /* @__PURE__ */ jsx(
|
|
6845
|
+
"button",
|
|
6846
|
+
{
|
|
6847
|
+
onClick: onDismiss,
|
|
6848
|
+
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",
|
|
6849
|
+
type: "button",
|
|
6850
|
+
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
6851
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
|
|
6852
|
+
}
|
|
6853
|
+
)
|
|
6854
|
+
]
|
|
6855
|
+
}
|
|
6856
|
+
);
|
|
6857
|
+
}
|
|
5773
6858
|
var ARTIFACTS_PANEL_WIDTH_DEFAULT = 352;
|
|
5774
6859
|
var ARTIFACTS_PANEL_WIDTH_MIN = 280;
|
|
5775
6860
|
var ARTIFACTS_PANEL_WIDTH_MAX = 560;
|
|
@@ -5786,13 +6871,34 @@ function ChatSessionCore({
|
|
|
5786
6871
|
actionsSlot,
|
|
5787
6872
|
onBack,
|
|
5788
6873
|
thinkingVerbs,
|
|
6874
|
+
onSessionRestored,
|
|
5789
6875
|
showArtifactsPanel = false,
|
|
5790
6876
|
artifactsPanelDefaultExpanded = false,
|
|
5791
6877
|
artifactsPanelStorageKey = "bichat.artifacts-panel.expanded"
|
|
5792
6878
|
}) {
|
|
5793
6879
|
const { t } = useTranslation();
|
|
5794
|
-
const {
|
|
5795
|
-
|
|
6880
|
+
const {
|
|
6881
|
+
session,
|
|
6882
|
+
fetching,
|
|
6883
|
+
error,
|
|
6884
|
+
errorRetryable,
|
|
6885
|
+
debugMode,
|
|
6886
|
+
sessionDebugUsage,
|
|
6887
|
+
debugLimits,
|
|
6888
|
+
currentSessionId,
|
|
6889
|
+
setError,
|
|
6890
|
+
retryFetchSession
|
|
6891
|
+
} = useChatSession();
|
|
6892
|
+
const {
|
|
6893
|
+
turns,
|
|
6894
|
+
loading,
|
|
6895
|
+
isStreaming,
|
|
6896
|
+
streamError,
|
|
6897
|
+
streamErrorRetryable,
|
|
6898
|
+
isCompacting,
|
|
6899
|
+
retryLastMessage,
|
|
6900
|
+
clearStreamError
|
|
6901
|
+
} = useChatMessaging();
|
|
5796
6902
|
const {
|
|
5797
6903
|
inputError,
|
|
5798
6904
|
message,
|
|
@@ -5800,9 +6906,23 @@ function ChatSessionCore({
|
|
|
5800
6906
|
setInputError,
|
|
5801
6907
|
handleSubmit,
|
|
5802
6908
|
messageQueue,
|
|
5803
|
-
handleUnqueue
|
|
6909
|
+
handleUnqueue,
|
|
6910
|
+
removeQueueItem,
|
|
6911
|
+
updateQueueItem
|
|
5804
6912
|
} = useChatInput();
|
|
5805
|
-
const
|
|
6913
|
+
const isArchived = session?.status === "archived";
|
|
6914
|
+
const effectiveReadOnly = Boolean(readOnly ?? isReadOnly) || isArchived;
|
|
6915
|
+
const [restoring, setRestoring] = useState(false);
|
|
6916
|
+
const handleRestore = useCallback(async () => {
|
|
6917
|
+
if (!session?.id) return;
|
|
6918
|
+
setRestoring(true);
|
|
6919
|
+
try {
|
|
6920
|
+
await dataSource.unarchiveSession(session.id);
|
|
6921
|
+
onSessionRestored?.(session.id);
|
|
6922
|
+
} finally {
|
|
6923
|
+
setRestoring(false);
|
|
6924
|
+
}
|
|
6925
|
+
}, [dataSource, session?.id, onSessionRestored]);
|
|
5806
6926
|
const [artifactsPanelExpanded, setArtifactsPanelExpanded] = useState(
|
|
5807
6927
|
artifactsPanelDefaultExpanded
|
|
5808
6928
|
);
|
|
@@ -5874,16 +6994,9 @@ function ChatSessionCore({
|
|
|
5874
6994
|
document.body.style.userSelect = "";
|
|
5875
6995
|
};
|
|
5876
6996
|
}, [isResizingArtifactsPanel, artifactsPanelStorageKey]);
|
|
5877
|
-
if (fetching) {
|
|
6997
|
+
if (fetching && turns.length === 0 && !session) {
|
|
5878
6998
|
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-gray-500 dark:text-gray-400", children: t("BiChat.Input.Processing") }) });
|
|
5879
6999
|
}
|
|
5880
|
-
if (error) {
|
|
5881
|
-
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-red-500 dark:text-red-400", children: [
|
|
5882
|
-
t("BiChat.Error.Generic"),
|
|
5883
|
-
": ",
|
|
5884
|
-
error
|
|
5885
|
-
] }) });
|
|
5886
|
-
}
|
|
5887
7000
|
const showWelcome = !session && turns.length === 0;
|
|
5888
7001
|
const activeSessionId = session?.id || (currentSessionId && currentSessionId !== "new" ? currentSessionId : void 0);
|
|
5889
7002
|
const supportsArtifactsPanel = typeof dataSource.fetchSessionArtifacts === "function";
|
|
@@ -5940,6 +7053,16 @@ function ChatSessionCore({
|
|
|
5940
7053
|
actionsSlot: headerActions
|
|
5941
7054
|
}
|
|
5942
7055
|
),
|
|
7056
|
+
error && /* @__PURE__ */ jsx(
|
|
7057
|
+
Alert_default,
|
|
7058
|
+
{
|
|
7059
|
+
variant: errorRetryable ? "warning" : "error",
|
|
7060
|
+
title: t("BiChat.Error.Generic"),
|
|
7061
|
+
message: error,
|
|
7062
|
+
onDismiss: () => setError(null),
|
|
7063
|
+
onRetry: errorRetryable ? retryFetchSession : void 0
|
|
7064
|
+
}
|
|
7065
|
+
),
|
|
5943
7066
|
/* @__PURE__ */ jsxs(
|
|
5944
7067
|
"div",
|
|
5945
7068
|
{
|
|
@@ -5948,6 +7071,15 @@ function ChatSessionCore({
|
|
|
5948
7071
|
children: [
|
|
5949
7072
|
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: showWelcome ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col overflow-auto", children: /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center px-4 py-8", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-5xl", children: [
|
|
5950
7073
|
welcomeSlot || /* @__PURE__ */ jsx(WelcomeContent_default, { onPromptSelect: handlePromptSelect, disabled: loading }),
|
|
7074
|
+
streamError && /* @__PURE__ */ jsx("div", { className: "px-6 pt-4", children: /* @__PURE__ */ jsx(
|
|
7075
|
+
StreamError,
|
|
7076
|
+
{
|
|
7077
|
+
error: streamError,
|
|
7078
|
+
compact: true,
|
|
7079
|
+
onRetry: streamErrorRetryable ? () => void retryLastMessage() : void 0,
|
|
7080
|
+
onDismiss: clearStreamError
|
|
7081
|
+
}
|
|
7082
|
+
) }),
|
|
5951
7083
|
!effectiveReadOnly && /* @__PURE__ */ jsx(
|
|
5952
7084
|
MessageInput,
|
|
5953
7085
|
{
|
|
@@ -5963,12 +7095,22 @@ function ChatSessionCore({
|
|
|
5963
7095
|
onSubmit: handleSubmit,
|
|
5964
7096
|
messageQueue,
|
|
5965
7097
|
onUnqueue: handleUnqueue,
|
|
7098
|
+
onRemoveQueueItem: removeQueueItem,
|
|
7099
|
+
onUpdateQueueItem: updateQueueItem,
|
|
5966
7100
|
containerClassName: "pt-6 px-6",
|
|
5967
7101
|
formClassName: "mx-auto"
|
|
5968
7102
|
}
|
|
5969
7103
|
),
|
|
5970
7104
|
/* @__PURE__ */ jsx("p", { className: "mt-4 pb-1 text-center text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Welcome.Disclaimer") })
|
|
5971
7105
|
] }) }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7106
|
+
isArchived && /* @__PURE__ */ jsx(
|
|
7107
|
+
ArchiveBanner_default,
|
|
7108
|
+
{
|
|
7109
|
+
show: true,
|
|
7110
|
+
onRestore: handleRestore,
|
|
7111
|
+
restoring
|
|
7112
|
+
}
|
|
7113
|
+
),
|
|
5972
7114
|
/* @__PURE__ */ jsx(
|
|
5973
7115
|
MessageList,
|
|
5974
7116
|
{
|
|
@@ -5978,6 +7120,22 @@ function ChatSessionCore({
|
|
|
5978
7120
|
readOnly: effectiveReadOnly
|
|
5979
7121
|
}
|
|
5980
7122
|
),
|
|
7123
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isCompacting && /* @__PURE__ */ jsx("div", { className: "flex justify-center px-4 pb-2", children: /* @__PURE__ */ jsx(
|
|
7124
|
+
CompactionDoodle_default,
|
|
7125
|
+
{
|
|
7126
|
+
title: t("BiChat.Slash.CompactingTitle"),
|
|
7127
|
+
subtitle: t("BiChat.Slash.CompactingSubtitle")
|
|
7128
|
+
}
|
|
7129
|
+
) }) }),
|
|
7130
|
+
streamError && /* @__PURE__ */ jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx(
|
|
7131
|
+
StreamError,
|
|
7132
|
+
{
|
|
7133
|
+
error: streamError,
|
|
7134
|
+
compact: true,
|
|
7135
|
+
onRetry: streamErrorRetryable ? () => void retryLastMessage() : void 0,
|
|
7136
|
+
onDismiss: clearStreamError
|
|
7137
|
+
}
|
|
7138
|
+
) }),
|
|
5981
7139
|
!effectiveReadOnly && /* @__PURE__ */ jsx(
|
|
5982
7140
|
MessageInput,
|
|
5983
7141
|
{
|
|
@@ -5992,7 +7150,9 @@ function ChatSessionCore({
|
|
|
5992
7150
|
onMessageChange: setMessage,
|
|
5993
7151
|
onSubmit: handleSubmit,
|
|
5994
7152
|
messageQueue,
|
|
5995
|
-
onUnqueue: handleUnqueue
|
|
7153
|
+
onUnqueue: handleUnqueue,
|
|
7154
|
+
onRemoveQueueItem: removeQueueItem,
|
|
7155
|
+
onUpdateQueueItem: updateQueueItem
|
|
5996
7156
|
}
|
|
5997
7157
|
)
|
|
5998
7158
|
] }) }),
|
|
@@ -6083,8 +7243,17 @@ function ChatSessionCore({
|
|
|
6083
7243
|
);
|
|
6084
7244
|
}
|
|
6085
7245
|
function ChatSession(props) {
|
|
6086
|
-
const { dataSource, sessionId, rateLimiter, ...coreProps } = props;
|
|
6087
|
-
return /* @__PURE__ */ jsx(
|
|
7246
|
+
const { dataSource, sessionId, rateLimiter, onSessionCreated, ...coreProps } = props;
|
|
7247
|
+
return /* @__PURE__ */ jsx(
|
|
7248
|
+
ChatSessionProvider,
|
|
7249
|
+
{
|
|
7250
|
+
dataSource,
|
|
7251
|
+
sessionId,
|
|
7252
|
+
rateLimiter,
|
|
7253
|
+
onSessionCreated,
|
|
7254
|
+
children: /* @__PURE__ */ jsx(ChatSessionCore, { dataSource, ...coreProps })
|
|
7255
|
+
}
|
|
7256
|
+
);
|
|
6088
7257
|
}
|
|
6089
7258
|
|
|
6090
7259
|
// ui/src/bichat/index.ts
|
|
@@ -6186,20 +7355,19 @@ var EditableText = forwardRef(
|
|
|
6186
7355
|
onSave,
|
|
6187
7356
|
maxLength = 100,
|
|
6188
7357
|
isLoading = false,
|
|
6189
|
-
placeholder
|
|
7358
|
+
placeholder,
|
|
6190
7359
|
className = "",
|
|
6191
7360
|
inputClassName = "",
|
|
6192
7361
|
size = "sm"
|
|
6193
7362
|
}, ref) => {
|
|
6194
7363
|
const { t } = useTranslation();
|
|
7364
|
+
const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Untitled");
|
|
6195
7365
|
const [isEditing, setIsEditing] = useState(false);
|
|
6196
7366
|
const [editValue, setEditValue] = useState(value);
|
|
6197
7367
|
const inputRef = useRef(null);
|
|
6198
7368
|
useImperativeHandle(ref, () => ({
|
|
6199
7369
|
startEditing: () => {
|
|
6200
|
-
|
|
6201
|
-
setIsEditing(true);
|
|
6202
|
-
}
|
|
7370
|
+
setIsEditing(true);
|
|
6203
7371
|
},
|
|
6204
7372
|
cancelEditing: () => {
|
|
6205
7373
|
setEditValue(value);
|
|
@@ -6241,9 +7409,7 @@ var EditableText = forwardRef(
|
|
|
6241
7409
|
}
|
|
6242
7410
|
};
|
|
6243
7411
|
const handleDoubleClick = () => {
|
|
6244
|
-
|
|
6245
|
-
setIsEditing(true);
|
|
6246
|
-
}
|
|
7412
|
+
setIsEditing(true);
|
|
6247
7413
|
};
|
|
6248
7414
|
const handleBlur = () => {
|
|
6249
7415
|
handleSave();
|
|
@@ -6265,7 +7431,7 @@ var EditableText = forwardRef(
|
|
|
6265
7431
|
onKeyDown: handleKeyDown,
|
|
6266
7432
|
onBlur: handleBlur,
|
|
6267
7433
|
maxLength,
|
|
6268
|
-
placeholder,
|
|
7434
|
+
placeholder: resolvedPlaceholder,
|
|
6269
7435
|
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}`,
|
|
6270
7436
|
"aria-label": t("BiChat.EditableText.AriaLabel")
|
|
6271
7437
|
}
|
|
@@ -6273,7 +7439,7 @@ var EditableText = forwardRef(
|
|
|
6273
7439
|
}
|
|
6274
7440
|
);
|
|
6275
7441
|
}
|
|
6276
|
-
const displayValue = value ||
|
|
7442
|
+
const displayValue = value || resolvedPlaceholder;
|
|
6277
7443
|
return /* @__PURE__ */ jsx(
|
|
6278
7444
|
"span",
|
|
6279
7445
|
{
|
|
@@ -6321,16 +7487,18 @@ var sizeClasses3 = {
|
|
|
6321
7487
|
function SearchInput({
|
|
6322
7488
|
value,
|
|
6323
7489
|
onChange,
|
|
6324
|
-
placeholder
|
|
7490
|
+
placeholder,
|
|
6325
7491
|
autoFocus = false,
|
|
6326
7492
|
onSubmit,
|
|
6327
7493
|
onEscape,
|
|
6328
7494
|
className = "",
|
|
6329
7495
|
size = "md",
|
|
6330
7496
|
disabled = false,
|
|
6331
|
-
ariaLabel
|
|
7497
|
+
ariaLabel
|
|
6332
7498
|
}) {
|
|
6333
7499
|
const { t } = useTranslation();
|
|
7500
|
+
const resolvedPlaceholder = placeholder ?? t("BiChat.Common.Search");
|
|
7501
|
+
const resolvedAriaLabel = ariaLabel ?? t("BiChat.Common.Search");
|
|
6334
7502
|
const inputRef = useRef(null);
|
|
6335
7503
|
const sizes = sizeClasses3[size];
|
|
6336
7504
|
useEffect(() => {
|
|
@@ -6373,10 +7541,10 @@ function SearchInput({
|
|
|
6373
7541
|
value,
|
|
6374
7542
|
onChange: (e) => onChange(e.target.value),
|
|
6375
7543
|
onKeyDown: handleKeyDown,
|
|
6376
|
-
placeholder,
|
|
7544
|
+
placeholder: resolvedPlaceholder,
|
|
6377
7545
|
disabled,
|
|
6378
7546
|
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`,
|
|
6379
|
-
"aria-label":
|
|
7547
|
+
"aria-label": resolvedAriaLabel
|
|
6380
7548
|
}
|
|
6381
7549
|
),
|
|
6382
7550
|
value && !disabled && /* @__PURE__ */ jsx(
|
|
@@ -6576,26 +7744,37 @@ var LoadingSpinner_default = MemoizedLoadingSpinner;
|
|
|
6576
7744
|
// ui/src/bichat/index.ts
|
|
6577
7745
|
init_TableExportButton();
|
|
6578
7746
|
init_TableWithExport();
|
|
7747
|
+
|
|
7748
|
+
// ui/src/bichat/components/Toast.tsx
|
|
7749
|
+
init_useTranslation();
|
|
6579
7750
|
var typeConfig = {
|
|
6580
7751
|
success: {
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
icon:
|
|
7752
|
+
accent: "text-emerald-600 dark:text-emerald-400",
|
|
7753
|
+
bg: "bg-emerald-50 dark:bg-emerald-950/40 border-emerald-200/80 dark:border-emerald-800/50",
|
|
7754
|
+
icon: "bg-emerald-100 dark:bg-emerald-900/50",
|
|
7755
|
+
progress: "bg-emerald-500 dark:bg-emerald-400",
|
|
7756
|
+
iconEl: CheckCircle
|
|
6584
7757
|
},
|
|
6585
7758
|
error: {
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
icon:
|
|
7759
|
+
accent: "text-red-600 dark:text-red-400",
|
|
7760
|
+
bg: "bg-red-50 dark:bg-red-950/40 border-red-200/80 dark:border-red-800/50",
|
|
7761
|
+
icon: "bg-red-100 dark:bg-red-900/50",
|
|
7762
|
+
progress: "bg-red-500 dark:bg-red-400",
|
|
7763
|
+
iconEl: XCircle
|
|
6589
7764
|
},
|
|
6590
7765
|
info: {
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
icon:
|
|
7766
|
+
accent: "text-blue-600 dark:text-blue-400",
|
|
7767
|
+
bg: "bg-blue-50 dark:bg-blue-950/40 border-blue-200/80 dark:border-blue-800/50",
|
|
7768
|
+
icon: "bg-blue-100 dark:bg-blue-900/50",
|
|
7769
|
+
progress: "bg-blue-500 dark:bg-blue-400",
|
|
7770
|
+
iconEl: Info
|
|
6594
7771
|
},
|
|
6595
7772
|
warning: {
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
icon:
|
|
7773
|
+
accent: "text-amber-600 dark:text-amber-400",
|
|
7774
|
+
bg: "bg-amber-50 dark:bg-amber-950/40 border-amber-200/80 dark:border-amber-800/50",
|
|
7775
|
+
icon: "bg-amber-100 dark:bg-amber-900/50",
|
|
7776
|
+
progress: "bg-amber-500 dark:bg-amber-400",
|
|
7777
|
+
iconEl: Warning
|
|
6599
7778
|
}
|
|
6600
7779
|
};
|
|
6601
7780
|
function Toast({
|
|
@@ -6604,55 +7783,117 @@ function Toast({
|
|
|
6604
7783
|
message,
|
|
6605
7784
|
duration = 5e3,
|
|
6606
7785
|
onDismiss,
|
|
6607
|
-
dismissLabel
|
|
7786
|
+
dismissLabel
|
|
6608
7787
|
}) {
|
|
7788
|
+
const { t } = useTranslation();
|
|
7789
|
+
const resolvedDismissLabel = dismissLabel ?? t("BiChat.Chat.DismissNotification");
|
|
6609
7790
|
const config = typeConfig[type];
|
|
7791
|
+
const Icon = config.iconEl;
|
|
7792
|
+
const [show, setShow] = useState(false);
|
|
7793
|
+
const [paused, setPaused] = useState(false);
|
|
7794
|
+
const remainingRef = useRef(duration);
|
|
7795
|
+
const startRef = useRef(Date.now());
|
|
7796
|
+
useEffect(() => {
|
|
7797
|
+
const frame = requestAnimationFrame(() => setShow(true));
|
|
7798
|
+
return () => cancelAnimationFrame(frame);
|
|
7799
|
+
}, []);
|
|
7800
|
+
useEffect(() => {
|
|
7801
|
+
if (paused) return;
|
|
7802
|
+
startRef.current = Date.now();
|
|
7803
|
+
const timer = setTimeout(() => {
|
|
7804
|
+
setShow(false);
|
|
7805
|
+
setTimeout(() => onDismiss(id), 200);
|
|
7806
|
+
}, remainingRef.current);
|
|
7807
|
+
return () => {
|
|
7808
|
+
const elapsed = Date.now() - startRef.current;
|
|
7809
|
+
remainingRef.current = Math.max(0, remainingRef.current - elapsed);
|
|
7810
|
+
clearTimeout(timer);
|
|
7811
|
+
};
|
|
7812
|
+
}, [id, paused, onDismiss]);
|
|
7813
|
+
const handleDismiss = useCallback(() => {
|
|
7814
|
+
setShow(false);
|
|
7815
|
+
setTimeout(() => onDismiss(id), 200);
|
|
7816
|
+
}, [id, onDismiss]);
|
|
6610
7817
|
const ariaLive = type === "error" ? "assertive" : "polite";
|
|
6611
7818
|
const role = type === "error" || type === "warning" ? "alert" : "status";
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
return () => clearTimeout(timer);
|
|
6615
|
-
}, [id, duration, onDismiss]);
|
|
6616
|
-
return /* @__PURE__ */ jsxs(
|
|
6617
|
-
motion.div,
|
|
7819
|
+
return /* @__PURE__ */ jsx(
|
|
7820
|
+
Transition,
|
|
6618
7821
|
{
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
"
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
"
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
children: /* @__PURE__ */ jsx(
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
7822
|
+
show,
|
|
7823
|
+
enter: "transition duration-200 ease-out",
|
|
7824
|
+
enterFrom: "-translate-y-2 opacity-0 scale-95",
|
|
7825
|
+
enterTo: "translate-y-0 opacity-100 scale-100",
|
|
7826
|
+
leave: "transition duration-150 ease-in",
|
|
7827
|
+
leaveFrom: "translate-y-0 opacity-100 scale-100",
|
|
7828
|
+
leaveTo: "-translate-y-2 opacity-0 scale-95",
|
|
7829
|
+
children: /* @__PURE__ */ jsxs(
|
|
7830
|
+
"div",
|
|
7831
|
+
{
|
|
7832
|
+
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}`,
|
|
7833
|
+
role,
|
|
7834
|
+
"aria-live": ariaLive,
|
|
7835
|
+
"aria-atomic": "true",
|
|
7836
|
+
onMouseEnter: () => setPaused(true),
|
|
7837
|
+
onMouseLeave: () => setPaused(false),
|
|
7838
|
+
children: [
|
|
7839
|
+
/* @__PURE__ */ jsx("div", { className: `mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg ${config.icon}`, children: /* @__PURE__ */ jsx(Icon, { size: 16, className: config.accent, weight: "fill" }) }),
|
|
7840
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 pt-0.5 text-sm font-medium leading-snug text-gray-800 dark:text-gray-100", children: message }),
|
|
7841
|
+
/* @__PURE__ */ jsx(
|
|
7842
|
+
"button",
|
|
7843
|
+
{
|
|
7844
|
+
onClick: handleDismiss,
|
|
7845
|
+
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",
|
|
7846
|
+
"aria-label": resolvedDismissLabel,
|
|
7847
|
+
children: /* @__PURE__ */ jsx(X, { size: 14, weight: "bold" })
|
|
7848
|
+
}
|
|
7849
|
+
),
|
|
7850
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 bottom-0 h-0.5 bg-black/5 dark:bg-white/5", children: /* @__PURE__ */ jsx(
|
|
7851
|
+
"div",
|
|
7852
|
+
{
|
|
7853
|
+
className: `h-full ${config.progress} origin-left`,
|
|
7854
|
+
style: {
|
|
7855
|
+
animation: `bichat-toast-progress ${duration}ms linear forwards`,
|
|
7856
|
+
animationPlayState: paused ? "paused" : "running"
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
) })
|
|
7860
|
+
]
|
|
7861
|
+
}
|
|
7862
|
+
)
|
|
6640
7863
|
}
|
|
6641
7864
|
);
|
|
6642
7865
|
}
|
|
7866
|
+
|
|
7867
|
+
// ui/src/bichat/components/ToastContainer.tsx
|
|
7868
|
+
init_useTranslation();
|
|
6643
7869
|
function ToastContainer({ toasts, onDismiss, dismissLabel }) {
|
|
6644
|
-
|
|
7870
|
+
const { t } = useTranslation();
|
|
7871
|
+
if (toasts.length === 0) return null;
|
|
7872
|
+
return /* @__PURE__ */ jsx(
|
|
7873
|
+
"div",
|
|
7874
|
+
{
|
|
7875
|
+
"aria-label": t("BiChat.Common.Notifications"),
|
|
7876
|
+
className: "fixed top-6 right-6 z-[var(--bichat-z-toast,60)] flex flex-col gap-2 pointer-events-none",
|
|
7877
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(Toast, { ...toast, onDismiss, dismissLabel }) }, toast.id))
|
|
7878
|
+
}
|
|
7879
|
+
);
|
|
6645
7880
|
}
|
|
7881
|
+
|
|
7882
|
+
// ui/src/bichat/components/ConfirmModal.tsx
|
|
7883
|
+
init_useTranslation();
|
|
6646
7884
|
function ConfirmModalBase({
|
|
6647
7885
|
isOpen,
|
|
6648
7886
|
title,
|
|
6649
7887
|
message,
|
|
6650
7888
|
onConfirm,
|
|
6651
7889
|
onCancel,
|
|
6652
|
-
confirmText
|
|
6653
|
-
cancelText
|
|
7890
|
+
confirmText,
|
|
7891
|
+
cancelText,
|
|
6654
7892
|
isDanger = false
|
|
6655
7893
|
}) {
|
|
7894
|
+
const { t } = useTranslation();
|
|
7895
|
+
const resolvedConfirmText = confirmText ?? t("BiChat.Common.Confirm");
|
|
7896
|
+
const resolvedCancelText = cancelText ?? t("BiChat.Common.Cancel");
|
|
6656
7897
|
return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onClose: onCancel, className: "relative z-40", children: [
|
|
6657
7898
|
/* @__PURE__ */ jsx(DialogBackdrop, { className: "fixed inset-0 bg-black/40 dark:bg-black/60 backdrop-blur-sm transition-opacity duration-200" }),
|
|
6658
7899
|
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 flex items-center justify-center z-50 p-4", children: /* @__PURE__ */ jsxs(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: [
|
|
@@ -6669,9 +7910,9 @@ function ConfirmModalBase({
|
|
|
6669
7910
|
{
|
|
6670
7911
|
onClick: onCancel,
|
|
6671
7912
|
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",
|
|
6672
|
-
"aria-label":
|
|
7913
|
+
"aria-label": resolvedCancelText,
|
|
6673
7914
|
"data-testid": "confirm-modal-cancel",
|
|
6674
|
-
children:
|
|
7915
|
+
children: resolvedCancelText
|
|
6675
7916
|
}
|
|
6676
7917
|
),
|
|
6677
7918
|
/* @__PURE__ */ jsx(
|
|
@@ -6684,9 +7925,9 @@ function ConfirmModalBase({
|
|
|
6684
7925
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-800",
|
|
6685
7926
|
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"
|
|
6686
7927
|
].join(" "),
|
|
6687
|
-
"aria-label":
|
|
7928
|
+
"aria-label": resolvedConfirmText,
|
|
6688
7929
|
"data-testid": "confirm-modal-confirm",
|
|
6689
|
-
children:
|
|
7930
|
+
children: resolvedConfirmText
|
|
6690
7931
|
}
|
|
6691
7932
|
)
|
|
6692
7933
|
] })
|
|
@@ -6776,28 +8017,44 @@ function PermissionGuard({
|
|
|
6776
8017
|
const permitted = mode === "all" ? permissions.every((p) => hasPermission2(p)) : permissions.some((p) => hasPermission2(p));
|
|
6777
8018
|
return permitted ? /* @__PURE__ */ jsx(Fragment, { children }) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
6778
8019
|
}
|
|
8020
|
+
|
|
8021
|
+
// ui/src/bichat/components/ErrorBoundary.tsx
|
|
8022
|
+
init_useTranslation();
|
|
6779
8023
|
function DefaultErrorContent({
|
|
6780
8024
|
error,
|
|
6781
8025
|
onReset,
|
|
6782
|
-
resetLabel
|
|
6783
|
-
errorTitle
|
|
8026
|
+
resetLabel,
|
|
8027
|
+
errorTitle
|
|
6784
8028
|
}) {
|
|
8029
|
+
const { t } = useTranslation();
|
|
8030
|
+
const resolvedResetLabel = resetLabel ?? t("BiChat.Common.TryAgain");
|
|
8031
|
+
const resolvedErrorTitle = errorTitle ?? t("BiChat.Error.SomethingWentWrong");
|
|
6785
8032
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: [
|
|
6786
|
-
/* @__PURE__ */ jsx(
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
className: "flex items-center
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
8033
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 overflow-hidden pointer-events-none opacity-[0.03] dark:opacity-[0.04]", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("svg", { className: "absolute -top-8 -right-8 w-64 h-64 text-red-500", viewBox: "0 0 200 200", fill: "currentColor", children: [
|
|
8034
|
+
/* @__PURE__ */ jsx("circle", { cx: "100", cy: "100", r: "80", opacity: "0.5" }),
|
|
8035
|
+
/* @__PURE__ */ jsx("circle", { cx: "100", cy: "100", r: "50", opacity: "0.3" }),
|
|
8036
|
+
/* @__PURE__ */ jsx("circle", { cx: "100", cy: "100", r: "25", opacity: "0.2" })
|
|
8037
|
+
] }) }),
|
|
8038
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
|
|
8039
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-5", children: [
|
|
8040
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-red-100 dark:bg-red-900/30 scale-150 blur-md" }),
|
|
8041
|
+
/* @__PURE__ */ 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__ */ jsx(WarningCircle, { size: 28, className: "text-red-500 dark:text-red-400", weight: "fill" }) })
|
|
8042
|
+
] }),
|
|
8043
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-1.5", children: resolvedErrorTitle }),
|
|
8044
|
+
/* @__PURE__ */ 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") }),
|
|
8045
|
+
onReset && /* @__PURE__ */ jsxs(
|
|
8046
|
+
"button",
|
|
8047
|
+
{
|
|
8048
|
+
type: "button",
|
|
8049
|
+
onClick: onReset,
|
|
8050
|
+
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",
|
|
8051
|
+
children: [
|
|
8052
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
8053
|
+
resolvedResetLabel
|
|
8054
|
+
]
|
|
8055
|
+
}
|
|
8056
|
+
)
|
|
8057
|
+
] })
|
|
6801
8058
|
] });
|
|
6802
8059
|
}
|
|
6803
8060
|
var ErrorBoundary = class extends Component {
|
|
@@ -6934,10 +8191,13 @@ var TouchContextMenu = ({
|
|
|
6934
8191
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
6935
8192
|
const menuRef = useRef(null);
|
|
6936
8193
|
const itemRefs = useRef([]);
|
|
6937
|
-
const enabledIndices =
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
8194
|
+
const enabledIndices = useMemo(
|
|
8195
|
+
() => items.reduce((acc, item, i) => {
|
|
8196
|
+
if (!item.disabled) acc.push(i);
|
|
8197
|
+
return acc;
|
|
8198
|
+
}, []),
|
|
8199
|
+
[items]
|
|
8200
|
+
);
|
|
6941
8201
|
const focusItem = useCallback((index) => {
|
|
6942
8202
|
setFocusedIndex(index);
|
|
6943
8203
|
itemRefs.current[index]?.focus();
|
|
@@ -6953,7 +8213,7 @@ var TouchContextMenu = ({
|
|
|
6953
8213
|
}
|
|
6954
8214
|
});
|
|
6955
8215
|
return () => cancelAnimationFrame(timer);
|
|
6956
|
-
}, [isOpen, enabledIndices
|
|
8216
|
+
}, [isOpen, enabledIndices, focusItem]);
|
|
6957
8217
|
useEffect(() => {
|
|
6958
8218
|
if (!isOpen) return;
|
|
6959
8219
|
const handleKeyDown = (e) => {
|
|
@@ -7251,7 +8511,7 @@ var SessionItem = memo(
|
|
|
7251
8511
|
MenuItems,
|
|
7252
8512
|
{
|
|
7253
8513
|
anchor: "bottom start",
|
|
7254
|
-
className: "w-52 bg-white
|
|
8514
|
+
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",
|
|
7255
8515
|
children: [
|
|
7256
8516
|
mode !== "archived" && onPin && /* @__PURE__ */ jsx(MenuItem, { children: ({ focus }) => /* @__PURE__ */ jsxs(
|
|
7257
8517
|
"button",
|
|
@@ -7377,108 +8637,18 @@ var SessionItem = memo(
|
|
|
7377
8637
|
onClose: () => setMenuOpen(false),
|
|
7378
8638
|
anchorRect: menuAnchor
|
|
7379
8639
|
}
|
|
7380
|
-
)
|
|
7381
|
-
] });
|
|
7382
|
-
}
|
|
7383
|
-
);
|
|
7384
|
-
SessionItem.displayName = "SessionItem";
|
|
7385
|
-
var SessionItem_default = SessionItem;
|
|
7386
|
-
function DateGroupHeader({ groupName, count }) {
|
|
7387
|
-
return /* @__PURE__ */ 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__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
7388
|
-
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300 font-semibold", children: groupName }),
|
|
7389
|
-
/* @__PURE__ */ 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 })
|
|
7390
|
-
] }) });
|
|
7391
|
-
}
|
|
7392
|
-
function TabBar({ tabs, activeTab, onTabChange }) {
|
|
7393
|
-
const tablistRef = useRef(null);
|
|
7394
|
-
const handleKeyDown = useCallback(
|
|
7395
|
-
(e) => {
|
|
7396
|
-
const currentIndex = tabs.findIndex((tab) => tab.id === activeTab);
|
|
7397
|
-
if (currentIndex < 0) return;
|
|
7398
|
-
let nextIndex = null;
|
|
7399
|
-
switch (e.key) {
|
|
7400
|
-
case "ArrowRight":
|
|
7401
|
-
e.preventDefault();
|
|
7402
|
-
nextIndex = (currentIndex + 1) % tabs.length;
|
|
7403
|
-
break;
|
|
7404
|
-
case "ArrowLeft":
|
|
7405
|
-
e.preventDefault();
|
|
7406
|
-
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
|
7407
|
-
break;
|
|
7408
|
-
case "Home":
|
|
7409
|
-
e.preventDefault();
|
|
7410
|
-
nextIndex = 0;
|
|
7411
|
-
break;
|
|
7412
|
-
case "End":
|
|
7413
|
-
e.preventDefault();
|
|
7414
|
-
nextIndex = tabs.length - 1;
|
|
7415
|
-
break;
|
|
7416
|
-
}
|
|
7417
|
-
if (nextIndex !== null) {
|
|
7418
|
-
onTabChange(tabs[nextIndex].id);
|
|
7419
|
-
const tablist = tablistRef.current;
|
|
7420
|
-
if (tablist) {
|
|
7421
|
-
const buttons = tablist.querySelectorAll('[role="tab"]');
|
|
7422
|
-
buttons[nextIndex]?.focus();
|
|
7423
|
-
}
|
|
7424
|
-
}
|
|
7425
|
-
},
|
|
7426
|
-
[tabs, activeTab, onTabChange]
|
|
7427
|
-
);
|
|
7428
|
-
if (tabs.length === 0) {
|
|
7429
|
-
return null;
|
|
8640
|
+
)
|
|
8641
|
+
] });
|
|
7430
8642
|
}
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
TabButton,
|
|
7440
|
-
{
|
|
7441
|
-
id: tab.id,
|
|
7442
|
-
label: tab.label,
|
|
7443
|
-
isActive: activeTab === tab.id,
|
|
7444
|
-
onClick: () => onTabChange(tab.id)
|
|
7445
|
-
},
|
|
7446
|
-
tab.id
|
|
7447
|
-
))
|
|
7448
|
-
}
|
|
7449
|
-
);
|
|
7450
|
-
}
|
|
7451
|
-
function TabButton({ id, label, isActive, onClick }) {
|
|
7452
|
-
return /* @__PURE__ */ jsxs(
|
|
7453
|
-
"button",
|
|
7454
|
-
{
|
|
7455
|
-
id,
|
|
7456
|
-
role: "tab",
|
|
7457
|
-
"aria-selected": isActive,
|
|
7458
|
-
"aria-controls": `${id}-panel`,
|
|
7459
|
-
tabIndex: isActive ? 0 : -1,
|
|
7460
|
-
onClick,
|
|
7461
|
-
className: `
|
|
7462
|
-
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
|
|
7463
|
-
${isActive ? "text-primary-700 dark:text-primary-400" : "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}
|
|
7464
|
-
`,
|
|
7465
|
-
children: [
|
|
7466
|
-
label,
|
|
7467
|
-
isActive && /* @__PURE__ */ jsx(
|
|
7468
|
-
motion.div,
|
|
7469
|
-
{
|
|
7470
|
-
layoutId: "activeTab",
|
|
7471
|
-
className: "absolute bottom-0 left-0 right-0 h-0.5 bg-primary-600 dark:bg-primary-500",
|
|
7472
|
-
transition: { duration: 0.2, ease: [0.4, 0, 0.2, 1] }
|
|
7473
|
-
}
|
|
7474
|
-
)
|
|
7475
|
-
]
|
|
7476
|
-
}
|
|
7477
|
-
);
|
|
8643
|
+
);
|
|
8644
|
+
SessionItem.displayName = "SessionItem";
|
|
8645
|
+
var SessionItem_default = SessionItem;
|
|
8646
|
+
function DateGroupHeader({ groupName, count }) {
|
|
8647
|
+
return /* @__PURE__ */ 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__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
8648
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300 font-semibold", children: groupName }),
|
|
8649
|
+
/* @__PURE__ */ 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 })
|
|
8650
|
+
] }) });
|
|
7478
8651
|
}
|
|
7479
|
-
var MemoizedTabBar = memo(TabBar);
|
|
7480
|
-
MemoizedTabBar.displayName = "TabBar";
|
|
7481
|
-
var TabBar_default = MemoizedTabBar;
|
|
7482
8652
|
init_useTranslation();
|
|
7483
8653
|
function UserFilter({ users, selectedUser, onUserChange, loading }) {
|
|
7484
8654
|
const { t } = useTranslation();
|
|
@@ -7814,12 +8984,32 @@ init_useTranslation();
|
|
|
7814
8984
|
function generateId() {
|
|
7815
8985
|
return Math.random().toString(36).substring(7);
|
|
7816
8986
|
}
|
|
8987
|
+
var DEDUPE_WINDOW_MS = 2500;
|
|
8988
|
+
var MAX_ACTIVE_TOASTS = 5;
|
|
7817
8989
|
function useToast() {
|
|
7818
8990
|
const [toasts, setToasts] = useState([]);
|
|
8991
|
+
const recentToastMapRef = useRef(/* @__PURE__ */ new Map());
|
|
7819
8992
|
const showToast = useCallback(
|
|
7820
8993
|
(type, message, duration) => {
|
|
8994
|
+
const normalizedMessage = message.trim().toLowerCase();
|
|
8995
|
+
const key2 = `${type}:${normalizedMessage}`;
|
|
8996
|
+
const now = Date.now();
|
|
8997
|
+
const lastShownAt = recentToastMapRef.current.get(key2);
|
|
8998
|
+
if (lastShownAt && now - lastShownAt < DEDUPE_WINDOW_MS) {
|
|
8999
|
+
return;
|
|
9000
|
+
}
|
|
9001
|
+
for (const [mapKey, ts] of recentToastMapRef.current.entries()) {
|
|
9002
|
+
if (now - ts > DEDUPE_WINDOW_MS * 4) {
|
|
9003
|
+
recentToastMapRef.current.delete(mapKey);
|
|
9004
|
+
}
|
|
9005
|
+
}
|
|
9006
|
+
recentToastMapRef.current.set(key2, now);
|
|
7821
9007
|
const id = generateId();
|
|
7822
|
-
setToasts((prev) =>
|
|
9008
|
+
setToasts((prev) => {
|
|
9009
|
+
const next = [...prev, { id, type, message, duration }];
|
|
9010
|
+
if (next.length <= MAX_ACTIVE_TOASTS) return next;
|
|
9011
|
+
return next.slice(next.length - MAX_ACTIVE_TOASTS);
|
|
9012
|
+
});
|
|
7823
9013
|
},
|
|
7824
9014
|
[]
|
|
7825
9015
|
);
|
|
@@ -7828,6 +9018,7 @@ function useToast() {
|
|
|
7828
9018
|
}, []);
|
|
7829
9019
|
const dismissAll = useCallback(() => {
|
|
7830
9020
|
setToasts([]);
|
|
9021
|
+
recentToastMapRef.current.clear();
|
|
7831
9022
|
}, []);
|
|
7832
9023
|
return {
|
|
7833
9024
|
toasts,
|
|
@@ -7886,49 +9077,6 @@ function groupSessionsByDate(sessions, t) {
|
|
|
7886
9077
|
});
|
|
7887
9078
|
return groups;
|
|
7888
9079
|
}
|
|
7889
|
-
|
|
7890
|
-
// ui/src/bichat/utils/errorDisplay.ts
|
|
7891
|
-
function isPermissionDeniedError(error) {
|
|
7892
|
-
if (!error) return false;
|
|
7893
|
-
if (error instanceof Error) {
|
|
7894
|
-
const msg = error.message.toLowerCase();
|
|
7895
|
-
if (msg.includes("forbidden") || msg.includes("permission denied")) return true;
|
|
7896
|
-
}
|
|
7897
|
-
if (typeof error === "object" && error !== null) {
|
|
7898
|
-
const obj = error;
|
|
7899
|
-
if (obj.code === "forbidden" || obj.code === 403) return true;
|
|
7900
|
-
if (obj.status === 403) return true;
|
|
7901
|
-
if (obj.statusCode === 403) return true;
|
|
7902
|
-
if (typeof obj.response === "object" && obj.response !== null) {
|
|
7903
|
-
const resp = obj.response;
|
|
7904
|
-
if (resp.status === 403) return true;
|
|
7905
|
-
}
|
|
7906
|
-
}
|
|
7907
|
-
if (typeof error === "string") {
|
|
7908
|
-
const lower = error.toLowerCase();
|
|
7909
|
-
if (lower.includes("forbidden") || lower.includes("permission denied")) return true;
|
|
7910
|
-
}
|
|
7911
|
-
return false;
|
|
7912
|
-
}
|
|
7913
|
-
function toErrorDisplay(error, fallbackTitle) {
|
|
7914
|
-
const permDenied = isPermissionDeniedError(error);
|
|
7915
|
-
let title = fallbackTitle;
|
|
7916
|
-
let description = "";
|
|
7917
|
-
if (error instanceof Error) {
|
|
7918
|
-
description = error.message;
|
|
7919
|
-
} else if (typeof error === "object" && error !== null) {
|
|
7920
|
-
const obj = error;
|
|
7921
|
-
if (typeof obj.message === "string" && obj.message) description = obj.message;
|
|
7922
|
-
if (typeof obj.title === "string" && obj.title) title = obj.title;
|
|
7923
|
-
if (typeof obj.detail === "string" && obj.detail) description = obj.detail;
|
|
7924
|
-
} else if (typeof error === "string") {
|
|
7925
|
-
description = error;
|
|
7926
|
-
}
|
|
7927
|
-
if (permDenied && !description) {
|
|
7928
|
-
description = "Your account does not have permission for this action.";
|
|
7929
|
-
}
|
|
7930
|
-
return { title, description, isPermissionDenied: permDenied };
|
|
7931
|
-
}
|
|
7932
9080
|
function ErrorAlert({ error }) {
|
|
7933
9081
|
const amber = error.isPermissionDenied;
|
|
7934
9082
|
return /* @__PURE__ */ jsxs(
|
|
@@ -8012,7 +9160,7 @@ function Sidebar2({
|
|
|
8012
9160
|
const shouldReduceMotion = useReducedMotion();
|
|
8013
9161
|
const sessionListRef = useRef(null);
|
|
8014
9162
|
const searchContainerRef = useRef(null);
|
|
8015
|
-
const { isCollapsed,
|
|
9163
|
+
const { isCollapsed, toggle, collapse } = useSidebarCollapse();
|
|
8016
9164
|
const collapsible = !onClose;
|
|
8017
9165
|
const handleSidebarClick = useCallback(
|
|
8018
9166
|
(e) => {
|
|
@@ -8023,17 +9171,6 @@ function Sidebar2({
|
|
|
8023
9171
|
},
|
|
8024
9172
|
[collapsible, toggle]
|
|
8025
9173
|
);
|
|
8026
|
-
const focusSearch = useCallback(() => {
|
|
8027
|
-
if (!collapsible) return;
|
|
8028
|
-
if (isCollapsedRef.current) {
|
|
8029
|
-
expand();
|
|
8030
|
-
setTimeout(() => {
|
|
8031
|
-
searchContainerRef.current?.querySelector("input")?.focus();
|
|
8032
|
-
}, 250);
|
|
8033
|
-
} else {
|
|
8034
|
-
searchContainerRef.current?.querySelector("input")?.focus();
|
|
8035
|
-
}
|
|
8036
|
-
}, [collapsible, expand, isCollapsedRef]);
|
|
8037
9174
|
useEffect(() => {
|
|
8038
9175
|
if (!collapsible) return;
|
|
8039
9176
|
const handleKeyDown = (e) => {
|
|
@@ -8042,14 +9179,10 @@ function Sidebar2({
|
|
|
8042
9179
|
e.preventDefault();
|
|
8043
9180
|
toggle();
|
|
8044
9181
|
}
|
|
8045
|
-
if (isMod && e.key === "k") {
|
|
8046
|
-
e.preventDefault();
|
|
8047
|
-
focusSearch();
|
|
8048
|
-
}
|
|
8049
9182
|
};
|
|
8050
9183
|
document.addEventListener("keydown", handleKeyDown);
|
|
8051
9184
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
8052
|
-
}, [collapsible, toggle
|
|
9185
|
+
}, [collapsible, toggle]);
|
|
8053
9186
|
useEffect(() => {
|
|
8054
9187
|
if (!collapsible) return;
|
|
8055
9188
|
const handler = (e) => {
|
|
@@ -8072,13 +9205,6 @@ function Sidebar2({
|
|
|
8072
9205
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
8073
9206
|
const [showConfirm, setShowConfirm] = useState(false);
|
|
8074
9207
|
const [sessionToArchive, setSessionToArchive] = useState(null);
|
|
8075
|
-
const tabs = useMemo(() => {
|
|
8076
|
-
const items = [{ id: "my-chats", label: t("BiChat.Sidebar.MyChats") }];
|
|
8077
|
-
if (showAllChatsTab) {
|
|
8078
|
-
items.push({ id: "all-chats", label: t("BiChat.Sidebar.AllChats") });
|
|
8079
|
-
}
|
|
8080
|
-
return items;
|
|
8081
|
-
}, [showAllChatsTab, t]);
|
|
8082
9208
|
const fetchSessions = useCallback(async () => {
|
|
8083
9209
|
try {
|
|
8084
9210
|
setLoading(true);
|
|
@@ -8291,14 +9417,6 @@ function Sidebar2({
|
|
|
8291
9417
|
}
|
|
8292
9418
|
)
|
|
8293
9419
|
] }),
|
|
8294
|
-
showAllChatsTab && /* @__PURE__ */ jsx(
|
|
8295
|
-
TabBar_default,
|
|
8296
|
-
{
|
|
8297
|
-
tabs,
|
|
8298
|
-
activeTab,
|
|
8299
|
-
onTabChange: (id) => setActiveTab(id)
|
|
8300
|
-
}
|
|
8301
|
-
),
|
|
8302
9420
|
activeTab === "all-chats" && showAllChatsTab ? /* @__PURE__ */ jsx(
|
|
8303
9421
|
AllChatsList,
|
|
8304
9422
|
{
|
|
@@ -8919,232 +10037,73 @@ function BiChatLayout({
|
|
|
8919
10037
|
return () => document.removeEventListener("keydown", onKeyDown);
|
|
8920
10038
|
}, [closeMobile, isMobile, isMobileOpen]);
|
|
8921
10039
|
const handleDrawerDragEnd = (_, info) => {
|
|
8922
|
-
if (info.offset.x < -80) {
|
|
8923
|
-
closeMobile();
|
|
8924
|
-
}
|
|
8925
|
-
};
|
|
8926
|
-
const content = routeKey ? /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx(
|
|
8927
|
-
motion.div,
|
|
8928
|
-
{
|
|
8929
|
-
className: "flex flex-1 min-h-0",
|
|
8930
|
-
initial: { opacity: 0, y: 4 },
|
|
8931
|
-
animate: { opacity: 1, y: 0 },
|
|
8932
|
-
exit: { opacity: 0, y: -4 },
|
|
8933
|
-
transition: { duration: 0.15, ease: "easeOut" },
|
|
8934
|
-
children
|
|
8935
|
-
},
|
|
8936
|
-
routeKey
|
|
8937
|
-
) }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 min-h-0", children });
|
|
8938
|
-
return /* @__PURE__ */ jsxs("div", { className: `relative flex flex-1 w-full h-full min-h-0 overflow-hidden ${className}`, children: [
|
|
8939
|
-
/* @__PURE__ */ jsx(SkipLink, {}),
|
|
8940
|
-
/* @__PURE__ */ jsx("div", { className: "hidden md:block", children: renderSidebar({}) }),
|
|
8941
|
-
/* @__PURE__ */ jsx(AnimatePresence, { children: isMobile && isMobileOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8942
|
-
/* @__PURE__ */ jsx(
|
|
8943
|
-
motion.div,
|
|
8944
|
-
{
|
|
8945
|
-
className: "fixed inset-0 z-40 bg-black/40",
|
|
8946
|
-
initial: { opacity: 0 },
|
|
8947
|
-
animate: { opacity: 1 },
|
|
8948
|
-
exit: { opacity: 0 },
|
|
8949
|
-
onClick: closeMobile,
|
|
8950
|
-
"aria-hidden": "true"
|
|
8951
|
-
},
|
|
8952
|
-
"sidebar-backdrop"
|
|
8953
|
-
),
|
|
8954
|
-
/* @__PURE__ */ jsx(
|
|
8955
|
-
motion.div,
|
|
8956
|
-
{
|
|
8957
|
-
className: "fixed inset-y-0 left-0 z-50 w-[18rem] max-w-[85vw] shadow-2xl",
|
|
8958
|
-
initial: { x: "-100%" },
|
|
8959
|
-
animate: { x: 0 },
|
|
8960
|
-
exit: { x: "-100%" },
|
|
8961
|
-
transition: { type: "spring", stiffness: 320, damping: 32 },
|
|
8962
|
-
drag: "x",
|
|
8963
|
-
dragDirectionLock: true,
|
|
8964
|
-
dragConstraints: { left: -120, right: 0 },
|
|
8965
|
-
dragElastic: { left: 0.2, right: 0 },
|
|
8966
|
-
onDragEnd: handleDrawerDragEnd,
|
|
8967
|
-
onClick: (e) => e.stopPropagation(),
|
|
8968
|
-
children: /* @__PURE__ */ jsx("div", { ref: drawerRef, className: "h-full bg-white dark:bg-gray-900", children: renderSidebar({ onClose: closeMobile }) })
|
|
8969
|
-
},
|
|
8970
|
-
"sidebar-drawer"
|
|
8971
|
-
)
|
|
8972
|
-
] }) }),
|
|
8973
|
-
/* @__PURE__ */ jsxs("main", { id: "main-content", className: "relative flex-1 flex flex-col min-h-0 overflow-hidden", children: [
|
|
8974
|
-
isMobile && !isMobileOpen && /* @__PURE__ */ jsx(
|
|
8975
|
-
"button",
|
|
8976
|
-
{
|
|
8977
|
-
ref: menuButtonRef,
|
|
8978
|
-
onClick: openMobile,
|
|
8979
|
-
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",
|
|
8980
|
-
"aria-label": t("BiChat.Layout.OpenSidebar"),
|
|
8981
|
-
title: t("BiChat.Layout.OpenSidebar"),
|
|
8982
|
-
children: /* @__PURE__ */ jsx(List, { size: 20, weight: "bold" })
|
|
8983
|
-
}
|
|
8984
|
-
),
|
|
8985
|
-
content
|
|
8986
|
-
] })
|
|
8987
|
-
] });
|
|
8988
|
-
}
|
|
8989
|
-
init_useTranslation();
|
|
8990
|
-
var variantStyles = {
|
|
8991
|
-
error: {
|
|
8992
|
-
container: "border-red-200 bg-red-50 dark:bg-red-900/20",
|
|
8993
|
-
title: "text-red-800 dark:text-red-300",
|
|
8994
|
-
message: "text-red-700 dark:text-red-400",
|
|
8995
|
-
icon: "text-red-600 dark:text-red-400",
|
|
8996
|
-
button: "text-red-400 hover:text-red-600 dark:hover:text-red-300",
|
|
8997
|
-
retryButton: "bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-800 text-white",
|
|
8998
|
-
Icon: XCircle
|
|
8999
|
-
},
|
|
9000
|
-
success: {
|
|
9001
|
-
container: "border-green-200 bg-green-50 dark:bg-green-900/20",
|
|
9002
|
-
title: "text-green-800 dark:text-green-300",
|
|
9003
|
-
message: "text-green-700 dark:text-green-400",
|
|
9004
|
-
icon: "text-green-600 dark:text-green-400",
|
|
9005
|
-
button: "text-green-400 hover:text-green-600 dark:hover:text-green-300",
|
|
9006
|
-
retryButton: "bg-green-600 dark:bg-green-700 hover:bg-green-700 dark:hover:bg-green-800 text-white",
|
|
9007
|
-
Icon: CheckCircle
|
|
9008
|
-
},
|
|
9009
|
-
warning: {
|
|
9010
|
-
container: "border-yellow-200 bg-yellow-50 dark:bg-yellow-900/20",
|
|
9011
|
-
title: "text-yellow-800 dark:text-yellow-300",
|
|
9012
|
-
message: "text-yellow-700 dark:text-yellow-400",
|
|
9013
|
-
icon: "text-yellow-600 dark:text-yellow-400",
|
|
9014
|
-
button: "text-yellow-400 hover:text-yellow-600 dark:hover:text-yellow-300",
|
|
9015
|
-
retryButton: "bg-yellow-600 dark:bg-yellow-700 hover:bg-yellow-700 dark:hover:bg-yellow-800 text-white",
|
|
9016
|
-
Icon: Warning
|
|
9017
|
-
},
|
|
9018
|
-
info: {
|
|
9019
|
-
container: "border-blue-200 bg-blue-50 dark:bg-blue-900/20",
|
|
9020
|
-
title: "text-blue-800 dark:text-blue-300",
|
|
9021
|
-
message: "text-blue-700 dark:text-blue-400",
|
|
9022
|
-
icon: "text-blue-600 dark:text-blue-400",
|
|
9023
|
-
button: "text-blue-400 hover:text-blue-600 dark:hover:text-blue-300",
|
|
9024
|
-
retryButton: "bg-blue-600 dark:bg-blue-700 hover:bg-blue-700 dark:hover:bg-blue-800 text-white",
|
|
9025
|
-
Icon: Info
|
|
9026
|
-
}
|
|
9027
|
-
};
|
|
9028
|
-
function Alert({
|
|
9029
|
-
variant = "info",
|
|
9030
|
-
message,
|
|
9031
|
-
title,
|
|
9032
|
-
onDismiss,
|
|
9033
|
-
onRetry,
|
|
9034
|
-
show = true,
|
|
9035
|
-
dismissible = true
|
|
9036
|
-
}) {
|
|
9037
|
-
const { t } = useTranslation();
|
|
9038
|
-
const styles = variantStyles[variant];
|
|
9039
|
-
const IconComponent = styles.Icon;
|
|
9040
|
-
return /* @__PURE__ */ jsx(AnimatePresence, { children: show && /* @__PURE__ */ jsx(
|
|
9041
|
-
motion.div,
|
|
9042
|
-
{
|
|
9043
|
-
variants: errorMessageVariants,
|
|
9044
|
-
initial: "initial",
|
|
9045
|
-
animate: "animate",
|
|
9046
|
-
exit: "exit",
|
|
9047
|
-
className: `border-t border ${styles.container} px-4 py-3`,
|
|
9048
|
-
role: "alert",
|
|
9049
|
-
"aria-live": "assertive",
|
|
9050
|
-
children: /* @__PURE__ */ jsxs("div", { className: "w-full flex items-start justify-between px-4", children: [
|
|
9051
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 flex-1", children: [
|
|
9052
|
-
/* @__PURE__ */ jsx(IconComponent, { size: 20, className: `w-5 h-5 ${styles.icon} flex-shrink-0 mt-0.5` }),
|
|
9053
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
9054
|
-
title && /* @__PURE__ */ jsx("p", { className: `text-sm ${styles.title} font-medium`, children: title }),
|
|
9055
|
-
/* @__PURE__ */ jsx("p", { className: `text-sm ${styles.message} ${title ? "mt-1" : ""}`, children: message }),
|
|
9056
|
-
onRetry && /* @__PURE__ */ jsx(
|
|
9057
|
-
"button",
|
|
9058
|
-
{
|
|
9059
|
-
onClick: onRetry,
|
|
9060
|
-
className: `mt-2 text-xs px-3 py-1.5 rounded ${styles.retryButton} transition-colors font-medium`,
|
|
9061
|
-
children: t("BiChat.Chat.Retry")
|
|
9062
|
-
}
|
|
9063
|
-
)
|
|
9064
|
-
] })
|
|
9065
|
-
] }),
|
|
9066
|
-
dismissible && onDismiss && /* @__PURE__ */ jsx(
|
|
9067
|
-
"button",
|
|
9068
|
-
{
|
|
9069
|
-
onClick: onDismiss,
|
|
9070
|
-
className: `${styles.button} transition-colors flex-shrink-0`,
|
|
9071
|
-
"aria-label": t("BiChat.Chat.DismissNotification"),
|
|
9072
|
-
children: /* @__PURE__ */ jsx(X, { size: 20, className: "w-5 h-5" })
|
|
9073
|
-
}
|
|
9074
|
-
)
|
|
9075
|
-
] })
|
|
9076
|
-
}
|
|
9077
|
-
) });
|
|
9078
|
-
}
|
|
9079
|
-
var Alert_default = memo(Alert);
|
|
9080
|
-
init_useTranslation();
|
|
9081
|
-
function ArchiveBanner({
|
|
9082
|
-
show = true,
|
|
9083
|
-
onRestore,
|
|
9084
|
-
restoring = false,
|
|
9085
|
-
onRestoreComplete
|
|
9086
|
-
}) {
|
|
9087
|
-
const { t } = useTranslation();
|
|
9088
|
-
const [error, setError] = useState(null);
|
|
9089
|
-
const handleRestore = async () => {
|
|
9090
|
-
try {
|
|
9091
|
-
setError(null);
|
|
9092
|
-
if (onRestore) {
|
|
9093
|
-
await onRestore();
|
|
9094
|
-
}
|
|
9095
|
-
if (onRestoreComplete) {
|
|
9096
|
-
onRestoreComplete();
|
|
9097
|
-
}
|
|
9098
|
-
} catch (err) {
|
|
9099
|
-
const message = err instanceof Error ? err.message : t("BiChat.Archive.RestoreFailed");
|
|
9100
|
-
setError(message);
|
|
10040
|
+
if (info.offset.x < -80) {
|
|
10041
|
+
closeMobile();
|
|
9101
10042
|
}
|
|
9102
10043
|
};
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
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
|
-
|
|
10044
|
+
const content = routeKey ? /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx(
|
|
10045
|
+
motion.div,
|
|
10046
|
+
{
|
|
10047
|
+
className: "flex flex-1 min-h-0",
|
|
10048
|
+
initial: { opacity: 0, y: 4 },
|
|
10049
|
+
animate: { opacity: 1, y: 0 },
|
|
10050
|
+
exit: { opacity: 0, y: -4 },
|
|
10051
|
+
transition: { duration: 0.15, ease: "easeOut" },
|
|
10052
|
+
children
|
|
10053
|
+
},
|
|
10054
|
+
routeKey
|
|
10055
|
+
) }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 min-h-0", children });
|
|
10056
|
+
return /* @__PURE__ */ jsxs("div", { className: `relative flex flex-1 w-full h-full min-h-0 overflow-hidden ${className}`, children: [
|
|
10057
|
+
/* @__PURE__ */ jsx(SkipLink, {}),
|
|
10058
|
+
/* @__PURE__ */ jsx("div", { className: "hidden md:block", children: renderSidebar({}) }),
|
|
10059
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isMobile && isMobileOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10060
|
+
/* @__PURE__ */ jsx(
|
|
10061
|
+
motion.div,
|
|
10062
|
+
{
|
|
10063
|
+
className: "fixed inset-0 z-40 bg-black/40",
|
|
10064
|
+
initial: { opacity: 0 },
|
|
10065
|
+
animate: { opacity: 1 },
|
|
10066
|
+
exit: { opacity: 0 },
|
|
10067
|
+
onClick: closeMobile,
|
|
10068
|
+
"aria-hidden": "true"
|
|
10069
|
+
},
|
|
10070
|
+
"sidebar-backdrop"
|
|
10071
|
+
),
|
|
10072
|
+
/* @__PURE__ */ jsx(
|
|
10073
|
+
motion.div,
|
|
10074
|
+
{
|
|
10075
|
+
className: "fixed inset-y-0 left-0 z-50 w-[18rem] max-w-[85vw] shadow-2xl",
|
|
10076
|
+
initial: { x: "-100%" },
|
|
10077
|
+
animate: { x: 0 },
|
|
10078
|
+
exit: { x: "-100%" },
|
|
10079
|
+
transition: { type: "spring", stiffness: 320, damping: 32 },
|
|
10080
|
+
drag: "x",
|
|
10081
|
+
dragDirectionLock: true,
|
|
10082
|
+
dragConstraints: { left: -120, right: 0 },
|
|
10083
|
+
dragElastic: { left: 0.2, right: 0 },
|
|
10084
|
+
onDragEnd: handleDrawerDragEnd,
|
|
10085
|
+
onClick: (e) => e.stopPropagation(),
|
|
10086
|
+
children: /* @__PURE__ */ jsx("div", { ref: drawerRef, className: "h-full bg-white dark:bg-gray-900", children: renderSidebar({ onClose: closeMobile }) })
|
|
10087
|
+
},
|
|
10088
|
+
"sidebar-drawer"
|
|
10089
|
+
)
|
|
10090
|
+
] }) }),
|
|
10091
|
+
/* @__PURE__ */ jsxs("main", { id: "main-content", className: "relative flex-1 flex flex-col min-h-0 overflow-hidden", children: [
|
|
10092
|
+
isMobile && !isMobileOpen && /* @__PURE__ */ jsx(
|
|
10093
|
+
"button",
|
|
10094
|
+
{
|
|
10095
|
+
ref: menuButtonRef,
|
|
10096
|
+
onClick: openMobile,
|
|
10097
|
+
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",
|
|
10098
|
+
"aria-label": t("BiChat.Layout.OpenSidebar"),
|
|
10099
|
+
title: t("BiChat.Layout.OpenSidebar"),
|
|
10100
|
+
children: /* @__PURE__ */ jsx(List, { size: 20, weight: "bold" })
|
|
10101
|
+
}
|
|
10102
|
+
),
|
|
10103
|
+
content
|
|
10104
|
+
] })
|
|
9145
10105
|
] });
|
|
9146
10106
|
}
|
|
9147
|
-
var ArchiveBanner_default = memo(ArchiveBanner);
|
|
9148
10107
|
|
|
9149
10108
|
// ui/src/bichat/components/RetryActionArea.tsx
|
|
9150
10109
|
init_useTranslation();
|
|
@@ -9198,66 +10157,6 @@ var RetryActionArea = memo(function RetryActionArea2({
|
|
|
9198
10157
|
)
|
|
9199
10158
|
);
|
|
9200
10159
|
});
|
|
9201
|
-
|
|
9202
|
-
// ui/src/bichat/components/StreamError.tsx
|
|
9203
|
-
init_useTranslation();
|
|
9204
|
-
function StreamError({
|
|
9205
|
-
error,
|
|
9206
|
-
onRetry,
|
|
9207
|
-
onRegenerate,
|
|
9208
|
-
compact = false
|
|
9209
|
-
}) {
|
|
9210
|
-
const { t } = useTranslation();
|
|
9211
|
-
return /* @__PURE__ */ jsxs(
|
|
9212
|
-
motion.div,
|
|
9213
|
-
{
|
|
9214
|
-
initial: { opacity: 0, y: 10 },
|
|
9215
|
-
animate: { opacity: 1, y: 0 },
|
|
9216
|
-
exit: { opacity: 0, y: -10 },
|
|
9217
|
-
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`,
|
|
9218
|
-
role: "alert",
|
|
9219
|
-
children: [
|
|
9220
|
-
/* @__PURE__ */ jsx(
|
|
9221
|
-
Warning,
|
|
9222
|
-
{
|
|
9223
|
-
className: "w-5 h-5 text-red-500 dark:text-red-400 flex-shrink-0",
|
|
9224
|
-
weight: "fill"
|
|
9225
|
-
}
|
|
9226
|
-
),
|
|
9227
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
9228
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-200", children: t("BiChat.Error.Generic") }),
|
|
9229
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-red-600 dark:text-red-300 break-words", children: error })
|
|
9230
|
-
] }),
|
|
9231
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [
|
|
9232
|
-
onRetry && /* @__PURE__ */ jsxs(
|
|
9233
|
-
"button",
|
|
9234
|
-
{
|
|
9235
|
-
onClick: onRetry,
|
|
9236
|
-
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",
|
|
9237
|
-
type: "button",
|
|
9238
|
-
children: [
|
|
9239
|
-
/* @__PURE__ */ jsx(ArrowClockwise, { className: "w-4 h-4" }),
|
|
9240
|
-
t("BiChat.StreamError.Retry")
|
|
9241
|
-
]
|
|
9242
|
-
}
|
|
9243
|
-
),
|
|
9244
|
-
onRegenerate && /* @__PURE__ */ jsxs(
|
|
9245
|
-
"button",
|
|
9246
|
-
{
|
|
9247
|
-
onClick: onRegenerate,
|
|
9248
|
-
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",
|
|
9249
|
-
type: "button",
|
|
9250
|
-
children: [
|
|
9251
|
-
/* @__PURE__ */ jsx(ArrowsCounterClockwise, { className: "w-4 h-4" }),
|
|
9252
|
-
t("BiChat.StreamError.Regenerate")
|
|
9253
|
-
]
|
|
9254
|
-
}
|
|
9255
|
-
)
|
|
9256
|
-
] })
|
|
9257
|
-
]
|
|
9258
|
-
}
|
|
9259
|
-
);
|
|
9260
|
-
}
|
|
9261
10160
|
init_useTranslation();
|
|
9262
10161
|
function MessageActions({
|
|
9263
10162
|
message,
|
|
@@ -10656,9 +11555,10 @@ function useMarkdownCopy(options = {}) {
|
|
|
10656
11555
|
const [copiedStates, setCopiedStates] = useState(/* @__PURE__ */ new Map());
|
|
10657
11556
|
const timeoutsRef = useRef(/* @__PURE__ */ new Map());
|
|
10658
11557
|
useEffect(() => {
|
|
11558
|
+
const timeouts = timeoutsRef.current;
|
|
10659
11559
|
return () => {
|
|
10660
|
-
|
|
10661
|
-
|
|
11560
|
+
timeouts.forEach((timeout) => clearTimeout(timeout));
|
|
11561
|
+
timeouts.clear();
|
|
10662
11562
|
};
|
|
10663
11563
|
}, []);
|
|
10664
11564
|
const isCopied = useCallback(
|
|
@@ -10781,8 +11681,9 @@ function ConfigProvider({ config, useGlobalConfig = false, children }) {
|
|
|
10781
11681
|
if (config) {
|
|
10782
11682
|
resolvedConfig = config;
|
|
10783
11683
|
} else if (useGlobalConfig && typeof window !== "undefined") {
|
|
10784
|
-
const
|
|
10785
|
-
const
|
|
11684
|
+
const w = window;
|
|
11685
|
+
const globalContext = w.__APPLET_CONTEXT__;
|
|
11686
|
+
const globalCSRF = w.__CSRF_TOKEN__;
|
|
10786
11687
|
if (globalContext) {
|
|
10787
11688
|
resolvedConfig = {
|
|
10788
11689
|
user: {
|
|
@@ -10979,98 +11880,6 @@ function createHeadersWithCSRF(init) {
|
|
|
10979
11880
|
return addCSRFHeader(headers);
|
|
10980
11881
|
}
|
|
10981
11882
|
|
|
10982
|
-
// ui/src/applet-devtools/enabled.ts
|
|
10983
|
-
function shouldEnableAppletDevtools() {
|
|
10984
|
-
if (typeof window === "undefined") return false;
|
|
10985
|
-
const url = new URL(window.location.href);
|
|
10986
|
-
if (url.searchParams.get("appletDebug") === "1") return true;
|
|
10987
|
-
try {
|
|
10988
|
-
return window.localStorage.getItem("iotaAppletDevtools") === "1";
|
|
10989
|
-
} catch {
|
|
10990
|
-
return false;
|
|
10991
|
-
}
|
|
10992
|
-
}
|
|
10993
|
-
|
|
10994
|
-
// ui/src/applet-host/rpc.ts
|
|
10995
|
-
var AppletRPCException = class extends Error {
|
|
10996
|
-
constructor(args) {
|
|
10997
|
-
super(args.message);
|
|
10998
|
-
this.name = "AppletRPCException";
|
|
10999
|
-
this.code = args.code;
|
|
11000
|
-
this.details = args.details;
|
|
11001
|
-
this.cause = args.cause;
|
|
11002
|
-
}
|
|
11003
|
-
};
|
|
11004
|
-
function createAppletRPCClient(options) {
|
|
11005
|
-
const fetcher = options.fetcher ?? fetch;
|
|
11006
|
-
async function call(method, params) {
|
|
11007
|
-
const req = { id: crypto.randomUUID(), method, params };
|
|
11008
|
-
const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
11009
|
-
maybeDispatchRPCEvent({
|
|
11010
|
-
id: req.id,
|
|
11011
|
-
method: req.method,
|
|
11012
|
-
status: "start"
|
|
11013
|
-
});
|
|
11014
|
-
try {
|
|
11015
|
-
const resp = await fetcher(options.endpoint, {
|
|
11016
|
-
method: "POST",
|
|
11017
|
-
headers: { "Content-Type": "application/json" },
|
|
11018
|
-
body: JSON.stringify(req)
|
|
11019
|
-
});
|
|
11020
|
-
if (!resp.ok) {
|
|
11021
|
-
throw new AppletRPCException({
|
|
11022
|
-
code: "http_error",
|
|
11023
|
-
message: `HTTP ${resp.status}`,
|
|
11024
|
-
details: { status: resp.status }
|
|
11025
|
-
});
|
|
11026
|
-
}
|
|
11027
|
-
const json = await resp.json();
|
|
11028
|
-
if (json.error) {
|
|
11029
|
-
throw new AppletRPCException({
|
|
11030
|
-
code: json.error.code,
|
|
11031
|
-
message: json.error.message,
|
|
11032
|
-
details: json.error.details
|
|
11033
|
-
});
|
|
11034
|
-
}
|
|
11035
|
-
if (json.result === void 0) {
|
|
11036
|
-
throw new AppletRPCException({
|
|
11037
|
-
code: "invalid_response",
|
|
11038
|
-
message: "Missing result in successful response"
|
|
11039
|
-
});
|
|
11040
|
-
}
|
|
11041
|
-
maybeDispatchRPCEvent({
|
|
11042
|
-
id: req.id,
|
|
11043
|
-
method: req.method,
|
|
11044
|
-
status: "success",
|
|
11045
|
-
durationMs: elapsedMs(startedAt)
|
|
11046
|
-
});
|
|
11047
|
-
return json.result;
|
|
11048
|
-
} catch (err) {
|
|
11049
|
-
maybeDispatchRPCEvent({
|
|
11050
|
-
id: req.id,
|
|
11051
|
-
method: req.method,
|
|
11052
|
-
status: "error",
|
|
11053
|
-
durationMs: elapsedMs(startedAt),
|
|
11054
|
-
error: err
|
|
11055
|
-
});
|
|
11056
|
-
throw err;
|
|
11057
|
-
}
|
|
11058
|
-
}
|
|
11059
|
-
async function callTyped(method, params) {
|
|
11060
|
-
return call(method, params);
|
|
11061
|
-
}
|
|
11062
|
-
return { call, callTyped };
|
|
11063
|
-
}
|
|
11064
|
-
function maybeDispatchRPCEvent(detail) {
|
|
11065
|
-
if (typeof window === "undefined") return;
|
|
11066
|
-
if (!shouldEnableAppletDevtools()) return;
|
|
11067
|
-
window.dispatchEvent(new CustomEvent("iota:applet-rpc", { detail }));
|
|
11068
|
-
}
|
|
11069
|
-
function elapsedMs(startedAt) {
|
|
11070
|
-
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
11071
|
-
return Math.max(0, Math.round(now - startedAt));
|
|
11072
|
-
}
|
|
11073
|
-
|
|
11074
11883
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
11075
11884
|
init_chartSpec();
|
|
11076
11885
|
|
|
@@ -11116,7 +11925,9 @@ async function* parseSSEStream(reader) {
|
|
|
11116
11925
|
reader.releaseLock();
|
|
11117
11926
|
}
|
|
11118
11927
|
}
|
|
11928
|
+
var TERMINAL_TYPES = /* @__PURE__ */ new Set(["done", "error"]);
|
|
11119
11929
|
async function* parseBichatStream(reader) {
|
|
11930
|
+
let yieldedTerminal = false;
|
|
11120
11931
|
for await (const event of parseSSEStream(reader)) {
|
|
11121
11932
|
const parsed = event;
|
|
11122
11933
|
const inferredType = parsed.type || (parsed.content ? "content" : "error");
|
|
@@ -11124,11 +11935,50 @@ async function* parseBichatStream(reader) {
|
|
|
11124
11935
|
...parsed,
|
|
11125
11936
|
type: inferredType
|
|
11126
11937
|
};
|
|
11938
|
+
if (TERMINAL_TYPES.has(inferredType)) {
|
|
11939
|
+
yieldedTerminal = true;
|
|
11940
|
+
}
|
|
11127
11941
|
yield normalized;
|
|
11128
11942
|
}
|
|
11943
|
+
if (!yieldedTerminal) {
|
|
11944
|
+
yield { type: "done" };
|
|
11945
|
+
}
|
|
11946
|
+
}
|
|
11947
|
+
async function* parseBichatStreamEvents(reader) {
|
|
11948
|
+
for await (const chunk of parseBichatStream(reader)) {
|
|
11949
|
+
const event = toStreamEvent(chunk);
|
|
11950
|
+
if (event) yield event;
|
|
11951
|
+
}
|
|
11952
|
+
}
|
|
11953
|
+
function toStreamEvent(chunk) {
|
|
11954
|
+
switch (chunk.type) {
|
|
11955
|
+
case "chunk":
|
|
11956
|
+
case "content":
|
|
11957
|
+
return { type: "content", content: chunk.content ?? "" };
|
|
11958
|
+
case "tool_start":
|
|
11959
|
+
return chunk.tool ? { type: "tool_start", tool: chunk.tool } : null;
|
|
11960
|
+
case "tool_end":
|
|
11961
|
+
return chunk.tool ? { type: "tool_end", tool: chunk.tool } : null;
|
|
11962
|
+
case "usage":
|
|
11963
|
+
return chunk.usage ? { type: "usage", usage: chunk.usage } : null;
|
|
11964
|
+
case "user_message":
|
|
11965
|
+
return chunk.sessionId ? { type: "user_message", sessionId: chunk.sessionId } : null;
|
|
11966
|
+
case "interrupt":
|
|
11967
|
+
return chunk.interrupt ? { type: "interrupt", interrupt: chunk.interrupt, sessionId: chunk.sessionId } : null;
|
|
11968
|
+
case "done":
|
|
11969
|
+
return { type: "done", sessionId: chunk.sessionId, generationMs: chunk.generationMs };
|
|
11970
|
+
case "error":
|
|
11971
|
+
return { type: "error", error: chunk.error ?? "Unknown error" };
|
|
11972
|
+
default:
|
|
11973
|
+
return null;
|
|
11974
|
+
}
|
|
11129
11975
|
}
|
|
11130
11976
|
|
|
11131
11977
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
11978
|
+
function isSessionNotFoundError(err) {
|
|
11979
|
+
if (!(err instanceof AppletRPCException)) return false;
|
|
11980
|
+
return err.code === "not_found" || err.code === "session_not_found";
|
|
11981
|
+
}
|
|
11132
11982
|
function toSession(session) {
|
|
11133
11983
|
return {
|
|
11134
11984
|
...session,
|
|
@@ -11316,34 +12166,18 @@ function attachArtifactsToTurns(turns, artifacts) {
|
|
|
11316
12166
|
}
|
|
11317
12167
|
};
|
|
11318
12168
|
});
|
|
11319
|
-
const assistantPositions = [];
|
|
11320
12169
|
const turnIndexByMessageID = /* @__PURE__ */ new Map();
|
|
11321
12170
|
nextTurns.forEach((turn, index) => {
|
|
11322
12171
|
turnIndexByMessageID.set(turn.userTurn.id, index);
|
|
11323
12172
|
const assistantTurn = turn.assistantTurn;
|
|
11324
12173
|
if (!assistantTurn) return;
|
|
11325
12174
|
turnIndexByMessageID.set(assistantTurn.id, index);
|
|
11326
|
-
assistantPositions.push({
|
|
11327
|
-
index,
|
|
11328
|
-
createdAtMs: toMillis(assistantTurn.createdAt || turn.createdAt)
|
|
11329
|
-
});
|
|
11330
12175
|
});
|
|
11331
|
-
if (assistantPositions.length === 0) return turns;
|
|
11332
|
-
const findFallbackAssistantIndex = (artifactCreatedAt) => {
|
|
11333
|
-
const artifactMs = toMillis(artifactCreatedAt);
|
|
11334
|
-
if (!Number.isFinite(artifactMs)) {
|
|
11335
|
-
return assistantPositions[assistantPositions.length - 1].index;
|
|
11336
|
-
}
|
|
11337
|
-
for (const pos of assistantPositions) {
|
|
11338
|
-
if (Number.isFinite(pos.createdAtMs) && pos.createdAtMs >= artifactMs) {
|
|
11339
|
-
return pos.index;
|
|
11340
|
-
}
|
|
11341
|
-
}
|
|
11342
|
-
return assistantPositions[assistantPositions.length - 1].index;
|
|
11343
|
-
};
|
|
11344
12176
|
for (const entry of downloadArtifacts) {
|
|
11345
12177
|
const messageID = entry.raw.messageId;
|
|
11346
|
-
|
|
12178
|
+
if (!messageID) continue;
|
|
12179
|
+
const targetIndex = turnIndexByMessageID.get(messageID);
|
|
12180
|
+
if (targetIndex === void 0) continue;
|
|
11347
12181
|
const assistantTurn = nextTurns[targetIndex]?.assistantTurn;
|
|
11348
12182
|
if (!assistantTurn) continue;
|
|
11349
12183
|
const exists = assistantTurn.artifacts.some(
|
|
@@ -11355,7 +12189,9 @@ function attachArtifactsToTurns(turns, artifacts) {
|
|
|
11355
12189
|
}
|
|
11356
12190
|
for (const raw of chartArtifacts) {
|
|
11357
12191
|
const messageID = raw.messageId;
|
|
11358
|
-
|
|
12192
|
+
if (!messageID) continue;
|
|
12193
|
+
const targetIndex = turnIndexByMessageID.get(messageID);
|
|
12194
|
+
if (targetIndex === void 0) continue;
|
|
11359
12195
|
const assistantTurn = nextTurns[targetIndex]?.assistantTurn;
|
|
11360
12196
|
if (!assistantTurn) continue;
|
|
11361
12197
|
if (assistantTurn.chartData) continue;
|
|
@@ -11381,7 +12217,8 @@ var HttpDataSource = class {
|
|
|
11381
12217
|
this.navigateToSession = config.navigateToSession;
|
|
11382
12218
|
}
|
|
11383
12219
|
this.rpc = createAppletRPCClient({
|
|
11384
|
-
endpoint: `${this.config.baseUrl}${this.config.rpcEndpoint}
|
|
12220
|
+
endpoint: `${this.config.baseUrl}${this.config.rpcEndpoint}`,
|
|
12221
|
+
timeoutMs: this.config.timeout
|
|
11385
12222
|
});
|
|
11386
12223
|
}
|
|
11387
12224
|
/**
|
|
@@ -11437,8 +12274,11 @@ var HttpDataSource = class {
|
|
|
11437
12274
|
pendingQuestion: toPendingQuestion(data.pendingQuestion)
|
|
11438
12275
|
};
|
|
11439
12276
|
} catch (err) {
|
|
12277
|
+
if (isSessionNotFoundError(err)) {
|
|
12278
|
+
return null;
|
|
12279
|
+
}
|
|
11440
12280
|
console.error("Failed to fetch session:", err);
|
|
11441
|
-
|
|
12281
|
+
throw err instanceof Error ? err : new Error("Failed to fetch session");
|
|
11442
12282
|
}
|
|
11443
12283
|
}
|
|
11444
12284
|
async fetchSessionArtifacts(sessionId, options) {
|
|
@@ -11527,15 +12367,28 @@ var HttpDataSource = class {
|
|
|
11527
12367
|
url: a.url
|
|
11528
12368
|
}))
|
|
11529
12369
|
};
|
|
12370
|
+
let connectionTimeoutID;
|
|
12371
|
+
let connectionTimedOut = false;
|
|
11530
12372
|
try {
|
|
12373
|
+
const timeoutMs = this.config.timeout ?? 0;
|
|
12374
|
+
if (timeoutMs > 0) {
|
|
12375
|
+
connectionTimeoutID = setTimeout(() => {
|
|
12376
|
+
connectionTimedOut = true;
|
|
12377
|
+
this.abortController?.abort();
|
|
12378
|
+
}, timeoutMs);
|
|
12379
|
+
}
|
|
11531
12380
|
const response = await fetch(url, {
|
|
11532
12381
|
method: "POST",
|
|
11533
12382
|
headers: this.createHeaders(),
|
|
11534
12383
|
body: JSON.stringify(payload),
|
|
11535
12384
|
signal: this.abortController.signal
|
|
11536
12385
|
});
|
|
12386
|
+
if (connectionTimeoutID !== void 0) {
|
|
12387
|
+
clearTimeout(connectionTimeoutID);
|
|
12388
|
+
connectionTimeoutID = void 0;
|
|
12389
|
+
}
|
|
11537
12390
|
if (!response.ok) {
|
|
11538
|
-
throw new Error(`Stream request failed: ${response.
|
|
12391
|
+
throw new Error(`Stream request failed: HTTP ${response.status}`);
|
|
11539
12392
|
}
|
|
11540
12393
|
if (!response.body) {
|
|
11541
12394
|
throw new Error("Response body is null");
|
|
@@ -11552,7 +12405,7 @@ var HttpDataSource = class {
|
|
|
11552
12405
|
if (err.name === "AbortError") {
|
|
11553
12406
|
yield {
|
|
11554
12407
|
type: "error",
|
|
11555
|
-
error: "Stream cancelled"
|
|
12408
|
+
error: connectionTimedOut ? `Stream request timed out after ${this.config.timeout}ms` : "Stream cancelled"
|
|
11556
12409
|
};
|
|
11557
12410
|
} else {
|
|
11558
12411
|
yield {
|
|
@@ -11567,6 +12420,9 @@ var HttpDataSource = class {
|
|
|
11567
12420
|
};
|
|
11568
12421
|
}
|
|
11569
12422
|
} finally {
|
|
12423
|
+
if (connectionTimeoutID !== void 0) {
|
|
12424
|
+
clearTimeout(connectionTimeoutID);
|
|
12425
|
+
}
|
|
11570
12426
|
if (signal && onExternalAbort) {
|
|
11571
12427
|
signal.removeEventListener("abort", onExternalAbort);
|
|
11572
12428
|
}
|
|
@@ -11681,6 +12537,6 @@ function createHttpDataSource(config) {
|
|
|
11681
12537
|
return new HttpDataSource(config);
|
|
11682
12538
|
}
|
|
11683
12539
|
|
|
11684
|
-
export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage,
|
|
12540
|
+
export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getValidChildren, groupSessionsByDate, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useFocusTrap, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
|
|
11685
12541
|
//# sourceMappingURL=index.mjs.map
|
|
11686
12542
|
//# sourceMappingURL=index.mjs.map
|