@iota-uz/sdk 0.4.17 → 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 +1507 -356
- package/dist/bichat/index.cjs.map +1 -1
- package/dist/bichat/index.d.cts +56 -7
- package/dist/bichat/index.d.ts +56 -7
- package/dist/bichat/index.mjs +1513 -363
- package/dist/bichat/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tailwind/compiled.css +1 -1
package/dist/bichat/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import React, { createContext,
|
|
2
|
-
import {
|
|
1
|
+
import React, { createContext, memo, useState, useEffect, useMemo, useCallback, lazy, forwardRef, useRef, useImperativeHandle, isValidElement, cloneElement, useContext, useId, useSyncExternalStore, Suspense, Component, Children } from 'react';
|
|
2
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import ReactApexChart from 'react-apexcharts';
|
|
4
4
|
import ApexCharts from 'apexcharts';
|
|
5
|
-
import { X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree,
|
|
5
|
+
import { Warning, ArrowClockwise, X, Bug, ArrowUp, ArrowDown, Stack, Paperclip, PaperPlaneRight, CircleNotch, ArrowUUpLeft, PencilSimple, Check, Bookmark, ArrowsClockwise, Archive, Trash, DotsThree, Image, ArrowCounterClockwise, ImageBroken, CaretLeft, CaretRight, MagnifyingGlassMinus, MagnifyingGlassPlus, ArrowsIn, Info, CheckCircle, XCircle, Spinner, MagnifyingGlass, WarningCircle, CaretDown, Copy, FilePdf, FileXls, FileCsv, FileDoc, FileCode, FileText, File as File$1, ChartBar, DownloadSimple, Download, ChatCircleDots, PencilSimpleLine, ArrowLeft, PaperPlaneTilt, ArrowRight, Timer, Lightning, Database, Wrench, ClockCounterClockwise, Lightbulb, Package, Plus, ArrowsCounterClockwise, Gear, Users, List, CaretLineLeft, CaretLineRight, Code, ArrowSquareOut, SpinnerGap, FloppyDisk, Sidebar } from '@phosphor-icons/react';
|
|
6
6
|
import { Prism } from 'react-syntax-highlighter';
|
|
7
7
|
import { vscDarkPlus, vs } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
|
8
8
|
import ReactMarkdown from 'react-markdown';
|
|
9
9
|
import remarkGfm from 'remark-gfm';
|
|
10
|
-
import { useMotionValue, useTransform,
|
|
10
|
+
import { motion, useMotionValue, useTransform, AnimatePresence, useReducedMotion } from 'framer-motion';
|
|
11
11
|
import 'react-dom/client';
|
|
12
12
|
import { startOfDay, differenceInDays, differenceInMinutes, differenceInHours, format } from 'date-fns';
|
|
13
|
-
import { Menu, MenuButton, MenuItems, MenuItem, Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description,
|
|
13
|
+
import { Menu, MenuButton, MenuItems, MenuItem, Dialog, DialogBackdrop, DialogPanel, DialogTitle, Description, Transition } from '@headlessui/react';
|
|
14
14
|
import { createPortal } from 'react-dom';
|
|
15
15
|
|
|
16
16
|
var __defProp = Object.defineProperty;
|
|
@@ -180,6 +180,37 @@ var init_ChartCard = __esm({
|
|
|
180
180
|
DEFAULT_COLORS = ["#6366f1", "#06b6d4", "#f59e0b", "#ef4444", "#8b5cf6"];
|
|
181
181
|
}
|
|
182
182
|
});
|
|
183
|
+
var TableExportButton;
|
|
184
|
+
var init_TableExportButton = __esm({
|
|
185
|
+
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
186
|
+
init_useTranslation();
|
|
187
|
+
TableExportButton = memo(function TableExportButton2({
|
|
188
|
+
onClick,
|
|
189
|
+
disabled = false,
|
|
190
|
+
label,
|
|
191
|
+
disabledTooltip
|
|
192
|
+
}) {
|
|
193
|
+
const { t } = useTranslation();
|
|
194
|
+
const resolvedLabel = label ?? t("BiChat.Export");
|
|
195
|
+
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
196
|
+
return /* @__PURE__ */ jsxs(
|
|
197
|
+
"button",
|
|
198
|
+
{
|
|
199
|
+
type: "button",
|
|
200
|
+
onClick,
|
|
201
|
+
disabled,
|
|
202
|
+
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",
|
|
203
|
+
"aria-label": resolvedLabel,
|
|
204
|
+
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ jsx(FileXls, { size: 16, weight: "fill" }),
|
|
207
|
+
/* @__PURE__ */ jsx("span", { children: resolvedLabel })
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
});
|
|
183
214
|
|
|
184
215
|
// ui/src/bichat/utils/citationProcessor.ts
|
|
185
216
|
function processCitations(content, citations) {
|
|
@@ -292,37 +323,6 @@ var init_chartSpec = __esm({
|
|
|
292
323
|
]);
|
|
293
324
|
}
|
|
294
325
|
});
|
|
295
|
-
var TableExportButton;
|
|
296
|
-
var init_TableExportButton = __esm({
|
|
297
|
-
"ui/src/bichat/components/TableExportButton.tsx"() {
|
|
298
|
-
init_useTranslation();
|
|
299
|
-
TableExportButton = memo(function TableExportButton2({
|
|
300
|
-
onClick,
|
|
301
|
-
disabled = false,
|
|
302
|
-
label,
|
|
303
|
-
disabledTooltip
|
|
304
|
-
}) {
|
|
305
|
-
const { t } = useTranslation();
|
|
306
|
-
const resolvedLabel = label ?? t("BiChat.Export");
|
|
307
|
-
const resolvedDisabledTooltip = disabledTooltip ?? t("BiChat.Common.PleaseWait");
|
|
308
|
-
return /* @__PURE__ */ jsxs(
|
|
309
|
-
"button",
|
|
310
|
-
{
|
|
311
|
-
type: "button",
|
|
312
|
-
onClick,
|
|
313
|
-
disabled,
|
|
314
|
-
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",
|
|
315
|
-
"aria-label": resolvedLabel,
|
|
316
|
-
title: disabled ? resolvedDisabledTooltip : resolvedLabel,
|
|
317
|
-
children: [
|
|
318
|
-
/* @__PURE__ */ jsx(FileXls, { size: 16, weight: "fill" }),
|
|
319
|
-
/* @__PURE__ */ jsx("span", { children: resolvedLabel })
|
|
320
|
-
]
|
|
321
|
-
}
|
|
322
|
-
);
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
326
|
var TableWithExport;
|
|
327
327
|
var init_TableWithExport = __esm({
|
|
328
328
|
"ui/src/bichat/components/TableWithExport.tsx"() {
|
|
@@ -1508,6 +1508,10 @@ var ChatMachine = class {
|
|
|
1508
1508
|
}
|
|
1509
1509
|
/** Sets turns from fetch, preserving pending user-only turns if server hasn't caught up. */
|
|
1510
1510
|
_setTurnsFromFetch(fetchedTurns) {
|
|
1511
|
+
if (!Array.isArray(fetchedTurns)) {
|
|
1512
|
+
console.warn("[ChatMachine] Ignoring malformed turns payload from fetchSession");
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1511
1515
|
const prev = this.state.messaging.turns;
|
|
1512
1516
|
const hasPendingUserOnly = prev.length > 0 && !prev[prev.length - 1].assistantTurn;
|
|
1513
1517
|
if (hasPendingUserOnly && (!fetchedTurns || fetchedTurns.length === 0)) {
|
|
@@ -1533,6 +1537,12 @@ var ChatMachine = class {
|
|
|
1533
1537
|
streamErrorRetryable: false
|
|
1534
1538
|
});
|
|
1535
1539
|
}
|
|
1540
|
+
_notifySessionsUpdated(reason, sessionId) {
|
|
1541
|
+
if (typeof window === "undefined") return;
|
|
1542
|
+
window.dispatchEvent(new CustomEvent("bichat:sessions-updated", {
|
|
1543
|
+
detail: { reason, sessionId }
|
|
1544
|
+
}));
|
|
1545
|
+
}
|
|
1536
1546
|
_cancel() {
|
|
1537
1547
|
if (this.abortController) {
|
|
1538
1548
|
this.abortController.abort();
|
|
@@ -1800,7 +1810,11 @@ var ChatMachine = class {
|
|
|
1800
1810
|
}
|
|
1801
1811
|
}
|
|
1802
1812
|
const targetSessionId = createdSessionId || activeSessionId;
|
|
1813
|
+
if (targetSessionId && targetSessionId !== "new") {
|
|
1814
|
+
this._notifySessionsUpdated("message_sent", targetSessionId);
|
|
1815
|
+
}
|
|
1803
1816
|
if (shouldNavigateAfter && targetSessionId && targetSessionId !== "new") {
|
|
1817
|
+
this._notifySessionsUpdated("session_created", targetSessionId);
|
|
1804
1818
|
if (this.onSessionCreated) {
|
|
1805
1819
|
this.onSessionCreated(targetSessionId);
|
|
1806
1820
|
} else {
|
|
@@ -1897,6 +1911,7 @@ var ChatMachine = class {
|
|
|
1897
1911
|
const curSessionId = this.state.session.currentSessionId;
|
|
1898
1912
|
const curPendingQuestion = this.state.messaging.pendingQuestion;
|
|
1899
1913
|
if (!curSessionId || !curPendingQuestion) return;
|
|
1914
|
+
const previousTurns = this.state.messaging.turns;
|
|
1900
1915
|
this._updateMessaging({ loading: true });
|
|
1901
1916
|
this._updateSession({ error: null, errorRetryable: false });
|
|
1902
1917
|
const previousPendingQuestion = curPendingQuestion;
|
|
@@ -1914,19 +1929,28 @@ var ChatMachine = class {
|
|
|
1914
1929
|
const fetchResult = await this.dataSource.fetchSession(curSessionId);
|
|
1915
1930
|
if (this.disposed) return;
|
|
1916
1931
|
if (fetchResult) {
|
|
1917
|
-
this.
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1932
|
+
this._updateSession({ session: fetchResult.session });
|
|
1933
|
+
this._updateMessaging({ pendingQuestion: fetchResult.pendingQuestion || null });
|
|
1934
|
+
const hasMalformedRefresh = previousTurns.length > 0 && Array.isArray(fetchResult.turns) && fetchResult.turns.length === 0;
|
|
1935
|
+
if (hasMalformedRefresh) {
|
|
1936
|
+
console.warn("[ChatMachine] Preserving previous turns due to empty post-HITL refetch payload", {
|
|
1937
|
+
sessionId: curSessionId,
|
|
1938
|
+
previousTurnCount: previousTurns.length
|
|
1939
|
+
});
|
|
1940
|
+
this._updateSession({
|
|
1941
|
+
error: "Failed to fully refresh session. Showing last known messages.",
|
|
1942
|
+
errorRetryable: true
|
|
1943
|
+
});
|
|
1944
|
+
} else {
|
|
1945
|
+
this._setTurnsFromFetch(fetchResult.turns);
|
|
1946
|
+
}
|
|
1921
1947
|
} else {
|
|
1922
|
-
this.
|
|
1923
|
-
this._updateSession({ error: "Failed to load updated session", errorRetryable: false });
|
|
1948
|
+
this._updateSession({ error: "Failed to load updated session", errorRetryable: true });
|
|
1924
1949
|
}
|
|
1925
1950
|
} catch (fetchErr) {
|
|
1926
1951
|
if (this.disposed) return;
|
|
1927
|
-
this._updateMessaging({ pendingQuestion: previousPendingQuestion });
|
|
1928
1952
|
const normalized = normalizeRPCError(fetchErr, "Failed to load updated session");
|
|
1929
|
-
this._updateSession({ error: normalized.userMessage, errorRetryable:
|
|
1953
|
+
this._updateSession({ error: normalized.userMessage, errorRetryable: true });
|
|
1930
1954
|
}
|
|
1931
1955
|
}
|
|
1932
1956
|
} else {
|
|
@@ -1980,6 +2004,8 @@ var ChatMachine = class {
|
|
|
1980
2004
|
this._clearStreamError();
|
|
1981
2005
|
const convertedAttachments = attachments.map((att) => ({
|
|
1982
2006
|
clientKey: att.clientKey || crypto.randomUUID(),
|
|
2007
|
+
id: att.id,
|
|
2008
|
+
uploadId: att.uploadId,
|
|
1983
2009
|
filename: att.filename,
|
|
1984
2010
|
mimeType: att.mimeType,
|
|
1985
2011
|
sizeBytes: att.sizeBytes,
|
|
@@ -2448,7 +2474,7 @@ function getFileVisual(mimeType, filename) {
|
|
|
2448
2474
|
};
|
|
2449
2475
|
}
|
|
2450
2476
|
return {
|
|
2451
|
-
icon: File,
|
|
2477
|
+
icon: File$1,
|
|
2452
2478
|
iconColor: "text-gray-400 dark:text-gray-500",
|
|
2453
2479
|
bgColor: "bg-gray-100 dark:bg-gray-800",
|
|
2454
2480
|
label: (mime.split("/")[1] || "FILE").toUpperCase().slice(0, 4)
|
|
@@ -2642,6 +2668,9 @@ var MemoizedAttachmentGrid = React.memo(AttachmentGrid);
|
|
|
2642
2668
|
MemoizedAttachmentGrid.displayName = "AttachmentGrid";
|
|
2643
2669
|
var AttachmentGrid_default = MemoizedAttachmentGrid;
|
|
2644
2670
|
init_useTranslation();
|
|
2671
|
+
var MIN_SCALE = 0.25;
|
|
2672
|
+
var MAX_SCALE = 5;
|
|
2673
|
+
var ZOOM_STEP = 0.25;
|
|
2645
2674
|
function ImageModal({
|
|
2646
2675
|
isOpen,
|
|
2647
2676
|
onClose,
|
|
@@ -2654,9 +2683,23 @@ function ImageModal({
|
|
|
2654
2683
|
const [isImageLoaded, setIsImageLoaded] = useState(false);
|
|
2655
2684
|
const [imageError, setImageError] = useState(false);
|
|
2656
2685
|
const [retryKey, setRetryKey] = useState(0);
|
|
2686
|
+
const [scale, setScale] = useState(1);
|
|
2687
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
2688
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
2689
|
+
const dragStartRef = useRef({ x: 0, y: 0 });
|
|
2690
|
+
const positionRef = useRef({ x: 0, y: 0 });
|
|
2691
|
+
const scaleRef = useRef(1);
|
|
2692
|
+
const imageAreaRef = useRef(null);
|
|
2657
2693
|
const hasMultipleImages = allAttachments && allAttachments.length > 1;
|
|
2658
2694
|
const canNavigatePrev = hasMultipleImages && currentIndex > 0;
|
|
2659
2695
|
const canNavigateNext = hasMultipleImages && currentIndex < (allAttachments?.length || 1) - 1;
|
|
2696
|
+
const isZoomed = scale > 1;
|
|
2697
|
+
useEffect(() => {
|
|
2698
|
+
scaleRef.current = scale;
|
|
2699
|
+
}, [scale]);
|
|
2700
|
+
useEffect(() => {
|
|
2701
|
+
positionRef.current = position;
|
|
2702
|
+
}, [position]);
|
|
2660
2703
|
useEffect(() => {
|
|
2661
2704
|
if (!isOpen) return;
|
|
2662
2705
|
const handleKeyDown = (e) => {
|
|
@@ -2664,6 +2707,14 @@ function ImageModal({
|
|
|
2664
2707
|
onNavigate("prev");
|
|
2665
2708
|
} else if (e.key === "ArrowRight" && onNavigate && canNavigateNext) {
|
|
2666
2709
|
onNavigate("next");
|
|
2710
|
+
} else if (e.key === "+" || e.key === "=") {
|
|
2711
|
+
setScale((s) => Math.min(s + ZOOM_STEP, MAX_SCALE));
|
|
2712
|
+
} else if (e.key === "-") {
|
|
2713
|
+
setScale((s) => Math.max(s - ZOOM_STEP, MIN_SCALE));
|
|
2714
|
+
if (scaleRef.current - ZOOM_STEP <= 1) setPosition({ x: 0, y: 0 });
|
|
2715
|
+
} else if (e.key === "0") {
|
|
2716
|
+
setScale(1);
|
|
2717
|
+
setPosition({ x: 0, y: 0 });
|
|
2667
2718
|
}
|
|
2668
2719
|
};
|
|
2669
2720
|
document.addEventListener("keydown", handleKeyDown);
|
|
@@ -2672,128 +2723,246 @@ function ImageModal({
|
|
|
2672
2723
|
useEffect(() => {
|
|
2673
2724
|
setIsImageLoaded(false);
|
|
2674
2725
|
setImageError(false);
|
|
2726
|
+
setScale(1);
|
|
2727
|
+
setPosition({ x: 0, y: 0 });
|
|
2675
2728
|
}, [attachment]);
|
|
2729
|
+
useEffect(() => {
|
|
2730
|
+
const el = imageAreaRef.current;
|
|
2731
|
+
if (!el || !isOpen) return;
|
|
2732
|
+
const handler = (e) => {
|
|
2733
|
+
const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;
|
|
2734
|
+
const current = scaleRef.current;
|
|
2735
|
+
const newScale = Math.min(Math.max(current + delta, MIN_SCALE), MAX_SCALE);
|
|
2736
|
+
if (newScale === current) return;
|
|
2737
|
+
e.preventDefault();
|
|
2738
|
+
setScale(newScale);
|
|
2739
|
+
if (newScale <= 1) setPosition({ x: 0, y: 0 });
|
|
2740
|
+
};
|
|
2741
|
+
el.addEventListener("wheel", handler, { passive: false });
|
|
2742
|
+
return () => el.removeEventListener("wheel", handler);
|
|
2743
|
+
}, [isOpen]);
|
|
2676
2744
|
const handleRetry = useCallback(() => {
|
|
2677
2745
|
setImageError(false);
|
|
2678
2746
|
setIsImageLoaded(false);
|
|
2679
2747
|
setRetryKey((k) => k + 1);
|
|
2680
2748
|
}, []);
|
|
2749
|
+
const zoomIn = useCallback(() => {
|
|
2750
|
+
setScale((s) => Math.min(s + ZOOM_STEP, MAX_SCALE));
|
|
2751
|
+
}, []);
|
|
2752
|
+
const zoomOut = useCallback(() => {
|
|
2753
|
+
setScale((s) => Math.max(s - ZOOM_STEP, MIN_SCALE));
|
|
2754
|
+
if (scaleRef.current - ZOOM_STEP <= 1) setPosition({ x: 0, y: 0 });
|
|
2755
|
+
}, []);
|
|
2756
|
+
const resetZoom = useCallback(() => {
|
|
2757
|
+
setScale(1);
|
|
2758
|
+
setPosition({ x: 0, y: 0 });
|
|
2759
|
+
}, []);
|
|
2760
|
+
const handleDoubleClick = useCallback(() => {
|
|
2761
|
+
const current = scaleRef.current;
|
|
2762
|
+
if (current !== 1) {
|
|
2763
|
+
setScale(1);
|
|
2764
|
+
setPosition({ x: 0, y: 0 });
|
|
2765
|
+
} else {
|
|
2766
|
+
setScale(2);
|
|
2767
|
+
}
|
|
2768
|
+
}, []);
|
|
2769
|
+
const handleMouseDown = useCallback((e) => {
|
|
2770
|
+
if (scaleRef.current <= 1) return;
|
|
2771
|
+
e.preventDefault();
|
|
2772
|
+
setIsDragging(true);
|
|
2773
|
+
dragStartRef.current = {
|
|
2774
|
+
x: e.clientX - positionRef.current.x,
|
|
2775
|
+
y: e.clientY - positionRef.current.y
|
|
2776
|
+
};
|
|
2777
|
+
}, []);
|
|
2778
|
+
const handleMouseMove = useCallback((e) => {
|
|
2779
|
+
if (!isDragging) return;
|
|
2780
|
+
setPosition({
|
|
2781
|
+
x: e.clientX - dragStartRef.current.x,
|
|
2782
|
+
y: e.clientY - dragStartRef.current.y
|
|
2783
|
+
});
|
|
2784
|
+
}, [isDragging]);
|
|
2785
|
+
const handleMouseUp = useCallback(() => {
|
|
2786
|
+
setIsDragging(false);
|
|
2787
|
+
}, []);
|
|
2788
|
+
const handleBackdropClick = useCallback((e) => {
|
|
2789
|
+
if (e.target === e.currentTarget && !isZoomed) {
|
|
2790
|
+
onClose();
|
|
2791
|
+
}
|
|
2792
|
+
}, [isZoomed, onClose]);
|
|
2681
2793
|
const previewUrl = attachment.preview || createDataUrl(attachment.base64Data, attachment.mimeType);
|
|
2794
|
+
const zoomPercent = Math.round(scale * 100);
|
|
2682
2795
|
return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onClose, className: "relative", style: { zIndex: 99999 }, children: [
|
|
2683
2796
|
/* @__PURE__ */ jsx(
|
|
2684
2797
|
DialogBackdrop,
|
|
2685
2798
|
{
|
|
2686
|
-
className: "fixed inset-0",
|
|
2687
|
-
style: { zIndex: 99999
|
|
2799
|
+
className: "fixed inset-0 bg-black/90 backdrop-blur-sm",
|
|
2800
|
+
style: { zIndex: 99999 }
|
|
2688
2801
|
}
|
|
2689
2802
|
),
|
|
2690
|
-
/* @__PURE__ */ jsxs(
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
/* @__PURE__ */ jsx("
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
onClick: onClose,
|
|
2705
|
-
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",
|
|
2706
|
-
"aria-label": t("BiChat.Image.Close"),
|
|
2707
|
-
type: "button",
|
|
2708
|
-
children: /* @__PURE__ */ jsx(X, { size: 18, weight: "bold" })
|
|
2709
|
-
}
|
|
2710
|
-
)
|
|
2711
|
-
] }),
|
|
2712
|
-
/* @__PURE__ */ jsxs(
|
|
2713
|
-
"div",
|
|
2714
|
-
{
|
|
2715
|
-
className: "relative flex-1 flex items-center justify-center min-h-0",
|
|
2716
|
-
onClick: (e) => {
|
|
2717
|
-
if (e.target === e.currentTarget) onClose();
|
|
2718
|
-
},
|
|
2719
|
-
children: [
|
|
2720
|
-
!isImageLoaded && !imageError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2721
|
-
/* @__PURE__ */ 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" }),
|
|
2722
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 dark:text-gray-500", children: t("BiChat.Loading") })
|
|
2723
|
-
] }) }),
|
|
2724
|
-
imageError && /* @__PURE__ */ jsxs("div", { role: "alert", className: "flex flex-col items-center justify-center text-center max-w-xs", children: [
|
|
2725
|
-
/* @__PURE__ */ 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__ */ jsx(ImageBroken, { size: 28, className: "text-gray-400 dark:text-gray-500", weight: "duotone" }) }),
|
|
2726
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-1", children: t("BiChat.Image.FailedToLoad") }),
|
|
2727
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 dark:text-gray-500 mb-5 truncate max-w-full", children: attachment.filename }),
|
|
2728
|
-
/* @__PURE__ */ jsxs(
|
|
2729
|
-
"button",
|
|
2730
|
-
{
|
|
2731
|
-
type: "button",
|
|
2732
|
-
onClick: handleRetry,
|
|
2733
|
-
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",
|
|
2734
|
-
"aria-label": t("BiChat.Image.Retry"),
|
|
2735
|
-
children: [
|
|
2736
|
-
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
2737
|
-
t("BiChat.Retry.Label")
|
|
2738
|
-
]
|
|
2739
|
-
}
|
|
2740
|
-
)
|
|
2803
|
+
/* @__PURE__ */ jsxs(
|
|
2804
|
+
DialogPanel,
|
|
2805
|
+
{
|
|
2806
|
+
className: "fixed inset-0 flex flex-col",
|
|
2807
|
+
style: { zIndex: 1e5 },
|
|
2808
|
+
onMouseMove: handleMouseMove,
|
|
2809
|
+
onMouseUp: handleMouseUp,
|
|
2810
|
+
onMouseLeave: handleMouseUp,
|
|
2811
|
+
children: [
|
|
2812
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center px-5 py-3 shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
2813
|
+
hasMultipleImages && /* @__PURE__ */ jsxs("span", { className: "text-xs text-white/50 tabular-nums whitespace-nowrap font-medium", children: [
|
|
2814
|
+
currentIndex + 1,
|
|
2815
|
+
" / ",
|
|
2816
|
+
allAttachments?.length
|
|
2741
2817
|
] }),
|
|
2742
|
-
/* @__PURE__ */ jsx(
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
className:
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2818
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-white/90 truncate font-medium", children: attachment.filename }),
|
|
2819
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-white/40 whitespace-nowrap", children: formatFileSize(attachment.sizeBytes) })
|
|
2820
|
+
] }) }),
|
|
2821
|
+
/* @__PURE__ */ jsxs(
|
|
2822
|
+
"div",
|
|
2823
|
+
{
|
|
2824
|
+
ref: imageAreaRef,
|
|
2825
|
+
className: "relative flex-1 flex items-center justify-center min-h-0 px-4 pb-4",
|
|
2826
|
+
onClick: handleBackdropClick,
|
|
2827
|
+
style: { cursor: isZoomed ? isDragging ? "grabbing" : "grab" : "default" },
|
|
2828
|
+
children: [
|
|
2829
|
+
/* @__PURE__ */ jsx(
|
|
2830
|
+
"button",
|
|
2831
|
+
{
|
|
2832
|
+
onClick: onClose,
|
|
2833
|
+
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",
|
|
2834
|
+
"aria-label": t("BiChat.Image.Close"),
|
|
2835
|
+
type: "button",
|
|
2836
|
+
children: /* @__PURE__ */ jsx(X, { size: 20, weight: "bold" })
|
|
2837
|
+
}
|
|
2838
|
+
),
|
|
2839
|
+
!isImageLoaded && !imageError && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center pointer-events-none", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2840
|
+
/* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-2 border-white/20 border-t-white/60 rounded-full animate-spin" }),
|
|
2841
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-white/40", children: t("BiChat.Loading") })
|
|
2842
|
+
] }) }),
|
|
2843
|
+
imageError && /* @__PURE__ */ jsxs("div", { role: "alert", className: "flex flex-col items-center justify-center text-center max-w-xs", children: [
|
|
2844
|
+
/* @__PURE__ */ 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__ */ jsx(ImageBroken, { size: 28, className: "text-white/30", weight: "duotone" }) }),
|
|
2845
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-white/70 mb-1", children: t("BiChat.Image.FailedToLoad") }),
|
|
2846
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-white/30 mb-5 truncate max-w-full", children: attachment.filename }),
|
|
2847
|
+
/* @__PURE__ */ jsxs(
|
|
2848
|
+
"button",
|
|
2849
|
+
{
|
|
2850
|
+
type: "button",
|
|
2851
|
+
onClick: handleRetry,
|
|
2852
|
+
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",
|
|
2853
|
+
"aria-label": t("BiChat.Image.Retry"),
|
|
2854
|
+
children: [
|
|
2855
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
2856
|
+
t("BiChat.Retry.Label")
|
|
2857
|
+
]
|
|
2858
|
+
}
|
|
2859
|
+
)
|
|
2860
|
+
] }),
|
|
2861
|
+
/* @__PURE__ */ jsx(
|
|
2862
|
+
"img",
|
|
2863
|
+
{
|
|
2864
|
+
src: previewUrl,
|
|
2865
|
+
alt: attachment.filename,
|
|
2866
|
+
className: [
|
|
2867
|
+
"relative z-0 max-w-[85vw] max-h-[calc(100vh-160px)] object-contain select-none rounded-lg",
|
|
2868
|
+
"transition-opacity duration-300 ease-out",
|
|
2869
|
+
isImageLoaded ? "opacity-100" : "opacity-0"
|
|
2870
|
+
].join(" "),
|
|
2871
|
+
style: {
|
|
2872
|
+
transform: `translate(${position.x}px, ${position.y}px) scale(${scale})`,
|
|
2873
|
+
transformOrigin: "center center",
|
|
2874
|
+
transition: isDragging ? "opacity 0.3s ease-out" : "transform 0.2s ease-out, opacity 0.3s ease-out"
|
|
2875
|
+
},
|
|
2876
|
+
onLoad: () => setIsImageLoaded(true),
|
|
2877
|
+
onError: () => setImageError(true),
|
|
2878
|
+
onMouseDown: handleMouseDown,
|
|
2879
|
+
onDoubleClick: handleDoubleClick,
|
|
2880
|
+
loading: "lazy",
|
|
2881
|
+
draggable: false
|
|
2882
|
+
},
|
|
2883
|
+
retryKey
|
|
2884
|
+
),
|
|
2885
|
+
hasMultipleImages && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2886
|
+
/* @__PURE__ */ jsx(
|
|
2887
|
+
"button",
|
|
2888
|
+
{
|
|
2889
|
+
onClick: () => onNavigate?.("prev"),
|
|
2890
|
+
disabled: !canNavigatePrev || !isImageLoaded || imageError,
|
|
2891
|
+
className: [
|
|
2892
|
+
"absolute left-4 top-1/2 -translate-y-1/2 z-20",
|
|
2893
|
+
"flex items-center justify-center w-11 h-11 rounded-full",
|
|
2894
|
+
"transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2895
|
+
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"
|
|
2896
|
+
].join(" "),
|
|
2897
|
+
"aria-label": t("BiChat.Image.Previous"),
|
|
2898
|
+
type: "button",
|
|
2899
|
+
children: /* @__PURE__ */ jsx(CaretLeft, { size: 20, weight: "bold" })
|
|
2900
|
+
}
|
|
2901
|
+
),
|
|
2902
|
+
/* @__PURE__ */ jsx(
|
|
2903
|
+
"button",
|
|
2904
|
+
{
|
|
2905
|
+
onClick: () => onNavigate?.("next"),
|
|
2906
|
+
disabled: !canNavigateNext || !isImageLoaded || imageError,
|
|
2907
|
+
className: [
|
|
2908
|
+
"absolute right-4 top-1/2 -translate-y-1/2 z-20",
|
|
2909
|
+
"flex items-center justify-center w-11 h-11 rounded-full",
|
|
2910
|
+
"transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/30",
|
|
2911
|
+
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"
|
|
2912
|
+
].join(" "),
|
|
2913
|
+
"aria-label": t("BiChat.Image.Next"),
|
|
2914
|
+
type: "button",
|
|
2915
|
+
children: /* @__PURE__ */ jsx(CaretRight, { size: 20, weight: "bold" })
|
|
2916
|
+
}
|
|
2917
|
+
)
|
|
2918
|
+
] }),
|
|
2919
|
+
isImageLoaded && !imageError && /* @__PURE__ */ 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: [
|
|
2920
|
+
/* @__PURE__ */ jsx(
|
|
2921
|
+
"button",
|
|
2922
|
+
{
|
|
2923
|
+
type: "button",
|
|
2924
|
+
onClick: zoomOut,
|
|
2925
|
+
disabled: scale <= MIN_SCALE,
|
|
2926
|
+
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",
|
|
2927
|
+
"aria-label": t("BiChat.Image.ZoomOut"),
|
|
2928
|
+
children: /* @__PURE__ */ jsx(MagnifyingGlassMinus, { size: 16, weight: "bold" })
|
|
2929
|
+
}
|
|
2930
|
+
),
|
|
2931
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-white/60 tabular-nums font-medium min-w-[3.5rem] text-center select-none", children: [
|
|
2932
|
+
zoomPercent,
|
|
2933
|
+
"%"
|
|
2934
|
+
] }),
|
|
2935
|
+
/* @__PURE__ */ jsx(
|
|
2936
|
+
"button",
|
|
2937
|
+
{
|
|
2938
|
+
type: "button",
|
|
2939
|
+
onClick: zoomIn,
|
|
2940
|
+
disabled: scale >= MAX_SCALE,
|
|
2941
|
+
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",
|
|
2942
|
+
"aria-label": t("BiChat.Image.ZoomIn"),
|
|
2943
|
+
children: /* @__PURE__ */ jsx(MagnifyingGlassPlus, { size: 16, weight: "bold" })
|
|
2944
|
+
}
|
|
2945
|
+
),
|
|
2946
|
+
isZoomed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2947
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-white/15 mx-1" }),
|
|
2948
|
+
/* @__PURE__ */ jsx(
|
|
2949
|
+
"button",
|
|
2950
|
+
{
|
|
2951
|
+
type: "button",
|
|
2952
|
+
onClick: resetZoom,
|
|
2953
|
+
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",
|
|
2954
|
+
"aria-label": t("BiChat.Image.ResetZoom"),
|
|
2955
|
+
children: /* @__PURE__ */ jsx(ArrowsIn, { size: 16, weight: "bold" })
|
|
2956
|
+
}
|
|
2957
|
+
)
|
|
2958
|
+
] })
|
|
2959
|
+
] })
|
|
2960
|
+
]
|
|
2961
|
+
}
|
|
2962
|
+
)
|
|
2963
|
+
]
|
|
2964
|
+
}
|
|
2965
|
+
)
|
|
2797
2966
|
] });
|
|
2798
2967
|
}
|
|
2799
2968
|
var ImageModal_default = ImageModal;
|
|
@@ -2845,6 +3014,7 @@ function UserMessage({
|
|
|
2845
3014
|
const [draftContent, setDraftContent] = useState("");
|
|
2846
3015
|
const [isCopied, setIsCopied] = useState(false);
|
|
2847
3016
|
const copyFeedbackTimeoutRef = useRef(null);
|
|
3017
|
+
const editTextareaRef = useRef(null);
|
|
2848
3018
|
const classes = mergeClassNames(defaultClassNames, classNameOverrides);
|
|
2849
3019
|
useEffect(() => {
|
|
2850
3020
|
return () => {
|
|
@@ -2854,6 +3024,16 @@ function UserMessage({
|
|
|
2854
3024
|
}
|
|
2855
3025
|
};
|
|
2856
3026
|
}, []);
|
|
3027
|
+
useEffect(() => {
|
|
3028
|
+
if (isEditing && editTextareaRef.current) {
|
|
3029
|
+
const textarea = editTextareaRef.current;
|
|
3030
|
+
textarea.focus();
|
|
3031
|
+
textarea.selectionStart = textarea.value.length;
|
|
3032
|
+
textarea.selectionEnd = textarea.value.length;
|
|
3033
|
+
textarea.style.height = "auto";
|
|
3034
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, 300)}px`;
|
|
3035
|
+
}
|
|
3036
|
+
}, [isEditing]);
|
|
2857
3037
|
const normalizedAttachments = turn.attachments.map((attachment) => {
|
|
2858
3038
|
if (!attachment.mimeType.startsWith("image/")) {
|
|
2859
3039
|
return attachment;
|
|
@@ -2938,6 +3118,21 @@ function UserMessage({
|
|
|
2938
3118
|
onEdit(turnId, newContent);
|
|
2939
3119
|
setIsEditing(false);
|
|
2940
3120
|
}, [onEdit, turnId, draftContent, turn.content]);
|
|
3121
|
+
const handleEditKeyDown = useCallback((e) => {
|
|
3122
|
+
if (e.key === "Escape") {
|
|
3123
|
+
e.preventDefault();
|
|
3124
|
+
handleEditCancel();
|
|
3125
|
+
} else if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
3126
|
+
e.preventDefault();
|
|
3127
|
+
handleEditSave();
|
|
3128
|
+
}
|
|
3129
|
+
}, [handleEditCancel, handleEditSave]);
|
|
3130
|
+
const handleDraftChange = useCallback((e) => {
|
|
3131
|
+
setDraftContent(e.target.value);
|
|
3132
|
+
const el = e.target;
|
|
3133
|
+
el.style.height = "auto";
|
|
3134
|
+
el.style.height = `${Math.min(el.scrollHeight, 300)}px`;
|
|
3135
|
+
}, []);
|
|
2941
3136
|
const handleNavigate = useCallback(
|
|
2942
3137
|
(direction) => {
|
|
2943
3138
|
if (selectedImageIndex === null) return;
|
|
@@ -2988,36 +3183,48 @@ function UserMessage({
|
|
|
2988
3183
|
}
|
|
2989
3184
|
)
|
|
2990
3185
|
) }),
|
|
2991
|
-
turn.content && /* @__PURE__ */ jsx("div", { className: classes.bubble, children: /* @__PURE__ */ jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxs("div", { className: "space-y-
|
|
3186
|
+
turn.content && /* @__PURE__ */ jsx("div", { className: classes.bubble, children: /* @__PURE__ */ jsx("div", { className: classes.content, children: isEditing ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2992
3187
|
/* @__PURE__ */ jsx(
|
|
2993
3188
|
"textarea",
|
|
2994
3189
|
{
|
|
3190
|
+
ref: editTextareaRef,
|
|
2995
3191
|
value: draftContent,
|
|
2996
|
-
onChange:
|
|
2997
|
-
|
|
2998
|
-
"
|
|
3192
|
+
onChange: handleDraftChange,
|
|
3193
|
+
onKeyDown: handleEditKeyDown,
|
|
3194
|
+
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",
|
|
3195
|
+
"aria-label": t("BiChat.Message.EditMessage"),
|
|
3196
|
+
rows: 1
|
|
2999
3197
|
}
|
|
3000
3198
|
),
|
|
3001
|
-
/* @__PURE__ */ jsxs("div", { className: "flex justify-
|
|
3002
|
-
/* @__PURE__ */
|
|
3003
|
-
"
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3199
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
3200
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px] text-white/30 select-none hidden sm:inline", children: [
|
|
3201
|
+
"Esc \xB7 ",
|
|
3202
|
+
typeof navigator !== "undefined" && /mac|iphone|ipad/i.test(
|
|
3203
|
+
navigator.userAgentData?.platform ?? navigator?.platform ?? ""
|
|
3204
|
+
) ? "\u2318" : "Ctrl",
|
|
3205
|
+
"+Enter"
|
|
3206
|
+
] }),
|
|
3207
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 ml-auto", children: [
|
|
3208
|
+
/* @__PURE__ */ jsx(
|
|
3209
|
+
"button",
|
|
3210
|
+
{
|
|
3211
|
+
type: "button",
|
|
3212
|
+
onClick: handleEditCancel,
|
|
3213
|
+
className: "cursor-pointer px-3 py-1.5 rounded-lg text-white/60 hover:text-white hover:bg-white/10 transition-colors text-sm",
|
|
3214
|
+
children: t("BiChat.Message.Cancel")
|
|
3215
|
+
}
|
|
3216
|
+
),
|
|
3217
|
+
/* @__PURE__ */ jsx(
|
|
3218
|
+
"button",
|
|
3219
|
+
{
|
|
3220
|
+
type: "button",
|
|
3221
|
+
onClick: handleEditSave,
|
|
3222
|
+
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",
|
|
3223
|
+
disabled: !draftContent.trim() || draftContent === turn.content,
|
|
3224
|
+
children: t("BiChat.Message.Save")
|
|
3225
|
+
}
|
|
3226
|
+
)
|
|
3227
|
+
] })
|
|
3021
3228
|
] })
|
|
3022
3229
|
] }) : renderSlot(slots?.content, contentSlotProps, turn.content) }) }),
|
|
3023
3230
|
!hideActions && /* @__PURE__ */ jsx("div", { className: `${classes.actions} ${isCopied ? "opacity-100" : ""}`, children: renderSlot(
|
|
@@ -3167,65 +3374,293 @@ var StreamingCursor_default = StreamingCursor;
|
|
|
3167
3374
|
|
|
3168
3375
|
// ui/src/bichat/components/AssistantMessage.tsx
|
|
3169
3376
|
init_ChartCard();
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3377
|
+
|
|
3378
|
+
// ui/src/bichat/components/InteractiveTableCard.tsx
|
|
3379
|
+
init_useTranslation();
|
|
3380
|
+
init_TableExportButton();
|
|
3381
|
+
var PAGE_SIZE_OPTIONS = [10, 25, 50, 100, 200];
|
|
3382
|
+
function formatCell(value) {
|
|
3383
|
+
if (value === null || value === void 0) return "NULL";
|
|
3384
|
+
if (typeof value === "object") {
|
|
3385
|
+
try {
|
|
3386
|
+
return JSON.stringify(value);
|
|
3387
|
+
} catch {
|
|
3388
|
+
return String(value);
|
|
3389
|
+
}
|
|
3173
3390
|
}
|
|
3174
|
-
return
|
|
3175
|
-
|
|
3391
|
+
return String(value);
|
|
3392
|
+
}
|
|
3393
|
+
var InteractiveTableCard = memo(function InteractiveTableCard2({
|
|
3394
|
+
table,
|
|
3395
|
+
onSendMessage,
|
|
3396
|
+
sendDisabled = false
|
|
3397
|
+
}) {
|
|
3398
|
+
const { t } = useTranslation();
|
|
3399
|
+
const defaultPageSize = Math.min(Math.max(table.pageSize || 25, 1), 200);
|
|
3400
|
+
const [page, setPage] = useState(1);
|
|
3401
|
+
const [pageSize, setPageSize] = useState(defaultPageSize);
|
|
3402
|
+
useEffect(() => {
|
|
3403
|
+
const nextPageSize = Math.min(Math.max(table.pageSize || 25, 1), 200);
|
|
3404
|
+
setPage(1);
|
|
3405
|
+
setPageSize(nextPageSize);
|
|
3406
|
+
}, [table.id, table.pageSize]);
|
|
3407
|
+
const totalRows = table.rows.length;
|
|
3408
|
+
const totalPages = Math.max(1, Math.ceil(totalRows / pageSize));
|
|
3409
|
+
useEffect(() => {
|
|
3410
|
+
if (page > totalPages) {
|
|
3411
|
+
setPage(totalPages);
|
|
3412
|
+
}
|
|
3413
|
+
}, [page, totalPages]);
|
|
3414
|
+
const pageRows = useMemo(() => {
|
|
3415
|
+
const start = (page - 1) * pageSize;
|
|
3416
|
+
return table.rows.slice(start, start + pageSize);
|
|
3417
|
+
}, [page, pageSize, table.rows]);
|
|
3418
|
+
const pageSizeOptions = useMemo(() => {
|
|
3419
|
+
const set = /* @__PURE__ */ new Set([...PAGE_SIZE_OPTIONS, defaultPageSize]);
|
|
3420
|
+
return [...set].sort((a, b) => a - b);
|
|
3421
|
+
}, [defaultPageSize]);
|
|
3422
|
+
const canExportViaPrompt = !!onSendMessage && !!table.exportPrompt;
|
|
3423
|
+
const exportDisabled = sendDisabled || !table.export?.url && !canExportViaPrompt;
|
|
3424
|
+
const handleExport = useCallback(() => {
|
|
3425
|
+
if (table.export?.url) {
|
|
3426
|
+
try {
|
|
3427
|
+
const parsed = new URL(table.export.url, window.location.origin);
|
|
3428
|
+
if (!["http:", "https:", "blob:"].includes(parsed.protocol)) {
|
|
3429
|
+
console.warn("[InteractiveTableCard] Blocked export URL with unsafe protocol:", parsed.protocol);
|
|
3430
|
+
return;
|
|
3431
|
+
}
|
|
3432
|
+
} catch {
|
|
3433
|
+
console.warn("[InteractiveTableCard] Blocked malformed export URL");
|
|
3434
|
+
return;
|
|
3435
|
+
}
|
|
3436
|
+
const link = document.createElement("a");
|
|
3437
|
+
link.href = table.export.url;
|
|
3438
|
+
link.download = table.export.filename || "table_export.xlsx";
|
|
3439
|
+
link.rel = "noopener noreferrer";
|
|
3440
|
+
document.body.appendChild(link);
|
|
3441
|
+
link.click();
|
|
3442
|
+
document.body.removeChild(link);
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
if (canExportViaPrompt && table.exportPrompt) {
|
|
3446
|
+
onSendMessage?.(table.exportPrompt);
|
|
3447
|
+
}
|
|
3448
|
+
}, [canExportViaPrompt, onSendMessage, table.export, table.exportPrompt]);
|
|
3449
|
+
const from = totalRows === 0 ? 0 : (page - 1) * pageSize + 1;
|
|
3450
|
+
const to = Math.min(page * pageSize, totalRows);
|
|
3451
|
+
return /* @__PURE__ */ jsxs("section", { className: "w-full rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900/40", children: [
|
|
3452
|
+
/* @__PURE__ */ 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: [
|
|
3453
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
3454
|
+
/* @__PURE__ */ jsx("h4", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: table.title || t("BiChat.Table.QueryResults") }),
|
|
3455
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
3456
|
+
totalRows === 1 ? t("BiChat.Table.OneRowLoaded") : t("BiChat.Table.RowsLoaded", { count: String(totalRows) }),
|
|
3457
|
+
table.truncated ? ` ${t("BiChat.Table.TruncatedSuffix")}` : ""
|
|
3458
|
+
] })
|
|
3459
|
+
] }),
|
|
3176
3460
|
/* @__PURE__ */ jsx(
|
|
3177
|
-
|
|
3461
|
+
TableExportButton,
|
|
3178
3462
|
{
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
children: /* @__PURE__ */ jsx(
|
|
3184
|
-
"path",
|
|
3185
|
-
{
|
|
3186
|
-
strokeLinecap: "round",
|
|
3187
|
-
strokeLinejoin: "round",
|
|
3188
|
-
strokeWidth: 2,
|
|
3189
|
-
d: "M9 5l7 7-7 7"
|
|
3190
|
-
}
|
|
3191
|
-
)
|
|
3463
|
+
onClick: handleExport,
|
|
3464
|
+
disabled: exportDisabled,
|
|
3465
|
+
label: t("BiChat.Table.ExportToExcel"),
|
|
3466
|
+
disabledTooltip: sendDisabled ? t("BiChat.Table.PleaseWait") : t("BiChat.Table.ExportUnavailable")
|
|
3192
3467
|
}
|
|
3193
|
-
)
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3468
|
+
)
|
|
3469
|
+
] }),
|
|
3470
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-[420px] overflow-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
|
|
3471
|
+
/* @__PURE__ */ jsx("thead", { className: "sticky top-0 bg-gray-100 dark:bg-gray-800 z-10", children: /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 dark:border-gray-700", children: table.headers.map((header, index) => /* @__PURE__ */ jsx(
|
|
3472
|
+
"th",
|
|
3473
|
+
{
|
|
3474
|
+
className: "px-3 py-2 text-left font-semibold text-gray-700 dark:text-gray-200 whitespace-nowrap",
|
|
3475
|
+
children: header
|
|
3476
|
+
},
|
|
3477
|
+
`${table.id}-header-${index}`
|
|
3478
|
+
)) }) }),
|
|
3479
|
+
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
3480
|
+
pageRows.map((row, rowIndex) => /* @__PURE__ */ jsx(
|
|
3481
|
+
"tr",
|
|
3482
|
+
{
|
|
3483
|
+
className: "border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800/40",
|
|
3484
|
+
children: table.columns.map((_, columnIndex) => /* @__PURE__ */ jsx(
|
|
3485
|
+
"td",
|
|
3486
|
+
{
|
|
3487
|
+
className: "px-3 py-2 text-gray-700 dark:text-gray-300 align-top",
|
|
3488
|
+
children: /* @__PURE__ */ jsx("span", { className: "block max-w-[420px] truncate", title: formatCell(row[columnIndex]), children: formatCell(row[columnIndex]) })
|
|
3489
|
+
},
|
|
3490
|
+
`${table.id}-cell-${rowIndex}-${columnIndex}`
|
|
3491
|
+
))
|
|
3492
|
+
},
|
|
3493
|
+
`${table.id}-row-${rowIndex}`
|
|
3494
|
+
)),
|
|
3495
|
+
pageRows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
3496
|
+
"td",
|
|
3497
|
+
{
|
|
3498
|
+
colSpan: table.columns.length,
|
|
3499
|
+
className: "px-3 py-6 text-center text-sm text-gray-500 dark:text-gray-400",
|
|
3500
|
+
children: t("BiChat.Table.NoRows")
|
|
3501
|
+
}
|
|
3502
|
+
) })
|
|
3503
|
+
] })
|
|
3504
|
+
] }) }),
|
|
3505
|
+
/* @__PURE__ */ 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: [
|
|
3506
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Table.Showing", {
|
|
3507
|
+
from: String(from),
|
|
3508
|
+
to: String(to),
|
|
3509
|
+
total: String(totalRows)
|
|
3510
|
+
}) }),
|
|
3511
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3512
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs text-gray-500 dark:text-gray-400", htmlFor: `${table.id}-page-size`, children: t("BiChat.Table.RowsLabel") }),
|
|
3513
|
+
/* @__PURE__ */ jsx(
|
|
3514
|
+
"select",
|
|
3515
|
+
{
|
|
3516
|
+
id: `${table.id}-page-size`,
|
|
3517
|
+
value: pageSize,
|
|
3518
|
+
onChange: (event) => {
|
|
3519
|
+
setPageSize(Number(event.target.value));
|
|
3520
|
+
setPage(1);
|
|
3521
|
+
},
|
|
3522
|
+
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",
|
|
3523
|
+
children: pageSizeOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, `${table.id}-size-${option}`))
|
|
3524
|
+
}
|
|
3525
|
+
),
|
|
3526
|
+
/* @__PURE__ */ jsx(
|
|
3527
|
+
"button",
|
|
3528
|
+
{
|
|
3529
|
+
type: "button",
|
|
3530
|
+
onClick: () => setPage((current) => Math.max(1, current - 1)),
|
|
3531
|
+
disabled: page <= 1,
|
|
3532
|
+
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",
|
|
3533
|
+
children: t("BiChat.Table.Prev")
|
|
3534
|
+
}
|
|
3535
|
+
),
|
|
3536
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Table.PageOf", { page: String(page), total: String(totalPages) }) }),
|
|
3537
|
+
/* @__PURE__ */ jsx(
|
|
3538
|
+
"button",
|
|
3539
|
+
{
|
|
3540
|
+
type: "button",
|
|
3541
|
+
onClick: () => setPage((current) => Math.min(totalPages, current + 1)),
|
|
3542
|
+
disabled: page >= totalPages,
|
|
3543
|
+
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",
|
|
3544
|
+
children: t("BiChat.Table.Next")
|
|
3545
|
+
}
|
|
3546
|
+
)
|
|
3198
3547
|
] })
|
|
3199
3548
|
] }),
|
|
3200
|
-
/* @__PURE__ */ jsx(
|
|
3201
|
-
|
|
3549
|
+
table.truncated && /* @__PURE__ */ 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") })
|
|
3550
|
+
] });
|
|
3551
|
+
});
|
|
3552
|
+
|
|
3553
|
+
// ui/src/bichat/components/SourcesPanel.tsx
|
|
3554
|
+
init_useTranslation();
|
|
3555
|
+
function extractDomain(url) {
|
|
3556
|
+
try {
|
|
3557
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
3558
|
+
} catch {
|
|
3559
|
+
return "";
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3562
|
+
var PALETTE = [
|
|
3563
|
+
"#c0392b",
|
|
3564
|
+
"#d35400",
|
|
3565
|
+
"#f39c12",
|
|
3566
|
+
"#27ae60",
|
|
3567
|
+
"#16a085",
|
|
3568
|
+
"#2980b9",
|
|
3569
|
+
"#8e44ad",
|
|
3570
|
+
"#d63384"
|
|
3571
|
+
];
|
|
3572
|
+
function domainColor(domain) {
|
|
3573
|
+
let h = 0;
|
|
3574
|
+
for (let i = 0; i < domain.length; i++) h = domain.charCodeAt(i) + ((h << 5) - h);
|
|
3575
|
+
return PALETTE[Math.abs(h) % PALETTE.length];
|
|
3576
|
+
}
|
|
3577
|
+
function SourcesPanel({ citations }) {
|
|
3578
|
+
const { t } = useTranslation();
|
|
3579
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
3580
|
+
const open = useCallback(() => setIsOpen(true), []);
|
|
3581
|
+
const close = useCallback(() => setIsOpen(false), []);
|
|
3582
|
+
if (!citations?.length) return null;
|
|
3583
|
+
const domains = [...new Set(
|
|
3584
|
+
citations.filter((c) => c.url).map((c) => extractDomain(c.url)).filter(Boolean)
|
|
3585
|
+
)];
|
|
3586
|
+
const previewDomains = domains.slice(0, 5);
|
|
3587
|
+
if (!isOpen) {
|
|
3588
|
+
return /* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
3589
|
+
"button",
|
|
3202
3590
|
{
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
'"'
|
|
3222
|
-
] })
|
|
3591
|
+
type: "button",
|
|
3592
|
+
onClick: open,
|
|
3593
|
+
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",
|
|
3594
|
+
children: [
|
|
3595
|
+
previewDomains.length > 0 && /* @__PURE__ */ jsx("span", { className: "flex -space-x-1.5", children: previewDomains.map((domain, i) => /* @__PURE__ */ jsx(
|
|
3596
|
+
"span",
|
|
3597
|
+
{
|
|
3598
|
+
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",
|
|
3599
|
+
style: { backgroundColor: domainColor(domain), zIndex: previewDomains.length - i },
|
|
3600
|
+
"aria-hidden": "true",
|
|
3601
|
+
children: domain[0]?.toUpperCase()
|
|
3602
|
+
},
|
|
3603
|
+
domain
|
|
3604
|
+
)) }),
|
|
3605
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-gray-600 dark:text-gray-300 tabular-nums", children: [
|
|
3606
|
+
citations.length,
|
|
3607
|
+
" ",
|
|
3608
|
+
t(citations.length === 1 ? "BiChat.Sources.Source" : "BiChat.Sources.Sources")
|
|
3223
3609
|
] })
|
|
3610
|
+
]
|
|
3611
|
+
}
|
|
3612
|
+
) });
|
|
3613
|
+
}
|
|
3614
|
+
return /* @__PURE__ */ 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: [
|
|
3615
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3", children: [
|
|
3616
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: t("BiChat.Sources.Title") }),
|
|
3617
|
+
/* @__PURE__ */ jsx(
|
|
3618
|
+
"button",
|
|
3619
|
+
{
|
|
3620
|
+
type: "button",
|
|
3621
|
+
onClick: close,
|
|
3622
|
+
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",
|
|
3623
|
+
"aria-label": t("BiChat.Sources.Close"),
|
|
3624
|
+
children: /* @__PURE__ */ jsx(X, { size: 14, weight: "bold" })
|
|
3625
|
+
}
|
|
3626
|
+
)
|
|
3627
|
+
] }),
|
|
3628
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-80 overflow-y-auto", children: citations.map((citation, index) => {
|
|
3629
|
+
const domain = citation.url ? extractDomain(citation.url) : "";
|
|
3630
|
+
const cardContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3631
|
+
/* @__PURE__ */ 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) }) }),
|
|
3632
|
+
citation.excerpt && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-gray-500 dark:text-gray-400 line-clamp-2 leading-relaxed", children: citation.excerpt }),
|
|
3633
|
+
domain && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1.5", children: [
|
|
3634
|
+
/* @__PURE__ */ jsx(
|
|
3635
|
+
"span",
|
|
3636
|
+
{
|
|
3637
|
+
className: "w-4 h-4 rounded-full flex items-center justify-center text-[7px] font-bold text-white flex-shrink-0 select-none",
|
|
3638
|
+
style: { backgroundColor: domainColor(domain) },
|
|
3639
|
+
"aria-hidden": "true",
|
|
3640
|
+
children: domain[0]?.toUpperCase()
|
|
3641
|
+
}
|
|
3642
|
+
),
|
|
3643
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-gray-400 dark:text-gray-500 truncate", children: domain })
|
|
3224
3644
|
] })
|
|
3225
|
-
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3645
|
+
] });
|
|
3646
|
+
const cardClass = "block px-4 py-3 border-t border-gray-100 dark:border-gray-700/50";
|
|
3647
|
+
if (citation.url) {
|
|
3648
|
+
return /* @__PURE__ */ jsx(
|
|
3649
|
+
"a",
|
|
3650
|
+
{
|
|
3651
|
+
href: citation.url,
|
|
3652
|
+
target: "_blank",
|
|
3653
|
+
rel: "noopener noreferrer",
|
|
3654
|
+
className: `${cardClass} hover:bg-gray-50 dark:hover:bg-gray-700/40 transition-colors duration-100
|
|
3655
|
+
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--bichat-primary)]/40`,
|
|
3656
|
+
children: cardContent
|
|
3657
|
+
},
|
|
3658
|
+
citation.id
|
|
3659
|
+
);
|
|
3660
|
+
}
|
|
3661
|
+
return /* @__PURE__ */ jsx("div", { className: cardClass, children: cardContent }, citation.id);
|
|
3662
|
+
}) })
|
|
3663
|
+
] });
|
|
3229
3664
|
}
|
|
3230
3665
|
var MIME_BY_TYPE = {
|
|
3231
3666
|
excel: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
@@ -3286,7 +3721,7 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3286
3721
|
const [currentStep, setCurrentStep] = useState(0);
|
|
3287
3722
|
const [answers, setAnswers] = useState({});
|
|
3288
3723
|
const [otherTexts, setOtherTexts] = useState({});
|
|
3289
|
-
const questions = pendingQuestion.questions;
|
|
3724
|
+
const questions = Array.isArray(pendingQuestion.questions) ? pendingQuestion.questions : [];
|
|
3290
3725
|
const currentQuestion = questions[currentStep];
|
|
3291
3726
|
const isLastStep = currentStep === questions.length - 1;
|
|
3292
3727
|
const isFirstStep = currentStep === 0;
|
|
@@ -3380,9 +3815,31 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3380
3815
|
e.preventDefault();
|
|
3381
3816
|
handleNext();
|
|
3382
3817
|
};
|
|
3383
|
-
if (!currentQuestion)
|
|
3818
|
+
if (!currentQuestion) {
|
|
3819
|
+
return /* @__PURE__ */ jsxs("div", { className: "animate-slide-up rounded-2xl border border-amber-200 dark:border-amber-700/50 bg-gradient-to-b from-amber-50/70 to-white dark:from-amber-950/20 dark:to-gray-900/80 shadow-sm overflow-hidden p-4", children: [
|
|
3820
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-800 dark:text-gray-200", children: t("BiChat.Error.SomethingWentWrong") }),
|
|
3821
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: t("BiChat.Error.UnexpectedError") }),
|
|
3822
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxs(
|
|
3823
|
+
"button",
|
|
3824
|
+
{
|
|
3825
|
+
type: "button",
|
|
3826
|
+
onClick: handleRejectPendingQuestion,
|
|
3827
|
+
disabled: loading,
|
|
3828
|
+
className: "cursor-pointer inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-40",
|
|
3829
|
+
children: [
|
|
3830
|
+
/* @__PURE__ */ jsx(X, { size: 14, weight: "bold" }),
|
|
3831
|
+
t("BiChat.InlineQuestion.Dismiss")
|
|
3832
|
+
]
|
|
3833
|
+
}
|
|
3834
|
+
) })
|
|
3835
|
+
] });
|
|
3836
|
+
}
|
|
3384
3837
|
const isMultiSelect = currentQuestion.type === "MULTIPLE_CHOICE";
|
|
3385
|
-
const options = currentQuestion.options || []
|
|
3838
|
+
const options = (currentQuestion.options || []).filter((option) => Boolean(option && typeof option.label === "string")).map((option, index) => ({
|
|
3839
|
+
id: option.id || `${currentQuestion.id}-option-${index}`,
|
|
3840
|
+
label: option.label,
|
|
3841
|
+
value: option.value || option.label
|
|
3842
|
+
}));
|
|
3386
3843
|
const isOtherSelected = currentAnswer?.customText !== void 0;
|
|
3387
3844
|
const canProceed = isCurrentAnswerValid();
|
|
3388
3845
|
return /* @__PURE__ */ jsx("div", { className: "animate-slide-up rounded-2xl border border-gray-200 dark:border-gray-700/50 bg-gradient-to-b from-primary-50/80 to-white dark:from-primary-950/30 dark:to-gray-900/80 shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, children: [
|
|
@@ -3555,6 +4012,59 @@ function InlineQuestionForm({ pendingQuestion }) {
|
|
|
3555
4012
|
] }) });
|
|
3556
4013
|
}
|
|
3557
4014
|
|
|
4015
|
+
// ui/src/bichat/components/RetryActionArea.tsx
|
|
4016
|
+
init_useTranslation();
|
|
4017
|
+
var RetryActionArea = memo(function RetryActionArea2({
|
|
4018
|
+
onRetry
|
|
4019
|
+
}) {
|
|
4020
|
+
const { t } = useTranslation();
|
|
4021
|
+
return (
|
|
4022
|
+
// Wrapper matches TurnBubble layout for assistant messages (justify-start = left-aligned)
|
|
4023
|
+
/* @__PURE__ */ jsx(
|
|
4024
|
+
motion.div,
|
|
4025
|
+
{
|
|
4026
|
+
initial: { opacity: 0, y: 10 },
|
|
4027
|
+
animate: { opacity: 1, y: 0 },
|
|
4028
|
+
exit: { opacity: 0, y: -10 },
|
|
4029
|
+
transition: { duration: 0.2 },
|
|
4030
|
+
className: "flex justify-start",
|
|
4031
|
+
children: /* @__PURE__ */ jsxs(
|
|
4032
|
+
"div",
|
|
4033
|
+
{
|
|
4034
|
+
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",
|
|
4035
|
+
role: "status",
|
|
4036
|
+
"aria-live": "polite",
|
|
4037
|
+
children: [
|
|
4038
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
4039
|
+
/* @__PURE__ */ jsx(
|
|
4040
|
+
Warning,
|
|
4041
|
+
{
|
|
4042
|
+
className: "w-5 h-5 text-amber-500 dark:text-amber-400 flex-shrink-0",
|
|
4043
|
+
weight: "fill"
|
|
4044
|
+
}
|
|
4045
|
+
),
|
|
4046
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t("BiChat.Retry.Subtitle") })
|
|
4047
|
+
] }),
|
|
4048
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs(
|
|
4049
|
+
"button",
|
|
4050
|
+
{
|
|
4051
|
+
onClick: onRetry,
|
|
4052
|
+
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",
|
|
4053
|
+
"aria-label": t("BiChat.Retry.Title"),
|
|
4054
|
+
children: [
|
|
4055
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, className: "w-4 h-4" }),
|
|
4056
|
+
t("BiChat.Retry.Button")
|
|
4057
|
+
]
|
|
4058
|
+
}
|
|
4059
|
+
) })
|
|
4060
|
+
]
|
|
4061
|
+
}
|
|
4062
|
+
)
|
|
4063
|
+
}
|
|
4064
|
+
)
|
|
4065
|
+
);
|
|
4066
|
+
});
|
|
4067
|
+
|
|
3558
4068
|
// ui/src/bichat/utils/debugMetrics.ts
|
|
3559
4069
|
function formatGenerationDuration(generationMs) {
|
|
3560
4070
|
return generationMs > 1e3 ? `${(generationMs / 1e3).toFixed(2)}s` : `${generationMs}ms`;
|
|
@@ -3853,6 +4363,7 @@ var defaultClassNames2 = {
|
|
|
3853
4363
|
bubble: "bg-white dark:bg-gray-800 rounded-2xl rounded-bl-sm px-4 py-3 shadow-sm",
|
|
3854
4364
|
codeOutputs: "",
|
|
3855
4365
|
charts: "mb-1 w-full",
|
|
4366
|
+
tables: "mb-1 flex flex-col gap-3",
|
|
3856
4367
|
artifacts: "mb-1 flex flex-wrap gap-2",
|
|
3857
4368
|
sources: "",
|
|
3858
4369
|
explanation: "mt-4 border-t border-gray-100 dark:border-gray-700 pt-4",
|
|
@@ -3869,6 +4380,7 @@ function mergeClassNames2(defaults, overrides) {
|
|
|
3869
4380
|
bubble: overrides.bubble ?? defaults.bubble,
|
|
3870
4381
|
codeOutputs: overrides.codeOutputs ?? defaults.codeOutputs,
|
|
3871
4382
|
charts: overrides.charts ?? defaults.charts,
|
|
4383
|
+
tables: overrides.tables ?? defaults.tables,
|
|
3872
4384
|
artifacts: overrides.artifacts ?? defaults.artifacts,
|
|
3873
4385
|
sources: overrides.sources ?? defaults.sources,
|
|
3874
4386
|
explanation: overrides.explanation ?? defaults.explanation,
|
|
@@ -3913,6 +4425,14 @@ function AssistantMessage({
|
|
|
3913
4425
|
const hasContent = turn.content?.trim().length > 0;
|
|
3914
4426
|
const hasExplanation = !!turn.explanation?.trim();
|
|
3915
4427
|
const hasPendingQuestion = !!pendingQuestion && pendingQuestion.status === "PENDING" && pendingQuestion.turnId === turnId;
|
|
4428
|
+
const hasCodeOutputs = !!turn.codeOutputs?.length;
|
|
4429
|
+
const hasChart = !!turn.chartData;
|
|
4430
|
+
const hasTables = !!turn.renderTables?.length;
|
|
4431
|
+
const hasArtifacts = !!turn.artifacts?.length;
|
|
4432
|
+
const hasDebug = showDebug && !!turn.debug;
|
|
4433
|
+
const hasRenderablePayload = hasContent || hasExplanation || hasPendingQuestion || hasCodeOutputs || hasChart || hasTables || hasArtifacts || hasDebug;
|
|
4434
|
+
const canRegenerate = !!onRegenerate && !!turnId && !isSystemMessage && isLastTurn;
|
|
4435
|
+
const showInlineRetry = !hasRenderablePayload && canRegenerate;
|
|
3916
4436
|
const handleCopyClick = useCallback(async () => {
|
|
3917
4437
|
try {
|
|
3918
4438
|
if (onCopy) {
|
|
@@ -3954,15 +4474,18 @@ function AssistantMessage({
|
|
|
3954
4474
|
const codeOutputsSlotProps = {
|
|
3955
4475
|
outputs: turn.codeOutputs || []
|
|
3956
4476
|
};
|
|
4477
|
+
const tablesSlotProps = {
|
|
4478
|
+
tables: turn.renderTables || []
|
|
4479
|
+
};
|
|
3957
4480
|
const artifactsSlotProps = {
|
|
3958
4481
|
artifacts: turn.artifacts || []
|
|
3959
4482
|
};
|
|
3960
4483
|
const actionsSlotProps = {
|
|
3961
4484
|
onCopy: handleCopyClick,
|
|
3962
|
-
onRegenerate:
|
|
4485
|
+
onRegenerate: canRegenerate ? handleRegenerateClick : void 0,
|
|
3963
4486
|
timestamp,
|
|
3964
4487
|
canCopy: hasContent,
|
|
3965
|
-
canRegenerate
|
|
4488
|
+
canRegenerate
|
|
3966
4489
|
};
|
|
3967
4490
|
const explanationSlotProps = {
|
|
3968
4491
|
explanation: turn.explanation || "",
|
|
@@ -3975,14 +4498,30 @@ function AssistantMessage({
|
|
|
3975
4498
|
return slot;
|
|
3976
4499
|
};
|
|
3977
4500
|
return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
|
|
3978
|
-
!hideAvatar && /* @__PURE__ */ jsx("div", { className: avatarClassName, children: renderSlot(slots?.avatar, avatarSlotProps, isSystemMessage ? "SYS" : "AI") }),
|
|
4501
|
+
!hideAvatar && !showInlineRetry && /* @__PURE__ */ jsx("div", { className: avatarClassName, children: renderSlot(slots?.avatar, avatarSlotProps, isSystemMessage ? "SYS" : "AI") }),
|
|
3979
4502
|
/* @__PURE__ */ jsxs("div", { className: classes.wrapper, children: [
|
|
4503
|
+
showInlineRetry && /* @__PURE__ */ jsx(RetryActionArea, { onRetry: () => {
|
|
4504
|
+
void handleRegenerateClick();
|
|
4505
|
+
} }),
|
|
3980
4506
|
turn.codeOutputs && turn.codeOutputs.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.codeOutputs, children: renderSlot(
|
|
3981
4507
|
slots?.codeOutputs,
|
|
3982
4508
|
codeOutputsSlotProps,
|
|
3983
4509
|
/* @__PURE__ */ jsx(CodeOutputsPanel_default, { outputs: turn.codeOutputs })
|
|
3984
4510
|
) }),
|
|
3985
4511
|
turn.chartData && /* @__PURE__ */ jsx("div", { className: classes.charts, children: renderSlot(slots?.charts, chartsSlotProps, /* @__PURE__ */ jsx(ChartCard, { chartData: turn.chartData })) }),
|
|
4512
|
+
turn.renderTables && turn.renderTables.length > 0 && /* @__PURE__ */ jsx("div", { className: classes.tables, children: renderSlot(
|
|
4513
|
+
slots?.tables,
|
|
4514
|
+
tablesSlotProps,
|
|
4515
|
+
turn.renderTables.map((table) => /* @__PURE__ */ jsx(
|
|
4516
|
+
InteractiveTableCard,
|
|
4517
|
+
{
|
|
4518
|
+
table,
|
|
4519
|
+
onSendMessage,
|
|
4520
|
+
sendDisabled: sendDisabled || isStreaming
|
|
4521
|
+
},
|
|
4522
|
+
table.id
|
|
4523
|
+
))
|
|
4524
|
+
) }),
|
|
3986
4525
|
hasContent && /* @__PURE__ */ jsxs("div", { className: bubbleClassName, children: [
|
|
3987
4526
|
renderSlot(
|
|
3988
4527
|
slots?.content,
|
|
@@ -4072,7 +4611,7 @@ function AssistantMessage({
|
|
|
4072
4611
|
children: isCopied ? /* @__PURE__ */ jsx(Check, { size: 14, weight: "bold" }) : /* @__PURE__ */ jsx(Copy, { size: 14, weight: "regular" })
|
|
4073
4612
|
}
|
|
4074
4613
|
),
|
|
4075
|
-
|
|
4614
|
+
canRegenerate && /* @__PURE__ */ jsx(
|
|
4076
4615
|
"button",
|
|
4077
4616
|
{
|
|
4078
4617
|
onClick: handleRegenerateClick,
|
|
@@ -4296,7 +4835,8 @@ function TurnBubble({
|
|
|
4296
4835
|
userTurn: classNames?.userTurn ?? defaultClassNames3.userTurn,
|
|
4297
4836
|
assistantTurn: classNames?.assistantTurn ?? defaultClassNames3.assistantTurn
|
|
4298
4837
|
};
|
|
4299
|
-
const
|
|
4838
|
+
const userContent = typeof turn.userTurn?.content === "string" ? turn.userTurn.content : "";
|
|
4839
|
+
const isSystemSummaryTurn = userContent.trim() === "" && turn.assistantTurn?.role === "system";
|
|
4300
4840
|
return /* @__PURE__ */ jsxs("div", { className: classes.root, "data-turn-id": turn.id, children: [
|
|
4301
4841
|
!isSystemSummaryTurn && /* @__PURE__ */ jsx("div", { className: classes.userTurn, children: renderUserTurn ? renderUserTurn(turn) : /* @__PURE__ */ jsx(
|
|
4302
4842
|
UserTurnView,
|
|
@@ -8074,12 +8614,63 @@ function DefaultErrorContent({
|
|
|
8074
8614
|
] })
|
|
8075
8615
|
] });
|
|
8076
8616
|
}
|
|
8617
|
+
function StaticEmergencyErrorContent({
|
|
8618
|
+
error,
|
|
8619
|
+
onReset
|
|
8620
|
+
}) {
|
|
8621
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center p-8 text-center min-h-[200px]", children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center", children: [
|
|
8622
|
+
/* @__PURE__ */ jsxs("div", { className: "relative mb-5", children: [
|
|
8623
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-red-100 scale-150 blur-md" }),
|
|
8624
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex items-center justify-center w-14 h-14 rounded-full bg-red-50 border border-red-200/60", children: /* @__PURE__ */ jsx(WarningCircle, { size: 28, className: "text-red-500", weight: "fill" }) })
|
|
8625
|
+
] }),
|
|
8626
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-1.5", children: "Something went wrong" }),
|
|
8627
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 mb-5 max-w-md leading-relaxed", children: error?.message || "An unexpected UI error occurred." }),
|
|
8628
|
+
onReset && /* @__PURE__ */ jsxs(
|
|
8629
|
+
"button",
|
|
8630
|
+
{
|
|
8631
|
+
type: "button",
|
|
8632
|
+
onClick: onReset,
|
|
8633
|
+
className: "flex items-center gap-2 px-5 py-2.5 bg-red-600 hover:bg-red-700 active:bg-red-800 text-white rounded-lg transition-colors shadow-sm text-sm font-medium",
|
|
8634
|
+
children: [
|
|
8635
|
+
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, weight: "bold" }),
|
|
8636
|
+
"Try again"
|
|
8637
|
+
]
|
|
8638
|
+
}
|
|
8639
|
+
)
|
|
8640
|
+
] }) });
|
|
8641
|
+
}
|
|
8642
|
+
var FallbackGuard = class extends Component {
|
|
8643
|
+
constructor(props) {
|
|
8644
|
+
super(props);
|
|
8645
|
+
this.state = { fallbackFailed: false };
|
|
8646
|
+
}
|
|
8647
|
+
static getDerivedStateFromError() {
|
|
8648
|
+
return { fallbackFailed: true };
|
|
8649
|
+
}
|
|
8650
|
+
componentDidCatch(error, errorInfo) {
|
|
8651
|
+
this.props.onFallbackError?.(error, errorInfo);
|
|
8652
|
+
}
|
|
8653
|
+
render() {
|
|
8654
|
+
if (this.state.fallbackFailed) {
|
|
8655
|
+
return /* @__PURE__ */ jsx(StaticEmergencyErrorContent, { error: this.props.primaryError, onReset: this.props.onReset });
|
|
8656
|
+
}
|
|
8657
|
+
return this.props.renderFallback();
|
|
8658
|
+
}
|
|
8659
|
+
};
|
|
8077
8660
|
var ErrorBoundary = class extends Component {
|
|
8078
8661
|
constructor(props) {
|
|
8079
8662
|
super(props);
|
|
8080
8663
|
this.handleReset = () => {
|
|
8081
8664
|
this.setState({ hasError: false, error: null });
|
|
8082
8665
|
};
|
|
8666
|
+
this.handleFallbackError = (error, errorInfo) => {
|
|
8667
|
+
console.error("React Error Boundary fallback crashed:", {
|
|
8668
|
+
primaryError: this.state.error,
|
|
8669
|
+
fallbackError: error,
|
|
8670
|
+
errorInfo
|
|
8671
|
+
});
|
|
8672
|
+
this.props.onError?.(error, errorInfo);
|
|
8673
|
+
};
|
|
8083
8674
|
this.state = { hasError: false, error: null };
|
|
8084
8675
|
}
|
|
8085
8676
|
static getDerivedStateFromError(error) {
|
|
@@ -8091,13 +8682,24 @@ var ErrorBoundary = class extends Component {
|
|
|
8091
8682
|
}
|
|
8092
8683
|
render() {
|
|
8093
8684
|
if (this.state.hasError) {
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8685
|
+
return /* @__PURE__ */ jsx(
|
|
8686
|
+
FallbackGuard,
|
|
8687
|
+
{
|
|
8688
|
+
primaryError: this.state.error,
|
|
8689
|
+
onReset: this.handleReset,
|
|
8690
|
+
onFallbackError: this.handleFallbackError,
|
|
8691
|
+
renderFallback: () => {
|
|
8692
|
+
if (this.props.fallback) {
|
|
8693
|
+
if (typeof this.props.fallback === "function") {
|
|
8694
|
+
return this.props.fallback(this.state.error, this.handleReset);
|
|
8695
|
+
}
|
|
8696
|
+
return this.props.fallback;
|
|
8697
|
+
}
|
|
8698
|
+
return /* @__PURE__ */ jsx(DefaultErrorContent, { error: this.state.error, onReset: this.handleReset });
|
|
8699
|
+
}
|
|
8700
|
+
},
|
|
8701
|
+
`${this.state.error?.name ?? "Error"}:${this.state.error?.message ?? ""}`
|
|
8702
|
+
);
|
|
8101
8703
|
}
|
|
8102
8704
|
return this.props.children;
|
|
8103
8705
|
}
|
|
@@ -9131,6 +9733,10 @@ function ErrorAlert({ error }) {
|
|
|
9131
9733
|
);
|
|
9132
9734
|
}
|
|
9133
9735
|
var COLLAPSE_STORAGE_KEY = "bichat-sidebar-collapsed";
|
|
9736
|
+
var SESSION_RECONCILE_POLL_INTERVAL_MS = 2e3;
|
|
9737
|
+
var SESSION_RECONCILE_MAX_POLLS = 30;
|
|
9738
|
+
var ACTIVE_SESSION_MISS_MAX_RETRIES = 8;
|
|
9739
|
+
var ACTIVE_SESSION_MISS_RETRY_DELAY_MS = 1e3;
|
|
9134
9740
|
function useSidebarCollapse() {
|
|
9135
9741
|
const [isCollapsed, setIsCollapsed] = useState(() => {
|
|
9136
9742
|
try {
|
|
@@ -9188,7 +9794,7 @@ function Sidebar2({
|
|
|
9188
9794
|
const shouldReduceMotion = useReducedMotion();
|
|
9189
9795
|
const sessionListRef = useRef(null);
|
|
9190
9796
|
const searchContainerRef = useRef(null);
|
|
9191
|
-
const
|
|
9797
|
+
const activeSessionMissRetriesRef = useRef({});
|
|
9192
9798
|
const { isCollapsed, toggle, collapse } = useSidebarCollapse();
|
|
9193
9799
|
const collapsible = !onClose;
|
|
9194
9800
|
const handleSidebarClick = useCallback(
|
|
@@ -9232,6 +9838,7 @@ function Sidebar2({
|
|
|
9232
9838
|
const [actionError, setActionError] = useState(null);
|
|
9233
9839
|
const accessDenied = loadError?.isPermissionDenied === true;
|
|
9234
9840
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
9841
|
+
const [reconcilePollToken, setReconcilePollToken] = useState(0);
|
|
9235
9842
|
const [showConfirm, setShowConfirm] = useState(false);
|
|
9236
9843
|
const [sessionToArchive, setSessionToArchive] = useState(null);
|
|
9237
9844
|
const fetchSessions = useCallback(async () => {
|
|
@@ -9252,8 +9859,13 @@ function Sidebar2({
|
|
|
9252
9859
|
fetchSessions();
|
|
9253
9860
|
}, [fetchSessions, refreshKey]);
|
|
9254
9861
|
useEffect(() => {
|
|
9255
|
-
const handleSessionsUpdated = () => {
|
|
9862
|
+
const handleSessionsUpdated = (event) => {
|
|
9256
9863
|
setRefreshKey((k) => k + 1);
|
|
9864
|
+
const detail = event.detail;
|
|
9865
|
+
const reason = detail?.reason;
|
|
9866
|
+
if (!reason || reason === "session_created" || reason === "message_sent" || reason === "title_regenerate_requested") {
|
|
9867
|
+
setReconcilePollToken((k) => k + 1);
|
|
9868
|
+
}
|
|
9257
9869
|
};
|
|
9258
9870
|
window.addEventListener("bichat:sessions-updated", handleSessionsUpdated);
|
|
9259
9871
|
return () => {
|
|
@@ -9261,31 +9873,33 @@ function Sidebar2({
|
|
|
9261
9873
|
};
|
|
9262
9874
|
}, []);
|
|
9263
9875
|
useEffect(() => {
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9876
|
+
activeSessionMissRetriesRef.current = {};
|
|
9877
|
+
}, [activeSessionId]);
|
|
9878
|
+
useEffect(() => {
|
|
9879
|
+
if (!activeSessionId) return;
|
|
9268
9880
|
if (loading) return;
|
|
9269
9881
|
const hasActiveSession = sessions.some((session) => session.id === activeSessionId);
|
|
9270
9882
|
if (hasActiveSession) {
|
|
9271
|
-
|
|
9272
|
-
refreshForActiveSessionRef.current = null;
|
|
9273
|
-
}
|
|
9883
|
+
delete activeSessionMissRetriesRef.current[activeSessionId];
|
|
9274
9884
|
return;
|
|
9275
9885
|
}
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9886
|
+
const attempts = activeSessionMissRetriesRef.current[activeSessionId] ?? 0;
|
|
9887
|
+
if (attempts >= ACTIVE_SESSION_MISS_MAX_RETRIES) {
|
|
9888
|
+
return;
|
|
9279
9889
|
}
|
|
9890
|
+
activeSessionMissRetriesRef.current[activeSessionId] = attempts + 1;
|
|
9891
|
+
const timeoutId = window.setTimeout(() => {
|
|
9892
|
+
setRefreshKey((k) => k + 1);
|
|
9893
|
+
setReconcilePollToken((k) => k + 1);
|
|
9894
|
+
}, ACTIVE_SESSION_MISS_RETRY_DELAY_MS);
|
|
9895
|
+
return () => window.clearTimeout(timeoutId);
|
|
9280
9896
|
}, [activeSessionId, loading, sessions]);
|
|
9281
9897
|
const hasPlaceholderTitles = useMemo(() => {
|
|
9282
9898
|
const newChatLabel = t("BiChat.Chat.NewChat");
|
|
9283
9899
|
return Array.isArray(sessions) && sessions.some((s) => s && (!s.title || s.title === newChatLabel));
|
|
9284
9900
|
}, [sessions, t]);
|
|
9285
9901
|
useEffect(() => {
|
|
9286
|
-
if (!hasPlaceholderTitles) return;
|
|
9287
|
-
const pollInterval = 2e3;
|
|
9288
|
-
const maxPolls = 5;
|
|
9902
|
+
if (!hasPlaceholderTitles && reconcilePollToken === 0) return;
|
|
9289
9903
|
let pollCount = 0;
|
|
9290
9904
|
const intervalId = setInterval(async () => {
|
|
9291
9905
|
pollCount++;
|
|
@@ -9294,12 +9908,12 @@ function Sidebar2({
|
|
|
9294
9908
|
setSessions(result.sessions);
|
|
9295
9909
|
} catch {
|
|
9296
9910
|
}
|
|
9297
|
-
if (pollCount >=
|
|
9911
|
+
if (pollCount >= SESSION_RECONCILE_MAX_POLLS) {
|
|
9298
9912
|
clearInterval(intervalId);
|
|
9299
9913
|
}
|
|
9300
|
-
},
|
|
9914
|
+
}, SESSION_RECONCILE_POLL_INTERVAL_MS);
|
|
9301
9915
|
return () => clearInterval(intervalId);
|
|
9302
|
-
}, [hasPlaceholderTitles, dataSource]);
|
|
9916
|
+
}, [hasPlaceholderTitles, dataSource, reconcilePollToken]);
|
|
9303
9917
|
const handleArchiveRequest = (sessionId) => {
|
|
9304
9918
|
setSessionToArchive(sessionId);
|
|
9305
9919
|
setShowConfirm(true);
|
|
@@ -9357,7 +9971,9 @@ function Sidebar2({
|
|
|
9357
9971
|
try {
|
|
9358
9972
|
await dataSource.regenerateSessionTitle(sessionId);
|
|
9359
9973
|
toast.success(t("BiChat.Sidebar.TitleRegenerated"));
|
|
9360
|
-
|
|
9974
|
+
window.dispatchEvent(new CustomEvent("bichat:sessions-updated", {
|
|
9975
|
+
detail: { reason: "title_regenerate_requested", sessionId }
|
|
9976
|
+
}));
|
|
9361
9977
|
} catch (err) {
|
|
9362
9978
|
console.error("Failed to regenerate title:", err);
|
|
9363
9979
|
const display = toErrorDisplay(err, t("BiChat.Sidebar.FailedToRegenerateTitle"));
|
|
@@ -10170,59 +10786,6 @@ function BiChatLayout({
|
|
|
10170
10786
|
] })
|
|
10171
10787
|
] });
|
|
10172
10788
|
}
|
|
10173
|
-
|
|
10174
|
-
// ui/src/bichat/components/RetryActionArea.tsx
|
|
10175
|
-
init_useTranslation();
|
|
10176
|
-
var RetryActionArea = memo(function RetryActionArea2({
|
|
10177
|
-
onRetry
|
|
10178
|
-
}) {
|
|
10179
|
-
const { t } = useTranslation();
|
|
10180
|
-
return (
|
|
10181
|
-
// Wrapper matches TurnBubble layout for assistant messages (justify-start = left-aligned)
|
|
10182
|
-
/* @__PURE__ */ jsx(
|
|
10183
|
-
motion.div,
|
|
10184
|
-
{
|
|
10185
|
-
initial: { opacity: 0, y: 10 },
|
|
10186
|
-
animate: { opacity: 1, y: 0 },
|
|
10187
|
-
exit: { opacity: 0, y: -10 },
|
|
10188
|
-
transition: { duration: 0.2 },
|
|
10189
|
-
className: "flex justify-start",
|
|
10190
|
-
children: /* @__PURE__ */ jsxs(
|
|
10191
|
-
"div",
|
|
10192
|
-
{
|
|
10193
|
-
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",
|
|
10194
|
-
role: "status",
|
|
10195
|
-
"aria-live": "polite",
|
|
10196
|
-
children: [
|
|
10197
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
10198
|
-
/* @__PURE__ */ jsx(
|
|
10199
|
-
Warning,
|
|
10200
|
-
{
|
|
10201
|
-
className: "w-5 h-5 text-amber-500 dark:text-amber-400 flex-shrink-0",
|
|
10202
|
-
weight: "fill"
|
|
10203
|
-
}
|
|
10204
|
-
),
|
|
10205
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: t("BiChat.Retry.Subtitle") })
|
|
10206
|
-
] }),
|
|
10207
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs(
|
|
10208
|
-
"button",
|
|
10209
|
-
{
|
|
10210
|
-
onClick: onRetry,
|
|
10211
|
-
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",
|
|
10212
|
-
"aria-label": t("BiChat.Retry.Title"),
|
|
10213
|
-
children: [
|
|
10214
|
-
/* @__PURE__ */ jsx(ArrowClockwise, { size: 16, className: "w-4 h-4" }),
|
|
10215
|
-
t("BiChat.Retry.Button")
|
|
10216
|
-
]
|
|
10217
|
-
}
|
|
10218
|
-
) })
|
|
10219
|
-
]
|
|
10220
|
-
}
|
|
10221
|
-
)
|
|
10222
|
-
}
|
|
10223
|
-
)
|
|
10224
|
-
);
|
|
10225
|
-
});
|
|
10226
10789
|
init_useTranslation();
|
|
10227
10790
|
function MessageActions({
|
|
10228
10791
|
message,
|
|
@@ -12040,6 +12603,75 @@ function toStreamEvent(chunk) {
|
|
|
12040
12603
|
}
|
|
12041
12604
|
}
|
|
12042
12605
|
|
|
12606
|
+
// ui/src/bichat/utils/tableSpec.ts
|
|
12607
|
+
function isRecord2(value) {
|
|
12608
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12609
|
+
}
|
|
12610
|
+
function readString(value) {
|
|
12611
|
+
if (typeof value !== "string") return null;
|
|
12612
|
+
const trimmed = value.trim();
|
|
12613
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12614
|
+
}
|
|
12615
|
+
function readPositiveInteger(value) {
|
|
12616
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
12617
|
+
const n = Math.floor(value);
|
|
12618
|
+
return n > 0 ? n : null;
|
|
12619
|
+
}
|
|
12620
|
+
function normalizeRows(value) {
|
|
12621
|
+
if (!Array.isArray(value)) return [];
|
|
12622
|
+
const rows = [];
|
|
12623
|
+
for (const row of value) {
|
|
12624
|
+
if (!Array.isArray(row)) continue;
|
|
12625
|
+
rows.push(row);
|
|
12626
|
+
}
|
|
12627
|
+
return rows;
|
|
12628
|
+
}
|
|
12629
|
+
function parseExport(value) {
|
|
12630
|
+
if (!isRecord2(value)) return void 0;
|
|
12631
|
+
const url = readString(value.url);
|
|
12632
|
+
if (!url) return void 0;
|
|
12633
|
+
return {
|
|
12634
|
+
url,
|
|
12635
|
+
filename: readString(value.filename) || "table_export.xlsx",
|
|
12636
|
+
rowCount: readPositiveInteger(value.row_count) || readPositiveInteger(value.rowCount) || void 0,
|
|
12637
|
+
fileSizeKB: readPositiveInteger(value.file_size_kb) || readPositiveInteger(value.fileSizeKB) || void 0
|
|
12638
|
+
};
|
|
12639
|
+
}
|
|
12640
|
+
function parseRenderTableDataFromJsonString(json, fallbackId) {
|
|
12641
|
+
const trimmed = json.trim();
|
|
12642
|
+
if (!trimmed) return null;
|
|
12643
|
+
let parsed;
|
|
12644
|
+
try {
|
|
12645
|
+
parsed = JSON.parse(trimmed);
|
|
12646
|
+
} catch {
|
|
12647
|
+
return null;
|
|
12648
|
+
}
|
|
12649
|
+
if (!isRecord2(parsed)) return null;
|
|
12650
|
+
const columns = Array.isArray(parsed.columns) ? parsed.columns.map((column) => readString(column)).filter((column) => column !== null) : [];
|
|
12651
|
+
if (columns.length === 0) return null;
|
|
12652
|
+
const rows = normalizeRows(parsed.rows);
|
|
12653
|
+
const headersRaw = Array.isArray(parsed.headers) ? parsed.headers.map((header) => readString(header)).filter((header) => header !== null) : [];
|
|
12654
|
+
const headers = headersRaw.length === columns.length ? headersRaw : columns;
|
|
12655
|
+
const totalRows = readPositiveInteger(parsed.total_rows) || readPositiveInteger(parsed.totalRows) || rows.length;
|
|
12656
|
+
const pageSize = readPositiveInteger(parsed.page_size) || readPositiveInteger(parsed.pageSize) || 25;
|
|
12657
|
+
const query = readString(parsed.query) || readString(parsed.sql);
|
|
12658
|
+
if (!query) return null;
|
|
12659
|
+
return {
|
|
12660
|
+
id: readString(parsed.id) || fallbackId,
|
|
12661
|
+
title: readString(parsed.title) || void 0,
|
|
12662
|
+
query,
|
|
12663
|
+
columns,
|
|
12664
|
+
headers,
|
|
12665
|
+
rows,
|
|
12666
|
+
totalRows,
|
|
12667
|
+
pageSize,
|
|
12668
|
+
truncated: parsed.truncated === true,
|
|
12669
|
+
truncatedReason: readString(parsed.truncated_reason) || readString(parsed.truncatedReason) || void 0,
|
|
12670
|
+
export: parseExport(parsed.export),
|
|
12671
|
+
exportPrompt: readString(parsed.export_prompt) || readString(parsed.exportPrompt) || void 0
|
|
12672
|
+
};
|
|
12673
|
+
}
|
|
12674
|
+
|
|
12043
12675
|
// ui/src/bichat/data/HttpDataSource.ts
|
|
12044
12676
|
function isSessionNotFoundError(err) {
|
|
12045
12677
|
if (!(err instanceof AppletRPCException)) return false;
|
|
@@ -12056,6 +12688,7 @@ function toSessionArtifact(artifact) {
|
|
|
12056
12688
|
id: artifact.id,
|
|
12057
12689
|
sessionId: artifact.sessionId,
|
|
12058
12690
|
messageId: artifact.messageId,
|
|
12691
|
+
uploadId: artifact.uploadId,
|
|
12059
12692
|
type: artifact.type,
|
|
12060
12693
|
name: artifact.name,
|
|
12061
12694
|
description: artifact.description,
|
|
@@ -12066,21 +12699,313 @@ function toSessionArtifact(artifact) {
|
|
|
12066
12699
|
createdAt: artifact.createdAt
|
|
12067
12700
|
};
|
|
12068
12701
|
}
|
|
12069
|
-
function
|
|
12070
|
-
|
|
12071
|
-
|
|
12072
|
-
|
|
12073
|
-
|
|
12074
|
-
|
|
12075
|
-
|
|
12076
|
-
|
|
12077
|
-
|
|
12078
|
-
|
|
12079
|
-
|
|
12080
|
-
|
|
12702
|
+
function warnMalformedSessionPayload(message, details) {
|
|
12703
|
+
console.warn(`[BiChat] ${message}`, details || {});
|
|
12704
|
+
}
|
|
12705
|
+
function readString2(value, fallback = "") {
|
|
12706
|
+
return typeof value === "string" ? value : fallback;
|
|
12707
|
+
}
|
|
12708
|
+
function readNonEmptyString(value) {
|
|
12709
|
+
if (typeof value !== "string") return null;
|
|
12710
|
+
const trimmed = value.trim();
|
|
12711
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
12712
|
+
}
|
|
12713
|
+
function readFiniteNumber(value, fallback = 0) {
|
|
12714
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
12715
|
+
}
|
|
12716
|
+
function readOptionalFiniteNumber(value) {
|
|
12717
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12718
|
+
}
|
|
12719
|
+
var MIME_TO_EXTENSION = {
|
|
12720
|
+
"image/jpeg": "jpg",
|
|
12721
|
+
"image/png": "png",
|
|
12722
|
+
"image/gif": "gif",
|
|
12723
|
+
"application/pdf": "pdf"
|
|
12724
|
+
};
|
|
12725
|
+
var SAFE_AUTOCORRECT_MIME_TYPES = new Set(Object.keys(MIME_TO_EXTENSION));
|
|
12726
|
+
function detectMimeFromSignature(bytes) {
|
|
12727
|
+
if (bytes.length >= 8) {
|
|
12728
|
+
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;
|
|
12729
|
+
if (isPng) return "image/png";
|
|
12730
|
+
}
|
|
12731
|
+
if (bytes.length >= 3) {
|
|
12732
|
+
const isJpeg = bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255;
|
|
12733
|
+
if (isJpeg) return "image/jpeg";
|
|
12734
|
+
}
|
|
12735
|
+
if (bytes.length >= 6) {
|
|
12736
|
+
const isGif = bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 56 && (bytes[4] === 55 || bytes[4] === 57) && bytes[5] === 97;
|
|
12737
|
+
if (isGif) return "image/gif";
|
|
12738
|
+
}
|
|
12739
|
+
if (bytes.length >= 4) {
|
|
12740
|
+
const isPdf = bytes[0] === 37 && bytes[1] === 80 && bytes[2] === 68 && bytes[3] === 70;
|
|
12741
|
+
if (isPdf) return "application/pdf";
|
|
12742
|
+
}
|
|
12743
|
+
return void 0;
|
|
12744
|
+
}
|
|
12745
|
+
function normalizeFilenameForMime(filename, mimeType) {
|
|
12746
|
+
const expectedExt = MIME_TO_EXTENSION[mimeType];
|
|
12747
|
+
if (!expectedExt) return filename;
|
|
12748
|
+
const lower = filename.toLowerCase();
|
|
12749
|
+
if (mimeType === "image/jpeg" && (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))) {
|
|
12750
|
+
return filename;
|
|
12751
|
+
}
|
|
12752
|
+
if (lower.endsWith(`.${expectedExt}`)) {
|
|
12753
|
+
return filename;
|
|
12754
|
+
}
|
|
12755
|
+
const dotIndex = filename.lastIndexOf(".");
|
|
12756
|
+
const baseName = dotIndex > 0 ? filename.slice(0, dotIndex) : filename;
|
|
12757
|
+
return `${baseName}.${expectedExt}`;
|
|
12758
|
+
}
|
|
12759
|
+
function normalizeQuestionType(rawType) {
|
|
12760
|
+
const normalized = readString2(rawType).trim().toUpperCase().replace(/[\s-]+/g, "_");
|
|
12761
|
+
return normalized === "MULTIPLE_CHOICE" ? "MULTIPLE_CHOICE" : "SINGLE_CHOICE";
|
|
12762
|
+
}
|
|
12763
|
+
function normalizeMessageRole(rawRole) {
|
|
12764
|
+
const normalized = readString2(rawRole).trim().toLowerCase();
|
|
12765
|
+
if (normalized === "user" /* User */) return "user" /* User */;
|
|
12766
|
+
if (normalized === "system" /* System */) return "system" /* System */;
|
|
12767
|
+
if (normalized === "tool" /* Tool */) return "tool" /* Tool */;
|
|
12768
|
+
return "assistant" /* Assistant */;
|
|
12769
|
+
}
|
|
12770
|
+
function sanitizeAttachment(rawAttachment, turnId, index) {
|
|
12771
|
+
if (!isRecord(rawAttachment)) {
|
|
12772
|
+
warnMalformedSessionPayload("Dropped malformed attachment entry", { turnId, index });
|
|
12773
|
+
return null;
|
|
12774
|
+
}
|
|
12775
|
+
const filename = readString2(rawAttachment.filename, "attachment");
|
|
12776
|
+
const mimeType = readString2(rawAttachment.mimeType, "application/octet-stream");
|
|
12777
|
+
const id = readNonEmptyString(rawAttachment.id) || void 0;
|
|
12778
|
+
const clientKey = readNonEmptyString(rawAttachment.clientKey) || id || `${turnId}-attachment-${index}`;
|
|
12779
|
+
return {
|
|
12780
|
+
id,
|
|
12781
|
+
clientKey,
|
|
12782
|
+
filename,
|
|
12783
|
+
mimeType,
|
|
12784
|
+
sizeBytes: readFiniteNumber(rawAttachment.sizeBytes),
|
|
12785
|
+
uploadId: readOptionalFiniteNumber(rawAttachment.uploadId),
|
|
12786
|
+
base64Data: readNonEmptyString(rawAttachment.base64Data) || void 0,
|
|
12787
|
+
url: readNonEmptyString(rawAttachment.url) || void 0,
|
|
12788
|
+
preview: readNonEmptyString(rawAttachment.preview) || void 0
|
|
12789
|
+
};
|
|
12790
|
+
}
|
|
12791
|
+
function sanitizeUserAttachments(rawAttachments, turnId) {
|
|
12792
|
+
if (!Array.isArray(rawAttachments)) return [];
|
|
12793
|
+
const result = [];
|
|
12794
|
+
for (let i = 0; i < rawAttachments.length; i++) {
|
|
12795
|
+
const sanitized = sanitizeAttachment(rawAttachments[i], turnId, i);
|
|
12796
|
+
if (sanitized) result.push(sanitized);
|
|
12797
|
+
}
|
|
12798
|
+
return result;
|
|
12799
|
+
}
|
|
12800
|
+
function sanitizeAssistantArtifacts(rawArtifacts, turnId) {
|
|
12801
|
+
if (!Array.isArray(rawArtifacts)) return [];
|
|
12802
|
+
const artifacts = [];
|
|
12803
|
+
for (let i = 0; i < rawArtifacts.length; i++) {
|
|
12804
|
+
const raw = rawArtifacts[i];
|
|
12805
|
+
if (!isRecord(raw)) {
|
|
12806
|
+
warnMalformedSessionPayload("Dropped malformed assistant artifact", { turnId, index: i });
|
|
12807
|
+
continue;
|
|
12808
|
+
}
|
|
12809
|
+
const type = readString2(raw.type).toLowerCase();
|
|
12810
|
+
if (type !== "excel" && type !== "pdf") {
|
|
12811
|
+
continue;
|
|
12812
|
+
}
|
|
12813
|
+
const url = readNonEmptyString(raw.url);
|
|
12814
|
+
if (!url) {
|
|
12815
|
+
warnMalformedSessionPayload("Dropped assistant artifact without url", { turnId, index: i });
|
|
12816
|
+
continue;
|
|
12817
|
+
}
|
|
12818
|
+
artifacts.push({
|
|
12819
|
+
type,
|
|
12820
|
+
filename: readString2(raw.filename, "download"),
|
|
12821
|
+
url,
|
|
12822
|
+
sizeReadable: readNonEmptyString(raw.sizeReadable) || void 0,
|
|
12823
|
+
rowCount: typeof raw.rowCount === "number" && Number.isFinite(raw.rowCount) ? raw.rowCount : void 0,
|
|
12824
|
+
description: readNonEmptyString(raw.description) || void 0
|
|
12825
|
+
});
|
|
12826
|
+
}
|
|
12827
|
+
return artifacts;
|
|
12828
|
+
}
|
|
12829
|
+
function sanitizeAssistantTurn(rawAssistantTurn, fallbackCreatedAt, turnId) {
|
|
12830
|
+
if (rawAssistantTurn == null) return void 0;
|
|
12831
|
+
if (!isRecord(rawAssistantTurn)) {
|
|
12832
|
+
warnMalformedSessionPayload("Dropped malformed assistant turn payload", { turnId });
|
|
12833
|
+
return void 0;
|
|
12834
|
+
}
|
|
12835
|
+
const assistantID = readNonEmptyString(rawAssistantTurn.id);
|
|
12836
|
+
if (!assistantID) {
|
|
12837
|
+
warnMalformedSessionPayload("Dropped assistant turn without id", { turnId });
|
|
12838
|
+
return void 0;
|
|
12839
|
+
}
|
|
12840
|
+
const citations = Array.isArray(rawAssistantTurn.citations) ? rawAssistantTurn.citations.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12841
|
+
id: readString2(item.id, `${assistantID}-citation-${index}`),
|
|
12842
|
+
type: readString2(item.type),
|
|
12843
|
+
title: readString2(item.title),
|
|
12844
|
+
url: readString2(item.url),
|
|
12845
|
+
startIndex: readFiniteNumber(item.startIndex),
|
|
12846
|
+
endIndex: readFiniteNumber(item.endIndex),
|
|
12847
|
+
excerpt: readNonEmptyString(item.excerpt) || void 0
|
|
12848
|
+
})) : [];
|
|
12849
|
+
const toolCalls = Array.isArray(rawAssistantTurn.toolCalls) ? rawAssistantTurn.toolCalls.filter((item) => isRecord(item)).map((item, index) => ({
|
|
12850
|
+
id: readString2(item.id, `${assistantID}-tool-${index}`),
|
|
12851
|
+
name: readString2(item.name),
|
|
12852
|
+
arguments: readString2(item.arguments),
|
|
12853
|
+
result: readNonEmptyString(item.result) || void 0,
|
|
12854
|
+
error: readNonEmptyString(item.error) || void 0,
|
|
12855
|
+
durationMs: readFiniteNumber(item.durationMs)
|
|
12856
|
+
})) : [];
|
|
12857
|
+
const codeOutputs = Array.isArray(rawAssistantTurn.codeOutputs) ? rawAssistantTurn.codeOutputs.filter((item) => isRecord(item)).map((item) => ({
|
|
12858
|
+
type: (() => {
|
|
12859
|
+
const normalizedType = readString2(item.type, "text").toLowerCase();
|
|
12860
|
+
if (normalizedType === "image" || normalizedType === "error") return normalizedType;
|
|
12861
|
+
return "text";
|
|
12862
|
+
})(),
|
|
12863
|
+
content: readString2(item.content),
|
|
12864
|
+
filename: readNonEmptyString(item.filename) || void 0,
|
|
12865
|
+
mimeType: readNonEmptyString(item.mimeType) || void 0,
|
|
12866
|
+
sizeBytes: readOptionalFiniteNumber(item.sizeBytes)
|
|
12867
|
+
})) : [];
|
|
12868
|
+
const debugTrace = isRecord(rawAssistantTurn.debug) ? {
|
|
12869
|
+
generationMs: readOptionalFiniteNumber(rawAssistantTurn.debug.generationMs),
|
|
12870
|
+
usage: isRecord(rawAssistantTurn.debug.usage) ? {
|
|
12871
|
+
promptTokens: readFiniteNumber(rawAssistantTurn.debug.usage.promptTokens),
|
|
12872
|
+
completionTokens: readFiniteNumber(rawAssistantTurn.debug.usage.completionTokens),
|
|
12873
|
+
totalTokens: readFiniteNumber(rawAssistantTurn.debug.usage.totalTokens),
|
|
12874
|
+
cachedTokens: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cachedTokens),
|
|
12875
|
+
cost: readOptionalFiniteNumber(rawAssistantTurn.debug.usage.cost)
|
|
12876
|
+
} : void 0,
|
|
12877
|
+
tools: Array.isArray(rawAssistantTurn.debug.tools) ? rawAssistantTurn.debug.tools.filter((tool) => isRecord(tool)).map((tool) => ({
|
|
12878
|
+
callId: readNonEmptyString(tool.callId) || void 0,
|
|
12879
|
+
name: readString2(tool.name),
|
|
12880
|
+
arguments: readNonEmptyString(tool.arguments) || void 0,
|
|
12881
|
+
result: readNonEmptyString(tool.result) || void 0,
|
|
12882
|
+
error: readNonEmptyString(tool.error) || void 0,
|
|
12883
|
+
durationMs: readOptionalFiniteNumber(tool.durationMs)
|
|
12884
|
+
})) : []
|
|
12885
|
+
} : void 0;
|
|
12886
|
+
return {
|
|
12887
|
+
id: assistantID,
|
|
12888
|
+
role: normalizeMessageRole(rawAssistantTurn.role),
|
|
12889
|
+
content: readString2(rawAssistantTurn.content),
|
|
12890
|
+
explanation: readNonEmptyString(rawAssistantTurn.explanation) || void 0,
|
|
12891
|
+
citations,
|
|
12892
|
+
toolCalls,
|
|
12893
|
+
chartData: void 0,
|
|
12894
|
+
renderTables: void 0,
|
|
12895
|
+
artifacts: sanitizeAssistantArtifacts(rawAssistantTurn.artifacts, turnId),
|
|
12896
|
+
codeOutputs,
|
|
12897
|
+
debug: debugTrace,
|
|
12898
|
+
createdAt: readString2(rawAssistantTurn.createdAt, fallbackCreatedAt)
|
|
12899
|
+
};
|
|
12900
|
+
}
|
|
12901
|
+
function sanitizeConversationTurn(rawTurn, index, fallbackSessionID) {
|
|
12902
|
+
if (!isRecord(rawTurn)) {
|
|
12903
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (not an object)", { index });
|
|
12904
|
+
return null;
|
|
12905
|
+
}
|
|
12906
|
+
if (!isRecord(rawTurn.userTurn)) {
|
|
12907
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn)", { index });
|
|
12908
|
+
return null;
|
|
12909
|
+
}
|
|
12910
|
+
const userTurnID = readNonEmptyString(rawTurn.userTurn.id);
|
|
12911
|
+
if (!userTurnID) {
|
|
12912
|
+
warnMalformedSessionPayload("Dropped malformed turn payload (missing user turn id)", { index });
|
|
12913
|
+
return null;
|
|
12914
|
+
}
|
|
12915
|
+
const turnID = readString2(rawTurn.id, userTurnID);
|
|
12916
|
+
const createdAt = readString2(
|
|
12917
|
+
rawTurn.createdAt,
|
|
12918
|
+
readString2(rawTurn.userTurn.createdAt, (/* @__PURE__ */ new Date()).toISOString())
|
|
12919
|
+
);
|
|
12920
|
+
return {
|
|
12921
|
+
id: turnID,
|
|
12922
|
+
sessionId: readString2(rawTurn.sessionId, fallbackSessionID),
|
|
12923
|
+
userTurn: {
|
|
12924
|
+
id: userTurnID,
|
|
12925
|
+
content: readString2(rawTurn.userTurn.content),
|
|
12926
|
+
attachments: sanitizeUserAttachments(rawTurn.userTurn.attachments, turnID),
|
|
12927
|
+
createdAt: readString2(rawTurn.userTurn.createdAt, createdAt)
|
|
12928
|
+
},
|
|
12929
|
+
assistantTurn: sanitizeAssistantTurn(rawTurn.assistantTurn, createdAt, turnID),
|
|
12930
|
+
createdAt
|
|
12931
|
+
};
|
|
12932
|
+
}
|
|
12933
|
+
function sanitizeConversationTurns(rawTurns, sessionID) {
|
|
12934
|
+
if (!Array.isArray(rawTurns)) {
|
|
12935
|
+
warnMalformedSessionPayload("Session payload contained non-array turns field", { sessionID });
|
|
12936
|
+
return [];
|
|
12937
|
+
}
|
|
12938
|
+
const turns = [];
|
|
12939
|
+
let dropped = 0;
|
|
12940
|
+
for (let i = 0; i < rawTurns.length; i++) {
|
|
12941
|
+
const sanitizedTurn = sanitizeConversationTurn(rawTurns[i], i, sessionID);
|
|
12942
|
+
if (sanitizedTurn) {
|
|
12943
|
+
turns.push(sanitizedTurn);
|
|
12944
|
+
} else {
|
|
12945
|
+
dropped++;
|
|
12946
|
+
}
|
|
12947
|
+
}
|
|
12948
|
+
if (dropped > 0) {
|
|
12949
|
+
warnMalformedSessionPayload("Dropped malformed turns from session payload", {
|
|
12950
|
+
sessionID,
|
|
12951
|
+
dropped,
|
|
12952
|
+
total: rawTurns.length
|
|
12953
|
+
});
|
|
12954
|
+
}
|
|
12955
|
+
return turns;
|
|
12956
|
+
}
|
|
12957
|
+
function sanitizePendingQuestion(rawPendingQuestion, sessionID) {
|
|
12958
|
+
if (!rawPendingQuestion) return null;
|
|
12959
|
+
const checkpointID = readNonEmptyString(rawPendingQuestion.checkpointId);
|
|
12960
|
+
if (!checkpointID) {
|
|
12961
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion without checkpointId", { sessionID });
|
|
12962
|
+
return null;
|
|
12963
|
+
}
|
|
12964
|
+
if (!Array.isArray(rawPendingQuestion.questions)) {
|
|
12965
|
+
warnMalformedSessionPayload("Pending question had non-array questions payload", {
|
|
12966
|
+
sessionID,
|
|
12967
|
+
checkpointID
|
|
12968
|
+
});
|
|
12969
|
+
}
|
|
12970
|
+
const questions = Array.isArray(rawPendingQuestion.questions) ? rawPendingQuestion.questions.filter((question) => {
|
|
12971
|
+
if (!question || !isRecord(question)) {
|
|
12972
|
+
warnMalformedSessionPayload("Dropped malformed question from pendingQuestion", {
|
|
12973
|
+
sessionID,
|
|
12974
|
+
checkpointID
|
|
12975
|
+
});
|
|
12976
|
+
return false;
|
|
12977
|
+
}
|
|
12978
|
+
return true;
|
|
12979
|
+
}).map((question, index) => {
|
|
12980
|
+
const questionID = readString2(question.id, `${checkpointID}-q-${index}`);
|
|
12981
|
+
const options = Array.isArray(question.options) ? question.options.filter((option) => {
|
|
12982
|
+
if (!option || !isRecord(option)) {
|
|
12983
|
+
warnMalformedSessionPayload("Dropped malformed pendingQuestion option", {
|
|
12984
|
+
sessionID,
|
|
12985
|
+
checkpointID,
|
|
12986
|
+
questionID
|
|
12987
|
+
});
|
|
12988
|
+
return false;
|
|
12989
|
+
}
|
|
12990
|
+
return true;
|
|
12991
|
+
}).map((option, optionIndex) => {
|
|
12992
|
+
const label = readString2(option.label);
|
|
12993
|
+
return {
|
|
12994
|
+
id: readString2(option.id, `${questionID}-opt-${optionIndex}`),
|
|
12995
|
+
label,
|
|
12996
|
+
value: label
|
|
12997
|
+
};
|
|
12998
|
+
}) : [];
|
|
12999
|
+
return {
|
|
13000
|
+
id: questionID,
|
|
13001
|
+
text: readString2(question.text),
|
|
13002
|
+
type: normalizeQuestionType(question.type),
|
|
13003
|
+
options
|
|
13004
|
+
};
|
|
13005
|
+
}) : [];
|
|
12081
13006
|
return {
|
|
12082
|
-
id:
|
|
12083
|
-
turnId:
|
|
13007
|
+
id: checkpointID,
|
|
13008
|
+
turnId: readString2(rawPendingQuestion.turnId),
|
|
12084
13009
|
questions,
|
|
12085
13010
|
status: "PENDING"
|
|
12086
13011
|
};
|
|
@@ -12152,6 +13077,18 @@ function extractChartDataFromToolCalls(toolCalls) {
|
|
|
12152
13077
|
}
|
|
12153
13078
|
return void 0;
|
|
12154
13079
|
}
|
|
13080
|
+
function extractRenderTablesFromToolCalls(toolCalls) {
|
|
13081
|
+
if (!toolCalls) return [];
|
|
13082
|
+
const tables = [];
|
|
13083
|
+
for (const tc of toolCalls) {
|
|
13084
|
+
if (tc.name !== "renderTable" || !tc.result) continue;
|
|
13085
|
+
const parsed = parseRenderTableDataFromJsonString(tc.result, tc.id);
|
|
13086
|
+
if (parsed) {
|
|
13087
|
+
tables.push(parsed);
|
|
13088
|
+
}
|
|
13089
|
+
}
|
|
13090
|
+
return tables;
|
|
13091
|
+
}
|
|
12155
13092
|
var EXPORT_TOOL_NAMES = {
|
|
12156
13093
|
export_query_to_excel: "excel",
|
|
12157
13094
|
export_data_to_excel: "excel",
|
|
@@ -12187,6 +13124,7 @@ function extractDownloadArtifactsFromToolCalls(toolCalls) {
|
|
|
12187
13124
|
function normalizeAssistantTurn(turn) {
|
|
12188
13125
|
const existingArtifacts = turn.artifacts || [];
|
|
12189
13126
|
const fromToolCalls = extractDownloadArtifactsFromToolCalls(turn.toolCalls);
|
|
13127
|
+
const renderTables = turn.renderTables || extractRenderTablesFromToolCalls(turn.toolCalls);
|
|
12190
13128
|
const merged = [...existingArtifacts];
|
|
12191
13129
|
for (const a of fromToolCalls) {
|
|
12192
13130
|
if (!merged.some((e) => e.url === a.url && e.filename === a.filename)) {
|
|
@@ -12197,6 +13135,7 @@ function normalizeAssistantTurn(turn) {
|
|
|
12197
13135
|
...turn,
|
|
12198
13136
|
role: turn.role || "assistant" /* Assistant */,
|
|
12199
13137
|
chartData: turn.chartData || extractChartDataFromToolCalls(turn.toolCalls),
|
|
13138
|
+
renderTables,
|
|
12200
13139
|
citations: turn.citations || [],
|
|
12201
13140
|
artifacts: merged,
|
|
12202
13141
|
codeOutputs: turn.codeOutputs || []
|
|
@@ -12276,6 +13215,7 @@ var HttpDataSource = class {
|
|
|
12276
13215
|
this.abortController = null;
|
|
12277
13216
|
this.config = {
|
|
12278
13217
|
streamEndpoint: "/stream",
|
|
13218
|
+
uploadEndpoint: "/api/uploads",
|
|
12279
13219
|
timeout: 12e4,
|
|
12280
13220
|
...config
|
|
12281
13221
|
};
|
|
@@ -12311,6 +13251,216 @@ var HttpDataSource = class {
|
|
|
12311
13251
|
}
|
|
12312
13252
|
return headers;
|
|
12313
13253
|
}
|
|
13254
|
+
createUploadHeaders(additionalHeaders) {
|
|
13255
|
+
const headers = new Headers({
|
|
13256
|
+
...this.config.headers,
|
|
13257
|
+
...additionalHeaders
|
|
13258
|
+
});
|
|
13259
|
+
const csrfToken = this.getCSRFToken();
|
|
13260
|
+
if (csrfToken) {
|
|
13261
|
+
headers.set("X-CSRF-Token", csrfToken);
|
|
13262
|
+
}
|
|
13263
|
+
headers.delete("Content-Type");
|
|
13264
|
+
return headers;
|
|
13265
|
+
}
|
|
13266
|
+
logAttachmentLifecycle(event, details) {
|
|
13267
|
+
const payload = {
|
|
13268
|
+
source: "HttpDataSource",
|
|
13269
|
+
event,
|
|
13270
|
+
...details
|
|
13271
|
+
};
|
|
13272
|
+
if (event.endsWith("_fail")) {
|
|
13273
|
+
console.warn("[bichat.attachments]", payload);
|
|
13274
|
+
return;
|
|
13275
|
+
}
|
|
13276
|
+
}
|
|
13277
|
+
async normalizeAttachmentFile(attachment, file) {
|
|
13278
|
+
const signatureBytes = new Uint8Array(await file.slice(0, 16).arrayBuffer());
|
|
13279
|
+
const detectedMimeType = detectMimeFromSignature(signatureBytes);
|
|
13280
|
+
const declaredMimeType = (attachment.mimeType || file.type || "").trim().toLowerCase();
|
|
13281
|
+
let resolvedMimeType = declaredMimeType || detectedMimeType || "application/octet-stream";
|
|
13282
|
+
let correctedFromDeclared = false;
|
|
13283
|
+
if (detectedMimeType && declaredMimeType && detectedMimeType !== declaredMimeType) {
|
|
13284
|
+
const safeToCorrect = SAFE_AUTOCORRECT_MIME_TYPES.has(detectedMimeType) && SAFE_AUTOCORRECT_MIME_TYPES.has(declaredMimeType);
|
|
13285
|
+
if (!safeToCorrect) {
|
|
13286
|
+
throw new Error(
|
|
13287
|
+
`Attachment "${attachment.filename}" MIME mismatch: declared "${declaredMimeType}", detected "${detectedMimeType}"`
|
|
13288
|
+
);
|
|
13289
|
+
}
|
|
13290
|
+
resolvedMimeType = detectedMimeType;
|
|
13291
|
+
correctedFromDeclared = true;
|
|
13292
|
+
} else if (detectedMimeType && !declaredMimeType) {
|
|
13293
|
+
resolvedMimeType = detectedMimeType;
|
|
13294
|
+
}
|
|
13295
|
+
const normalizedName = normalizeFilenameForMime(attachment.filename, resolvedMimeType);
|
|
13296
|
+
const normalized = new File([file], normalizedName, {
|
|
13297
|
+
type: resolvedMimeType,
|
|
13298
|
+
lastModified: file.lastModified
|
|
13299
|
+
});
|
|
13300
|
+
this.logAttachmentLifecycle("attachment_decode_success", {
|
|
13301
|
+
attachmentKey: attachment.clientKey,
|
|
13302
|
+
filename: attachment.filename,
|
|
13303
|
+
normalizedFilename: normalized.name,
|
|
13304
|
+
declaredMimeType: declaredMimeType || void 0,
|
|
13305
|
+
detectedMimeType,
|
|
13306
|
+
resolvedMimeType,
|
|
13307
|
+
correctedFromDeclared,
|
|
13308
|
+
sizeBytes: normalized.size
|
|
13309
|
+
});
|
|
13310
|
+
return normalized;
|
|
13311
|
+
}
|
|
13312
|
+
async uploadFile(file) {
|
|
13313
|
+
const formData = new FormData();
|
|
13314
|
+
formData.append("file", file);
|
|
13315
|
+
const response = await fetch(`${this.config.baseUrl}${this.config.uploadEndpoint}`, {
|
|
13316
|
+
method: "POST",
|
|
13317
|
+
headers: this.createUploadHeaders(),
|
|
13318
|
+
body: formData
|
|
13319
|
+
});
|
|
13320
|
+
let payload = null;
|
|
13321
|
+
try {
|
|
13322
|
+
payload = await response.json();
|
|
13323
|
+
} catch {
|
|
13324
|
+
payload = null;
|
|
13325
|
+
}
|
|
13326
|
+
if (!response.ok) {
|
|
13327
|
+
const errorMessage = isRecord(payload) && typeof payload.error === "string" ? payload.error : `Upload failed: HTTP ${response.status}`;
|
|
13328
|
+
throw new Error(errorMessage);
|
|
13329
|
+
}
|
|
13330
|
+
if (!isRecord(payload) || typeof payload.id !== "number" || payload.id <= 0) {
|
|
13331
|
+
throw new Error("Upload failed: invalid response payload");
|
|
13332
|
+
}
|
|
13333
|
+
return {
|
|
13334
|
+
id: payload.id,
|
|
13335
|
+
url: typeof payload.url === "string" ? payload.url : "",
|
|
13336
|
+
path: typeof payload.path === "string" ? payload.path : "",
|
|
13337
|
+
name: typeof payload.name === "string" ? payload.name : file.name,
|
|
13338
|
+
mimetype: typeof payload.mimetype === "string" ? payload.mimetype : file.type,
|
|
13339
|
+
size: typeof payload.size === "number" && Number.isFinite(payload.size) ? payload.size : file.size
|
|
13340
|
+
};
|
|
13341
|
+
}
|
|
13342
|
+
async attachmentToFile(attachment) {
|
|
13343
|
+
if (attachment.base64Data && attachment.base64Data.trim().length > 0) {
|
|
13344
|
+
try {
|
|
13345
|
+
const base64Data = attachment.base64Data.trim();
|
|
13346
|
+
const dataUrl = base64Data.startsWith("data:") ? base64Data : `data:${attachment.mimeType || "application/octet-stream"};base64,${base64Data}`;
|
|
13347
|
+
const blob = await fetch(dataUrl).then((response) => response.blob());
|
|
13348
|
+
return new File([blob], attachment.filename, {
|
|
13349
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
13350
|
+
});
|
|
13351
|
+
} catch (err) {
|
|
13352
|
+
const message = err instanceof Error ? err.message : "Unknown decode error";
|
|
13353
|
+
throw new Error(`Attachment "${attachment.filename}" decode failed: ${message}`);
|
|
13354
|
+
}
|
|
13355
|
+
}
|
|
13356
|
+
if (attachment.url) {
|
|
13357
|
+
let parsed;
|
|
13358
|
+
try {
|
|
13359
|
+
parsed = new URL(attachment.url, window.location?.origin ?? "https://localhost");
|
|
13360
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
13361
|
+
throw new Error(`Attachment "${attachment.filename}" URL has disallowed protocol: ${parsed.protocol}`);
|
|
13362
|
+
}
|
|
13363
|
+
} catch (err) {
|
|
13364
|
+
if (err instanceof Error && err.message.includes("Attachment")) throw err;
|
|
13365
|
+
throw new Error(`Attachment "${attachment.filename}" has invalid or malformed URL`);
|
|
13366
|
+
}
|
|
13367
|
+
const response = await fetch(parsed.href);
|
|
13368
|
+
if (!response.ok) {
|
|
13369
|
+
throw new Error(`Attachment "${attachment.filename}" decode failed: source HTTP ${response.status}`);
|
|
13370
|
+
}
|
|
13371
|
+
const blob = await response.blob();
|
|
13372
|
+
return new File([blob], attachment.filename, {
|
|
13373
|
+
type: attachment.mimeType || blob.type || "application/octet-stream"
|
|
13374
|
+
});
|
|
13375
|
+
}
|
|
13376
|
+
throw new Error(`Attachment "${attachment.filename}" has no uploadable data`);
|
|
13377
|
+
}
|
|
13378
|
+
assertUploadReferences(uploads) {
|
|
13379
|
+
return uploads.map((upload, index) => {
|
|
13380
|
+
if (typeof upload.id !== "number" || !Number.isFinite(upload.id) || upload.id <= 0) {
|
|
13381
|
+
throw new Error(`Attachment upload reference is invalid at index ${index}`);
|
|
13382
|
+
}
|
|
13383
|
+
return { uploadId: upload.id };
|
|
13384
|
+
});
|
|
13385
|
+
}
|
|
13386
|
+
async ensureAttachmentUpload(attachment, context) {
|
|
13387
|
+
if (typeof attachment.uploadId === "number" && attachment.uploadId > 0) {
|
|
13388
|
+
this.logAttachmentLifecycle("attachment_upload_success", {
|
|
13389
|
+
sessionId: context.sessionId,
|
|
13390
|
+
attachmentIndex: context.attachmentIndex,
|
|
13391
|
+
attachmentKey: attachment.clientKey,
|
|
13392
|
+
filename: attachment.filename,
|
|
13393
|
+
uploadId: attachment.uploadId,
|
|
13394
|
+
reusedUploadId: true
|
|
13395
|
+
});
|
|
13396
|
+
return {
|
|
13397
|
+
id: attachment.uploadId,
|
|
13398
|
+
url: attachment.url || "",
|
|
13399
|
+
path: "",
|
|
13400
|
+
name: attachment.filename,
|
|
13401
|
+
mimetype: attachment.mimeType,
|
|
13402
|
+
size: attachment.sizeBytes
|
|
13403
|
+
};
|
|
13404
|
+
}
|
|
13405
|
+
this.logAttachmentLifecycle("attachment_decode_start", {
|
|
13406
|
+
sessionId: context.sessionId,
|
|
13407
|
+
attachmentIndex: context.attachmentIndex,
|
|
13408
|
+
attachmentKey: attachment.clientKey,
|
|
13409
|
+
filename: attachment.filename,
|
|
13410
|
+
hasBase64Data: Boolean(attachment.base64Data && attachment.base64Data.trim().length > 0),
|
|
13411
|
+
hasURL: Boolean(attachment.url)
|
|
13412
|
+
});
|
|
13413
|
+
let file;
|
|
13414
|
+
try {
|
|
13415
|
+
const rawFile = await this.attachmentToFile(attachment);
|
|
13416
|
+
file = await this.normalizeAttachmentFile(attachment, rawFile);
|
|
13417
|
+
validateAttachmentFile(file);
|
|
13418
|
+
} catch (err) {
|
|
13419
|
+
const message = err instanceof Error ? err.message : "Unknown attachment decode/validation error";
|
|
13420
|
+
this.logAttachmentLifecycle("attachment_decode_fail", {
|
|
13421
|
+
sessionId: context.sessionId,
|
|
13422
|
+
attachmentIndex: context.attachmentIndex,
|
|
13423
|
+
attachmentKey: attachment.clientKey,
|
|
13424
|
+
filename: attachment.filename,
|
|
13425
|
+
error: message
|
|
13426
|
+
});
|
|
13427
|
+
throw new Error(message);
|
|
13428
|
+
}
|
|
13429
|
+
this.logAttachmentLifecycle("attachment_upload_start", {
|
|
13430
|
+
sessionId: context.sessionId,
|
|
13431
|
+
attachmentIndex: context.attachmentIndex,
|
|
13432
|
+
attachmentKey: attachment.clientKey,
|
|
13433
|
+
filename: file.name,
|
|
13434
|
+
mimeType: file.type,
|
|
13435
|
+
sizeBytes: file.size
|
|
13436
|
+
});
|
|
13437
|
+
try {
|
|
13438
|
+
const upload = await this.uploadFile(file);
|
|
13439
|
+
attachment.uploadId = upload.id;
|
|
13440
|
+
attachment.mimeType = upload.mimetype || file.type;
|
|
13441
|
+
attachment.filename = upload.name || file.name;
|
|
13442
|
+
attachment.sizeBytes = upload.size || file.size;
|
|
13443
|
+
this.logAttachmentLifecycle("attachment_upload_success", {
|
|
13444
|
+
sessionId: context.sessionId,
|
|
13445
|
+
attachmentIndex: context.attachmentIndex,
|
|
13446
|
+
attachmentKey: attachment.clientKey,
|
|
13447
|
+
filename: attachment.filename,
|
|
13448
|
+
uploadId: upload.id,
|
|
13449
|
+
reusedUploadId: false
|
|
13450
|
+
});
|
|
13451
|
+
return upload;
|
|
13452
|
+
} catch (err) {
|
|
13453
|
+
const message = err instanceof Error ? err.message : "Unknown upload error";
|
|
13454
|
+
this.logAttachmentLifecycle("attachment_upload_fail", {
|
|
13455
|
+
sessionId: context.sessionId,
|
|
13456
|
+
attachmentIndex: context.attachmentIndex,
|
|
13457
|
+
attachmentKey: attachment.clientKey,
|
|
13458
|
+
filename: file.name,
|
|
13459
|
+
error: message
|
|
13460
|
+
});
|
|
13461
|
+
throw new Error(`Attachment "${attachment.filename}" upload failed: ${message}`);
|
|
13462
|
+
}
|
|
13463
|
+
}
|
|
12314
13464
|
async callRPC(method, params) {
|
|
12315
13465
|
return this.rpc.callTyped(method, params);
|
|
12316
13466
|
}
|
|
@@ -12333,11 +13483,22 @@ var HttpDataSource = class {
|
|
|
12333
13483
|
return { artifacts: [], hasMore: false, nextOffset: 0 };
|
|
12334
13484
|
})
|
|
12335
13485
|
]);
|
|
12336
|
-
const
|
|
13486
|
+
const sanitizedTurns = sanitizeConversationTurns(data.turns, id);
|
|
13487
|
+
const turns = attachArtifactsToTurns(
|
|
13488
|
+
normalizeTurns(sanitizedTurns),
|
|
13489
|
+
artifactsData.artifacts || []
|
|
13490
|
+
);
|
|
13491
|
+
const pendingQuestion = sanitizePendingQuestion(data.pendingQuestion, id);
|
|
13492
|
+
if (data.pendingQuestion && pendingQuestion && pendingQuestion.questions.length === 0) {
|
|
13493
|
+
warnMalformedSessionPayload("Pending question normalized to zero renderable questions", {
|
|
13494
|
+
sessionID: id,
|
|
13495
|
+
checkpointID: pendingQuestion.id
|
|
13496
|
+
});
|
|
13497
|
+
}
|
|
12337
13498
|
return {
|
|
12338
13499
|
session: toSession(data.session),
|
|
12339
13500
|
turns,
|
|
12340
|
-
pendingQuestion
|
|
13501
|
+
pendingQuestion
|
|
12341
13502
|
};
|
|
12342
13503
|
} catch (err) {
|
|
12343
13504
|
if (isSessionNotFoundError(err)) {
|
|
@@ -12369,27 +13530,12 @@ var HttpDataSource = class {
|
|
|
12369
13530
|
return { artifacts: [] };
|
|
12370
13531
|
}
|
|
12371
13532
|
validateFileCount(0, files.length, 10);
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
validateAttachmentFile(file);
|
|
12375
|
-
const base64Data = await convertToBase64(file);
|
|
12376
|
-
attachments.push({
|
|
12377
|
-
clientKey: crypto.randomUUID(),
|
|
12378
|
-
filename: file.name,
|
|
12379
|
-
mimeType: file.type,
|
|
12380
|
-
sizeBytes: file.size,
|
|
12381
|
-
base64Data
|
|
12382
|
-
});
|
|
12383
|
-
}
|
|
13533
|
+
files.forEach((file) => validateAttachmentFile(file));
|
|
13534
|
+
const uploads = await Promise.all(files.map((file) => this.uploadFile(file)));
|
|
12384
13535
|
const data = await this.callRPC("bichat.session.uploadArtifacts", {
|
|
12385
13536
|
sessionId,
|
|
12386
|
-
attachments:
|
|
12387
|
-
|
|
12388
|
-
// Backend will assign ID
|
|
12389
|
-
filename: a.filename,
|
|
12390
|
-
mimeType: a.mimeType,
|
|
12391
|
-
sizeBytes: a.sizeBytes,
|
|
12392
|
-
base64Data: a.base64Data
|
|
13537
|
+
attachments: uploads.map((upload) => ({
|
|
13538
|
+
uploadId: upload.id
|
|
12393
13539
|
}))
|
|
12394
13540
|
});
|
|
12395
13541
|
return {
|
|
@@ -12420,22 +13566,26 @@ var HttpDataSource = class {
|
|
|
12420
13566
|
signal.addEventListener("abort", onExternalAbort);
|
|
12421
13567
|
}
|
|
12422
13568
|
const url = `${this.config.baseUrl}${this.config.streamEndpoint}`;
|
|
12423
|
-
const payload = {
|
|
12424
|
-
sessionId,
|
|
12425
|
-
content,
|
|
12426
|
-
debugMode: options?.debugMode ?? false,
|
|
12427
|
-
replaceFromMessageId: options?.replaceFromMessageID,
|
|
12428
|
-
attachments: attachments.map((a) => ({
|
|
12429
|
-
filename: a.filename,
|
|
12430
|
-
mimeType: a.mimeType,
|
|
12431
|
-
sizeBytes: a.sizeBytes,
|
|
12432
|
-
base64Data: a.base64Data,
|
|
12433
|
-
url: a.url
|
|
12434
|
-
}))
|
|
12435
|
-
};
|
|
12436
13569
|
let connectionTimeoutID;
|
|
12437
13570
|
let connectionTimedOut = false;
|
|
12438
13571
|
try {
|
|
13572
|
+
const uploads = await Promise.all(
|
|
13573
|
+
attachments.map(
|
|
13574
|
+
(attachment, attachmentIndex) => this.ensureAttachmentUpload(attachment, { sessionId, attachmentIndex })
|
|
13575
|
+
)
|
|
13576
|
+
);
|
|
13577
|
+
const streamAttachments = this.assertUploadReferences(uploads);
|
|
13578
|
+
this.logAttachmentLifecycle("stream_send_with_upload_ids", {
|
|
13579
|
+
sessionId,
|
|
13580
|
+
attachmentCount: streamAttachments.length
|
|
13581
|
+
});
|
|
13582
|
+
const payload = {
|
|
13583
|
+
sessionId,
|
|
13584
|
+
content,
|
|
13585
|
+
debugMode: options?.debugMode ?? false,
|
|
13586
|
+
replaceFromMessageId: options?.replaceFromMessageID,
|
|
13587
|
+
attachments: streamAttachments
|
|
13588
|
+
};
|
|
12439
13589
|
const timeoutMs = this.config.timeout ?? 0;
|
|
12440
13590
|
if (timeoutMs > 0) {
|
|
12441
13591
|
connectionTimeoutID = setTimeout(() => {
|
|
@@ -12603,6 +13753,6 @@ function createHttpDataSource(config) {
|
|
|
12603
13753
|
return new HttpDataSource(config);
|
|
12604
13754
|
}
|
|
12605
13755
|
|
|
12606
|
-
export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getValidChildren, groupSessionsByDate, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useFocusTrap, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
|
|
13756
|
+
export { ATTACHMENT_ACCEPT_ATTRIBUTE, ActionButton, Alert_default as Alert, AllChatsList, ArchiveBanner_default as ArchiveBanner, ArchivedChatList, AssistantMessage, AssistantTurnView, MemoizedAttachmentGrid as AttachmentGrid, AttachmentPreview_default as AttachmentPreview, AttachmentUpload_default as AttachmentUpload, Avatar, BiChatLayout, Bubble, CHART_VISUAL, ChartCard, ChatHeader, ChatMachine, ChatSession, ChatSessionProvider, MemoizedCodeBlock as CodeBlock, CodeOutputsPanel, CompactionDoodle, ConfigProvider, ConfirmModal, ConfirmationStep, DateGroupHeader, DebugPanel, DefaultErrorContent, DownloadCard, MemoizedEditableText as EditableText, MemoizedEmptyState as EmptyState, ErrorBoundary, HttpDataSource, ImageModal, InlineQuestionForm, InteractiveTableCard, IotaContextProvider, ListItemSkeleton, MemoizedLoadingSpinner as LoadingSpinner, MemoizedMarkdownRenderer as MarkdownRenderer, MessageActions, MessageInput, MessageList, MessageRole, PermissionGuard, QuestionForm, QuestionStep, RateLimiter, RetryActionArea, ScreenReaderAnnouncer, ScrollToBottomButton, MemoizedSearchInput as SearchInput, SessionArtifactList, SessionArtifactPreview, SessionArtifactsPanel, SessionItem_default as SessionItem, SessionSkeleton, Sidebar2 as Sidebar, MemoizedSkeleton as Skeleton, SkeletonAvatar, SkeletonCard, SkeletonGroup, SkeletonText, SkipLink, Slot, SourcesPanel, StreamError, StreamingCursor, SystemMessage, TableExportButton, TableWithExport, ThemeProvider, Toast, ToastContainer, TouchContextMenu, Turn, TurnBubble, MemoizedTypingIndicator as TypingIndicator, MemoizedUserAvatar as UserAvatar, MemoizedUserFilter as UserFilter, UserMessage, UserTurnView, WelcomeContent, addCSRFHeader, backdropVariants, buttonVariants, convertToBase64, createDataUrl, createHeadersWithCSRF, createHttpDataSource, darkTheme, dropdownVariants, errorMessageVariants, fadeInUpVariants, fadeInVariants, floatingButtonVariants, formatFileSize, getCSRFToken, getFileVisual, getValidChildren, groupSessionsByDate, hasPermission, isImageMimeType, isPermissionDeniedError, lightTheme, listItemVariants, messageContainerVariants, messageVariants, parseBichatStream, parseBichatStreamEvents, parseSSEStream, scaleFadeVariants, sessionItemVariants, staggerContainerVariants, toErrorDisplay, typingDotVariants, useActionButtonContext, useAttachments, useAutoScroll, useAvatarContext, useBubbleContext, useChatInput, useChatMessaging, useChatSession, useConfig, useFocusTrap, useImageGallery, useIotaContext, useKeyboardShortcuts, useLongPress, useMarkdownCopy, useMessageActions, useModalLock, useOptionalChatMessaging, useRequiredConfig, useScrollToBottom, useSidebarState, useStreaming, useTheme, useToast, useTranslation, useTurnContext, validateAttachmentFile, validateFileCount, validateImageFile, verbTransitionVariants };
|
|
12607
13757
|
//# sourceMappingURL=index.mjs.map
|
|
12608
13758
|
//# sourceMappingURL=index.mjs.map
|