@iota-uz/sdk 0.4.19 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bichat/index.cjs +1065 -338
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.d.cts +41 -1
- package/dist/bichat/index.d.ts +41 -1
- package/dist/bichat/index.mjs +1070 -344
- package/dist/bichat/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tailwind/compiled.css +1 -1
package/dist/bichat/index.cjs
CHANGED
|
@@ -190,6 +190,37 @@ var init_ChartCard = __esm({
|
|
|
190
190
|
DEFAULT_COLORS = ["#6366f1", "#06b6d4", "#f59e0b", "#ef4444", "#8b5cf6"];
|
|
191
191
|
}
|
|
192
192
|
});
|
|
193
|
+
exports.TableExportButton = void 0;
|
|
194
|
+
var init_TableExportButton = __esm({
|
|
195
|
+
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
196
|
+
init_useTranslation();
|
|
197
|
+
exports.TableExportButton = React.memo(function TableExportButton2({
|
|
198
|
+
onClick,
|
|
199
|
+
disabled = false,
|
|
200
|
+
label,
|
|
201
|
+
disabledTooltip
|
|
202
|
+
}) {
|
|
203
|
+
const { t } = useTranslation();
|
|
204
|
+
const resolvedLabel = label ?? t("BiChat.Export");
|
|
205
|
+
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
206
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
207
|
+
"button",
|
|
208
|
+
{
|
|
209
|
+
type: "button",
|
|
210
|
+
onClick,
|
|
211
|
+
disabled,
|
|
212
|
+
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",
|
|
213
|
+
"aria-label": resolvedLabel,
|
|
214
|
+
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
215
|
+
children: [
|
|
216
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.FileXls, { size: 16, weight: "fill" }),
|
|
217
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedLabel })
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
193
224
|
|
|
194
225
|
// ui/src/bichat/utils/citationProcessor.ts
|
|
195
226
|
function processCitations(content, citations) {
|
|
@@ -302,37 +333,6 @@ var init_chartSpec = __esm({
|
|
|
302
333
|
]);
|
|
303
334
|
}
|
|
304
335
|
});
|
|
305
|
-
exports.TableExportButton = void 0;
|
|
306
|
-
var init_TableExportButton = __esm({
|
|
307
|
-
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
308
|
-
init_useTranslation();
|
|
309
|
-
exports.TableExportButton = React.memo(function TableExportButton2({
|
|
310
|
-
onClick,
|
|
311
|
-
disabled = false,
|
|
312
|
-
label,
|
|
313
|
-
disabledTooltip
|
|
314
|
-
}) {
|
|
315
|
-
const { t } = useTranslation();
|
|
316
|
-
const resolvedLabel = label ?? t("BiChat.Export");
|
|
317
|
-
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
318
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
319
|
-
"button",
|
|
320
|
-
{
|
|
321
|
-
type: "button",
|
|
322
|
-
onClick,
|
|
323
|
-
disabled,
|
|
324
|
-
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",
|
|
325
|
-
"aria-label": resolvedLabel,
|
|
326
|
-
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
327
|
-
children: [
|
|
328
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.FileXls, { size: 16, weight: "fill" }),
|
|
329
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: resolvedLabel })
|
|
330
|
-
]
|
|
331
|
-
}
|
|
332
|
-
);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
336
|
exports.TableWithExport = void 0;
|
|
337
337
|
var init_TableWithExport = __esm({
|
|
338
338
|
"ui/src/bichat/components/TableWithExport.tsx"() {
|
|
@@ -1547,6 +1547,12 @@ var ChatMachine = class {
|
|
|
1547
1547
|
streamErrorRetryable: false
|
|
1548
1548
|
});
|
|
1549
1549
|
}
|
|
1550
|
+
_notifySessionsUpdated(reason, sessionId) {
|
|
1551
|
+
if (typeof window === "undefined") return;
|
|
1552
|
+
window.dispatchEvent(new CustomEvent("bichat:sessions-updated", {
|
|
1553
|
+
detail: { reason, sessionId }
|
|
1554
|
+
}));
|
|
1555
|
+
}
|
|
1550
1556
|
_cancel() {
|
|
1551
1557
|
if (this.abortController) {
|
|
1552
1558
|
this.abortController.abort();
|
|
@@ -1814,7 +1820,11 @@ var ChatMachine = class {
|
|
|
1814
1820
|
}
|
|
1815
1821
|
}
|
|
1816
1822
|
const targetSessionId = createdSessionId || activeSessionId;
|
|
1823
|
+
if (targetSessionId && targetSessionId !== "new") {
|
|
1824
|
+
this._notifySessionsUpdated("message_sent", targetSessionId);
|
|
1825
|
+
}
|
|
1817
1826
|
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1827
|
+
this._notifySessionsUpdated("session_created", targetSessionId);
|
|
1818
1828
|
if (this.onSessionCreated) {
|
|
1819
1829
|
this.onSessionCreated(targetSessionId);
|
|
1820
1830
|
} else {
|
|
@@ -2004,6 +2014,8 @@ var ChatMachine = class {
|
|
|
2004
2014
|
this._clearStreamError();
|
|
2005
2015
|
const convertedAttachments = attachments.map((att) => ({
|
|
2006
2016
|
clientKey: att.clientKey || crypto.randomUUID(),
|
|
2017
|
+
id: att.id,
|
|
2018
|
+
uploadId: att.uploadId,
|
|
2007
2019
|
filename: att.filename,
|
|
2008
2020
|
mimeType: att.mimeType,
|
|
2009
2021
|
sizeBytes: att.sizeBytes,
|
|
@@ -2666,6 +2678,9 @@ var MemoizedAttachmentGrid = React__default.default.memo(AttachmentGrid);
|
|
|
2666
2678
|
MemoizedAttachmentGrid.displayName = "AttachmentGrid";
|
|
2667
2679
|
var AttachmentGrid_default = MemoizedAttachmentGrid;
|
|
2668
2680
|
init_useTranslation();
|
|
2681
|
+
var MIN_SCALE = 0.25;
|
|
2682
|
+
var MAX_SCALE = 5;
|
|
2683
|
+
var ZOOM_STEP = 0.25;
|
|
2669
2684
|
function ImageModal({
|
|
2670
2685
|
isOpen,
|
|
2671
2686
|
onClose,
|
|
@@ -2678,9 +2693,23 @@ function ImageModal({
|
|
|
2678
2693
|
const [isImageLoaded, setIsImageLoaded] = React.useState(false);
|
|
2679
2694
|
const [imageError, setImageError] = React.useState(false);
|
|
2680
2695
|
const [retryKey, setRetryKey] = React.useState(0);
|
|
2696
|
+
const [scale, setScale] = React.useState(1);
|
|
2697
|
+
const [position, setPosition] = React.useState({ x: 0, y: 0 });
|
|
2698
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
2699
|
+
const dragStartRef = React.useRef({ x: 0, y: 0 });
|
|
2700
|
+
const positionRef = React.useRef({ x: 0, y: 0 });
|
|
2701
|
+
const scaleRef = React.useRef(1);
|
|
2702
|
+
const imageAreaRef = React.useRef(null);
|
|
2681
2703
|
const hasMultipleImages = allAttachments && allAttachments.length > 1;
|
|
2682
2704
|
const canNavigatePrev = hasMultipleImages && currentIndex > 0;
|
|
2683
2705
|
const canNavigateNext = hasMultipleImages && currentIndex < (allAttachments?.length || 1) - 1;
|
|
2706
|
+
const isZoomed = scale > 1;
|
|
2707
|
+
React.useEffect(() => {
|
|
2708
|
+
scaleRef.current = scale;
|
|
2709
|
+
}, [scale]);
|
|
2710
|
+
React.useEffect(() => {
|
|
2711
|
+
positionRef.current = position;
|
|
2712
|
+
}, [position]);
|
|
2684
2713
|
React.useEffect(() => {
|
|
2685
2714
|
if (!isOpen) return;
|
|
2686
2715
|
const handleKeyDown = (e) => {
|
|
@@ -2688,6 +2717,14 @@ function ImageModal({
|
|
|
2688
2717
|
onNavigate("prev");
|
|
2689
2718
|
} else if (e.key === "ArrowRight" && onNavigate && canNavigateNext) {
|
|
2690
2719
|
onNavigate("next");
|
|
2720
|
+
} else if (e.key === "+" || e.key === "=") {
|
|
2721
|
+
setScale((s) => Math.min(s + ZOOM_STEP, MAX_SCALE));
|
|
2722
|
+
} else if (e.key === "-") {
|
|
2723
|
+
setScale((s) => Math.max(s - ZOOM_STEP, MIN_SCALE));
|
|
2724
|
+
if (scaleRef.current - ZOOM_STEP <= 1) setPosition({ x: 0, y: 0 });
|
|
2725
|
+
} else if (e.key === "0") {
|
|
2726
|
+
setScale(1);
|
|
2727
|
+
setPosition({ x: 0, y: 0 });
|
|
2691
2728
|
}
|
|
2692
2729
|
};
|
|
2693
2730
|
document.addEventListener("keydown", handleKeyDown);
|
|
@@ -2696,128 +2733,246 @@ function ImageModal({
|
|
|
2696
2733
|
React.useEffect(() => {
|
|
2697
2734
|
setIsImageLoaded(false);
|
|
2698
2735
|
setImageError(false);
|
|
2736
|
+
setScale(1);
|
|
2737
|
+
setPosition({ x: 0, y: 0 });
|
|
2699
2738
|
}, [attachment]);
|
|
2739
|
+
React.useEffect(() => {
|
|
2740
|
+
const el = imageAreaRef.current;
|
|
2741
|
+
if (!el || !isOpen) return;
|
|
2742
|
+
const handler = (e) => {
|
|
2743
|
+
const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;
|
|
2744
|
+
const current = scaleRef.current;
|
|
2745
|
+
const newScale = Math.min(Math.max(current + delta, MIN_SCALE), MAX_SCALE);
|
|
2746
|
+
if (newScale === current) return;
|
|
2747
|
+
e.preventDefault();
|
|
2748
|
+
setScale(newScale);
|
|
2749
|
+
if (newScale <= 1) setPosition({ x: 0, y: 0 });
|
|
2750
|
+
};
|
|
2751
|
+
el.addEventListener("wheel", handler, { passive: false });
|
|
2752
|
+
return () => el.removeEventListener("wheel", handler);
|
|
2753
|
+
}, [isOpen]);
|
|
2700
2754
|
const handleRetry = React.useCallback(() => {
|
|
2701
2755
|
setImageError(false);
|
|
2702
2756
|
setIsImageLoaded(false);
|
|
2703
2757
|
setRetryKey((k) => k + 1);
|
|
2704
2758
|
}, []);
|
|
2759
|
+
const zoomIn = React.useCallback(() => {
|
|
2760
|
+
setScale((s) => Math.min(s + ZOOM_STEP, MAX_SCALE));
|
|
2761
|
+
}, []);
|
|
2762
|
+
const zoomOut = React.useCallback(() => {
|
|
2763
|
+
setScale((s) => Math.max(s - ZOOM_STEP, MIN_SCALE));
|
|
2764
|
+
if (scaleRef.current - ZOOM_STEP <= 1) setPosition({ x: 0, y: 0 });
|
|
2765
|
+
}, []);
|
|
2766
|
+
const resetZoom = React.useCallback(() => {
|
|
2767
|
+
setScale(1);
|
|
2768
|
+
setPosition({ x: 0, y: 0 });
|
|
2769
|
+
}, []);
|
|
2770
|
+
const handleDoubleClick = React.useCallback(() => {
|
|
2771
|
+
const current = scaleRef.current;
|
|
2772
|
+
if (current !== 1) {
|
|
2773
|
+
setScale(1);
|
|
2774
|
+
setPosition({ x: 0, y: 0 });
|
|
2775
|
+
} else {
|
|
2776
|
+
setScale(2);
|
|
2777
|
+
}
|
|
2778
|
+
}, []);
|
|
2779
|
+
const handleMouseDown = React.useCallback((e) => {
|
|
2780
|
+
if (scaleRef.current <= 1) return;
|
|
2781
|
+
e.preventDefault();
|
|
2782
|
+
setIsDragging(true);
|
|
2783
|
+
dragStartRef.current = {
|
|
2784
|
+
x: e.clientX - positionRef.current.x,
|
|
2785
|
+
y: e.clientY - positionRef.current.y
|
|
2786
|
+
};
|
|
2787
|
+
}, []);
|
|
2788
|
+
const handleMouseMove = React.useCallback((e) => {
|
|
2789
|
+
if (!isDragging) return;
|
|
2790
|
+
setPosition({
|
|
2791
|
+
x: e.clientX - dragStartRef.current.x,
|
|
2792
|
+
y: e.clientY - dragStartRef.current.y
|
|
2793
|
+
});
|
|
2794
|
+
}, [isDragging]);
|
|
2795
|
+
const handleMouseUp = React.useCallback(() => {
|
|
2796
|
+
setIsDragging(false);
|
|
2797
|
+
}, []);
|
|
2798
|
+
const handleBackdropClick = React.useCallback((e) => {
|
|
2799
|
+
if (e.target === e.currentTarget && !isZoomed) {
|
|
2800
|
+
onClose();
|
|
2801
|
+
}
|
|
2802
|
+
}, [isZoomed, onClose]);
|
|
2705
2803
|
const previewUrl = attachment.preview || createDataUrl(attachment.base64Data, attachment.mimeType);
|
|
2804
|
+
const zoomPercent = Math.round(scale * 100);
|
|
2706
2805
|
return /* @__PURE__ */ jsxRuntime.jsxs(react$1.Dialog, { open: isOpen, onClose, className: "relative", style: { zIndex: 99999 }, children: [
|
|
2707
2806
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2708
2807
|
react$1.DialogBackdrop,
|
|
2709
2808
|
{
|
|
2710
|
-
className: "fixed inset-0",
|
|
2711
|
-
style: { zIndex: 99999
|
|
2809
|
+
className: "fixed inset-0 bg-black/90 backdrop-blur-sm",
|
|
2810
|
+
style: { zIndex: 99999 }
|
|
2712
2811
|
}
|
|
2713
2812
|
),
|
|
2714
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
onClick: onClose,
|
|
2729
|
-
className: "cursor-pointer flex items-center justify-center w-8 h-8 rounded-md bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400",
|
|
2730
|
-
"aria-label": t("BiChat.Image.Close"),
|
|
2731
|
-
type: "button",
|
|
2732
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 18, weight: "bold" })
|
|
2733
|
-
}
|
|
2734
|
-
)
|
|
2735
|
-
] }),
|
|
2736
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2737
|
-
"div",
|
|
2738
|
-
{
|
|
2739
|
-
className: "relative flex-1 flex items-center justify-center min-h-0",
|
|
2740
|
-
onClick: (e) => {
|
|
2741
|
-
if (e.target === e.currentTarget) onClose();
|
|
2742
|
-
},
|
|
2743
|
-
children: [
|
|
2744
|
-
!isImageLoaded && !imageError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2745
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-2 border-gray-300 dark:border-gray-700 border-t-gray-500 dark:border-t-gray-400 rounded-full animate-spin" }),
|
|
2746
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Loading") })
|
|
2747
|
-
] }) }),
|
|
2748
|
-
imageError && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: "flex flex-col items-center justify-center text-center max-w-xs", children: [
|
|
2749
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-16 h-16 rounded-2xl bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 mb-5", children: /* @__PURE__ */ jsxRuntime.jsx(react.ImageBroken, { size: 28, className: "text-gray-400 dark:text-gray-500", weight: "duotone" }) }),
|
|
2750
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-1", children: t("BiChat.Image.FailedToLoad") }),
|
|
2751
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 dark:text-gray-500 mb-5 truncate max-w-full", children: attachment.filename }),
|
|
2752
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2753
|
-
"button",
|
|
2754
|
-
{
|
|
2755
|
-
type: "button",
|
|
2756
|
-
onClick: handleRetry,
|
|
2757
|
-
className: "cursor-pointer inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700 border border-gray-200 dark:border-gray-700 rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-400",
|
|
2758
|
-
"aria-label": t("BiChat.Image.Retry"),
|
|
2759
|
-
children: [
|
|
2760
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, weight: "bold" }),
|
|
2761
|
-
t("BiChat.Retry.Label")
|
|
2762
|
-
]
|
|
2763
|
-
}
|
|
2764
|
-
)
|
|
2813
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2814
|
+
react$1.DialogPanel,
|
|
2815
|
+
{
|
|
2816
|
+
className: "fixed inset-0 flex flex-col",
|
|
2817
|
+
style: { zIndex: 1e5 },
|
|
2818
|
+
onMouseMove: handleMouseMove,
|
|
2819
|
+
onMouseUp: handleMouseUp,
|
|
2820
|
+
onMouseLeave: handleMouseUp,
|
|
2821
|
+
children: [
|
|
2822
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center px-5 py-3 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
2823
|
+
hasMultipleImages && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/50 tabular-nums whitespace-nowrap font-medium", children: [
|
|
2824
|
+
currentIndex + 1,
|
|
2825
|
+
" / ",
|
|
2826
|
+
allAttachments?.length
|
|
2765
2827
|
] }),
|
|
2766
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
className:
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2828
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-white/90 truncate font-medium", children: attachment.filename }),
|
|
2829
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/40 whitespace-nowrap", children: formatFileSize(attachment.sizeBytes) })
|
|
2830
|
+
] }) }),
|
|
2831
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2832
|
+
"div",
|
|
2833
|
+
{
|
|
2834
|
+
ref: imageAreaRef,
|
|
2835
|
+
className: "relative flex-1 flex items-center justify-center min-h-0 px-4 pb-4",
|
|
2836
|
+
onClick: handleBackdropClick,
|
|
2837
|
+
style: { cursor: isZoomed ? isDragging ? "grabbing" : "grab" : "default" },
|
|
2838
|
+
children: [
|
|
2839
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2840
|
+
"button",
|
|
2841
|
+
{
|
|
2842
|
+
onClick: onClose,
|
|
2843
|
+
className: "absolute top-3 right-5 z-30 cursor-pointer flex items-center justify-center w-10 h-10 rounded-full bg-black/50 hover:bg-black/70 backdrop-blur-md text-white/80 hover:text-white border border-white/10 transition-all duration-200 shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2844
|
+
"aria-label": t("BiChat.Image.Close"),
|
|
2845
|
+
type: "button",
|
|
2846
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 20, weight: "bold" })
|
|
2847
|
+
}
|
|
2848
|
+
),
|
|
2849
|
+
!isImageLoaded && !imageError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2850
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 border-2 border-white/20 border-t-white/60 rounded-full animate-spin" }),
|
|
2851
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-white/40", children: t("BiChat.Loading") })
|
|
2852
|
+
] }) }),
|
|
2853
|
+
imageError && /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "alert", className: "flex flex-col items-center justify-center text-center max-w-xs", children: [
|
|
2854
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center w-16 h-16 rounded-2xl bg-white/5 border border-white/10 mb-5", children: /* @__PURE__ */ jsxRuntime.jsx(react.ImageBroken, { size: 28, className: "text-white/30", weight: "duotone" }) }),
|
|
2855
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-white/70 mb-1", children: t("BiChat.Image.FailedToLoad") }),
|
|
2856
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-white/30 mb-5 truncate max-w-full", children: attachment.filename }),
|
|
2857
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2858
|
+
"button",
|
|
2859
|
+
{
|
|
2860
|
+
type: "button",
|
|
2861
|
+
onClick: handleRetry,
|
|
2862
|
+
className: "cursor-pointer inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-white/80 bg-white/10 hover:bg-white/15 border border-white/10 rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2863
|
+
"aria-label": t("BiChat.Image.Retry"),
|
|
2864
|
+
children: [
|
|
2865
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, weight: "bold" }),
|
|
2866
|
+
t("BiChat.Retry.Label")
|
|
2867
|
+
]
|
|
2868
|
+
}
|
|
2869
|
+
)
|
|
2870
|
+
] }),
|
|
2871
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2872
|
+
"img",
|
|
2873
|
+
{
|
|
2874
|
+
src: previewUrl,
|
|
2875
|
+
alt: attachment.filename,
|
|
2876
|
+
className: [
|
|
2877
|
+
"relative z-0 max-w-[85vw] max-h-[calc(100vh-160px)] object-contain select-none rounded-lg",
|
|
2878
|
+
"transition-opacity duration-300 ease-out",
|
|
2879
|
+
isImageLoaded ? "opacity-100" : "opacity-0"
|
|
2880
|
+
].join(" "),
|
|
2881
|
+
style: {
|
|
2882
|
+
transform: `translate(${position.x}px, ${position.y}px) scale(${scale})`,
|
|
2883
|
+
transformOrigin: "center center",
|
|
2884
|
+
transition: isDragging ? "opacity 0.3s ease-out" : "transform 0.2s ease-out, opacity 0.3s ease-out"
|
|
2885
|
+
},
|
|
2886
|
+
onLoad: () => setIsImageLoaded(true),
|
|
2887
|
+
onError: () => setImageError(true),
|
|
2888
|
+
onMouseDown: handleMouseDown,
|
|
2889
|
+
onDoubleClick: handleDoubleClick,
|
|
2890
|
+
loading: "lazy",
|
|
2891
|
+
draggable: false
|
|
2892
|
+
},
|
|
2893
|
+
retryKey
|
|
2894
|
+
),
|
|
2895
|
+
hasMultipleImages && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2896
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2897
|
+
"button",
|
|
2898
|
+
{
|
|
2899
|
+
onClick: () => onNavigate?.("prev"),
|
|
2900
|
+
disabled: !canNavigatePrev || !isImageLoaded || imageError,
|
|
2901
|
+
className: [
|
|
2902
|
+
"absolute left-4 top-1/2 -translate-y-1/2 z-20",
|
|
2903
|
+
"flex items-center justify-center w-11 h-11 rounded-full",
|
|
2904
|
+
"transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2905
|
+
canNavigatePrev && isImageLoaded && !imageError ? "cursor-pointer bg-black/40 hover:bg-black/60 backdrop-blur-md text-white/80 hover:text-white shadow-lg border border-white/10" : "bg-black/20 text-white/20 cursor-not-allowed"
|
|
2906
|
+
].join(" "),
|
|
2907
|
+
"aria-label": t("BiChat.Image.Previous"),
|
|
2908
|
+
type: "button",
|
|
2909
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.CaretLeft, { size: 20, weight: "bold" })
|
|
2910
|
+
}
|
|
2911
|
+
),
|
|
2912
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2913
|
+
"button",
|
|
2914
|
+
{
|
|
2915
|
+
onClick: () => onNavigate?.("next"),
|
|
2916
|
+
disabled: !canNavigateNext || !isImageLoaded || imageError,
|
|
2917
|
+
className: [
|
|
2918
|
+
"absolute right-4 top-1/2 -translate-y-1/2 z-20",
|
|
2919
|
+
"flex items-center justify-center w-11 h-11 rounded-full",
|
|
2920
|
+
"transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2921
|
+
canNavigateNext && isImageLoaded && !imageError ? "cursor-pointer bg-black/40 hover:bg-black/60 backdrop-blur-md text-white/80 hover:text-white shadow-lg border border-white/10" : "bg-black/20 text-white/20 cursor-not-allowed"
|
|
2922
|
+
].join(" "),
|
|
2923
|
+
"aria-label": t("BiChat.Image.Next"),
|
|
2924
|
+
type: "button",
|
|
2925
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.CaretRight, { size: 20, weight: "bold" })
|
|
2926
|
+
}
|
|
2927
|
+
)
|
|
2928
|
+
] }),
|
|
2929
|
+
isImageLoaded && !imageError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 z-20 flex items-center gap-0.5 bg-black/50 backdrop-blur-xl rounded-full px-1.5 py-1.5 border border-white/10 shadow-2xl", children: [
|
|
2930
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2931
|
+
"button",
|
|
2932
|
+
{
|
|
2933
|
+
type: "button",
|
|
2934
|
+
onClick: zoomOut,
|
|
2935
|
+
disabled: scale <= MIN_SCALE,
|
|
2936
|
+
className: "cursor-pointer flex items-center justify-center w-8 h-8 rounded-full text-white/70 hover:text-white hover:bg-white/10 transition-colors disabled:text-white/20 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
2937
|
+
"aria-label": t("BiChat.Image.ZoomOut"),
|
|
2938
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.MagnifyingGlassMinus, { size: 16, weight: "bold" })
|
|
2939
|
+
}
|
|
2940
|
+
),
|
|
2941
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-white/60 tabular-nums font-medium min-w-[3.5rem] text-center select-none", children: [
|
|
2942
|
+
zoomPercent,
|
|
2943
|
+
"%"
|
|
2944
|
+
] }),
|
|
2945
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2946
|
+
"button",
|
|
2947
|
+
{
|
|
2948
|
+
type: "button",
|
|
2949
|
+
onClick: zoomIn,
|
|
2950
|
+
disabled: scale >= MAX_SCALE,
|
|
2951
|
+
className: "cursor-pointer flex items-center justify-center w-8 h-8 rounded-full text-white/70 hover:text-white hover:bg-white/10 transition-colors disabled:text-white/20 disabled:cursor-not-allowed disabled:hover:bg-transparent",
|
|
2952
|
+
"aria-label": t("BiChat.Image.ZoomIn"),
|
|
2953
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.MagnifyingGlassPlus, { size: 16, weight: "bold" })
|
|
2954
|
+
}
|
|
2955
|
+
),
|
|
2956
|
+
isZoomed && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2957
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-white/15 mx-1" }),
|
|
2958
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2959
|
+
"button",
|
|
2960
|
+
{
|
|
2961
|
+
type: "button",
|
|
2962
|
+
onClick: resetZoom,
|
|
2963
|
+
className: "cursor-pointer flex items-center justify-center w-8 h-8 rounded-full text-white/70 hover:text-white hover:bg-white/10 transition-colors",
|
|
2964
|
+
"aria-label": t("BiChat.Image.ResetZoom"),
|
|
2965
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.ArrowsIn, { size: 16, weight: "bold" })
|
|
2966
|
+
}
|
|
2967
|
+
)
|
|
2968
|
+
] })
|
|
2969
|
+
] })
|
|
2970
|
+
]
|
|
2971
|
+
}
|
|
2972
|
+
)
|
|
2973
|
+
]
|
|
2974
|
+
}
|
|
2975
|
+
)
|
|
2821
2976
|
] });
|
|
2822
2977
|
}
|
|
2823
2978
|
var ImageModal_default = ImageModal;
|
|
@@ -2869,6 +3024,7 @@ function UserMessage({
|
|
|
2869
3024
|
const [draftContent, setDraftContent] = React.useState("");
|
|
2870
3025
|
const [isCopied, setIsCopied] = React.useState(false);
|
|
2871
3026
|
const copyFeedbackTimeoutRef = React.useRef(null);
|
|
3027
|
+
const editTextareaRef = React.useRef(null);
|
|
2872
3028
|
const classes = mergeClassNames(defaultClassNames, classNameOverrides);
|
|
2873
3029
|
React.useEffect(() => {
|
|
2874
3030
|
return () => {
|
|
@@ -2878,6 +3034,16 @@ function UserMessage({
|
|
|
2878
3034
|
}
|
|
2879
3035
|
};
|
|
2880
3036
|
}, []);
|
|
3037
|
+
React.useEffect(() => {
|
|
3038
|
+
if (isEditing && editTextareaRef.current) {
|
|
3039
|
+
const textarea = editTextareaRef.current;
|
|
3040
|
+
textarea.focus();
|
|
3041
|
+
textarea.selectionStart = textarea.value.length;
|
|
3042
|
+
textarea.selectionEnd = textarea.value.length;
|
|
3043
|
+
textarea.style.height = "auto";
|
|
3044
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 300)}px`;
|
|
3045
|
+
}
|
|
3046
|
+
}, [isEditing]);
|
|
2881
3047
|
const normalizedAttachments = turn.attachments.map((attachment) => {
|
|
2882
3048
|
if (!attachment.mimeType.startsWith("image/")) {
|
|
2883
3049
|
return attachment;
|
|
@@ -2962,6 +3128,21 @@ function UserMessage({
|
|
|
2962
3128
|
onEdit(turnId, newContent);
|
|
2963
3129
|
setIsEditing(false);
|
|
2964
3130
|
}, [onEdit, turnId, draftContent, turn.content]);
|
|
3131
|
+
const handleEditKeyDown = React.useCallback((e) => {
|
|
3132
|
+
if (e.key === "Escape") {
|
|
3133
|
+
e.preventDefault();
|
|
3134
|
+
handleEditCancel();
|
|
3135
|
+
} else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
3136
|
+
e.preventDefault();
|
|
3137
|
+
handleEditSave();
|
|
3138
|
+
}
|
|
3139
|
+
}, [handleEditCancel, handleEditSave]);
|
|
3140
|
+
const handleDraftChange = React.useCallback((e) => {
|
|
3141
|
+
setDraftContent(e.target.value);
|
|
3142
|
+
const el = e.target;
|
|
3143
|
+
el.style.height = "auto";
|
|
3144
|
+
el.style.height = `${Math.min(el.scrollHeight, 300)}px`;
|
|
3145
|
+
}, []);
|
|
2965
3146
|
const handleNavigate = React.useCallback(
|
|
2966
3147
|
(direction) => {
|
|
2967
3148
|
if (selectedImageIndex === null) return;
|
|
@@ -3012,36 +3193,48 @@ function UserMessage({
|
|
|
3012
3193
|
}
|
|
3013
3194
|
)
|
|
3014
3195
|
) }),
|
|
3015
|
-
turn.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.bubble, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-
|
|
3196
|
+
turn.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.bubble, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
3016
3197
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3017
3198
|
"textarea",
|
|
3018
3199
|
{
|
|
3200
|
+
ref: editTextareaRef,
|
|
3019
3201
|
value: draftContent,
|
|
3020
|
-
onChange:
|
|
3021
|
-
|
|
3022
|
-
"
|
|
3202
|
+
onChange: handleDraftChange,
|
|
3203
|
+
onKeyDown: handleEditKeyDown,
|
|
3204
|
+
className: "w-full min-h-[60px] max-h-[300px] resize-none rounded-xl border border-white/20 bg-white/[0.08] px-3.5 py-2.5 text-sm text-white leading-relaxed outline-none focus:bg-white/[0.12] focus:border-white/30 focus:ring-1 focus:ring-white/20 transition-all duration-200",
|
|
3205
|
+
"aria-label": t("BiChat.Message.EditMessage"),
|
|
3206
|
+
rows: 1
|
|
3023
3207
|
}
|
|
3024
3208
|
),
|
|
3025
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-
|
|
3026
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3027
|
-
"
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3209
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
3210
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-white/30 select-none hidden sm:inline", children: [
|
|
3211
|
+
"Esc \xB7 ",
|
|
3212
|
+
typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(
|
|
3213
|
+
navigator.userAgentData?.platform ?? navigator?.platform ?? ""
|
|
3214
|
+
) ? "\u2318" : "Ctrl",
|
|
3215
|
+
"+Enter"
|
|
3216
|
+
] }),
|
|
3217
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
3218
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3219
|
+
"button",
|
|
3220
|
+
{
|
|
3221
|
+
type: "button",
|
|
3222
|
+
onClick: handleEditCancel,
|
|
3223
|
+
className: "cursor-pointer px-3 py-1.5 rounded-lg text-white/60 hover:text-white hover:bg-white/10 transition-colors text-sm",
|
|
3224
|
+
children: t("BiChat.Message.Cancel")
|
|
3225
|
+
}
|
|
3226
|
+
),
|
|
3227
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3228
|
+
"button",
|
|
3229
|
+
{
|
|
3230
|
+
type: "button",
|
|
3231
|
+
onClick: handleEditSave,
|
|
3232
|
+
className: "cursor-pointer px-4 py-1.5 rounded-lg bg-white text-primary-700 font-medium text-sm hover:bg-white/90 transition-all shadow-sm disabled:opacity-40 disabled:cursor-not-allowed disabled:shadow-none",
|
|
3233
|
+
disabled: !draftContent.trim() || draftContent === turn.content,
|
|
3234
|
+
children: t("BiChat.Message.Save")
|
|
3235
|
+
}
|
|
3236
|
+
)
|
|
3237
|
+
] })
|
|
3045
3238
|
] })
|
|
3046
3239
|
] }) : renderSlot(slots?.content, contentSlotProps, turn.content) }) }),
|
|
3047
3240
|
!hideActions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${classes.actions} ${isCopied ? "opacity-100" : ""}`, children: renderSlot(
|
|
@@ -3189,67 +3382,295 @@ function StreamingCursor() {
|
|
|
3189
3382
|
}
|
|
3190
3383
|
var StreamingCursor_default = StreamingCursor;
|
|
3191
3384
|
|
|
3192
|
-
// ui/src/bichat/components/AssistantMessage.tsx
|
|
3193
|
-
init_ChartCard();
|
|
3385
|
+
// ui/src/bichat/components/AssistantMessage.tsx
|
|
3386
|
+
init_ChartCard();
|
|
3387
|
+
|
|
3388
|
+
// ui/src/bichat/components/InteractiveTableCard.tsx
|
|
3389
|
+
init_useTranslation();
|
|
3390
|
+
init_TableExportButton();
|
|
3391
|
+
var PAGE_SIZE_OPTIONS = [10, 25, 50, 100, 200];
|
|
3392
|
+
function formatCell(value) {
|
|
3393
|
+
if (value === null || value === void 0) return "NULL";
|
|
3394
|
+
if (typeof value === "object") {
|
|
3395
|
+
try {
|
|
3396
|
+
return JSON.stringify(value);
|
|
3397
|
+
} catch {
|
|
3398
|
+
return String(value);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
3401
|
+
return String(value);
|
|
3402
|
+
}
|
|
3403
|
+
var InteractiveTableCard = React.memo(function InteractiveTableCard2({
|
|
3404
|
+
table,
|
|
3405
|
+
onSendMessage,
|
|
3406
|
+
sendDisabled = false
|
|
3407
|
+
}) {
|
|
3408
|
+
const { t } = useTranslation();
|
|
3409
|
+
const defaultPageSize = Math.min(Math.max(table.pageSize || 25, 1), 200);
|
|
3410
|
+
const [page, setPage] = React.useState(1);
|
|
3411
|
+
const [pageSize, setPageSize] = React.useState(defaultPageSize);
|
|
3412
|
+
React.useEffect(() => {
|
|
3413
|
+
const nextPageSize = Math.min(Math.max(table.pageSize || 25, 1), 200);
|
|
3414
|
+
setPage(1);
|
|
3415
|
+
setPageSize(nextPageSize);
|
|
3416
|
+
}, [table.id, table.pageSize]);
|
|
3417
|
+
const totalRows = table.rows.length;
|
|
3418
|
+
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
|
|
3419
|
+
React.useEffect(() => {
|
|
3420
|
+
if (page > totalPages) {
|
|
3421
|
+
setPage(totalPages);
|
|
3422
|
+
}
|
|
3423
|
+
}, [page, totalPages]);
|
|
3424
|
+
const pageRows = React.useMemo(() => {
|
|
3425
|
+
const start = (page - 1) * pageSize;
|
|
3426
|
+
return table.rows.slice(start, start + pageSize);
|
|
3427
|
+
}, [page, pageSize, table.rows]);
|
|
3428
|
+
const pageSizeOptions = React.useMemo(() => {
|
|
3429
|
+
const set = /* @__PURE__ */ new Set([...PAGE_SIZE_OPTIONS, defaultPageSize]);
|
|
3430
|
+
return [...set].sort((a, b) => a - b);
|
|
3431
|
+
}, [defaultPageSize]);
|
|
3432
|
+
const canExportViaPrompt = !!onSendMessage && !!table.exportPrompt;
|
|
3433
|
+
const exportDisabled = sendDisabled || !table.export?.url && !canExportViaPrompt;
|
|
3434
|
+
const handleExport = React.useCallback(() => {
|
|
3435
|
+
if (table.export?.url) {
|
|
3436
|
+
try {
|
|
3437
|
+
const parsed = new URL(table.export.url, window.location.origin);
|
|
3438
|
+
if (!["http:", "https:", "blob:"].includes(parsed.protocol)) {
|
|
3439
|
+
console.warn("[InteractiveTableCard] Blocked export URL with unsafe protocol:", parsed.protocol);
|
|
3440
|
+
return;
|
|
3441
|
+
}
|
|
3442
|
+
} catch {
|
|
3443
|
+
console.warn("[InteractiveTableCard] Blocked malformed export URL");
|
|
3444
|
+
return;
|
|
3445
|
+
}
|
|
3446
|
+
const link = document.createElement("a");
|
|
3447
|
+
link.href = table.export.url;
|
|
3448
|
+
link.download = table.export.filename || "table_export.xlsx";
|
|
3449
|
+
link.rel = "noopener noreferrer";
|
|
3450
|
+
document.body.appendChild(link);
|
|
3451
|
+
link.click();
|
|
3452
|
+
document.body.removeChild(link);
|
|
3453
|
+
return;
|
|
3454
|
+
}
|
|
3455
|
+
if (canExportViaPrompt && table.exportPrompt) {
|
|
3456
|
+
onSendMessage?.(table.exportPrompt);
|
|
3457
|
+
}
|
|
3458
|
+
}, [canExportViaPrompt, onSendMessage, table.export, table.exportPrompt]);
|
|
3459
|
+
const from = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
3460
|
+
const to = Math.min(page * pageSize, totalRows);
|
|
3461
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "w-full rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900/40", children: [
|
|
3462
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-wrap items-center justify-between gap-2 border-b border-gray-200 dark:border-gray-700 px-3 py-2", children: [
|
|
3463
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
3464
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: table.title || t("BiChat.Table.QueryResults") }),
|
|
3465
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
3466
|
+
totalRows === 1 ? t("BiChat.Table.OneRowLoaded") : t("BiChat.Table.RowsLoaded", { count: String(totalRows) }),
|
|
3467
|
+
table.truncated ? ` ${t("BiChat.Table.TruncatedSuffix")}` : ""
|
|
3468
|
+
] })
|
|
3469
|
+
] }),
|
|
3470
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3471
|
+
exports.TableExportButton,
|
|
3472
|
+
{
|
|
3473
|
+
onClick: handleExport,
|
|
3474
|
+
disabled: exportDisabled,
|
|
3475
|
+
label: t("BiChat.Table.ExportToExcel"),
|
|
3476
|
+
disabledTooltip: sendDisabled ? t("BiChat.Table.PleaseWait") : t("BiChat.Table.ExportUnavailable")
|
|
3477
|
+
}
|
|
3478
|
+
)
|
|
3479
|
+
] }),
|
|
3480
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[420px] overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse text-sm", children: [
|
|
3481
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "sticky top-0 bg-gray-100 dark:bg-gray-800 z-10", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-gray-200 dark:border-gray-700", children: table.headers.map((header, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3482
|
+
"th",
|
|
3483
|
+
{
|
|
3484
|
+
className: "px-3 py-2 text-left font-semibold text-gray-700 dark:text-gray-200 whitespace-nowrap",
|
|
3485
|
+
children: header
|
|
3486
|
+
},
|
|
3487
|
+
`${table.id}-header-${index}`
|
|
3488
|
+
)) }) }),
|
|
3489
|
+
/* @__PURE__ */ jsxRuntime.jsxs("tbody", { children: [
|
|
3490
|
+
pageRows.map((row, rowIndex) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3491
|
+
"tr",
|
|
3492
|
+
{
|
|
3493
|
+
className: "border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/40",
|
|
3494
|
+
children: table.columns.map((_, columnIndex) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3495
|
+
"td",
|
|
3496
|
+
{
|
|
3497
|
+
className: "px-3 py-2 text-gray-700 dark:text-gray-300 align-top",
|
|
3498
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block max-w-[420px] truncate", title: formatCell(row[columnIndex]), children: formatCell(row[columnIndex]) })
|
|
3499
|
+
},
|
|
3500
|
+
`${table.id}-cell-${rowIndex}-${columnIndex}`
|
|
3501
|
+
))
|
|
3502
|
+
},
|
|
3503
|
+
`${table.id}-row-${rowIndex}`
|
|
3504
|
+
)),
|
|
3505
|
+
pageRows.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3506
|
+
"td",
|
|
3507
|
+
{
|
|
3508
|
+
colSpan: table.columns.length,
|
|
3509
|
+
className: "px-3 py-6 text-center text-sm text-gray-500 dark:text-gray-400",
|
|
3510
|
+
children: t("BiChat.Table.NoRows")
|
|
3511
|
+
}
|
|
3512
|
+
) })
|
|
3513
|
+
] })
|
|
3514
|
+
] }) }),
|
|
3515
|
+
/* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "flex flex-wrap items-center justify-between gap-2 border-t border-gray-200 dark:border-gray-700 px-3 py-2", children: [
|
|
3516
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Table.Showing", {
|
|
3517
|
+
from: String(from),
|
|
3518
|
+
to: String(to),
|
|
3519
|
+
total: String(totalRows)
|
|
3520
|
+
}) }),
|
|
3521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3522
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs text-gray-500 dark:text-gray-400", htmlFor: `${table.id}-page-size`, children: t("BiChat.Table.RowsLabel") }),
|
|
3523
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3524
|
+
"select",
|
|
3525
|
+
{
|
|
3526
|
+
id: `${table.id}-page-size`,
|
|
3527
|
+
value: pageSize,
|
|
3528
|
+
onChange: (event) => {
|
|
3529
|
+
setPageSize(Number(event.target.value));
|
|
3530
|
+
setPage(1);
|
|
3531
|
+
},
|
|
3532
|
+
className: "rounded border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 text-xs text-gray-700 dark:text-gray-200",
|
|
3533
|
+
children: pageSizeOptions.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option, children: option }, `${table.id}-size-${option}`))
|
|
3534
|
+
}
|
|
3535
|
+
),
|
|
3536
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3537
|
+
"button",
|
|
3538
|
+
{
|
|
3539
|
+
type: "button",
|
|
3540
|
+
onClick: () => setPage((current) => Math.max(1, current - 1)),
|
|
3541
|
+
disabled: page <= 1,
|
|
3542
|
+
className: "cursor-pointer rounded border border-gray-300 dark:border-gray-600 px-2 py-1 text-xs text-gray-700 dark:text-gray-200 disabled:cursor-not-allowed disabled:opacity-50",
|
|
3543
|
+
children: t("BiChat.Table.Prev")
|
|
3544
|
+
}
|
|
3545
|
+
),
|
|
3546
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Table.PageOf", { page: String(page), total: String(totalPages) }) }),
|
|
3547
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3548
|
+
"button",
|
|
3549
|
+
{
|
|
3550
|
+
type: "button",
|
|
3551
|
+
onClick: () => setPage((current) => Math.min(totalPages, current + 1)),
|
|
3552
|
+
disabled: page >= totalPages,
|
|
3553
|
+
className: "cursor-pointer rounded border border-gray-300 dark:border-gray-600 px-2 py-1 text-xs text-gray-700 dark:text-gray-200 disabled:cursor-not-allowed disabled:opacity-50",
|
|
3554
|
+
children: t("BiChat.Table.Next")
|
|
3555
|
+
}
|
|
3556
|
+
)
|
|
3557
|
+
] })
|
|
3558
|
+
] }),
|
|
3559
|
+
table.truncated && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "border-t border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-700 dark:border-amber-700/60 dark:bg-amber-900/20 dark:text-amber-300", children: t("BiChat.Table.TruncatedNotice") })
|
|
3560
|
+
] });
|
|
3561
|
+
});
|
|
3562
|
+
|
|
3563
|
+
// ui/src/bichat/components/SourcesPanel.tsx
|
|
3564
|
+
init_useTranslation();
|
|
3565
|
+
function extractDomain(url) {
|
|
3566
|
+
try {
|
|
3567
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
3568
|
+
} catch {
|
|
3569
|
+
return "";
|
|
3570
|
+
}
|
|
3571
|
+
}
|
|
3572
|
+
var PALETTE = [
|
|
3573
|
+
"#c0392b",
|
|
3574
|
+
"#d35400",
|
|
3575
|
+
"#f39c12",
|
|
3576
|
+
"#27ae60",
|
|
3577
|
+
"#16a085",
|
|
3578
|
+
"#2980b9",
|
|
3579
|
+
"#8e44ad",
|
|
3580
|
+
"#d63384"
|
|
3581
|
+
];
|
|
3582
|
+
function domainColor(domain) {
|
|
3583
|
+
let h = 0;
|
|
3584
|
+
for (let i = 0; i < domain.length; i++) h = domain.charCodeAt(i) + ((h << 5) - h);
|
|
3585
|
+
return PALETTE[Math.abs(h) % PALETTE.length];
|
|
3586
|
+
}
|
|
3194
3587
|
function SourcesPanel({ citations }) {
|
|
3195
|
-
|
|
3196
|
-
|
|
3588
|
+
const { t } = useTranslation();
|
|
3589
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
3590
|
+
const open = React.useCallback(() => setIsOpen(true), []);
|
|
3591
|
+
const close = React.useCallback(() => setIsOpen(false), []);
|
|
3592
|
+
if (!citations?.length) return null;
|
|
3593
|
+
const domains = [...new Set(
|
|
3594
|
+
citations.filter((c) => c.url).map((c) => extractDomain(c.url)).filter(Boolean)
|
|
3595
|
+
)];
|
|
3596
|
+
const previewDomains = domains.slice(0, 5);
|
|
3597
|
+
if (!isOpen) {
|
|
3598
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3599
|
+
"button",
|
|
3600
|
+
{
|
|
3601
|
+
type: "button",
|
|
3602
|
+
onClick: open,
|
|
3603
|
+
className: "cursor-pointer inline-flex items-center gap-2 rounded-full px-3 py-1.5\n bg-gray-50 hover:bg-gray-100 dark:bg-gray-700/50 dark:hover:bg-gray-600/60\n border border-gray-200/70 dark:border-gray-600/40\n transition-colors duration-150\n focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--bichat-primary,theme(colors.blue.500))]/40",
|
|
3604
|
+
children: [
|
|
3605
|
+
previewDomains.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex -space-x-1.5", children: previewDomains.map((domain, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3606
|
+
"span",
|
|
3607
|
+
{
|
|
3608
|
+
className: "relative w-5 h-5 rounded-full flex items-center justify-center text-[8px] font-bold text-white\n ring-2 ring-white dark:ring-gray-800 select-none",
|
|
3609
|
+
style: { backgroundColor: domainColor(domain), zIndex: previewDomains.length - i },
|
|
3610
|
+
"aria-hidden": "true",
|
|
3611
|
+
children: domain[0]?.toUpperCase()
|
|
3612
|
+
},
|
|
3613
|
+
domain
|
|
3614
|
+
)) }),
|
|
3615
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-300 tabular-nums", children: [
|
|
3616
|
+
citations.length,
|
|
3617
|
+
" ",
|
|
3618
|
+
t(citations.length === 1 ? "BiChat.Sources.Source" : "BiChat.Sources.Sources")
|
|
3619
|
+
] })
|
|
3620
|
+
]
|
|
3621
|
+
}
|
|
3622
|
+
) });
|
|
3197
3623
|
}
|
|
3198
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
3199
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3624
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800/90 shadow-sm overflow-hidden", children: [
|
|
3625
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [
|
|
3626
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: t("BiChat.Sources.Title") }),
|
|
3200
3627
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3201
|
-
"
|
|
3628
|
+
"button",
|
|
3202
3629
|
{
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3208
|
-
"path",
|
|
3209
|
-
{
|
|
3210
|
-
strokeLinecap: "round",
|
|
3211
|
-
strokeLinejoin: "round",
|
|
3212
|
-
strokeWidth: 2,
|
|
3213
|
-
d: "M9 5l7 7-7 7"
|
|
3214
|
-
}
|
|
3215
|
-
)
|
|
3630
|
+
type: "button",
|
|
3631
|
+
onClick: close,
|
|
3632
|
+
className: "cursor-pointer flex items-center justify-center w-7 h-7 rounded-full\n text-gray-400 hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300\n hover:bg-gray-100 dark:hover:bg-gray-700\n transition-colors duration-150\n focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--bichat-primary)]/40",
|
|
3633
|
+
"aria-label": t("BiChat.Sources.Close"),
|
|
3634
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 14, weight: "bold" })
|
|
3216
3635
|
}
|
|
3217
|
-
)
|
|
3218
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3219
|
-
citations.length,
|
|
3220
|
-
" ",
|
|
3221
|
-
citations.length === 1 ? "source" : "sources"
|
|
3222
|
-
] })
|
|
3636
|
+
)
|
|
3223
3637
|
] }),
|
|
3224
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3225
|
-
"
|
|
3226
|
-
{
|
|
3227
|
-
className: "
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
"
|
|
3234
|
-
{
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
}
|
|
3241
|
-
),
|
|
3242
|
-
citation.excerpt && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 text-gray-600 dark:text-gray-400 italic", children: [
|
|
3243
|
-
'"',
|
|
3244
|
-
citation.excerpt,
|
|
3245
|
-
'"'
|
|
3246
|
-
] })
|
|
3247
|
-
] })
|
|
3638
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-80 overflow-y-auto", children: citations.map((citation, index) => {
|
|
3639
|
+
const domain = citation.url ? extractDomain(citation.url) : "";
|
|
3640
|
+
const cardContent = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3641
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-medium leading-snug text-[var(--bichat-color-accent,theme(colors.blue.600))] dark:text-blue-400", children: citation.title || t("BiChat.Sources.SourceN", { n: String(index + 1) }) }),
|
|
3642
|
+
citation.excerpt && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs text-gray-500 dark:text-gray-400 line-clamp-2 leading-relaxed", children: citation.excerpt }),
|
|
3643
|
+
domain && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 mt-1.5", children: [
|
|
3644
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3645
|
+
"span",
|
|
3646
|
+
{
|
|
3647
|
+
className: "w-4 h-4 rounded-full flex items-center justify-center text-[7px] font-bold text-white flex-shrink-0 select-none",
|
|
3648
|
+
style: { backgroundColor: domainColor(domain) },
|
|
3649
|
+
"aria-hidden": "true",
|
|
3650
|
+
children: domain[0]?.toUpperCase()
|
|
3651
|
+
}
|
|
3652
|
+
),
|
|
3653
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate", children: domain })
|
|
3248
3654
|
] })
|
|
3249
|
-
}
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3655
|
+
] });
|
|
3656
|
+
const cardClass = "block px-4 py-3 border-t border-gray-100 dark:border-gray-700/50";
|
|
3657
|
+
if (citation.url) {
|
|
3658
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3659
|
+
"a",
|
|
3660
|
+
{
|
|
3661
|
+
href: citation.url,
|
|
3662
|
+
target: "_blank",
|
|
3663
|
+
rel: "noopener noreferrer",
|
|
3664
|
+
className: `${cardClass} hover:bg-gray-50 dark:hover:bg-gray-700/40 transition-colors duration-100
|
|
3665
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--bichat-primary)]/40`,
|
|
3666
|
+
children: cardContent
|
|
3667
|
+
},
|
|
3668
|
+
citation.id
|
|
3669
|
+
);
|
|
3670
|
+
}
|
|
3671
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cardClass, children: cardContent }, citation.id);
|
|
3672
|
+
}) })
|
|
3673
|
+
] });
|
|
3253
3674
|
}
|
|
3254
3675
|
var MIME_BY_TYPE = {
|
|
3255
3676
|
excel: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
@@ -3601,6 +4022,59 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3601
4022
|
] }) });
|
|
3602
4023
|
}
|
|
3603
4024
|
|
|
4025
|
+
// ui/src/bichat/components/RetryActionArea.tsx
|
|
4026
|
+
init_useTranslation();
|
|
4027
|
+
var RetryActionArea = React.memo(function RetryActionArea2({
|
|
4028
|
+
onRetry
|
|
4029
|
+
}) {
|
|
4030
|
+
const { t } = useTranslation();
|
|
4031
|
+
return (
|
|
4032
|
+
// Wrapper matches TurnBubble layout for assistant messages (justify-start = left-aligned)
|
|
4033
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4034
|
+
framerMotion.motion.div,
|
|
4035
|
+
{
|
|
4036
|
+
initial: { opacity: 0, y: 10 },
|
|
4037
|
+
animate: { opacity: 1, y: 0 },
|
|
4038
|
+
exit: { opacity: 0, y: -10 },
|
|
4039
|
+
transition: { duration: 0.2 },
|
|
4040
|
+
className: "flex justify-start",
|
|
4041
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4042
|
+
"div",
|
|
4043
|
+
{
|
|
4044
|
+
className: "flex flex-col gap-3 max-w-2xl rounded-2xl px-5 py-3 shadow-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700",
|
|
4045
|
+
role: "status",
|
|
4046
|
+
"aria-live": "polite",
|
|
4047
|
+
children: [
|
|
4048
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4049
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4050
|
+
react.Warning,
|
|
4051
|
+
{
|
|
4052
|
+
className: "w-5 h-5 text-amber-500 dark:text-amber-400 flex-shrink-0",
|
|
4053
|
+
weight: "fill"
|
|
4054
|
+
}
|
|
4055
|
+
),
|
|
4056
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t("BiChat.Retry.Subtitle") })
|
|
4057
|
+
] }),
|
|
4058
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4059
|
+
"button",
|
|
4060
|
+
{
|
|
4061
|
+
onClick: onRetry,
|
|
4062
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600 rounded-lg transition-colors 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",
|
|
4063
|
+
"aria-label": t("BiChat.Retry.Title"),
|
|
4064
|
+
children: [
|
|
4065
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, className: "w-4 h-4" }),
|
|
4066
|
+
t("BiChat.Retry.Button")
|
|
4067
|
+
]
|
|
4068
|
+
}
|
|
4069
|
+
) })
|
|
4070
|
+
]
|
|
4071
|
+
}
|
|
4072
|
+
)
|
|
4073
|
+
}
|
|
4074
|
+
)
|
|
4075
|
+
);
|
|
4076
|
+
});
|
|
4077
|
+
|
|
3604
4078
|
// ui/src/bichat/utils/debugMetrics.ts
|
|
3605
4079
|
function formatGenerationDuration(generationMs) {
|
|
3606
4080
|
return generationMs > 1e3 ? `${(generationMs / 1e3).toFixed(2)}s` : `${generationMs}ms`;
|
|
@@ -3899,6 +4373,7 @@ var defaultClassNames2 = {
|
|
|
3899
4373
|
bubble: "bg-white dark:bg-gray-800 rounded-2xl rounded-bl-sm px-4 py-3 shadow-sm",
|
|
3900
4374
|
codeOutputs: "",
|
|
3901
4375
|
charts: "mb-1 w-full",
|
|
4376
|
+
tables: "mb-1 flex flex-col gap-3",
|
|
3902
4377
|
artifacts: "mb-1 flex flex-wrap gap-2",
|
|
3903
4378
|
sources: "",
|
|
3904
4379
|
explanation: "mt-4 border-t border-gray-100 dark:border-gray-700 pt-4",
|
|
@@ -3915,6 +4390,7 @@ function mergeClassNames2(defaults, overrides) {
|
|
|
3915
4390
|
bubble: overrides.bubble ?? defaults.bubble,
|
|
3916
4391
|
codeOutputs: overrides.codeOutputs ?? defaults.codeOutputs,
|
|
3917
4392
|
charts: overrides.charts ?? defaults.charts,
|
|
4393
|
+
tables: overrides.tables ?? defaults.tables,
|
|
3918
4394
|
artifacts: overrides.artifacts ?? defaults.artifacts,
|
|
3919
4395
|
sources: overrides.sources ?? defaults.sources,
|
|
3920
4396
|
explanation: overrides.explanation ?? defaults.explanation,
|
|
@@ -3959,6 +4435,14 @@ function AssistantMessage({
|
|
|
3959
4435
|
const hasContent = turn.content?.trim().length > 0;
|
|
3960
4436
|
const hasExplanation = !!turn.explanation?.trim();
|
|
3961
4437
|
const hasPendingQuestion = !!pendingQuestion && pendingQuestion.status === "PENDING" && pendingQuestion.turnId === turnId;
|
|
4438
|
+
const hasCodeOutputs = !!turn.codeOutputs?.length;
|
|
4439
|
+
const hasChart = !!turn.chartData;
|
|
4440
|
+
const hasTables = !!turn.renderTables?.length;
|
|
4441
|
+
const hasArtifacts = !!turn.artifacts?.length;
|
|
4442
|
+
const hasDebug = showDebug && !!turn.debug;
|
|
4443
|
+
const hasRenderablePayload = hasContent || hasExplanation || hasPendingQuestion || hasCodeOutputs || hasChart || hasTables || hasArtifacts || hasDebug;
|
|
4444
|
+
const canRegenerate = !!onRegenerate && !!turnId && !isSystemMessage && isLastTurn;
|
|
4445
|
+
const showInlineRetry = !hasRenderablePayload && canRegenerate;
|
|
3962
4446
|
const handleCopyClick = React.useCallback(async () => {
|
|
3963
4447
|
try {
|
|
3964
4448
|
if (onCopy) {
|
|
@@ -4000,15 +4484,18 @@ function AssistantMessage({
|
|
|
4000
4484
|
const codeOutputsSlotProps = {
|
|
4001
4485
|
outputs: turn.codeOutputs || []
|
|
4002
4486
|
};
|
|
4487
|
+
const tablesSlotProps = {
|
|
4488
|
+
tables: turn.renderTables || []
|
|
4489
|
+
};
|
|
4003
4490
|
const artifactsSlotProps = {
|
|
4004
4491
|
artifacts: turn.artifacts || []
|
|
4005
4492
|
};
|
|
4006
4493
|
const actionsSlotProps = {
|
|
4007
4494
|
onCopy: handleCopyClick,
|
|
4008
|
-
onRegenerate:
|
|
4495
|
+
onRegenerate: canRegenerate ? handleRegenerateClick : void 0,
|
|
4009
4496
|
timestamp,
|
|
4010
4497
|
canCopy: hasContent,
|
|
4011
|
-
canRegenerate
|
|
4498
|
+
canRegenerate
|
|
4012
4499
|
};
|
|
4013
4500
|
const explanationSlotProps = {
|
|
4014
4501
|
explanation: turn.explanation || "",
|
|
@@ -4021,14 +4508,30 @@ function AssistantMessage({
|
|
|
4021
4508
|
return slot;
|
|
4022
4509
|
};
|
|
4023
4510
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: classes.root, children: [
|
|
4024
|
-
!hideAvatar && /* @__PURE__ */ jsxRuntime.jsx("div", { className: avatarClassName, children: renderSlot(slots?.avatar, avatarSlotProps, isSystemMessage ? "SYS" : "AI") }),
|
|
4511
|
+
!hideAvatar && !showInlineRetry && /* @__PURE__ */ jsxRuntime.jsx("div", { className: avatarClassName, children: renderSlot(slots?.avatar, avatarSlotProps, isSystemMessage ? "SYS" : "AI") }),
|
|
4025
4512
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: classes.wrapper, children: [
|
|
4513
|
+
showInlineRetry && /* @__PURE__ */ jsxRuntime.jsx(RetryActionArea, { onRetry: () => {
|
|
4514
|
+
void handleRegenerateClick();
|
|
4515
|
+
} }),
|
|
4026
4516
|
turn.codeOutputs && turn.codeOutputs.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.codeOutputs, children: renderSlot(
|
|
4027
4517
|
slots?.codeOutputs,
|
|
4028
4518
|
codeOutputsSlotProps,
|
|
4029
4519
|
/* @__PURE__ */ jsxRuntime.jsx(CodeOutputsPanel_default, { outputs: turn.codeOutputs })
|
|
4030
4520
|
) }),
|
|
4031
4521
|
turn.chartData && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.charts, children: renderSlot(slots?.charts, chartsSlotProps, /* @__PURE__ */ jsxRuntime.jsx(ChartCard, { chartData: turn.chartData })) }),
|
|
4522
|
+
turn.renderTables && turn.renderTables.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: classes.tables, children: renderSlot(
|
|
4523
|
+
slots?.tables,
|
|
4524
|
+
tablesSlotProps,
|
|
4525
|
+
turn.renderTables.map((table) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4526
|
+
InteractiveTableCard,
|
|
4527
|
+
{
|
|
4528
|
+
table,
|
|
4529
|
+
onSendMessage,
|
|
4530
|
+
sendDisabled: sendDisabled || isStreaming
|
|
4531
|
+
},
|
|
4532
|
+
table.id
|
|
4533
|
+
))
|
|
4534
|
+
) }),
|
|
4032
4535
|
hasContent && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: bubbleClassName, children: [
|
|
4033
4536
|
renderSlot(
|
|
4034
4537
|
slots?.content,
|
|
@@ -4118,7 +4621,7 @@ function AssistantMessage({
|
|
|
4118
4621
|
children: isCopied ? /* @__PURE__ */ jsxRuntime.jsx(react.Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsxRuntime.jsx(react.Copy, { size: 14, weight: "regular" })
|
|
4119
4622
|
}
|
|
4120
4623
|
),
|
|
4121
|
-
|
|
4624
|
+
canRegenerate && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4122
4625
|
"button",
|
|
4123
4626
|
{
|
|
4124
4627
|
onClick: handleRegenerateClick,
|
|
@@ -9240,6 +9743,10 @@ function ErrorAlert({ error }) {
|
|
|
9240
9743
|
);
|
|
9241
9744
|
}
|
|
9242
9745
|
var COLLAPSE_STORAGE_KEY = "bichat-sidebar-collapsed";
|
|
9746
|
+
var SESSION_RECONCILE_POLL_INTERVAL_MS = 2e3;
|
|
9747
|
+
var SESSION_RECONCILE_MAX_POLLS = 30;
|
|
9748
|
+
var ACTIVE_SESSION_MISS_MAX_RETRIES = 8;
|
|
9749
|
+
var ACTIVE_SESSION_MISS_RETRY_DELAY_MS = 1e3;
|
|
9243
9750
|
function useSidebarCollapse() {
|
|
9244
9751
|
const [isCollapsed, setIsCollapsed] = React.useState(() => {
|
|
9245
9752
|
try {
|
|
@@ -9297,7 +9804,7 @@ function Sidebar2({
|
|
|
9297
9804
|
const shouldReduceMotion = framerMotion.useReducedMotion();
|
|
9298
9805
|
const sessionListRef = React.useRef(null);
|
|
9299
9806
|
const searchContainerRef = React.useRef(null);
|
|
9300
|
-
const
|
|
9807
|
+
const activeSessionMissRetriesRef = React.useRef({});
|
|
9301
9808
|
const { isCollapsed, toggle, collapse } = useSidebarCollapse();
|
|
9302
9809
|
const collapsible = !onClose;
|
|
9303
9810
|
const handleSidebarClick = React.useCallback(
|
|
@@ -9341,6 +9848,7 @@ function Sidebar2({
|
|
|
9341
9848
|
const [actionError, setActionError] = React.useState(null);
|
|
9342
9849
|
const accessDenied = loadError?.isPermissionDenied === true;
|
|
9343
9850
|
const [refreshKey, setRefreshKey] = React.useState(0);
|
|
9851
|
+
const [reconcilePollToken, setReconcilePollToken] = React.useState(0);
|
|
9344
9852
|
const [showConfirm, setShowConfirm] = React.useState(false);
|
|
9345
9853
|
const [sessionToArchive, setSessionToArchive] = React.useState(null);
|
|
9346
9854
|
const fetchSessions = React.useCallback(async () => {
|
|
@@ -9361,8 +9869,13 @@ function Sidebar2({
|
|
|
9361
9869
|
fetchSessions();
|
|
9362
9870
|
}, [fetchSessions, refreshKey]);
|
|
9363
9871
|
React.useEffect(() => {
|
|
9364
|
-
const handleSessionsUpdated = () => {
|
|
9872
|
+
const handleSessionsUpdated = (event) => {
|
|
9365
9873
|
setRefreshKey((k) => k + 1);
|
|
9874
|
+
const detail = event.detail;
|
|
9875
|
+
const reason = detail?.reason;
|
|
9876
|
+
if (!reason || reason === "session_created" || reason === "message_sent" || reason === "title_regenerate_requested") {
|
|
9877
|
+
setReconcilePollToken((k) => k + 1);
|
|
9878
|
+
}
|
|
9366
9879
|
};
|
|
9367
9880
|
window.addEventListener("bichat:sessions-updated", handleSessionsUpdated);
|
|
9368
9881
|
return () => {
|
|
@@ -9370,31 +9883,33 @@ function Sidebar2({
|
|
|
9370
9883
|
};
|
|
9371
9884
|
}, []);
|
|
9372
9885
|
React.useEffect(() => {
|
|
9373
|
-
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9886
|
+
activeSessionMissRetriesRef.current = {};
|
|
9887
|
+
}, [activeSessionId]);
|
|
9888
|
+
React.useEffect(() => {
|
|
9889
|
+
if (!activeSessionId) return;
|
|
9377
9890
|
if (loading) return;
|
|
9378
9891
|
const hasActiveSession = sessions.some((session) => session.id === activeSessionId);
|
|
9379
9892
|
if (hasActiveSession) {
|
|
9380
|
-
|
|
9381
|
-
refreshForActiveSessionRef.current = null;
|
|
9382
|
-
}
|
|
9893
|
+
delete activeSessionMissRetriesRef.current[activeSessionId];
|
|
9383
9894
|
return;
|
|
9384
9895
|
}
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9896
|
+
const attempts = activeSessionMissRetriesRef.current[activeSessionId] ?? 0;
|
|
9897
|
+
if (attempts >= ACTIVE_SESSION_MISS_MAX_RETRIES) {
|
|
9898
|
+
return;
|
|
9388
9899
|
}
|
|
9900
|
+
activeSessionMissRetriesRef.current[activeSessionId] = attempts + 1;
|
|
9901
|
+
const timeoutId = window.setTimeout(() => {
|
|
9902
|
+
setRefreshKey((k) => k + 1);
|
|
9903
|
+
setReconcilePollToken((k) => k + 1);
|
|
9904
|
+
}, ACTIVE_SESSION_MISS_RETRY_DELAY_MS);
|
|
9905
|
+
return () => window.clearTimeout(timeoutId);
|
|
9389
9906
|
}, [activeSessionId, loading, sessions]);
|
|
9390
9907
|
const hasPlaceholderTitles = React.useMemo(() => {
|
|
9391
9908
|
const newChatLabel = t("BiChat.Chat.NewChat");
|
|
9392
9909
|
return Array.isArray(sessions) && sessions.some((s) => s && (!s.title || s.title === newChatLabel));
|
|
9393
9910
|
}, [sessions, t]);
|
|
9394
9911
|
React.useEffect(() => {
|
|
9395
|
-
if (!hasPlaceholderTitles) return;
|
|
9396
|
-
const pollInterval = 2e3;
|
|
9397
|
-
const maxPolls = 5;
|
|
9912
|
+
if (!hasPlaceholderTitles && reconcilePollToken === 0) return;
|
|
9398
9913
|
let pollCount = 0;
|
|
9399
9914
|
const intervalId = setInterval(async () => {
|
|
9400
9915
|
pollCount++;
|
|
@@ -9403,12 +9918,12 @@ function Sidebar2({
|
|
|
9403
9918
|
setSessions(result.sessions);
|
|
9404
9919
|
} catch {
|
|
9405
9920
|
}
|
|
9406
|
-
if (pollCount >=
|
|
9921
|
+
if (pollCount >= SESSION_RECONCILE_MAX_POLLS) {
|
|
9407
9922
|
clearInterval(intervalId);
|
|
9408
9923
|
}
|
|
9409
|
-
},
|
|
9924
|
+
}, SESSION_RECONCILE_POLL_INTERVAL_MS);
|
|
9410
9925
|
return () => clearInterval(intervalId);
|
|
9411
|
-
}, [hasPlaceholderTitles, dataSource]);
|
|
9926
|
+
}, [hasPlaceholderTitles, dataSource, reconcilePollToken]);
|
|
9412
9927
|
const handleArchiveRequest = (sessionId) => {
|
|
9413
9928
|
setSessionToArchive(sessionId);
|
|
9414
9929
|
setShowConfirm(true);
|
|
@@ -9466,7 +9981,9 @@ function Sidebar2({
|
|
|
9466
9981
|
try {
|
|
9467
9982
|
await dataSource.regenerateSessionTitle(sessionId);
|
|
9468
9983
|
toast.success(t("BiChat.Sidebar.TitleRegenerated"));
|
|
9469
|
-
|
|
9984
|
+
window.dispatchEvent(new CustomEvent("bichat:sessions-updated", {
|
|
9985
|
+
detail: { reason: "title_regenerate_requested", sessionId }
|
|
9986
|
+
}));
|
|
9470
9987
|
} catch (err) {
|
|
9471
9988
|
console.error("Failed to regenerate title:", err);
|
|
9472
9989
|
const display = toErrorDisplay(err, t("BiChat.Sidebar.FailedToRegenerateTitle"));
|
|
@@ -10279,59 +10796,6 @@ function BiChatLayout({
|
|
|
10279
10796
|
] })
|
|
10280
10797
|
] });
|
|
10281
10798
|
}
|
|
10282
|
-
|
|
10283
|
-
// ui/src/bichat/components/RetryActionArea.tsx
|
|
10284
|
-
init_useTranslation();
|
|
10285
|
-
var RetryActionArea = React.memo(function RetryActionArea2({
|
|
10286
|
-
onRetry
|
|
10287
|
-
}) {
|
|
10288
|
-
const { t } = useTranslation();
|
|
10289
|
-
return (
|
|
10290
|
-
// Wrapper matches TurnBubble layout for assistant messages (justify-start = left-aligned)
|
|
10291
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10292
|
-
framerMotion.motion.div,
|
|
10293
|
-
{
|
|
10294
|
-
initial: { opacity: 0, y: 10 },
|
|
10295
|
-
animate: { opacity: 1, y: 0 },
|
|
10296
|
-
exit: { opacity: 0, y: -10 },
|
|
10297
|
-
transition: { duration: 0.2 },
|
|
10298
|
-
className: "flex justify-start",
|
|
10299
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10300
|
-
"div",
|
|
10301
|
-
{
|
|
10302
|
-
className: "flex flex-col gap-3 max-w-2xl rounded-2xl px-5 py-3 shadow-sm bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700",
|
|
10303
|
-
role: "status",
|
|
10304
|
-
"aria-live": "polite",
|
|
10305
|
-
children: [
|
|
10306
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
10307
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10308
|
-
react.Warning,
|
|
10309
|
-
{
|
|
10310
|
-
className: "w-5 h-5 text-amber-500 dark:text-amber-400 flex-shrink-0",
|
|
10311
|
-
weight: "fill"
|
|
10312
|
-
}
|
|
10313
|
-
),
|
|
10314
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t("BiChat.Retry.Subtitle") })
|
|
10315
|
-
] }),
|
|
10316
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10317
|
-
"button",
|
|
10318
|
-
{
|
|
10319
|
-
onClick: onRetry,
|
|
10320
|
-
className: "cursor-pointer inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 dark:bg-primary-700 dark:hover:bg-primary-600 rounded-lg transition-colors 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",
|
|
10321
|
-
"aria-label": t("BiChat.Retry.Title"),
|
|
10322
|
-
children: [
|
|
10323
|
-
/* @__PURE__ */ jsxRuntime.jsx(react.ArrowClockwise, { size: 16, className: "w-4 h-4" }),
|
|
10324
|
-
t("BiChat.Retry.Button")
|
|
10325
|
-
]
|
|
10326
|
-
}
|
|
10327
|
-
) })
|
|
10328
|
-
]
|
|
10329
|
-
}
|
|
10330
|
-
)
|
|
10331
|
-
}
|
|
10332
|
-
)
|
|
10333
|
-
);
|
|
10334
|
-
});
|
|
10335
10799
|
init_useTranslation();
|
|
10336
10800
|
function MessageActions({
|
|
10337
10801
|
message,
|
|
@@ -12149,6 +12613,75 @@ function toStreamEvent(chunk) {
|
|
|
12149
12613
|
}
|
|
12150
12614
|
}
|
|
12151
12615
|
|
|
12616
|
+
// ui/src/bichat/utils/tableSpec.ts
|
|
12617
|
+
function isRecord2(value) {
|
|
12618
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12619
|
+
}
|
|
12620
|
+
function readString(value) {
|
|
12621
|
+
if (typeof value !== "string") return null;
|
|
12622
|
+
const trimmed = value.trim();
|
|
12623
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12624
|
+
}
|
|
12625
|
+
function readPositiveInteger(value) {
|
|
12626
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
12627
|
+
const n = Math.floor(value);
|
|
12628
|
+
return n > 0 ? n : null;
|
|
12629
|
+
}
|
|
12630
|
+
function normalizeRows(value) {
|
|
12631
|
+
if (!Array.isArray(value)) return [];
|
|
12632
|
+
const rows = [];
|
|
12633
|
+
for (const row of value) {
|
|
12634
|
+
if (!Array.isArray(row)) continue;
|
|
12635
|
+
rows.push(row);
|
|
12636
|
+
}
|
|
12637
|
+
return rows;
|
|
12638
|
+
}
|
|
12639
|
+
function parseExport(value) {
|
|
12640
|
+
if (!isRecord2(value)) return void 0;
|
|
12641
|
+
const url = readString(value.url);
|
|
12642
|
+
if (!url) return void 0;
|
|
12643
|
+
return {
|
|
12644
|
+
url,
|
|
12645
|
+
filename: readString(value.filename) || "table_export.xlsx",
|
|
12646
|
+
rowCount: readPositiveInteger(value.row_count) || readPositiveInteger(value.rowCount) || void 0,
|
|
12647
|
+
fileSizeKB: readPositiveInteger(value.file_size_kb) || readPositiveInteger(value.fileSizeKB) || void 0
|
|
12648
|
+
};
|
|
12649
|
+
}
|
|
12650
|
+
function parseRenderTableDataFromJsonString(json, fallbackId) {
|
|
12651
|
+
const trimmed = json.trim();
|
|
12652
|
+
if (!trimmed) return null;
|
|
12653
|
+
let parsed;
|
|
12654
|
+
try {
|
|
12655
|
+
parsed = JSON.parse(trimmed);
|
|
12656
|
+
} catch {
|
|
12657
|
+
return null;
|
|
12658
|
+
}
|
|
12659
|
+
if (!isRecord2(parsed)) return null;
|
|
12660
|
+
const columns = Array.isArray(parsed.columns) ? parsed.columns.map((column) => readString(column)).filter((column) => column !== null) : [];
|
|
12661
|
+
if (columns.length === 0) return null;
|
|
12662
|
+
const rows = normalizeRows(parsed.rows);
|
|
12663
|
+
const headersRaw = Array.isArray(parsed.headers) ? parsed.headers.map((header) => readString(header)).filter((header) => header !== null) : [];
|
|
12664
|
+
const headers = headersRaw.length === columns.length ? headersRaw : columns;
|
|
12665
|
+
const totalRows = readPositiveInteger(parsed.total_rows) || readPositiveInteger(parsed.totalRows) || rows.length;
|
|
12666
|
+
const pageSize = readPositiveInteger(parsed.page_size) || readPositiveInteger(parsed.pageSize) || 25;
|
|
12667
|
+
const query = readString(parsed.query) || readString(parsed.sql);
|
|
12668
|
+
if (!query) return null;
|
|
12669
|
+
return {
|
|
12670
|
+
id: readString(parsed.id) || fallbackId,
|
|
12671
|
+
title: readString(parsed.title) || void 0,
|
|
12672
|
+
query,
|
|
12673
|
+
columns,
|
|
12674
|
+
headers,
|
|
12675
|
+
rows,
|
|
12676
|
+
totalRows,
|
|
12677
|
+
pageSize,
|
|
12678
|
+
truncated: parsed.truncated === true,
|
|
12679
|
+
truncatedReason: readString(parsed.truncated_reason) || readString(parsed.truncatedReason) || void 0,
|
|
12680
|
+
export: parseExport(parsed.export),
|
|
12681
|
+
exportPrompt: readString(parsed.export_prompt) || readString(parsed.exportPrompt) || void 0
|
|
12682
|
+
};
|
|
12683
|
+
}
|
|
12684
|
+
|
|
12152
12685
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
12153
12686
|
function isSessionNotFoundError(err) {
|
|
12154
12687
|
if (!(err instanceof AppletRPCException)) return false;
|
|
@@ -12179,7 +12712,7 @@ function toSessionArtifact(artifact) {
|
|
|
12179
12712
|
function warnMalformedSessionPayload(message, details) {
|
|
12180
12713
|
console.warn(`[BiChat] ${message}`, details || {});
|
|
12181
12714
|
}
|
|
12182
|
-
function
|
|
12715
|
+
function readString2(value, fallback = "") {
|
|
12183
12716
|
return typeof value === "string" ? value : fallback;
|
|
12184
12717
|
}
|
|
12185
12718
|
function readNonEmptyString(value) {
|
|
@@ -12193,12 +12726,52 @@ function readFiniteNumber(value, fallback = 0) {
|
|
|
12193
12726
|
function readOptionalFiniteNumber(value) {
|
|
12194
12727
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12195
12728
|
}
|
|
12729
|
+
var MIME_TO_EXTENSION = {
|
|
12730
|
+
"image/jpeg": "jpg",
|
|
12731
|
+
"image/png": "png",
|
|
12732
|
+
"image/gif": "gif",
|
|
12733
|
+
"application/pdf": "pdf"
|
|
12734
|
+
};
|
|
12735
|
+
var SAFE_AUTOCORRECT_MIME_TYPES = new Set(Object.keys(MIME_TO_EXTENSION));
|
|
12736
|
+
function detectMimeFromSignature(bytes) {
|
|
12737
|
+
if (bytes.length >= 8) {
|
|
12738
|
+
const isPng = bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10;
|
|
12739
|
+
if (isPng) return "image/png";
|
|
12740
|
+
}
|
|
12741
|
+
if (bytes.length >= 3) {
|
|
12742
|
+
const isJpeg = bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
12743
|
+
if (isJpeg) return "image/jpeg";
|
|
12744
|
+
}
|
|
12745
|
+
if (bytes.length >= 6) {
|
|
12746
|
+
const isGif = bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56 && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97;
|
|
12747
|
+
if (isGif) return "image/gif";
|
|
12748
|
+
}
|
|
12749
|
+
if (bytes.length >= 4) {
|
|
12750
|
+
const isPdf = bytes[0] === 37 && bytes[1] === 80 && bytes[2] === 68 && bytes[3] === 70;
|
|
12751
|
+
if (isPdf) return "application/pdf";
|
|
12752
|
+
}
|
|
12753
|
+
return void 0;
|
|
12754
|
+
}
|
|
12755
|
+
function normalizeFilenameForMime(filename, mimeType) {
|
|
12756
|
+
const expectedExt = MIME_TO_EXTENSION[mimeType];
|
|
12757
|
+
if (!expectedExt) return filename;
|
|
12758
|
+
const lower = filename.toLowerCase();
|
|
12759
|
+
if (mimeType === "image/jpeg" && (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
|
|
12760
|
+
return filename;
|
|
12761
|
+
}
|
|
12762
|
+
if (lower.endsWith(`.${expectedExt}`)) {
|
|
12763
|
+
return filename;
|
|
12764
|
+
}
|
|
12765
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
12766
|
+
const baseName = dotIndex > 0 ? filename.slice(0, dotIndex) : filename;
|
|
12767
|
+
return `${baseName}.${expectedExt}`;
|
|
12768
|
+
}
|
|
12196
12769
|
function normalizeQuestionType(rawType) {
|
|
12197
|
-
const normalized =
|
|
12770
|
+
const normalized = readString2(rawType).trim().toUpperCase().replace(/[\s-]+/g, "_");
|
|
12198
12771
|
return normalized === "MULTIPLE_CHOICE" ? "MULTIPLE_CHOICE" : "SINGLE_CHOICE";
|
|
12199
12772
|
}
|
|
12200
12773
|
function normalizeMessageRole(rawRole) {
|
|
12201
|
-
const normalized =
|
|
12774
|
+
const normalized = readString2(rawRole).trim().toLowerCase();
|
|
12202
12775
|
if (normalized === "user" /* User */) return "user" /* User */;
|
|
12203
12776
|
if (normalized === "system" /* System */) return "system" /* System */;
|
|
12204
12777
|
if (normalized === "tool" /* Tool */) return "tool" /* Tool */;
|
|
@@ -12209,8 +12782,8 @@ function sanitizeAttachment(rawAttachment, turnId, index) {
|
|
|
12209
12782
|
warnMalformedSessionPayload("Dropped malformed attachment entry", { turnId, index });
|
|
12210
12783
|
return null;
|
|
12211
12784
|
}
|
|
12212
|
-
const filename =
|
|
12213
|
-
const mimeType =
|
|
12785
|
+
const filename = readString2(rawAttachment.filename, "attachment");
|
|
12786
|
+
const mimeType = readString2(rawAttachment.mimeType, "application/octet-stream");
|
|
12214
12787
|
const id = readNonEmptyString(rawAttachment.id) || void 0;
|
|
12215
12788
|
const clientKey = readNonEmptyString(rawAttachment.clientKey) || id || `${turnId}-attachment-${index}`;
|
|
12216
12789
|
return {
|
|
@@ -12243,7 +12816,7 @@ function sanitizeAssistantArtifacts(rawArtifacts, turnId) {
|
|
|
12243
12816
|
warnMalformedSessionPayload("Dropped malformed assistant artifact", { turnId, index: i });
|
|
12244
12817
|
continue;
|
|
12245
12818
|
}
|
|
12246
|
-
const type =
|
|
12819
|
+
const type = readString2(raw.type).toLowerCase();
|
|
12247
12820
|
if (type !== "excel" && type !== "pdf") {
|
|
12248
12821
|
continue;
|
|
12249
12822
|
}
|
|
@@ -12254,7 +12827,7 @@ function sanitizeAssistantArtifacts(rawArtifacts, turnId) {
|
|
|
12254
12827
|
}
|
|
12255
12828
|
artifacts.push({
|
|
12256
12829
|
type,
|
|
12257
|
-
filename:
|
|
12830
|
+
filename: readString2(raw.filename, "download"),
|
|
12258
12831
|
url,
|
|
12259
12832
|
sizeReadable: readNonEmptyString(raw.sizeReadable) || void 0,
|
|
12260
12833
|
rowCount: typeof raw.rowCount === "number" && Number.isFinite(raw.rowCount) ? raw.rowCount : void 0,
|
|
@@ -12275,29 +12848,29 @@ function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
|
12275
12848
|
return void 0;
|
|
12276
12849
|
}
|
|
12277
12850
|
const citations = Array.isArray(rawAssistantTurn.citations) ? rawAssistantTurn.citations.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12278
|
-
id:
|
|
12279
|
-
type:
|
|
12280
|
-
title:
|
|
12281
|
-
url:
|
|
12851
|
+
id: readString2(item.id, `${assistantID}-citation-${index}`),
|
|
12852
|
+
type: readString2(item.type),
|
|
12853
|
+
title: readString2(item.title),
|
|
12854
|
+
url: readString2(item.url),
|
|
12282
12855
|
startIndex: readFiniteNumber(item.startIndex),
|
|
12283
12856
|
endIndex: readFiniteNumber(item.endIndex),
|
|
12284
12857
|
excerpt: readNonEmptyString(item.excerpt) || void 0
|
|
12285
12858
|
})) : [];
|
|
12286
12859
|
const toolCalls = Array.isArray(rawAssistantTurn.toolCalls) ? rawAssistantTurn.toolCalls.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12287
|
-
id:
|
|
12288
|
-
name:
|
|
12289
|
-
arguments:
|
|
12860
|
+
id: readString2(item.id, `${assistantID}-tool-${index}`),
|
|
12861
|
+
name: readString2(item.name),
|
|
12862
|
+
arguments: readString2(item.arguments),
|
|
12290
12863
|
result: readNonEmptyString(item.result) || void 0,
|
|
12291
12864
|
error: readNonEmptyString(item.error) || void 0,
|
|
12292
12865
|
durationMs: readFiniteNumber(item.durationMs)
|
|
12293
12866
|
})) : [];
|
|
12294
12867
|
const codeOutputs = Array.isArray(rawAssistantTurn.codeOutputs) ? rawAssistantTurn.codeOutputs.filter((item) => isRecord(item)).map((item) => ({
|
|
12295
12868
|
type: (() => {
|
|
12296
|
-
const normalizedType =
|
|
12869
|
+
const normalizedType = readString2(item.type, "text").toLowerCase();
|
|
12297
12870
|
if (normalizedType === "image" || normalizedType === "error") return normalizedType;
|
|
12298
12871
|
return "text";
|
|
12299
12872
|
})(),
|
|
12300
|
-
content:
|
|
12873
|
+
content: readString2(item.content),
|
|
12301
12874
|
filename: readNonEmptyString(item.filename) || void 0,
|
|
12302
12875
|
mimeType: readNonEmptyString(item.mimeType) || void 0,
|
|
12303
12876
|
sizeBytes: readOptionalFiniteNumber(item.sizeBytes)
|
|
@@ -12313,7 +12886,7 @@ function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
|
12313
12886
|
} : void 0,
|
|
12314
12887
|
tools: Array.isArray(rawAssistantTurn.debug.tools) ? rawAssistantTurn.debug.tools.filter((tool) => isRecord(tool)).map((tool) => ({
|
|
12315
12888
|
callId: readNonEmptyString(tool.callId) || void 0,
|
|
12316
|
-
name:
|
|
12889
|
+
name: readString2(tool.name),
|
|
12317
12890
|
arguments: readNonEmptyString(tool.arguments) || void 0,
|
|
12318
12891
|
result: readNonEmptyString(tool.result) || void 0,
|
|
12319
12892
|
error: readNonEmptyString(tool.error) || void 0,
|
|
@@ -12323,15 +12896,16 @@ function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
|
12323
12896
|
return {
|
|
12324
12897
|
id: assistantID,
|
|
12325
12898
|
role: normalizeMessageRole(rawAssistantTurn.role),
|
|
12326
|
-
content:
|
|
12899
|
+
content: readString2(rawAssistantTurn.content),
|
|
12327
12900
|
explanation: readNonEmptyString(rawAssistantTurn.explanation) || void 0,
|
|
12328
12901
|
citations,
|
|
12329
12902
|
toolCalls,
|
|
12330
12903
|
chartData: void 0,
|
|
12904
|
+
renderTables: void 0,
|
|
12331
12905
|
artifacts: sanitizeAssistantArtifacts(rawAssistantTurn.artifacts, turnId),
|
|
12332
12906
|
codeOutputs,
|
|
12333
12907
|
debug: debugTrace,
|
|
12334
|
-
createdAt:
|
|
12908
|
+
createdAt: readString2(rawAssistantTurn.createdAt, fallbackCreatedAt)
|
|
12335
12909
|
};
|
|
12336
12910
|
}
|
|
12337
12911
|
function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
|
|
@@ -12348,19 +12922,19 @@ function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
|
|
|
12348
12922
|
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn id)", { index });
|
|
12349
12923
|
return null;
|
|
12350
12924
|
}
|
|
12351
|
-
const turnID =
|
|
12352
|
-
const createdAt =
|
|
12925
|
+
const turnID = readString2(rawTurn.id, userTurnID);
|
|
12926
|
+
const createdAt = readString2(
|
|
12353
12927
|
rawTurn.createdAt,
|
|
12354
|
-
|
|
12928
|
+
readString2(rawTurn.userTurn.createdAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
12355
12929
|
);
|
|
12356
12930
|
return {
|
|
12357
12931
|
id: turnID,
|
|
12358
|
-
sessionId:
|
|
12932
|
+
sessionId: readString2(rawTurn.sessionId, fallbackSessionID),
|
|
12359
12933
|
userTurn: {
|
|
12360
12934
|
id: userTurnID,
|
|
12361
|
-
content:
|
|
12935
|
+
content: readString2(rawTurn.userTurn.content),
|
|
12362
12936
|
attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
|
|
12363
|
-
createdAt:
|
|
12937
|
+
createdAt: readString2(rawTurn.userTurn.createdAt, createdAt)
|
|
12364
12938
|
},
|
|
12365
12939
|
assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
|
|
12366
12940
|
createdAt
|
|
@@ -12413,7 +12987,7 @@ function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
|
|
|
12413
12987
|
}
|
|
12414
12988
|
return true;
|
|
12415
12989
|
}).map((question, index) => {
|
|
12416
|
-
const questionID =
|
|
12990
|
+
const questionID = readString2(question.id, `${checkpointID}-q-${index}`);
|
|
12417
12991
|
const options = Array.isArray(question.options) ? question.options.filter((option) => {
|
|
12418
12992
|
if (!option || !isRecord(option)) {
|
|
12419
12993
|
warnMalformedSessionPayload("Dropped malformed pendingQuestion option", {
|
|
@@ -12425,23 +12999,23 @@ function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
|
|
|
12425
12999
|
}
|
|
12426
13000
|
return true;
|
|
12427
13001
|
}).map((option, optionIndex) => {
|
|
12428
|
-
const label =
|
|
13002
|
+
const label = readString2(option.label);
|
|
12429
13003
|
return {
|
|
12430
|
-
id:
|
|
13004
|
+
id: readString2(option.id, `${questionID}-opt-${optionIndex}`),
|
|
12431
13005
|
label,
|
|
12432
13006
|
value: label
|
|
12433
13007
|
};
|
|
12434
13008
|
}) : [];
|
|
12435
13009
|
return {
|
|
12436
13010
|
id: questionID,
|
|
12437
|
-
text:
|
|
13011
|
+
text: readString2(question.text),
|
|
12438
13012
|
type: normalizeQuestionType(question.type),
|
|
12439
13013
|
options
|
|
12440
13014
|
};
|
|
12441
13015
|
}) : [];
|
|
12442
13016
|
return {
|
|
12443
13017
|
id: checkpointID,
|
|
12444
|
-
turnId:
|
|
13018
|
+
turnId: readString2(rawPendingQuestion.turnId),
|
|
12445
13019
|
questions,
|
|
12446
13020
|
status: "PENDING"
|
|
12447
13021
|
};
|
|
@@ -12513,6 +13087,18 @@ function extractChartDataFromToolCalls(toolCalls) {
|
|
|
12513
13087
|
}
|
|
12514
13088
|
return void 0;
|
|
12515
13089
|
}
|
|
13090
|
+
function extractRenderTablesFromToolCalls(toolCalls) {
|
|
13091
|
+
if (!toolCalls) return [];
|
|
13092
|
+
const tables = [];
|
|
13093
|
+
for (const tc of toolCalls) {
|
|
13094
|
+
if (tc.name !== "renderTable" || !tc.result) continue;
|
|
13095
|
+
const parsed = parseRenderTableDataFromJsonString(tc.result, tc.id);
|
|
13096
|
+
if (parsed) {
|
|
13097
|
+
tables.push(parsed);
|
|
13098
|
+
}
|
|
13099
|
+
}
|
|
13100
|
+
return tables;
|
|
13101
|
+
}
|
|
12516
13102
|
var EXPORT_TOOL_NAMES = {
|
|
12517
13103
|
export_query_to_excel: "excel",
|
|
12518
13104
|
export_data_to_excel: "excel",
|
|
@@ -12548,6 +13134,7 @@ function extractDownloadArtifactsFromToolCalls(toolCalls) {
|
|
|
12548
13134
|
function normalizeAssistantTurn(turn) {
|
|
12549
13135
|
const existingArtifacts = turn.artifacts || [];
|
|
12550
13136
|
const fromToolCalls = extractDownloadArtifactsFromToolCalls(turn.toolCalls);
|
|
13137
|
+
const renderTables = turn.renderTables || extractRenderTablesFromToolCalls(turn.toolCalls);
|
|
12551
13138
|
const merged = [...existingArtifacts];
|
|
12552
13139
|
for (const a of fromToolCalls) {
|
|
12553
13140
|
if (!merged.some((e) => e.url === a.url && e.filename === a.filename)) {
|
|
@@ -12558,6 +13145,7 @@ function normalizeAssistantTurn(turn) {
|
|
|
12558
13145
|
...turn,
|
|
12559
13146
|
role: turn.role || "assistant" /* Assistant */,
|
|
12560
13147
|
chartData: turn.chartData || extractChartDataFromToolCalls(turn.toolCalls),
|
|
13148
|
+
renderTables,
|
|
12561
13149
|
citations: turn.citations || [],
|
|
12562
13150
|
artifacts: merged,
|
|
12563
13151
|
codeOutputs: turn.codeOutputs || []
|
|
@@ -12685,6 +13273,52 @@ var HttpDataSource = class {
|
|
|
12685
13273
|
headers.delete("Content-Type");
|
|
12686
13274
|
return headers;
|
|
12687
13275
|
}
|
|
13276
|
+
logAttachmentLifecycle(event, details) {
|
|
13277
|
+
const payload = {
|
|
13278
|
+
source: "HttpDataSource",
|
|
13279
|
+
event,
|
|
13280
|
+
...details
|
|
13281
|
+
};
|
|
13282
|
+
if (event.endsWith("_fail")) {
|
|
13283
|
+
console.warn("[bichat.attachments]", payload);
|
|
13284
|
+
return;
|
|
13285
|
+
}
|
|
13286
|
+
}
|
|
13287
|
+
async normalizeAttachmentFile(attachment, file) {
|
|
13288
|
+
const signatureBytes = new Uint8Array(await file.slice(0, 16).arrayBuffer());
|
|
13289
|
+
const detectedMimeType = detectMimeFromSignature(signatureBytes);
|
|
13290
|
+
const declaredMimeType = (attachment.mimeType || file.type || "").trim().toLowerCase();
|
|
13291
|
+
let resolvedMimeType = declaredMimeType || detectedMimeType || "application/octet-stream";
|
|
13292
|
+
let correctedFromDeclared = false;
|
|
13293
|
+
if (detectedMimeType && declaredMimeType && detectedMimeType !== declaredMimeType) {
|
|
13294
|
+
const safeToCorrect = SAFE_AUTOCORRECT_MIME_TYPES.has(detectedMimeType) && SAFE_AUTOCORRECT_MIME_TYPES.has(declaredMimeType);
|
|
13295
|
+
if (!safeToCorrect) {
|
|
13296
|
+
throw new Error(
|
|
13297
|
+
`Attachment "${attachment.filename}" MIME mismatch: declared "${declaredMimeType}", detected "${detectedMimeType}"`
|
|
13298
|
+
);
|
|
13299
|
+
}
|
|
13300
|
+
resolvedMimeType = detectedMimeType;
|
|
13301
|
+
correctedFromDeclared = true;
|
|
13302
|
+
} else if (detectedMimeType && !declaredMimeType) {
|
|
13303
|
+
resolvedMimeType = detectedMimeType;
|
|
13304
|
+
}
|
|
13305
|
+
const normalizedName = normalizeFilenameForMime(attachment.filename, resolvedMimeType);
|
|
13306
|
+
const normalized = new File([file], normalizedName, {
|
|
13307
|
+
type: resolvedMimeType,
|
|
13308
|
+
lastModified: file.lastModified
|
|
13309
|
+
});
|
|
13310
|
+
this.logAttachmentLifecycle("attachment_decode_success", {
|
|
13311
|
+
attachmentKey: attachment.clientKey,
|
|
13312
|
+
filename: attachment.filename,
|
|
13313
|
+
normalizedFilename: normalized.name,
|
|
13314
|
+
declaredMimeType: declaredMimeType || void 0,
|
|
13315
|
+
detectedMimeType,
|
|
13316
|
+
resolvedMimeType,
|
|
13317
|
+
correctedFromDeclared,
|
|
13318
|
+
sizeBytes: normalized.size
|
|
13319
|
+
});
|
|
13320
|
+
return normalized;
|
|
13321
|
+
}
|
|
12688
13322
|
async uploadFile(file) {
|
|
12689
13323
|
const formData = new FormData();
|
|
12690
13324
|
formData.append("file", file);
|
|
@@ -12717,17 +13351,32 @@ var HttpDataSource = class {
|
|
|
12717
13351
|
}
|
|
12718
13352
|
async attachmentToFile(attachment) {
|
|
12719
13353
|
if (attachment.base64Data && attachment.base64Data.trim().length > 0) {
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
|
|
13354
|
+
try {
|
|
13355
|
+
const base64Data = attachment.base64Data.trim();
|
|
13356
|
+
const dataUrl = base64Data.startsWith("data:") ? base64Data : `data:${attachment.mimeType || "application/octet-stream"};base64,${base64Data}`;
|
|
13357
|
+
const blob = await fetch(dataUrl).then((response) => response.blob());
|
|
13358
|
+
return new File([blob], attachment.filename, {
|
|
13359
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
13360
|
+
});
|
|
13361
|
+
} catch (err) {
|
|
13362
|
+
const message = err instanceof Error ? err.message : "Unknown decode error";
|
|
13363
|
+
throw new Error(`Attachment "${attachment.filename}" decode failed: ${message}`);
|
|
13364
|
+
}
|
|
12726
13365
|
}
|
|
12727
13366
|
if (attachment.url) {
|
|
12728
|
-
|
|
13367
|
+
let parsed;
|
|
13368
|
+
try {
|
|
13369
|
+
parsed = new URL(attachment.url, window.location?.origin ?? "https://localhost");
|
|
13370
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
13371
|
+
throw new Error(`Attachment "${attachment.filename}" URL has disallowed protocol: ${parsed.protocol}`);
|
|
13372
|
+
}
|
|
13373
|
+
} catch (err) {
|
|
13374
|
+
if (err instanceof Error && err.message.includes("Attachment")) throw err;
|
|
13375
|
+
throw new Error(`Attachment "${attachment.filename}" has invalid or malformed URL`);
|
|
13376
|
+
}
|
|
13377
|
+
const response = await fetch(parsed.href);
|
|
12729
13378
|
if (!response.ok) {
|
|
12730
|
-
throw new Error(`
|
|
13379
|
+
throw new Error(`Attachment "${attachment.filename}" decode failed: source HTTP ${response.status}`);
|
|
12731
13380
|
}
|
|
12732
13381
|
const blob = await response.blob();
|
|
12733
13382
|
return new File([blob], attachment.filename, {
|
|
@@ -12736,8 +13385,24 @@ var HttpDataSource = class {
|
|
|
12736
13385
|
}
|
|
12737
13386
|
throw new Error(`Attachment "${attachment.filename}" has no uploadable data`);
|
|
12738
13387
|
}
|
|
12739
|
-
|
|
13388
|
+
assertUploadReferences(uploads) {
|
|
13389
|
+
return uploads.map((upload, index) => {
|
|
13390
|
+
if (typeof upload.id !== "number" || !Number.isFinite(upload.id) || upload.id <= 0) {
|
|
13391
|
+
throw new Error(`Attachment upload reference is invalid at index ${index}`);
|
|
13392
|
+
}
|
|
13393
|
+
return { uploadId: upload.id };
|
|
13394
|
+
});
|
|
13395
|
+
}
|
|
13396
|
+
async ensureAttachmentUpload(attachment, context) {
|
|
12740
13397
|
if (typeof attachment.uploadId === "number" && attachment.uploadId > 0) {
|
|
13398
|
+
this.logAttachmentLifecycle("attachment_upload_success", {
|
|
13399
|
+
sessionId: context.sessionId,
|
|
13400
|
+
attachmentIndex: context.attachmentIndex,
|
|
13401
|
+
attachmentKey: attachment.clientKey,
|
|
13402
|
+
filename: attachment.filename,
|
|
13403
|
+
uploadId: attachment.uploadId,
|
|
13404
|
+
reusedUploadId: true
|
|
13405
|
+
});
|
|
12741
13406
|
return {
|
|
12742
13407
|
id: attachment.uploadId,
|
|
12743
13408
|
url: attachment.url || "",
|
|
@@ -12747,8 +13412,64 @@ var HttpDataSource = class {
|
|
|
12747
13412
|
size: attachment.sizeBytes
|
|
12748
13413
|
};
|
|
12749
13414
|
}
|
|
12750
|
-
|
|
12751
|
-
|
|
13415
|
+
this.logAttachmentLifecycle("attachment_decode_start", {
|
|
13416
|
+
sessionId: context.sessionId,
|
|
13417
|
+
attachmentIndex: context.attachmentIndex,
|
|
13418
|
+
attachmentKey: attachment.clientKey,
|
|
13419
|
+
filename: attachment.filename,
|
|
13420
|
+
hasBase64Data: Boolean(attachment.base64Data && attachment.base64Data.trim().length > 0),
|
|
13421
|
+
hasURL: Boolean(attachment.url)
|
|
13422
|
+
});
|
|
13423
|
+
let file;
|
|
13424
|
+
try {
|
|
13425
|
+
const rawFile = await this.attachmentToFile(attachment);
|
|
13426
|
+
file = await this.normalizeAttachmentFile(attachment, rawFile);
|
|
13427
|
+
validateAttachmentFile(file);
|
|
13428
|
+
} catch (err) {
|
|
13429
|
+
const message = err instanceof Error ? err.message : "Unknown attachment decode/validation error";
|
|
13430
|
+
this.logAttachmentLifecycle("attachment_decode_fail", {
|
|
13431
|
+
sessionId: context.sessionId,
|
|
13432
|
+
attachmentIndex: context.attachmentIndex,
|
|
13433
|
+
attachmentKey: attachment.clientKey,
|
|
13434
|
+
filename: attachment.filename,
|
|
13435
|
+
error: message
|
|
13436
|
+
});
|
|
13437
|
+
throw new Error(message);
|
|
13438
|
+
}
|
|
13439
|
+
this.logAttachmentLifecycle("attachment_upload_start", {
|
|
13440
|
+
sessionId: context.sessionId,
|
|
13441
|
+
attachmentIndex: context.attachmentIndex,
|
|
13442
|
+
attachmentKey: attachment.clientKey,
|
|
13443
|
+
filename: file.name,
|
|
13444
|
+
mimeType: file.type,
|
|
13445
|
+
sizeBytes: file.size
|
|
13446
|
+
});
|
|
13447
|
+
try {
|
|
13448
|
+
const upload = await this.uploadFile(file);
|
|
13449
|
+
attachment.uploadId = upload.id;
|
|
13450
|
+
attachment.mimeType = upload.mimetype || file.type;
|
|
13451
|
+
attachment.filename = upload.name || file.name;
|
|
13452
|
+
attachment.sizeBytes = upload.size || file.size;
|
|
13453
|
+
this.logAttachmentLifecycle("attachment_upload_success", {
|
|
13454
|
+
sessionId: context.sessionId,
|
|
13455
|
+
attachmentIndex: context.attachmentIndex,
|
|
13456
|
+
attachmentKey: attachment.clientKey,
|
|
13457
|
+
filename: attachment.filename,
|
|
13458
|
+
uploadId: upload.id,
|
|
13459
|
+
reusedUploadId: false
|
|
13460
|
+
});
|
|
13461
|
+
return upload;
|
|
13462
|
+
} catch (err) {
|
|
13463
|
+
const message = err instanceof Error ? err.message : "Unknown upload error";
|
|
13464
|
+
this.logAttachmentLifecycle("attachment_upload_fail", {
|
|
13465
|
+
sessionId: context.sessionId,
|
|
13466
|
+
attachmentIndex: context.attachmentIndex,
|
|
13467
|
+
attachmentKey: attachment.clientKey,
|
|
13468
|
+
filename: file.name,
|
|
13469
|
+
error: message
|
|
13470
|
+
});
|
|
13471
|
+
throw new Error(`Attachment "${attachment.filename}" upload failed: ${message}`);
|
|
13472
|
+
}
|
|
12752
13473
|
}
|
|
12753
13474
|
async callRPC(method, params) {
|
|
12754
13475
|
return this.rpc.callTyped(method, params);
|
|
@@ -12859,16 +13580,21 @@ var HttpDataSource = class {
|
|
|
12859
13580
|
let connectionTimedOut = false;
|
|
12860
13581
|
try {
|
|
12861
13582
|
const uploads = await Promise.all(
|
|
12862
|
-
attachments.map(
|
|
13583
|
+
attachments.map(
|
|
13584
|
+
(attachment, attachmentIndex) => this.ensureAttachmentUpload(attachment, { sessionId, attachmentIndex })
|
|
13585
|
+
)
|
|
12863
13586
|
);
|
|
13587
|
+
const streamAttachments = this.assertUploadReferences(uploads);
|
|
13588
|
+
this.logAttachmentLifecycle("stream_send_with_upload_ids", {
|
|
13589
|
+
sessionId,
|
|
13590
|
+
attachmentCount: streamAttachments.length
|
|
13591
|
+
});
|
|
12864
13592
|
const payload = {
|
|
12865
13593
|
sessionId,
|
|
12866
13594
|
content,
|
|
12867
13595
|
debugMode: options?.debugMode ?? false,
|
|
12868
13596
|
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12869
|
-
attachments:
|
|
12870
|
-
uploadId: upload.id
|
|
12871
|
-
}))
|
|
13597
|
+
attachments: streamAttachments
|
|
12872
13598
|
};
|
|
12873
13599
|
const timeoutMs = this.config.timeout ?? 0;
|
|
12874
13600
|
if (timeoutMs > 0) {
|
|
@@ -13072,6 +13798,7 @@ exports.ErrorBoundary = ErrorBoundary;
|
|
|
13072
13798
|
exports.HttpDataSource = HttpDataSource;
|
|
13073
13799
|
exports.ImageModal = ImageModal;
|
|
13074
13800
|
exports.InlineQuestionForm = InlineQuestionForm;
|
|
13801
|
+
exports.InteractiveTableCard = InteractiveTableCard;
|
|
13075
13802
|
exports.IotaContextProvider = IotaContextProvider;
|
|
13076
13803
|
exports.ListItemSkeleton = ListItemSkeleton;
|
|
13077
13804
|
exports.LoadingSpinner = MemoizedLoadingSpinner;
|