@retrivora-ai/rag-engine 1.8.1 → 1.8.2
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/{ILLMProvider-BfRgI1Xh.d.mts → ILLMProvider-BOJFz3Na.d.mts} +47 -1
- package/dist/{ILLMProvider-BfRgI1Xh.d.ts → ILLMProvider-BOJFz3Na.d.ts} +47 -1
- package/dist/{MultiTablePostgresProvider-YY7LPNJK.mjs → MultiTablePostgresProvider-ZLGSKTJR.mjs} +1 -1
- package/dist/chunk-ICKRMZQK.mjs +76 -0
- package/dist/{chunk-BFYLQYQU.mjs → chunk-LZVVLSDN.mjs} +192 -100
- package/dist/{chunk-R3RGUMHE.mjs → chunk-OZFBG4BA.mjs} +121 -48
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +368 -147
- package/dist/handlers/index.mjs +4 -1
- package/dist/{index-BV0z5mb6.d.mts → index-BwpcaziY.d.ts} +4 -2
- package/dist/{index-1Z4GuYBi.d.ts → index-D3V9Et2M.d.mts} +4 -2
- package/dist/index.d.mts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +1143 -790
- package/dist/index.mjs +1065 -770
- package/dist/server.d.mts +15 -25
- package/dist/server.d.ts +15 -25
- package/dist/server.js +366 -147
- package/dist/server.mjs +3 -2
- package/package.json +4 -2
- package/src/app/api/upload/route.ts +4 -0
- package/src/app/constants.tsx +2 -2
- package/src/app/page.tsx +12 -321
- package/src/components/AmbientBackground.tsx +29 -0
- package/src/components/ArchitectureCard.tsx +17 -0
- package/src/components/ArchitectureCardsSection.tsx +15 -0
- package/src/components/ChatWindow.tsx +32 -0
- package/src/components/CodeViewer.tsx +51 -0
- package/src/components/ConfigProvider.tsx +1 -0
- package/src/components/DocViewer.tsx +37 -0
- package/src/components/DocumentUpload.tsx +44 -1
- package/src/components/Documentation.tsx +58 -0
- package/src/components/DynamicChart.tsx +27 -2
- package/src/components/Hero.tsx +59 -0
- package/src/components/HourglassLoader.tsx +87 -0
- package/src/components/Lifecycle.tsx +37 -0
- package/src/components/MarkdownComponents.tsx +140 -0
- package/src/components/MessageBubble.tsx +88 -1010
- package/src/components/Navbar.tsx +55 -0
- package/src/components/ObservabilityPanel.tsx +374 -0
- package/src/components/ProductCard.tsx +3 -1
- package/src/components/UIDispatcher.tsx +344 -0
- package/src/components/VisualizationRenderer.tsx +48 -26
- package/src/core/Pipeline.ts +186 -76
- package/src/handlers/index.ts +72 -12
- package/src/hooks/useRagChat.ts +19 -9
- package/src/index.ts +9 -1
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
- package/src/types/chat.ts +2 -0
- package/src/types/index.ts +52 -0
- package/src/types/props.ts +9 -1
- package/src/utils/ProductExtractor.ts +347 -0
- package/src/utils/UITransformer.ts +4 -53
- package/src/utils/synonyms.ts +78 -0
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
addSynonyms,
|
|
3
|
+
resolveMetadataValue
|
|
4
|
+
} from "./chunk-ICKRMZQK.mjs";
|
|
1
5
|
import {
|
|
2
6
|
mergeDefined
|
|
3
7
|
} from "./chunk-YLTMFW4M.mjs";
|
|
@@ -8,15 +12,15 @@ import {
|
|
|
8
12
|
} from "./chunk-X4TOT24V.mjs";
|
|
9
13
|
|
|
10
14
|
// src/components/ChatWidget.tsx
|
|
11
|
-
import
|
|
12
|
-
import { MessageSquare, X as
|
|
15
|
+
import React15, { useState as useState6 } from "react";
|
|
16
|
+
import { MessageSquare, X as X3 } from "lucide-react";
|
|
13
17
|
|
|
14
18
|
// src/components/ChatWindow.tsx
|
|
15
|
-
import
|
|
19
|
+
import React14, { useState as useState5, useRef as useRef4, useEffect as useEffect4, useCallback as useCallback2 } from "react";
|
|
16
20
|
import {
|
|
17
21
|
Bot as Bot2,
|
|
18
22
|
Trash2,
|
|
19
|
-
X,
|
|
23
|
+
X as X2,
|
|
20
24
|
Sparkles,
|
|
21
25
|
ArrowUp,
|
|
22
26
|
TriangleAlert,
|
|
@@ -24,47 +28,256 @@ import {
|
|
|
24
28
|
Maximize2,
|
|
25
29
|
Minimize2,
|
|
26
30
|
Mic,
|
|
27
|
-
MicOff
|
|
31
|
+
MicOff,
|
|
32
|
+
Paperclip
|
|
28
33
|
} from "lucide-react";
|
|
29
34
|
|
|
35
|
+
// src/components/DocumentUpload.tsx
|
|
36
|
+
import React2, { useState, useRef } from "react";
|
|
37
|
+
import { Upload, File, X, CheckCircle, AlertCircle, Loader2 } from "lucide-react";
|
|
38
|
+
|
|
39
|
+
// src/components/ConfigProvider.tsx
|
|
40
|
+
import React, { createContext, useContext } from "react";
|
|
41
|
+
|
|
42
|
+
// src/config/uiConstants.ts
|
|
43
|
+
var DEFAULT_CONFIG = {
|
|
44
|
+
projectId: "default",
|
|
45
|
+
ui: {
|
|
46
|
+
title: "AI Assistant",
|
|
47
|
+
subtitle: "Powered by RAG",
|
|
48
|
+
primaryColor: "#6366f1",
|
|
49
|
+
accentColor: "#8b5cf6",
|
|
50
|
+
logoUrl: "",
|
|
51
|
+
placeholder: "Ask me anything\u2026",
|
|
52
|
+
showSources: true,
|
|
53
|
+
welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
54
|
+
showWidget: true,
|
|
55
|
+
poweredBy: "RAG",
|
|
56
|
+
visualStyle: "glass",
|
|
57
|
+
borderRadius: "xl",
|
|
58
|
+
allowUpload: true,
|
|
59
|
+
allowResize: true,
|
|
60
|
+
enableVoiceInput: true
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var BORDER_RADIUS_MAP = {
|
|
64
|
+
none: "rounded-none",
|
|
65
|
+
sm: "rounded-sm",
|
|
66
|
+
md: "rounded-md",
|
|
67
|
+
lg: "rounded-lg",
|
|
68
|
+
xl: "rounded-xl",
|
|
69
|
+
full: "rounded-3xl"
|
|
70
|
+
};
|
|
71
|
+
var CHAT_SUGGESTIONS = [
|
|
72
|
+
"What can you help me with?",
|
|
73
|
+
"Summarise the key topics",
|
|
74
|
+
"Show me an example"
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// src/components/ConfigProvider.tsx
|
|
78
|
+
var ConfigContext = createContext(DEFAULT_CONFIG);
|
|
79
|
+
function ConfigProvider({
|
|
80
|
+
config,
|
|
81
|
+
children
|
|
82
|
+
}) {
|
|
83
|
+
const merged = {
|
|
84
|
+
projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
|
|
85
|
+
ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui),
|
|
86
|
+
embedding: config == null ? void 0 : config.embedding
|
|
87
|
+
};
|
|
88
|
+
return /* @__PURE__ */ React.createElement(ConfigContext.Provider, { value: merged }, children);
|
|
89
|
+
}
|
|
90
|
+
function useConfig() {
|
|
91
|
+
return useContext(ConfigContext);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/components/DocumentUpload.tsx
|
|
95
|
+
function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
96
|
+
var _a;
|
|
97
|
+
const { ui, embedding } = useConfig();
|
|
98
|
+
const [fileStates, setFileStates] = useState([]);
|
|
99
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
100
|
+
const fileInputRef = useRef(null);
|
|
101
|
+
const MULTI_DIMENSION_MODELS = {
|
|
102
|
+
"text-embedding-3-small": [512, 1536],
|
|
103
|
+
"text-embedding-3-large": [256, 1024, 3072],
|
|
104
|
+
"nomic-embed-text": [64, 128, 256, 512, 768]
|
|
105
|
+
};
|
|
106
|
+
const COMMON_DIMENSIONS = [64, 128, 256, 384, 512, 768, 1024, 1536, 3072];
|
|
107
|
+
const currentModel = (embedding == null ? void 0 : embedding.model) || "unknown";
|
|
108
|
+
const defaultDimension = (embedding == null ? void 0 : embedding.dimensions) || 1536;
|
|
109
|
+
let availableDimensions = (_a = Object.entries(MULTI_DIMENSION_MODELS).find(([k]) => currentModel.includes(k))) == null ? void 0 : _a[1];
|
|
110
|
+
if (!availableDimensions) {
|
|
111
|
+
availableDimensions = COMMON_DIMENSIONS.includes(defaultDimension) ? COMMON_DIMENSIONS : [...COMMON_DIMENSIONS, defaultDimension].sort((a, b) => a - b);
|
|
112
|
+
}
|
|
113
|
+
const isChangeable = true;
|
|
114
|
+
const [dimension, setDimension] = useState(defaultDimension);
|
|
115
|
+
const addFiles = (files) => {
|
|
116
|
+
const newStates = files.map((file) => ({ file, status: "idle" }));
|
|
117
|
+
setFileStates((prev) => [...prev, ...newStates]);
|
|
118
|
+
};
|
|
119
|
+
const removeFile = (index) => {
|
|
120
|
+
setFileStates((prev) => prev.filter((_, i) => i !== index));
|
|
121
|
+
};
|
|
122
|
+
const onDragOver = (e) => {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
setIsDragging(true);
|
|
125
|
+
};
|
|
126
|
+
const onDragLeave = () => {
|
|
127
|
+
setIsDragging(false);
|
|
128
|
+
};
|
|
129
|
+
const onDrop = (e) => {
|
|
130
|
+
e.preventDefault();
|
|
131
|
+
setIsDragging(false);
|
|
132
|
+
if (e.dataTransfer.files) {
|
|
133
|
+
addFiles(Array.from(e.dataTransfer.files));
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
const onFileSelect = (e) => {
|
|
137
|
+
if (e.target.files) {
|
|
138
|
+
addFiles(Array.from(e.target.files));
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const uploadFiles = async () => {
|
|
142
|
+
const idleFiles = fileStates.filter((s) => s.status === "idle");
|
|
143
|
+
if (idleFiles.length === 0) return;
|
|
144
|
+
setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
|
|
145
|
+
const formData = new FormData();
|
|
146
|
+
idleFiles.forEach((s) => formData.append("files", s.file));
|
|
147
|
+
if (namespace) formData.append("namespace", namespace);
|
|
148
|
+
formData.append("dimension", dimension.toString());
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch("/api/upload", {
|
|
151
|
+
method: "POST",
|
|
152
|
+
body: formData
|
|
153
|
+
});
|
|
154
|
+
const result = await response.json();
|
|
155
|
+
if (!response.ok) throw new Error(result.error || "Upload failed");
|
|
156
|
+
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
|
|
157
|
+
if (onUploadComplete) onUploadComplete(result);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const message = err instanceof Error ? err.message : "Upload failed";
|
|
160
|
+
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const isUploading = fileStates.some((s) => s.status === "uploading");
|
|
164
|
+
const hasIdle = fileStates.some((s) => s.status === "idle");
|
|
165
|
+
return /* @__PURE__ */ React2.createElement(
|
|
166
|
+
"div",
|
|
167
|
+
{
|
|
168
|
+
className: `p-6 border-2 border-dashed transition-all duration-300 rounded-2xl ${isDragging ? "border-emerald-500 bg-emerald-50/50 dark:bg-emerald-500/5" : "border-slate-200 dark:border-white/10 bg-white dark:bg-white/5"} ${className}`,
|
|
169
|
+
onDragOver,
|
|
170
|
+
onDragLeave,
|
|
171
|
+
onDrop
|
|
172
|
+
},
|
|
173
|
+
/* @__PURE__ */ React2.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ React2.createElement(
|
|
174
|
+
"div",
|
|
175
|
+
{
|
|
176
|
+
className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
|
|
177
|
+
style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
|
|
178
|
+
},
|
|
179
|
+
/* @__PURE__ */ React2.createElement(Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
|
|
180
|
+
), /* @__PURE__ */ React2.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ React2.createElement("p", { className: "text-slate-500 dark:text-white/40 text-sm mb-6 max-w-xs" }, "Drag and drop PDF, DOCX, TXT, or JSON files to train your AI on your own data."), /* @__PURE__ */ React2.createElement(
|
|
181
|
+
"button",
|
|
182
|
+
{
|
|
183
|
+
onClick: () => {
|
|
184
|
+
var _a2;
|
|
185
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
186
|
+
},
|
|
187
|
+
disabled: isUploading,
|
|
188
|
+
className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
|
|
189
|
+
style: {
|
|
190
|
+
backgroundColor: `${ui.primaryColor}15`,
|
|
191
|
+
color: ui.primaryColor,
|
|
192
|
+
border: `1px solid ${ui.primaryColor}30`
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"Select Files"
|
|
196
|
+
), /* @__PURE__ */ React2.createElement(
|
|
197
|
+
"input",
|
|
198
|
+
{
|
|
199
|
+
ref: fileInputRef,
|
|
200
|
+
type: "file",
|
|
201
|
+
multiple: true,
|
|
202
|
+
onChange: onFileSelect,
|
|
203
|
+
className: "hidden",
|
|
204
|
+
accept: ".pdf,.docx,.txt,.md,.json,.csv"
|
|
205
|
+
}
|
|
206
|
+
), /* @__PURE__ */ React2.createElement("div", { className: "mt-6 flex flex-col items-center gap-2 text-sm" }, /* @__PURE__ */ React2.createElement("label", { className: "text-slate-600 dark:text-white/60 font-medium" }, "Embedding Dimension"), isChangeable ? /* @__PURE__ */ React2.createElement(
|
|
207
|
+
"select",
|
|
208
|
+
{
|
|
209
|
+
value: dimension,
|
|
210
|
+
onChange: (e) => setDimension(Number(e.target.value)),
|
|
211
|
+
disabled: isUploading,
|
|
212
|
+
className: "px-3 py-1.5 rounded-lg bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 outline-none focus:border-emerald-500 transition-colors"
|
|
213
|
+
},
|
|
214
|
+
availableDimensions.map((dim) => /* @__PURE__ */ React2.createElement("option", { key: dim, value: dim, className: "bg-white dark:bg-slate-900" }, dim))
|
|
215
|
+
) : /* @__PURE__ */ React2.createElement("div", { className: "px-3 py-1.5 rounded-lg bg-slate-100 dark:bg-white/10 border border-slate-200 dark:border-white/10 text-slate-500 dark:text-white/50 cursor-not-allowed text-xs" }, dimension, " (Fixed for ", currentModel, ")"))),
|
|
216
|
+
fileStates.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ React2.createElement(
|
|
217
|
+
"div",
|
|
218
|
+
{
|
|
219
|
+
key: i,
|
|
220
|
+
className: "flex items-center justify-between p-3 rounded-xl bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/10"
|
|
221
|
+
},
|
|
222
|
+
/* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ React2.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ React2.createElement(File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ React2.createElement("div", { className: "truncate" }, /* @__PURE__ */ React2.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ React2.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
|
|
223
|
+
/* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ React2.createElement(Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ React2.createElement(CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ React2.createElement("div", { title: state.error }, /* @__PURE__ */ React2.createElement(AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ React2.createElement(
|
|
224
|
+
"button",
|
|
225
|
+
{
|
|
226
|
+
onClick: () => removeFile(i),
|
|
227
|
+
className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
228
|
+
},
|
|
229
|
+
/* @__PURE__ */ React2.createElement(X, { className: "w-3.5 h-3.5 text-slate-400" })
|
|
230
|
+
))
|
|
231
|
+
)), hasIdle && !isUploading && /* @__PURE__ */ React2.createElement(
|
|
232
|
+
"button",
|
|
233
|
+
{
|
|
234
|
+
onClick: uploadFiles,
|
|
235
|
+
className: "w-full mt-4 py-2.5 rounded-xl text-sm font-semibold text-white shadow-lg transition-all hover:scale-[1.02] active:scale-[0.98]",
|
|
236
|
+
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
237
|
+
},
|
|
238
|
+
"Start Upload"
|
|
239
|
+
))
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
30
243
|
// src/components/MessageBubble.tsx
|
|
31
|
-
import
|
|
32
|
-
import { Bot, User, ChevronDown as
|
|
244
|
+
import React12 from "react";
|
|
245
|
+
import { Bot, User, ChevronDown as ChevronDown3, ChevronRight as ChevronRight3, Activity } from "lucide-react";
|
|
33
246
|
import ReactMarkdown from "react-markdown";
|
|
34
247
|
|
|
35
248
|
// src/components/SourceCard.tsx
|
|
36
|
-
import
|
|
249
|
+
import React3 from "react";
|
|
37
250
|
import { ChevronDown, ChevronUp } from "lucide-react";
|
|
38
251
|
function SourceCard({ source, index }) {
|
|
39
252
|
var _a, _b;
|
|
40
|
-
const [expanded, setExpanded] =
|
|
253
|
+
const [expanded, setExpanded] = React3.useState(false);
|
|
41
254
|
const scorePercent = Math.round(source.score * 100);
|
|
42
255
|
const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
|
|
43
256
|
const scoreBg = source.score >= 0.8 ? "bg-emerald-50 border-emerald-100 dark:bg-emerald-400/10 dark:border-emerald-400/20" : source.score >= 0.6 ? "bg-amber-50 border-amber-100 dark:bg-amber-400/10 dark:border-amber-400/20" : "bg-rose-50 border-rose-100 dark:bg-rose-400/10 dark:border-rose-400/20";
|
|
44
257
|
const preview = source.content.slice(0, 120);
|
|
45
258
|
const hasMore = source.content.length > 120;
|
|
46
|
-
return /* @__PURE__ */
|
|
259
|
+
return /* @__PURE__ */ React3.createElement(
|
|
47
260
|
"div",
|
|
48
261
|
{
|
|
49
262
|
className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
|
|
50
263
|
onClick: () => setExpanded((e) => !e)
|
|
51
264
|
},
|
|
52
|
-
/* @__PURE__ */
|
|
53
|
-
/* @__PURE__ */
|
|
54
|
-
hasMore && /* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ React3.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React3.createElement("span", { className: "flex-shrink-0 w-5 h-5 rounded-full bg-slate-200 dark:bg-white/10 flex items-center justify-center text-[10px] font-bold text-slate-500 dark:text-white/60" }, index + 1), /* @__PURE__ */ React3.createElement("span", { className: "text-xs text-slate-500 dark:text-white/50 truncate font-mono" }, (_b = (_a = source.metadata) == null ? void 0 : _a.docId) != null ? _b : source.id)), /* @__PURE__ */ React3.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
|
|
266
|
+
/* @__PURE__ */ React3.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
|
|
267
|
+
hasMore && /* @__PURE__ */ React3.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ChevronDown, { className: "w-3 h-3" }), " Show more"))
|
|
55
268
|
);
|
|
56
269
|
}
|
|
57
270
|
|
|
58
271
|
// src/components/ProductCarousel.tsx
|
|
59
|
-
import
|
|
272
|
+
import React5, { useRef as useRef2, useState as useState2, useEffect } from "react";
|
|
60
273
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
61
274
|
|
|
62
275
|
// src/components/ProductCard.tsx
|
|
63
|
-
import
|
|
276
|
+
import React4 from "react";
|
|
64
277
|
import Image from "next/image";
|
|
65
278
|
import { ShoppingCart, ExternalLink } from "lucide-react";
|
|
66
279
|
function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
67
|
-
return /* @__PURE__ */
|
|
280
|
+
return /* @__PURE__ */ React4.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ React4.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ React4.createElement(
|
|
68
281
|
Image,
|
|
69
282
|
{
|
|
70
283
|
src: product.image,
|
|
@@ -73,16 +286,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
73
286
|
unoptimized: true,
|
|
74
287
|
className: "object-cover group-hover:scale-105 transition-transform duration-500"
|
|
75
288
|
}
|
|
76
|
-
) : /* @__PURE__ */
|
|
289
|
+
) : /* @__PURE__ */ React4.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ React4.createElement("div", { className: "absolute top-2 left-2 px-2 py-1 bg-white/90 dark:bg-black/60 backdrop-blur-md rounded-md text-[10px] font-bold uppercase tracking-wider text-slate-700 dark:text-white/90 shadow-sm" }, product.brand)), /* @__PURE__ */ React4.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ React4.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ React4.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ React4.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : String(product.price).match(/^[\d.]/) ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ React4.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ React4.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ React4.createElement(
|
|
77
290
|
"button",
|
|
78
291
|
{
|
|
79
292
|
onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
|
|
80
293
|
className: "flex-1 py-2 px-3 rounded-lg text-[11px] font-medium text-white shadow-sm hover:opacity-90 transition-opacity flex items-center justify-center gap-1.5",
|
|
81
294
|
style: { background: primaryColor }
|
|
82
295
|
},
|
|
83
|
-
/* @__PURE__ */
|
|
296
|
+
/* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-3 h-3" }),
|
|
84
297
|
"Add to Cart"
|
|
85
|
-
), product.link && /* @__PURE__ */
|
|
298
|
+
), product.link && /* @__PURE__ */ React4.createElement(
|
|
86
299
|
"a",
|
|
87
300
|
{
|
|
88
301
|
href: product.link,
|
|
@@ -90,14 +303,14 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
90
303
|
rel: "noopener noreferrer",
|
|
91
304
|
className: "p-2 rounded-lg bg-slate-100 dark:bg-white/10 text-slate-600 dark:text-white/70 hover:bg-slate-200 dark:hover:bg-white/20 transition-colors"
|
|
92
305
|
},
|
|
93
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ React4.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
|
|
94
307
|
))));
|
|
95
308
|
}
|
|
96
309
|
|
|
97
310
|
// src/components/ProductCarousel.tsx
|
|
98
311
|
function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
99
|
-
const scrollRef =
|
|
100
|
-
const [canScroll, setCanScroll] =
|
|
312
|
+
const scrollRef = useRef2(null);
|
|
313
|
+
const [canScroll, setCanScroll] = useState2(false);
|
|
101
314
|
useEffect(() => {
|
|
102
315
|
const checkScroll = () => {
|
|
103
316
|
if (scrollRef.current) {
|
|
@@ -122,9 +335,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
122
335
|
};
|
|
123
336
|
if (!products || products.length === 0) return null;
|
|
124
337
|
if (products.length === 1) {
|
|
125
|
-
return /* @__PURE__ */
|
|
338
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React5.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
|
|
126
339
|
}
|
|
127
|
-
return /* @__PURE__ */
|
|
340
|
+
return /* @__PURE__ */ React5.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React5.createElement(
|
|
128
341
|
"button",
|
|
129
342
|
{
|
|
130
343
|
onClick: (e) => {
|
|
@@ -134,8 +347,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
134
347
|
className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
|
|
135
348
|
"aria-label": "Scroll left"
|
|
136
349
|
},
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
-
)), /* @__PURE__ */
|
|
350
|
+
/* @__PURE__ */ React5.createElement(ChevronLeft, { className: "w-5 h-5" })
|
|
351
|
+
)), /* @__PURE__ */ React5.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ React5.createElement(
|
|
139
352
|
"button",
|
|
140
353
|
{
|
|
141
354
|
onClick: (e) => {
|
|
@@ -145,27 +358,50 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
145
358
|
className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
|
|
146
359
|
"aria-label": "Scroll right"
|
|
147
360
|
},
|
|
148
|
-
/* @__PURE__ */
|
|
149
|
-
))), /* @__PURE__ */
|
|
361
|
+
/* @__PURE__ */ React5.createElement(ChevronRight, { className: "w-5 h-5" })
|
|
362
|
+
))), /* @__PURE__ */ React5.createElement(
|
|
150
363
|
"div",
|
|
151
364
|
{
|
|
152
365
|
ref: scrollRef,
|
|
153
366
|
className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
|
|
154
367
|
style: { scrollbarWidth: "none", msOverflowStyle: "none" }
|
|
155
368
|
},
|
|
156
|
-
products.map((product) => /* @__PURE__ */
|
|
369
|
+
products.map((product) => /* @__PURE__ */ React5.createElement(
|
|
157
370
|
"div",
|
|
158
371
|
{
|
|
159
372
|
key: product.id,
|
|
160
373
|
className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
|
|
161
374
|
},
|
|
162
|
-
/* @__PURE__ */
|
|
375
|
+
/* @__PURE__ */ React5.createElement(ProductCard, { product, primaryColor, onAddToCart })
|
|
163
376
|
))
|
|
164
377
|
));
|
|
165
378
|
}
|
|
166
379
|
|
|
380
|
+
// src/components/VisualizationRenderer.tsx
|
|
381
|
+
import React7 from "react";
|
|
382
|
+
import {
|
|
383
|
+
PieChart as PieChart2,
|
|
384
|
+
Pie as Pie2,
|
|
385
|
+
Cell as Cell2,
|
|
386
|
+
BarChart as BarChart2,
|
|
387
|
+
Bar as Bar2,
|
|
388
|
+
LineChart as LineChart2,
|
|
389
|
+
Line as Line2,
|
|
390
|
+
XAxis as XAxis2,
|
|
391
|
+
YAxis as YAxis2,
|
|
392
|
+
CartesianGrid as CartesianGrid2,
|
|
393
|
+
Tooltip as Tooltip2,
|
|
394
|
+
Legend as Legend2,
|
|
395
|
+
ResponsiveContainer as ResponsiveContainer2,
|
|
396
|
+
RadarChart,
|
|
397
|
+
Radar,
|
|
398
|
+
PolarGrid,
|
|
399
|
+
PolarAngleAxis,
|
|
400
|
+
PolarRadiusAxis
|
|
401
|
+
} from "recharts";
|
|
402
|
+
|
|
167
403
|
// src/components/DynamicChart.tsx
|
|
168
|
-
import
|
|
404
|
+
import React6 from "react";
|
|
169
405
|
import {
|
|
170
406
|
BarChart,
|
|
171
407
|
Bar,
|
|
@@ -181,18 +417,19 @@ import {
|
|
|
181
417
|
Legend,
|
|
182
418
|
ResponsiveContainer
|
|
183
419
|
} from "recharts";
|
|
184
|
-
var
|
|
420
|
+
var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
|
|
185
421
|
function DynamicChart({
|
|
186
422
|
config,
|
|
187
423
|
primaryColor = "#6366f1",
|
|
188
424
|
accentColor = "#8b5cf6",
|
|
189
|
-
viewportSize = "large"
|
|
425
|
+
viewportSize = "large",
|
|
426
|
+
isStreaming = false
|
|
190
427
|
}) {
|
|
191
428
|
const { type, data } = config;
|
|
192
|
-
const colors = config.colors || [primaryColor, accentColor, ...
|
|
193
|
-
const containerRef =
|
|
194
|
-
const [containerWidth, setContainerWidth] =
|
|
195
|
-
|
|
429
|
+
const colors = config.colors || [primaryColor, accentColor, ...CHART_COLORS];
|
|
430
|
+
const containerRef = React6.useRef(null);
|
|
431
|
+
const [containerWidth, setContainerWidth] = React6.useState(0);
|
|
432
|
+
React6.useEffect(() => {
|
|
196
433
|
const element = containerRef.current;
|
|
197
434
|
if (!element || typeof ResizeObserver === "undefined") return;
|
|
198
435
|
const updateWidth = (width) => {
|
|
@@ -218,7 +455,7 @@ function DynamicChart({
|
|
|
218
455
|
return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
|
|
219
456
|
};
|
|
220
457
|
const chartMargin = isTiny ? { top: 8, right: 4, left: -28, bottom: 0 } : isCompact ? { top: 10, right: 6, left: -24, bottom: 0 } : { top: 10, right: 10, left: -20, bottom: 0 };
|
|
221
|
-
const sanitizedData =
|
|
458
|
+
const sanitizedData = React6.useMemo(() => {
|
|
222
459
|
if (!data) return [];
|
|
223
460
|
return data.map((item) => {
|
|
224
461
|
const newItem = {};
|
|
@@ -250,8 +487,19 @@ function DynamicChart({
|
|
|
250
487
|
return newItem;
|
|
251
488
|
});
|
|
252
489
|
}, [data]);
|
|
490
|
+
if (isStreaming) {
|
|
491
|
+
const bars = type === "pie" ? 6 : 5;
|
|
492
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, type === "pie" ? /* @__PURE__ */ React6.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }) : /* @__PURE__ */ React6.createElement("div", { className: "flex items-end gap-2 h-32 px-2" }, Array.from({ length: bars }).map((_, i) => /* @__PURE__ */ React6.createElement(
|
|
493
|
+
"div",
|
|
494
|
+
{
|
|
495
|
+
key: i,
|
|
496
|
+
className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10",
|
|
497
|
+
style: { height: `${40 + i % 3 * 25}%` }
|
|
498
|
+
}
|
|
499
|
+
))));
|
|
500
|
+
}
|
|
253
501
|
if (!data || data.length === 0) {
|
|
254
|
-
return /* @__PURE__ */
|
|
502
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
|
|
255
503
|
}
|
|
256
504
|
const firstItem = sanitizedData[0];
|
|
257
505
|
const allKeys = Object.keys(firstItem);
|
|
@@ -276,13 +524,13 @@ function DynamicChart({
|
|
|
276
524
|
const renderTooltip = (row, label) => {
|
|
277
525
|
var _a, _b;
|
|
278
526
|
if (!row) return null;
|
|
279
|
-
return /* @__PURE__ */
|
|
527
|
+
return /* @__PURE__ */ React6.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, /* @__PURE__ */ React6.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, String((_b = (_a = row[finalXKey]) != null ? _a : label) != null ? _b : "Details")), Object.entries(row).map(([key, value]) => {
|
|
280
528
|
var _a2;
|
|
281
529
|
if (value === null || value === void 0 || key === finalXKey) return null;
|
|
282
530
|
if (typeof value === "object") return null;
|
|
283
531
|
const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
|
|
284
532
|
if (forbiddenKeys.includes(key)) return null;
|
|
285
|
-
return /* @__PURE__ */
|
|
533
|
+
return /* @__PURE__ */ React6.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React6.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ React6.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
|
|
286
534
|
}));
|
|
287
535
|
};
|
|
288
536
|
const getTooltipRow = (payload) => {
|
|
@@ -294,7 +542,7 @@ function DynamicChart({
|
|
|
294
542
|
return row;
|
|
295
543
|
};
|
|
296
544
|
if (type === "pie") {
|
|
297
|
-
return /* @__PURE__ */
|
|
545
|
+
return /* @__PURE__ */ React6.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React6.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(PieChart, null, /* @__PURE__ */ React6.createElement(
|
|
298
546
|
Pie,
|
|
299
547
|
{
|
|
300
548
|
data: sanitizedData,
|
|
@@ -306,10 +554,10 @@ function DynamicChart({
|
|
|
306
554
|
label: false,
|
|
307
555
|
labelLine: false
|
|
308
556
|
},
|
|
309
|
-
sanitizedData.map((_entry, index) => /* @__PURE__ */
|
|
310
|
-
), /* @__PURE__ */
|
|
557
|
+
sanitizedData.map((_entry, index) => /* @__PURE__ */ React6.createElement(Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
|
|
558
|
+
), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
|
|
311
559
|
}
|
|
312
|
-
return /* @__PURE__ */
|
|
560
|
+
return /* @__PURE__ */ React6.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React6.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React6.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React6.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React6.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React6.createElement(
|
|
313
561
|
Bar,
|
|
314
562
|
{
|
|
315
563
|
key,
|
|
@@ -318,7 +566,7 @@ function DynamicChart({
|
|
|
318
566
|
radius: [4, 4, 0, 0],
|
|
319
567
|
barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
|
|
320
568
|
}
|
|
321
|
-
))) : /* @__PURE__ */
|
|
569
|
+
))) : /* @__PURE__ */ React6.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React6.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React6.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React6.createElement(
|
|
322
570
|
Line,
|
|
323
571
|
{
|
|
324
572
|
key,
|
|
@@ -333,45 +581,6 @@ function DynamicChart({
|
|
|
333
581
|
}
|
|
334
582
|
|
|
335
583
|
// src/components/VisualizationRenderer.tsx
|
|
336
|
-
import React5 from "react";
|
|
337
|
-
import {
|
|
338
|
-
PieChart as PieChart2,
|
|
339
|
-
Pie as Pie2,
|
|
340
|
-
Cell as Cell2,
|
|
341
|
-
BarChart as BarChart2,
|
|
342
|
-
Bar as Bar2,
|
|
343
|
-
LineChart as LineChart2,
|
|
344
|
-
Line as Line2,
|
|
345
|
-
XAxis as XAxis2,
|
|
346
|
-
YAxis as YAxis2,
|
|
347
|
-
CartesianGrid as CartesianGrid2,
|
|
348
|
-
Tooltip as Tooltip2,
|
|
349
|
-
Legend as Legend2,
|
|
350
|
-
ResponsiveContainer as ResponsiveContainer2,
|
|
351
|
-
RadarChart,
|
|
352
|
-
Radar,
|
|
353
|
-
PolarGrid,
|
|
354
|
-
PolarAngleAxis,
|
|
355
|
-
PolarRadiusAxis
|
|
356
|
-
} from "recharts";
|
|
357
|
-
var CHART_COLORS = [
|
|
358
|
-
"#6366f1",
|
|
359
|
-
// indigo
|
|
360
|
-
"#10b981",
|
|
361
|
-
// emerald
|
|
362
|
-
"#f59e0b",
|
|
363
|
-
// amber
|
|
364
|
-
"#ef4444",
|
|
365
|
-
// red
|
|
366
|
-
"#8b5cf6",
|
|
367
|
-
// purple
|
|
368
|
-
"#ec4899",
|
|
369
|
-
// pink
|
|
370
|
-
"#06b6d4",
|
|
371
|
-
// cyan
|
|
372
|
-
"#f97316"
|
|
373
|
-
// orange
|
|
374
|
-
];
|
|
375
584
|
function getColor(index) {
|
|
376
585
|
return CHART_COLORS[index % CHART_COLORS.length];
|
|
377
586
|
}
|
|
@@ -381,60 +590,71 @@ function CustomTooltip({
|
|
|
381
590
|
label
|
|
382
591
|
}) {
|
|
383
592
|
if (!active || !payload || payload.length === 0) return null;
|
|
384
|
-
return /* @__PURE__ */
|
|
593
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, label && /* @__PURE__ */ React7.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ React7.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React7.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ React7.createElement(
|
|
385
594
|
"span",
|
|
386
595
|
{
|
|
387
596
|
className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
|
|
388
597
|
style: { background: entry.color }
|
|
389
598
|
}
|
|
390
|
-
), entry.name), /* @__PURE__ */
|
|
599
|
+
), entry.name), /* @__PURE__ */ React7.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
|
|
391
600
|
}
|
|
392
601
|
function VisualizationRenderer({
|
|
393
602
|
data,
|
|
394
603
|
className = "",
|
|
395
604
|
primaryColor,
|
|
396
|
-
onAddToCart
|
|
605
|
+
onAddToCart,
|
|
606
|
+
isStreaming
|
|
397
607
|
}) {
|
|
398
608
|
var _a;
|
|
399
609
|
if (!data) {
|
|
400
|
-
return /* @__PURE__ */
|
|
610
|
+
return /* @__PURE__ */ React7.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
|
|
401
611
|
}
|
|
402
|
-
return /* @__PURE__ */
|
|
612
|
+
return /* @__PURE__ */ React7.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ React7.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React7.createElement("h3", { className: "text-sm font-semibold text-slate-900 dark:text-white" }, data.title), data.description && data.description !== ((_a = data.data) == null ? void 0 : _a.content) && /* @__PURE__ */ React7.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor, isStreaming));
|
|
403
613
|
}
|
|
404
|
-
function renderVisualization(type, data, onAddToCart, primaryColor) {
|
|
614
|
+
function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
|
|
405
615
|
try {
|
|
406
616
|
switch (type) {
|
|
407
617
|
case "pie_chart":
|
|
408
|
-
return /* @__PURE__ */
|
|
618
|
+
return /* @__PURE__ */ React7.createElement(PieChartView, { data, isStreaming });
|
|
409
619
|
case "bar_chart":
|
|
410
|
-
return /* @__PURE__ */
|
|
620
|
+
return /* @__PURE__ */ React7.createElement(BarChartView, { data, primaryColor, isStreaming });
|
|
411
621
|
case "line_chart":
|
|
412
|
-
return /* @__PURE__ */
|
|
622
|
+
return /* @__PURE__ */ React7.createElement(LineChartView, { data, primaryColor, isStreaming });
|
|
413
623
|
case "radar_chart":
|
|
414
|
-
return /* @__PURE__ */
|
|
624
|
+
return /* @__PURE__ */ React7.createElement(RadarChartView, { data, isStreaming });
|
|
415
625
|
case "table":
|
|
416
|
-
return /* @__PURE__ */
|
|
626
|
+
return /* @__PURE__ */ React7.createElement(TableView, { data, isStreaming });
|
|
417
627
|
case "product_carousel":
|
|
418
628
|
case "carousel":
|
|
419
|
-
return /* @__PURE__ */
|
|
629
|
+
return /* @__PURE__ */ React7.createElement(CarouselView, { data, onAddToCart, primaryColor });
|
|
420
630
|
case "text":
|
|
421
631
|
default:
|
|
422
|
-
return /* @__PURE__ */
|
|
632
|
+
return /* @__PURE__ */ React7.createElement(TextView, { data });
|
|
423
633
|
}
|
|
424
634
|
} catch (error) {
|
|
425
635
|
console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
|
|
426
|
-
return /* @__PURE__ */
|
|
636
|
+
return /* @__PURE__ */ React7.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function ChartSkeleton({ type }) {
|
|
640
|
+
if (type === "pie") {
|
|
641
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, /* @__PURE__ */ React7.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }));
|
|
427
642
|
}
|
|
643
|
+
if (type === "table") {
|
|
644
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse space-y-2" }, /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }));
|
|
645
|
+
}
|
|
646
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse flex items-end gap-2 h-32 px-2" }, Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ React7.createElement("div", { key: i, className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10", style: { height: `${40 + i % 3 * 25}%` } })));
|
|
428
647
|
}
|
|
429
|
-
function PieChartView({ data }) {
|
|
648
|
+
function PieChartView({ data, isStreaming }) {
|
|
649
|
+
if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
|
|
430
650
|
if (!Array.isArray(data) || data.length === 0) {
|
|
431
|
-
return /* @__PURE__ */
|
|
651
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
|
|
432
652
|
}
|
|
433
653
|
const chartData = data.map((item) => ({
|
|
434
654
|
name: item.label,
|
|
435
655
|
value: item.value
|
|
436
656
|
}));
|
|
437
|
-
return /* @__PURE__ */
|
|
657
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 280 }, /* @__PURE__ */ React7.createElement(PieChart2, null, /* @__PURE__ */ React7.createElement(
|
|
438
658
|
Pie2,
|
|
439
659
|
{
|
|
440
660
|
data: chartData,
|
|
@@ -446,12 +666,13 @@ function PieChartView({ data }) {
|
|
|
446
666
|
label: false,
|
|
447
667
|
labelLine: false
|
|
448
668
|
},
|
|
449
|
-
chartData.map((_entry, index) => /* @__PURE__ */
|
|
450
|
-
), /* @__PURE__ */
|
|
669
|
+
chartData.map((_entry, index) => /* @__PURE__ */ React7.createElement(Cell2, { key: `cell-${index}`, fill: getColor(index) }))
|
|
670
|
+
), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
|
|
451
671
|
}
|
|
452
|
-
function BarChartView({ data, primaryColor }) {
|
|
672
|
+
function BarChartView({ data, primaryColor, isStreaming }) {
|
|
673
|
+
if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
|
|
453
674
|
if (!Array.isArray(data) || data.length === 0) {
|
|
454
|
-
return /* @__PURE__ */
|
|
675
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
|
|
455
676
|
}
|
|
456
677
|
const chartData = data.map((item) => __spreadValues(__spreadValues({
|
|
457
678
|
category: item.category,
|
|
@@ -459,7 +680,7 @@ function BarChartView({ data, primaryColor }) {
|
|
|
459
680
|
}, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
|
|
460
681
|
const barColor = primaryColor != null ? primaryColor : getColor(0);
|
|
461
682
|
const hasStock = chartData.some((d) => "inStockCount" in d);
|
|
462
|
-
return /* @__PURE__ */
|
|
683
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React7.createElement(BarChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(
|
|
463
684
|
XAxis2,
|
|
464
685
|
{
|
|
465
686
|
dataKey: "category",
|
|
@@ -467,11 +688,12 @@ function BarChartView({ data, primaryColor }) {
|
|
|
467
688
|
tickLine: false,
|
|
468
689
|
axisLine: { stroke: "#cbd5e1" }
|
|
469
690
|
}
|
|
470
|
-
), /* @__PURE__ */
|
|
691
|
+
), /* @__PURE__ */ React7.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
|
|
471
692
|
}
|
|
472
|
-
function LineChartView({ data, primaryColor }) {
|
|
693
|
+
function LineChartView({ data, primaryColor, isStreaming }) {
|
|
694
|
+
if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
|
|
473
695
|
if (!Array.isArray(data) || data.length === 0) {
|
|
474
|
-
return /* @__PURE__ */
|
|
696
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
|
|
475
697
|
}
|
|
476
698
|
const chartData = data.map((point) => {
|
|
477
699
|
var _a;
|
|
@@ -481,7 +703,7 @@ function LineChartView({ data, primaryColor }) {
|
|
|
481
703
|
};
|
|
482
704
|
});
|
|
483
705
|
const lineColor = primaryColor != null ? primaryColor : getColor(0);
|
|
484
|
-
return /* @__PURE__ */
|
|
706
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React7.createElement(LineChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(
|
|
485
707
|
XAxis2,
|
|
486
708
|
{
|
|
487
709
|
dataKey: "label",
|
|
@@ -489,7 +711,7 @@ function LineChartView({ data, primaryColor }) {
|
|
|
489
711
|
tickLine: false,
|
|
490
712
|
axisLine: { stroke: "#cbd5e1" }
|
|
491
713
|
}
|
|
492
|
-
), /* @__PURE__ */
|
|
714
|
+
), /* @__PURE__ */ React7.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(
|
|
493
715
|
Line2,
|
|
494
716
|
{
|
|
495
717
|
type: "monotone",
|
|
@@ -502,14 +724,15 @@ function LineChartView({ data, primaryColor }) {
|
|
|
502
724
|
}
|
|
503
725
|
))));
|
|
504
726
|
}
|
|
505
|
-
function RadarChartView({ data }) {
|
|
727
|
+
function RadarChartView({ data, isStreaming }) {
|
|
728
|
+
if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
|
|
506
729
|
if (!Array.isArray(data) || data.length === 0) {
|
|
507
|
-
return /* @__PURE__ */
|
|
730
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
|
|
508
731
|
}
|
|
509
732
|
const productNames = Array.from(
|
|
510
733
|
new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
|
|
511
734
|
);
|
|
512
|
-
return /* @__PURE__ */
|
|
735
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 300 }, /* @__PURE__ */ React7.createElement(RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ React7.createElement(PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ React7.createElement(PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ React7.createElement(
|
|
513
736
|
Radar,
|
|
514
737
|
{
|
|
515
738
|
key: productName,
|
|
@@ -519,26 +742,27 @@ function RadarChartView({ data }) {
|
|
|
519
742
|
fill: getColor(index),
|
|
520
743
|
fillOpacity: 0.4
|
|
521
744
|
}
|
|
522
|
-
)), /* @__PURE__ */
|
|
745
|
+
)), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }))));
|
|
523
746
|
}
|
|
524
|
-
function TableView({ data }) {
|
|
747
|
+
function TableView({ data, isStreaming }) {
|
|
748
|
+
if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "table" });
|
|
525
749
|
if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
|
|
526
|
-
return /* @__PURE__ */
|
|
750
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
|
|
527
751
|
}
|
|
528
|
-
return /* @__PURE__ */
|
|
752
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, /* @__PURE__ */ React7.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React7.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ React7.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React7.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ React7.createElement(
|
|
529
753
|
"th",
|
|
530
754
|
{
|
|
531
755
|
key: idx,
|
|
532
756
|
className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
|
|
533
757
|
},
|
|
534
758
|
col
|
|
535
|
-
)))), /* @__PURE__ */
|
|
759
|
+
)))), /* @__PURE__ */ React7.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React7.createElement(
|
|
536
760
|
"tr",
|
|
537
761
|
{
|
|
538
762
|
key: rowIdx,
|
|
539
763
|
className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
540
764
|
},
|
|
541
|
-
row.map((cell, cellIdx) => /* @__PURE__ */
|
|
765
|
+
row.map((cell, cellIdx) => /* @__PURE__ */ React7.createElement(
|
|
542
766
|
"td",
|
|
543
767
|
{
|
|
544
768
|
key: cellIdx,
|
|
@@ -546,7 +770,7 @@ function TableView({ data }) {
|
|
|
546
770
|
},
|
|
547
771
|
String(cell)
|
|
548
772
|
))
|
|
549
|
-
))))), /* @__PURE__ */
|
|
773
|
+
))))), /* @__PURE__ */ React7.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ React7.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
|
|
550
774
|
}
|
|
551
775
|
function CarouselView({
|
|
552
776
|
data,
|
|
@@ -554,7 +778,7 @@ function CarouselView({
|
|
|
554
778
|
primaryColor
|
|
555
779
|
}) {
|
|
556
780
|
if (!Array.isArray(data)) {
|
|
557
|
-
return /* @__PURE__ */
|
|
781
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
|
|
558
782
|
}
|
|
559
783
|
const products = data.map((product) => ({
|
|
560
784
|
id: product.id,
|
|
@@ -566,7 +790,7 @@ function CarouselView({
|
|
|
566
790
|
description: product.description,
|
|
567
791
|
inStock: product.inStock
|
|
568
792
|
}));
|
|
569
|
-
return /* @__PURE__ */
|
|
793
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "w-full" }, /* @__PURE__ */ React7.createElement(
|
|
570
794
|
ProductCarousel,
|
|
571
795
|
{
|
|
572
796
|
products,
|
|
@@ -577,10 +801,272 @@ function CarouselView({
|
|
|
577
801
|
}
|
|
578
802
|
function TextView({ data }) {
|
|
579
803
|
const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
|
|
580
|
-
return /* @__PURE__ */
|
|
804
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
|
|
581
805
|
}
|
|
582
806
|
|
|
583
|
-
// src/components/
|
|
807
|
+
// src/components/ObservabilityPanel.tsx
|
|
808
|
+
import React8 from "react";
|
|
809
|
+
import {
|
|
810
|
+
Clock,
|
|
811
|
+
Cpu,
|
|
812
|
+
Zap,
|
|
813
|
+
AlertTriangle,
|
|
814
|
+
FileText,
|
|
815
|
+
Layers,
|
|
816
|
+
ChevronDown as ChevronDown2,
|
|
817
|
+
ChevronRight as ChevronRight2,
|
|
818
|
+
CheckCircle as CheckCircle2,
|
|
819
|
+
AlertCircle as AlertCircle2
|
|
820
|
+
} from "lucide-react";
|
|
821
|
+
function fmtMs(ms) {
|
|
822
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
823
|
+
}
|
|
824
|
+
function fmtTokens(n) {
|
|
825
|
+
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
826
|
+
}
|
|
827
|
+
function ScoreBadge({ score }) {
|
|
828
|
+
const pct = Math.round(score * 100);
|
|
829
|
+
const color = pct <= 20 ? "text-emerald-600 bg-emerald-50 border-emerald-200 dark:text-emerald-400 dark:bg-emerald-500/10 dark:border-emerald-500/20" : pct <= 50 ? "text-amber-600 bg-amber-50 border-amber-200 dark:text-amber-400 dark:bg-amber-500/10 dark:border-amber-500/20" : "text-red-600 bg-red-50 border-red-200 dark:text-red-400 dark:bg-red-500/10 dark:border-red-500/20";
|
|
830
|
+
return /* @__PURE__ */ React8.createElement("span", { className: `inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold border ${color}` }, pct <= 20 ? /* @__PURE__ */ React8.createElement(CheckCircle2, { className: "w-3 h-3" }) : /* @__PURE__ */ React8.createElement(AlertCircle2, { className: "w-3 h-3" }), pct, "% hallucination risk");
|
|
831
|
+
}
|
|
832
|
+
function RetrievalTab({ trace }) {
|
|
833
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/40" }, trace.chunks.length, " chunk", trace.chunks.length !== 1 ? "s" : "", " retrieved", trace.rewrittenQuery && /* @__PURE__ */ React8.createElement("span", { className: "ml-2 italic" }, "(query rewritten: ", /* @__PURE__ */ React8.createElement("em", { className: "text-indigo-500" }, trace.rewrittenQuery), ")")), /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 max-h-56 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => /* @__PURE__ */ React8.createElement(
|
|
834
|
+
"div",
|
|
835
|
+
{
|
|
836
|
+
key: String(chunk.id),
|
|
837
|
+
className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5"
|
|
838
|
+
},
|
|
839
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, "[", i + 1, "] ", String(chunk.id).slice(0, 20)), /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, "score: ", chunk.score.toFixed(3))),
|
|
840
|
+
/* @__PURE__ */ React8.createElement("div", { className: "h-1 rounded-full bg-slate-200 dark:bg-white/10 mb-2 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
|
|
841
|
+
"div",
|
|
842
|
+
{
|
|
843
|
+
className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
|
|
844
|
+
style: { width: `${Math.min(100, chunk.score * 100)}%` }
|
|
845
|
+
}
|
|
846
|
+
)),
|
|
847
|
+
/* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 line-clamp-2 leading-relaxed" }, chunk.content)
|
|
848
|
+
))));
|
|
849
|
+
}
|
|
850
|
+
function LatencyTab({ trace }) {
|
|
851
|
+
const { latency } = trace;
|
|
852
|
+
const bars = [
|
|
853
|
+
{ label: "Embed", ms: latency.embedMs, color: "#6366f1" },
|
|
854
|
+
{ label: "Retrieve", ms: latency.retrieveMs, color: "#10b981" },
|
|
855
|
+
...latency.rerankMs != null ? [{ label: "Rerank", ms: latency.rerankMs, color: "#f59e0b" }] : [],
|
|
856
|
+
{ label: "Generate", ms: latency.generateMs, color: "#8b5cf6" }
|
|
857
|
+
];
|
|
858
|
+
const maxMs = Math.max(...bars.map((b) => b.ms), 1);
|
|
859
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-slate-500 dark:text-white/40" }, "Pipeline stages"), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-semibold text-slate-700 dark:text-white/70" }, "Total: ", fmtMs(latency.totalMs))), /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, bars.map((bar) => /* @__PURE__ */ React8.createElement("div", { key: bar.label, className: "space-y-0.5" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-slate-600 dark:text-white/60" }, bar.label), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, fmtMs(bar.ms))), /* @__PURE__ */ React8.createElement("div", { className: "h-2 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
|
|
860
|
+
"div",
|
|
861
|
+
{
|
|
862
|
+
className: "h-full rounded-full transition-all duration-700",
|
|
863
|
+
style: {
|
|
864
|
+
width: `${bar.ms / maxMs * 100}%`,
|
|
865
|
+
background: bar.color,
|
|
866
|
+
opacity: 0.85
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
))))));
|
|
870
|
+
}
|
|
871
|
+
function TokensTab({ trace }) {
|
|
872
|
+
const { tokens } = trace;
|
|
873
|
+
if (!tokens) {
|
|
874
|
+
return /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Token data not available.");
|
|
875
|
+
}
|
|
876
|
+
const stats = [
|
|
877
|
+
{ label: "Prompt Tokens", value: fmtTokens(tokens.promptTokens), color: "text-indigo-600 dark:text-indigo-400" },
|
|
878
|
+
{ label: "Completion Tokens", value: fmtTokens(tokens.completionTokens), color: "text-emerald-600 dark:text-emerald-400" },
|
|
879
|
+
{ label: "Total Tokens", value: fmtTokens(tokens.totalTokens), color: "text-slate-700 dark:text-white/80" }
|
|
880
|
+
];
|
|
881
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-3 gap-2" }, stats.map((s) => /* @__PURE__ */ React8.createElement("div", { key: s.label, className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5 text-center" }, /* @__PURE__ */ React8.createElement("p", { className: `text-lg font-bold ${s.color}` }, s.value), /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 mt-0.5" }, s.label)))), tokens.estimatedCostUsd != null && /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between rounded-lg border border-emerald-100 dark:border-emerald-500/20 bg-emerald-50 dark:bg-emerald-500/10 px-3 py-2" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-emerald-700 dark:text-emerald-400 font-medium" }, "Estimated Cost"), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-bold text-emerald-700 dark:text-emerald-400" }, "$", tokens.estimatedCostUsd < 1e-4 ? tokens.estimatedCostUsd.toExponential(2) : tokens.estimatedCostUsd.toFixed(5), " USD")), /* @__PURE__ */ React8.createElement("div", { className: "h-2 rounded-full overflow-hidden flex" }, /* @__PURE__ */ React8.createElement(
|
|
882
|
+
"div",
|
|
883
|
+
{
|
|
884
|
+
className: "h-full bg-indigo-500 dark:bg-indigo-400",
|
|
885
|
+
style: { width: `${tokens.promptTokens / tokens.totalTokens * 100}%` },
|
|
886
|
+
title: "Prompt tokens"
|
|
887
|
+
}
|
|
888
|
+
), /* @__PURE__ */ React8.createElement(
|
|
889
|
+
"div",
|
|
890
|
+
{
|
|
891
|
+
className: "h-full bg-emerald-500 dark:bg-emerald-400",
|
|
892
|
+
style: { width: `${tokens.completionTokens / tokens.totalTokens * 100}%` },
|
|
893
|
+
title: "Completion tokens"
|
|
894
|
+
}
|
|
895
|
+
)), /* @__PURE__ */ React8.createElement("div", { className: "flex gap-3 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-indigo-500" }), " Prompt"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " Completion")));
|
|
896
|
+
}
|
|
897
|
+
function HallucinationTab({ trace }) {
|
|
898
|
+
if (trace.hallucinationScore == null) {
|
|
899
|
+
return /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Hallucination scoring not available for this response.");
|
|
900
|
+
}
|
|
901
|
+
const score = trace.hallucinationScore;
|
|
902
|
+
const pct = Math.round(score * 100);
|
|
903
|
+
const barColor = pct <= 20 ? "#10b981" : pct <= 50 ? "#f59e0b" : "#ef4444";
|
|
904
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement(ScoreBadge, { score }), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, pct, "/100")), /* @__PURE__ */ React8.createElement("div", { className: "h-3 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
|
|
905
|
+
"div",
|
|
906
|
+
{
|
|
907
|
+
className: "h-full rounded-full transition-all duration-700",
|
|
908
|
+
style: { width: `${pct}%`, background: barColor }
|
|
909
|
+
}
|
|
910
|
+
)), trace.hallucinationReason && /* @__PURE__ */ React8.createElement("div", { className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5" }, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/40 font-medium mb-0.5" }, "AI self-assessment"), /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-700 dark:text-white/70 leading-relaxed" }, trace.hallucinationReason)), /* @__PURE__ */ React8.createElement("div", { className: "flex gap-2 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " 0\u201320%: Grounded"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-amber-500" }), " 21\u201350%: Uncertain"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-red-500" }), " 51\u2013100%: Risk")));
|
|
911
|
+
}
|
|
912
|
+
function PromptTab({ trace }) {
|
|
913
|
+
const [showSystem, setShowSystem] = React8.useState(false);
|
|
914
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement(
|
|
915
|
+
"button",
|
|
916
|
+
{
|
|
917
|
+
onClick: () => setShowSystem((s) => !s),
|
|
918
|
+
className: "flex items-center gap-1.5 text-[11px] font-semibold text-slate-600 dark:text-white/60 hover:text-slate-900 dark:hover:text-white/80 transition-colors mb-1.5"
|
|
919
|
+
},
|
|
920
|
+
showSystem ? /* @__PURE__ */ React8.createElement(ChevronDown2, { className: "w-3 h-3" }) : /* @__PURE__ */ React8.createElement(ChevronRight2, { className: "w-3 h-3" }),
|
|
921
|
+
"System Prompt"
|
|
922
|
+
), showSystem && /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-600 dark:text-white/60 bg-slate-900 dark:bg-black/30 rounded-lg p-3 overflow-auto max-h-40 whitespace-pre-wrap leading-relaxed" }, trace.systemPrompt || "(empty)")), /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] font-semibold text-slate-600 dark:text-white/60 mb-1.5" }, "User Prompt"), /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-600 dark:text-white/60 bg-slate-900 dark:bg-black/30 rounded-lg p-3 overflow-auto max-h-40 whitespace-pre-wrap leading-relaxed" }, trace.userPrompt)));
|
|
923
|
+
}
|
|
924
|
+
function ChunksTab({ trace }) {
|
|
925
|
+
const [expanded, setExpanded] = React8.useState(/* @__PURE__ */ new Set());
|
|
926
|
+
const toggle = (i) => setExpanded((prev) => {
|
|
927
|
+
const next = new Set(prev);
|
|
928
|
+
if (next.has(i)) {
|
|
929
|
+
next.delete(i);
|
|
930
|
+
} else {
|
|
931
|
+
next.add(i);
|
|
932
|
+
}
|
|
933
|
+
return next;
|
|
934
|
+
});
|
|
935
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 max-h-64 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => {
|
|
936
|
+
const isOpen = expanded.has(i);
|
|
937
|
+
return /* @__PURE__ */ React8.createElement(
|
|
938
|
+
"div",
|
|
939
|
+
{
|
|
940
|
+
key: String(chunk.id),
|
|
941
|
+
className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 overflow-hidden"
|
|
942
|
+
},
|
|
943
|
+
/* @__PURE__ */ React8.createElement(
|
|
944
|
+
"button",
|
|
945
|
+
{
|
|
946
|
+
onClick: () => toggle(i),
|
|
947
|
+
className: "w-full flex items-center justify-between px-3 py-2 text-left hover:bg-slate-100 dark:hover:bg-white/5 transition-colors"
|
|
948
|
+
},
|
|
949
|
+
/* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2" }, isOpen ? /* @__PURE__ */ React8.createElement(ChevronDown2, { className: "w-3 h-3 text-slate-400" }) : /* @__PURE__ */ React8.createElement(ChevronRight2, { className: "w-3 h-3 text-slate-400" }), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-medium text-slate-700 dark:text-white/70" }, "Chunk ", i + 1), /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, String(chunk.id).slice(0, 16))),
|
|
950
|
+
/* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, chunk.score.toFixed(3))
|
|
951
|
+
),
|
|
952
|
+
isOpen && /* @__PURE__ */ React8.createElement("div", { className: "px-3 pb-3 space-y-2 border-t border-slate-100 dark:border-white/5 pt-2" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Content"), /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 leading-relaxed whitespace-pre-wrap" }, chunk.content)), Object.keys(chunk.metadata).length > 0 && /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Metadata"), /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-500 dark:text-white/40 bg-slate-900 dark:bg-black/30 rounded-md p-2 overflow-auto max-h-24" }, JSON.stringify(chunk.metadata, null, 2))))
|
|
953
|
+
);
|
|
954
|
+
}));
|
|
955
|
+
}
|
|
956
|
+
var TABS = [
|
|
957
|
+
{ id: "retrieval", label: "Retrieval", icon: /* @__PURE__ */ React8.createElement(Layers, { className: "w-3 h-3" }) },
|
|
958
|
+
{ id: "latency", label: "Latency", icon: /* @__PURE__ */ React8.createElement(Clock, { className: "w-3 h-3" }) },
|
|
959
|
+
{ id: "tokens", label: "Tokens", icon: /* @__PURE__ */ React8.createElement(Cpu, { className: "w-3 h-3" }) },
|
|
960
|
+
{ id: "hallucination", label: "Grounding", icon: /* @__PURE__ */ React8.createElement(AlertTriangle, { className: "w-3 h-3" }) },
|
|
961
|
+
{ id: "prompt", label: "Prompt", icon: /* @__PURE__ */ React8.createElement(FileText, { className: "w-3 h-3" }) },
|
|
962
|
+
{ id: "chunks", label: "Chunks", icon: /* @__PURE__ */ React8.createElement(Zap, { className: "w-3 h-3" }) }
|
|
963
|
+
];
|
|
964
|
+
function ObservabilityPanel({ trace, primaryColor = "#6366f1" }) {
|
|
965
|
+
const [activeTab, setActiveTab] = React8.useState("retrieval");
|
|
966
|
+
return /* @__PURE__ */ React8.createElement("div", { className: "mt-2 w-full rounded-xl border border-slate-200 dark:border-white/8 bg-white dark:bg-slate-900/60 shadow-sm overflow-hidden text-[12px]" }, /* @__PURE__ */ React8.createElement(
|
|
967
|
+
"div",
|
|
968
|
+
{
|
|
969
|
+
className: "flex items-center gap-2 px-3 py-2 border-b border-slate-100 dark:border-white/8",
|
|
970
|
+
style: { background: `${primaryColor}08` }
|
|
971
|
+
},
|
|
972
|
+
/* @__PURE__ */ React8.createElement(
|
|
973
|
+
"span",
|
|
974
|
+
{
|
|
975
|
+
className: "inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider",
|
|
976
|
+
style: { color: primaryColor }
|
|
977
|
+
},
|
|
978
|
+
/* @__PURE__ */ React8.createElement(Zap, { className: "w-3 h-3" }),
|
|
979
|
+
"Observability Trace"
|
|
980
|
+
),
|
|
981
|
+
/* @__PURE__ */ React8.createElement("span", { className: "text-[10px] text-slate-400 dark:text-white/30 ml-auto font-mono" }, trace.requestId)
|
|
982
|
+
), /* @__PURE__ */ React8.createElement("div", { className: "flex border-b border-slate-100 dark:border-white/8 overflow-x-auto" }, TABS.map((tab) => /* @__PURE__ */ React8.createElement(
|
|
983
|
+
"button",
|
|
984
|
+
{
|
|
985
|
+
key: tab.id,
|
|
986
|
+
onClick: () => setActiveTab(tab.id),
|
|
987
|
+
className: `flex items-center gap-1.5 px-3 py-2 text-[10px] font-semibold whitespace-nowrap transition-colors border-b-2 ${activeTab === tab.id ? "border-b-2 text-indigo-600 dark:text-indigo-400" : "border-transparent text-slate-400 dark:text-white/30 hover:text-slate-600 dark:hover:text-white/60"}`,
|
|
988
|
+
style: activeTab === tab.id ? { borderBottomColor: primaryColor, color: primaryColor } : {}
|
|
989
|
+
},
|
|
990
|
+
tab.icon,
|
|
991
|
+
tab.label
|
|
992
|
+
))), /* @__PURE__ */ React8.createElement("div", { className: "p-3" }, activeTab === "retrieval" && /* @__PURE__ */ React8.createElement(RetrievalTab, { trace }), activeTab === "latency" && /* @__PURE__ */ React8.createElement(LatencyTab, { trace }), activeTab === "tokens" && /* @__PURE__ */ React8.createElement(TokensTab, { trace }), activeTab === "hallucination" && /* @__PURE__ */ React8.createElement(HallucinationTab, { trace }), activeTab === "prompt" && /* @__PURE__ */ React8.createElement(PromptTab, { trace }), activeTab === "chunks" && /* @__PURE__ */ React8.createElement(ChunksTab, { trace })));
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// src/components/HourglassLoader.tsx
|
|
996
|
+
import React9 from "react";
|
|
997
|
+
var HourglassLoader = ({
|
|
998
|
+
size = 16,
|
|
999
|
+
primaryColor = "#10b981",
|
|
1000
|
+
glow = false
|
|
1001
|
+
}) => {
|
|
1002
|
+
return /* @__PURE__ */ React9.createElement(
|
|
1003
|
+
"svg",
|
|
1004
|
+
{
|
|
1005
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1006
|
+
viewBox: "0 0 100 100",
|
|
1007
|
+
preserveAspectRatio: "xMidYMid",
|
|
1008
|
+
style: {
|
|
1009
|
+
width: size,
|
|
1010
|
+
height: size,
|
|
1011
|
+
background: "transparent",
|
|
1012
|
+
filter: glow ? `drop-shadow(0 0 4px ${primaryColor})` : "none"
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
/* @__PURE__ */ React9.createElement("g", null, /* @__PURE__ */ React9.createElement(
|
|
1016
|
+
"animateTransform",
|
|
1017
|
+
{
|
|
1018
|
+
attributeName: "transform",
|
|
1019
|
+
type: "rotate",
|
|
1020
|
+
calcMode: "spline",
|
|
1021
|
+
values: "0 50 50; 0 50 50; 180 50 50",
|
|
1022
|
+
keyTimes: "0; 0.8; 1",
|
|
1023
|
+
keySplines: "0 0 1 1; 0.4 0 0.2 1",
|
|
1024
|
+
dur: "2s",
|
|
1025
|
+
repeatCount: "indefinite"
|
|
1026
|
+
}
|
|
1027
|
+
), /* @__PURE__ */ React9.createElement(
|
|
1028
|
+
"path",
|
|
1029
|
+
{
|
|
1030
|
+
d: "M 25 15 L 75 15 L 75 20 L 55 50 L 75 80 L 75 85 L 25 85 L 25 80 L 45 50 L 25 20 Z",
|
|
1031
|
+
fill: "none",
|
|
1032
|
+
stroke: primaryColor,
|
|
1033
|
+
strokeWidth: "5",
|
|
1034
|
+
strokeLinejoin: "round",
|
|
1035
|
+
strokeLinecap: "round"
|
|
1036
|
+
}
|
|
1037
|
+
), /* @__PURE__ */ React9.createElement("path", { fill: primaryColor }, /* @__PURE__ */ React9.createElement(
|
|
1038
|
+
"animate",
|
|
1039
|
+
{
|
|
1040
|
+
attributeName: "d",
|
|
1041
|
+
values: "M 30 20 L 70 20 L 50 50 L 50 50 Z; M 50 50 L 50 50 L 50 50 L 50 50 Z; M 50 50 L 50 50 L 50 50 L 50 50 Z",
|
|
1042
|
+
keyTimes: "0; 0.8; 1",
|
|
1043
|
+
dur: "2s",
|
|
1044
|
+
repeatCount: "indefinite"
|
|
1045
|
+
}
|
|
1046
|
+
)), /* @__PURE__ */ React9.createElement("path", { fill: primaryColor }, /* @__PURE__ */ React9.createElement(
|
|
1047
|
+
"animate",
|
|
1048
|
+
{
|
|
1049
|
+
attributeName: "d",
|
|
1050
|
+
values: "M 30 80 L 70 80 L 70 80 L 30 80 Z; M 30 80 L 70 80 L 50 50 L 50 50 Z; M 30 80 L 70 80 L 50 50 L 50 50 Z",
|
|
1051
|
+
keyTimes: "0; 0.8; 1",
|
|
1052
|
+
dur: "2s",
|
|
1053
|
+
repeatCount: "indefinite"
|
|
1054
|
+
}
|
|
1055
|
+
)), /* @__PURE__ */ React9.createElement("line", { x1: "50", y1: "50", x2: "50", y2: "80", stroke: primaryColor, strokeWidth: "3" }, /* @__PURE__ */ React9.createElement(
|
|
1056
|
+
"animate",
|
|
1057
|
+
{
|
|
1058
|
+
attributeName: "opacity",
|
|
1059
|
+
values: "0; 1; 1; 0; 0",
|
|
1060
|
+
keyTimes: "0; 0.1; 0.7; 0.8; 1",
|
|
1061
|
+
dur: "2s",
|
|
1062
|
+
repeatCount: "indefinite"
|
|
1063
|
+
}
|
|
1064
|
+
)))
|
|
1065
|
+
);
|
|
1066
|
+
};
|
|
1067
|
+
var HourglassLoader_default = HourglassLoader;
|
|
1068
|
+
|
|
1069
|
+
// src/utils/ProductExtractor.ts
|
|
584
1070
|
function sanitizeJson(raw) {
|
|
585
1071
|
let s = raw.trim();
|
|
586
1072
|
s = s.replace(
|
|
@@ -597,36 +1083,34 @@ function sanitizeJson(raw) {
|
|
|
597
1083
|
s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
|
|
598
1084
|
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
599
1085
|
s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
if (ch === '"') {
|
|
614
|
-
inString = !inString;
|
|
615
|
-
continue;
|
|
616
|
-
}
|
|
617
|
-
if (inString) continue;
|
|
618
|
-
if (ch === "{" || ch === "[") {
|
|
619
|
-
stack.push(ch);
|
|
620
|
-
} else if (ch === "}") {
|
|
621
|
-
if (stack[stack.length - 1] === "{") stack.pop();
|
|
622
|
-
} else if (ch === "]") {
|
|
623
|
-
if (stack[stack.length - 1] === "[") stack.pop();
|
|
624
|
-
}
|
|
1086
|
+
const stack = [];
|
|
1087
|
+
let inString = false;
|
|
1088
|
+
let escape = false;
|
|
1089
|
+
for (const ch of s) {
|
|
1090
|
+
if (escape) {
|
|
1091
|
+
escape = false;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
if (ch === "\\" && inString) {
|
|
1095
|
+
escape = true;
|
|
1096
|
+
continue;
|
|
625
1097
|
}
|
|
626
|
-
if (
|
|
627
|
-
|
|
628
|
-
|
|
1098
|
+
if (ch === '"') {
|
|
1099
|
+
inString = !inString;
|
|
1100
|
+
continue;
|
|
629
1101
|
}
|
|
1102
|
+
if (inString) continue;
|
|
1103
|
+
if (ch === "{" || ch === "[") {
|
|
1104
|
+
stack.push(ch);
|
|
1105
|
+
} else if (ch === "}") {
|
|
1106
|
+
if (stack[stack.length - 1] === "{") stack.pop();
|
|
1107
|
+
} else if (ch === "]") {
|
|
1108
|
+
if (stack[stack.length - 1] === "[") stack.pop();
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
if (inString) s += '"';
|
|
1112
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
1113
|
+
s += stack[i] === "{" ? "}" : "]";
|
|
630
1114
|
}
|
|
631
1115
|
const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
|
|
632
1116
|
if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
|
|
@@ -727,10 +1211,6 @@ function extractStructuredPayload(raw) {
|
|
|
727
1211
|
text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
|
|
728
1212
|
};
|
|
729
1213
|
}
|
|
730
|
-
function isLikelyUiOnlyMessage(text) {
|
|
731
|
-
const trimmed = text.trim();
|
|
732
|
-
return trimmed.length === 0 || trimmed.length < 30 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary|availability|inventory status|details)\s*:?\s*$/i.test(trimmed);
|
|
733
|
-
}
|
|
734
1214
|
function resolveImage(data) {
|
|
735
1215
|
const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
|
|
736
1216
|
for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
|
|
@@ -740,92 +1220,140 @@ function resolveImage(data) {
|
|
|
740
1220
|
}
|
|
741
1221
|
return void 0;
|
|
742
1222
|
}
|
|
743
|
-
function
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
const foundKey = metaKeys.find((k) => searchKeys.includes(k.toLowerCase()));
|
|
747
|
-
if (foundKey) return meta[foundKey];
|
|
748
|
-
const partialKey = metaKeys.find((k) => {
|
|
749
|
-
const kLow = k.toLowerCase();
|
|
750
|
-
return searchKeys.some((sk) => kLow.includes(sk) || sk.includes(kLow));
|
|
751
|
-
});
|
|
752
|
-
return partialKey ? meta[partialKey] : void 0;
|
|
753
|
-
}
|
|
754
|
-
function normaliseChild(children) {
|
|
755
|
-
if (typeof children === "object" && children !== null && !Array.isArray(children) && !React6.isValidElement(children)) {
|
|
756
|
-
return JSON.stringify(children);
|
|
757
|
-
}
|
|
758
|
-
return children;
|
|
759
|
-
}
|
|
760
|
-
function normaliseFieldName(value) {
|
|
761
|
-
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
762
|
-
}
|
|
763
|
-
function formatColumnLabel(value) {
|
|
764
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1223
|
+
function isLikelyUiOnlyMessage(text) {
|
|
1224
|
+
const trimmed = text.trim();
|
|
1225
|
+
return trimmed.length === 0 || trimmed.length < 30 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary|availability|inventory status|details)\s*:?\s*$/i.test(trimmed);
|
|
765
1226
|
}
|
|
766
|
-
function
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
)
|
|
1227
|
+
function extractProductsFromSources(sources, isUser) {
|
|
1228
|
+
if (isUser || !sources) return [];
|
|
1229
|
+
return sources.filter((s) => {
|
|
1230
|
+
var _a;
|
|
1231
|
+
const m = (_a = s.metadata) != null ? _a : {};
|
|
1232
|
+
const keys = Object.keys(m).map((k) => k.toLowerCase());
|
|
1233
|
+
const hasProductKey = keys.some(
|
|
1234
|
+
(k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
|
|
774
1235
|
);
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1236
|
+
const hasPricePattern = /\$\s*\d+/.test(s.content);
|
|
1237
|
+
return hasProductKey || hasPricePattern || m.type === "product";
|
|
1238
|
+
}).map((s) => {
|
|
1239
|
+
var _a, _b;
|
|
1240
|
+
const m = (_a = s.metadata) != null ? _a : {};
|
|
1241
|
+
const name = resolveMetadataValue(m, "name");
|
|
1242
|
+
const brand = resolveMetadataValue(m, "brand");
|
|
1243
|
+
const price = resolveMetadataValue(m, "price");
|
|
1244
|
+
const description = resolveMetadataValue(m, "description");
|
|
1245
|
+
console.log("\u{1F680} ~ extractProductsFromSources ~ description:", s);
|
|
1246
|
+
return {
|
|
1247
|
+
id: s.id,
|
|
1248
|
+
name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
|
|
1249
|
+
brand,
|
|
1250
|
+
price,
|
|
1251
|
+
image: resolveImage(m),
|
|
1252
|
+
link: resolveMetadataValue(m, "link"),
|
|
1253
|
+
description: description ? description : ""
|
|
1254
|
+
//description ?? s.content,
|
|
782
1255
|
};
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
function extractProductsFromContent(content, payloadText, payloadCandidates, productsFromSources) {
|
|
1259
|
+
var _a, _b;
|
|
1260
|
+
const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
|
|
1261
|
+
const products = [];
|
|
1262
|
+
let cleanContent = payloadText;
|
|
1263
|
+
for (const candidate of payloadCandidates) {
|
|
1264
|
+
try {
|
|
1265
|
+
const data = JSON.parse(sanitizeJson(candidate));
|
|
1266
|
+
const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
|
|
1267
|
+
if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
|
|
1268
|
+
products.push(
|
|
1269
|
+
...itemSet.map((item) => {
|
|
1270
|
+
var _a2;
|
|
1271
|
+
return __spreadProps(__spreadValues({}, item), {
|
|
1272
|
+
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1273
|
+
});
|
|
1274
|
+
})
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
} catch (e) {
|
|
788
1278
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
1279
|
+
}
|
|
1280
|
+
if (cleanContent.includes('"type": "products"') || cleanContent.includes('"type":"products"')) {
|
|
1281
|
+
for (const match of Array.from(cleanContent.matchAll(jsonRegex))) {
|
|
1282
|
+
try {
|
|
1283
|
+
const data = JSON.parse(sanitizeJson(match[1]));
|
|
1284
|
+
if (data.type === "products" && Array.isArray(data.items)) {
|
|
1285
|
+
products.push(
|
|
1286
|
+
...data.items.map((item) => {
|
|
1287
|
+
var _a2;
|
|
1288
|
+
return __spreadProps(__spreadValues({}, item), {
|
|
1289
|
+
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1290
|
+
});
|
|
1291
|
+
})
|
|
1292
|
+
);
|
|
1293
|
+
cleanContent = cleanContent.replace(match[0], "");
|
|
1294
|
+
}
|
|
1295
|
+
} catch (e) {
|
|
1296
|
+
}
|
|
795
1297
|
}
|
|
796
|
-
return rowKeys.map((key) => ({ label: formatColumnLabel(key), accessor: key }));
|
|
797
|
-
}, [config]);
|
|
798
|
-
if (!config.data.length || !columns.length) {
|
|
799
|
-
return /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
|
|
800
1298
|
}
|
|
801
|
-
const
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
1299
|
+
const bulletRegex = /^[ \t]*(?:[•*-]|\d+\.)\s+(?:\*\*)?([^\n\-\$–—\(]+?)(?:\*\*)?(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gim;
|
|
1300
|
+
const matches = Array.from(cleanContent.matchAll(bulletRegex));
|
|
1301
|
+
if (matches.length >= 2) {
|
|
1302
|
+
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1303
|
+
for (const match of matches) {
|
|
1304
|
+
let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
|
|
1305
|
+
name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
|
|
1306
|
+
name = name.replace(/^\*\*|\*\*$/g, "").replace(/^\*|\*$/g, "").trim();
|
|
1307
|
+
let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
|
|
1308
|
+
price = price.replace(/[,\s]+$/, "").trim();
|
|
1309
|
+
if (name && price) {
|
|
1310
|
+
const newProduct = {
|
|
1311
|
+
id: `text-prod-${name}-${price}`,
|
|
1312
|
+
name,
|
|
1313
|
+
price: `$${price}`
|
|
1314
|
+
};
|
|
1315
|
+
const normName = normalize(name);
|
|
1316
|
+
const sourceMatch = productsFromSources.find((s) => {
|
|
1317
|
+
const sn = normalize(s.name);
|
|
1318
|
+
return sn.includes(normName) || normName.includes(sn);
|
|
1319
|
+
});
|
|
1320
|
+
if (sourceMatch) {
|
|
1321
|
+
newProduct.image = sourceMatch.image;
|
|
1322
|
+
newProduct.brand = sourceMatch.brand;
|
|
1323
|
+
newProduct.link = sourceMatch.link;
|
|
1324
|
+
newProduct.description = sourceMatch.description;
|
|
1325
|
+
}
|
|
1326
|
+
products.push(newProduct);
|
|
1327
|
+
cleanContent = cleanContent.replace(match[0], `
|
|
1328
|
+
**${name}** \u2014 $${price}`);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
cleanContent = cleanContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
1332
|
+
}
|
|
1333
|
+
return { productsFromContent: products, cleanContent: cleanContent.trim() };
|
|
828
1334
|
}
|
|
1335
|
+
function deduplicateProducts(productsFromSources, productsFromContent, messageContent) {
|
|
1336
|
+
if (productsFromContent.length > 0) return productsFromContent;
|
|
1337
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1338
|
+
const contentLower = messageContent.toLowerCase();
|
|
1339
|
+
return productsFromSources.filter((p) => {
|
|
1340
|
+
var _a;
|
|
1341
|
+
const id = String((_a = p.id) != null ? _a : p.name);
|
|
1342
|
+
if (seen.has(id)) return false;
|
|
1343
|
+
const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
|
|
1344
|
+
if (mentioned) {
|
|
1345
|
+
seen.add(id);
|
|
1346
|
+
return true;
|
|
1347
|
+
}
|
|
1348
|
+
return false;
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// src/components/MarkdownComponents.tsx
|
|
1353
|
+
import React11 from "react";
|
|
1354
|
+
|
|
1355
|
+
// src/components/UIDispatcher.tsx
|
|
1356
|
+
import React10 from "react";
|
|
829
1357
|
function normalizeTabularRows(rows, columns) {
|
|
830
1358
|
if (!Array.isArray(rows)) return [];
|
|
831
1359
|
const SYNONYMS = {
|
|
@@ -893,8 +1421,7 @@ function resolveStructuredRows(config) {
|
|
|
893
1421
|
}
|
|
894
1422
|
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
895
1423
|
var _a;
|
|
896
|
-
const result =
|
|
897
|
-
if (isStreaming) return { loading: true };
|
|
1424
|
+
const result = React10.useMemo(() => {
|
|
898
1425
|
try {
|
|
899
1426
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
900
1427
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
@@ -923,10 +1450,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
923
1450
|
} else if (viewStr.includes("chart") || viewStr.includes("pie") || viewStr.includes("bar") || viewStr.includes("line") || hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
|
|
924
1451
|
config2.view = "chart";
|
|
925
1452
|
config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
|
|
926
|
-
} else if (viewStr.includes("table")) {
|
|
1453
|
+
} else if (viewStr.includes("table") || config2.type === "table") {
|
|
927
1454
|
config2.view = "table";
|
|
928
1455
|
} else {
|
|
929
|
-
config2.view = "
|
|
1456
|
+
config2.view = "none";
|
|
930
1457
|
}
|
|
931
1458
|
}
|
|
932
1459
|
const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
|
|
@@ -934,22 +1461,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
934
1461
|
});
|
|
935
1462
|
return { config: normalizedConfig };
|
|
936
1463
|
} catch (err) {
|
|
1464
|
+
if (isStreaming) return { loading: true };
|
|
937
1465
|
return { error: String(err) };
|
|
938
1466
|
}
|
|
939
1467
|
}, [rawContent, isStreaming]);
|
|
940
1468
|
if ("loading" in result) {
|
|
941
|
-
return /* @__PURE__ */
|
|
1469
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse" }, /* @__PURE__ */ React10.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React10.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
|
|
942
1470
|
}
|
|
943
1471
|
if ("error" in result) {
|
|
944
|
-
return /* @__PURE__ */
|
|
1472
|
+
return /* @__PURE__ */ React10.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ React10.createElement("code", null, rawContent));
|
|
945
1473
|
}
|
|
946
1474
|
const { config } = result;
|
|
947
1475
|
const isCompact = viewportSize === "compact";
|
|
948
1476
|
const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
|
|
949
1477
|
const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
|
|
1478
|
+
console.log("\u{1F680} ~ UIDispatcher ~ description:", config.description);
|
|
1479
|
+
console.log("\u{1F680} ~ UIDispatcher ~ hasDescription:", hasDescription);
|
|
950
1480
|
switch (config.view) {
|
|
951
1481
|
case "chart":
|
|
952
|
-
return /* @__PURE__ */
|
|
1482
|
+
return /* @__PURE__ */ React10.createElement("div", { className: `${isCompact ? "my-4 p-3" : "my-6 p-4"} bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden` }, config.title && /* @__PURE__ */ React10.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(
|
|
953
1483
|
DynamicChart,
|
|
954
1484
|
{
|
|
955
1485
|
config: {
|
|
@@ -961,9 +1491,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
961
1491
|
},
|
|
962
1492
|
primaryColor,
|
|
963
1493
|
accentColor,
|
|
964
|
-
viewportSize
|
|
1494
|
+
viewportSize,
|
|
1495
|
+
isStreaming
|
|
965
1496
|
}
|
|
966
|
-
), hasInsights && /* @__PURE__ */
|
|
1497
|
+
), hasInsights && /* @__PURE__ */ React10.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ React10.createElement(
|
|
967
1498
|
"span",
|
|
968
1499
|
{
|
|
969
1500
|
key: `insight-${i}`,
|
|
@@ -972,26 +1503,151 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
972
1503
|
String(insight)
|
|
973
1504
|
))));
|
|
974
1505
|
case "carousel":
|
|
975
|
-
return /* @__PURE__ */
|
|
1506
|
+
return /* @__PURE__ */ React10.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React10.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(ProductCarousel, { products: config.data.map((item) => {
|
|
976
1507
|
var _a2;
|
|
977
1508
|
return __spreadProps(__spreadValues({}, item), {
|
|
978
1509
|
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
979
1510
|
});
|
|
980
1511
|
}), primaryColor, onAddToCart }));
|
|
981
1512
|
case "table":
|
|
982
|
-
return /* @__PURE__ */
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1513
|
+
return /* @__PURE__ */ React10.createElement("div", { className: `${isCompact ? "my-3 p-3 max-h-[320px]" : "my-4 p-4 max-h-[400px]"} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto` }, config.title && /* @__PURE__ */ React10.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(
|
|
1514
|
+
VisualizationRenderer,
|
|
1515
|
+
{
|
|
1516
|
+
data: {
|
|
1517
|
+
type: "table",
|
|
1518
|
+
title: config.title || "",
|
|
1519
|
+
description: config.description,
|
|
1520
|
+
data: {
|
|
1521
|
+
columns: config.columns || Object.keys(config.data[0] || {}),
|
|
1522
|
+
rows: config.data.map((row) => Object.values(row))
|
|
1523
|
+
}
|
|
1524
|
+
},
|
|
1525
|
+
primaryColor,
|
|
1526
|
+
isStreaming
|
|
1527
|
+
}
|
|
1528
|
+
));
|
|
991
1529
|
default:
|
|
992
1530
|
return null;
|
|
993
1531
|
}
|
|
994
1532
|
}
|
|
1533
|
+
|
|
1534
|
+
// src/components/MarkdownComponents.tsx
|
|
1535
|
+
function createMarkdownComponents({
|
|
1536
|
+
primaryColor,
|
|
1537
|
+
accentColor,
|
|
1538
|
+
isStreaming,
|
|
1539
|
+
onAddToCart,
|
|
1540
|
+
viewportSize
|
|
1541
|
+
}) {
|
|
1542
|
+
return {
|
|
1543
|
+
table: (_a) => {
|
|
1544
|
+
var props = __objRest(_a, []);
|
|
1545
|
+
if (isStreaming) {
|
|
1546
|
+
return /* @__PURE__ */ React11.createElement("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none" }, /* @__PURE__ */ React11.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React11.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1547
|
+
}
|
|
1548
|
+
return /* @__PURE__ */ React11.createElement("div", { className: "not-prose overflow-hidden my-5 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm bg-white dark:bg-slate-900/50" }, /* @__PURE__ */ React11.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React11.createElement(
|
|
1549
|
+
"table",
|
|
1550
|
+
__spreadValues({
|
|
1551
|
+
className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
|
|
1552
|
+
}, props)
|
|
1553
|
+
)));
|
|
1554
|
+
},
|
|
1555
|
+
thead: (_b) => {
|
|
1556
|
+
var props = __objRest(_b, []);
|
|
1557
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1558
|
+
"thead",
|
|
1559
|
+
__spreadValues({
|
|
1560
|
+
className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
|
|
1561
|
+
}, props)
|
|
1562
|
+
);
|
|
1563
|
+
},
|
|
1564
|
+
tbody: (_c) => {
|
|
1565
|
+
var props = __objRest(_c, []);
|
|
1566
|
+
return /* @__PURE__ */ React11.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
|
|
1567
|
+
},
|
|
1568
|
+
tr: (_d) => {
|
|
1569
|
+
var props = __objRest(_d, []);
|
|
1570
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1571
|
+
"tr",
|
|
1572
|
+
__spreadValues({
|
|
1573
|
+
className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
|
|
1574
|
+
}, props)
|
|
1575
|
+
);
|
|
1576
|
+
},
|
|
1577
|
+
th: (_e) => {
|
|
1578
|
+
var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
|
|
1579
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1580
|
+
"th",
|
|
1581
|
+
__spreadValues({
|
|
1582
|
+
className: "!table-cell px-4 py-3 font-bold text-slate-700 dark:text-white/90 whitespace-nowrap text-[11px] uppercase tracking-wider text-left"
|
|
1583
|
+
}, props),
|
|
1584
|
+
normaliseChild(children)
|
|
1585
|
+
);
|
|
1586
|
+
},
|
|
1587
|
+
td: (_g) => {
|
|
1588
|
+
var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
|
|
1589
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1590
|
+
"td",
|
|
1591
|
+
__spreadValues({
|
|
1592
|
+
className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
|
|
1593
|
+
}, props),
|
|
1594
|
+
normaliseChild(children)
|
|
1595
|
+
);
|
|
1596
|
+
},
|
|
1597
|
+
code(_i) {
|
|
1598
|
+
var _j = _i, {
|
|
1599
|
+
inline,
|
|
1600
|
+
className,
|
|
1601
|
+
children
|
|
1602
|
+
} = _j, props = __objRest(_j, [
|
|
1603
|
+
"inline",
|
|
1604
|
+
"className",
|
|
1605
|
+
"children"
|
|
1606
|
+
]);
|
|
1607
|
+
var _a;
|
|
1608
|
+
const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
|
|
1609
|
+
if (!inline && (lang === "ui" || lang === "chart" || lang === "json")) {
|
|
1610
|
+
const content = String(children != null ? children : "").trim();
|
|
1611
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1612
|
+
UIDispatcher,
|
|
1613
|
+
{
|
|
1614
|
+
rawContent: content,
|
|
1615
|
+
primaryColor,
|
|
1616
|
+
accentColor,
|
|
1617
|
+
isStreaming,
|
|
1618
|
+
onAddToCart,
|
|
1619
|
+
viewportSize
|
|
1620
|
+
}
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
if (!inline) {
|
|
1624
|
+
const content = String(children != null ? children : "").trim();
|
|
1625
|
+
if (content.startsWith("{") && content.endsWith("}")) {
|
|
1626
|
+
return /* @__PURE__ */ React11.createElement(
|
|
1627
|
+
UIDispatcher,
|
|
1628
|
+
{
|
|
1629
|
+
rawContent: content,
|
|
1630
|
+
primaryColor,
|
|
1631
|
+
accentColor,
|
|
1632
|
+
isStreaming,
|
|
1633
|
+
onAddToCart,
|
|
1634
|
+
viewportSize
|
|
1635
|
+
}
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
return /* @__PURE__ */ React11.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
function normaliseChild(children) {
|
|
1644
|
+
if (typeof children === "object" && children !== null && !Array.isArray(children) && !React11.isValidElement(children)) {
|
|
1645
|
+
return JSON.stringify(children);
|
|
1646
|
+
}
|
|
1647
|
+
return children;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/components/MessageBubble.tsx
|
|
995
1651
|
function MessageBubble({
|
|
996
1652
|
message,
|
|
997
1653
|
sources,
|
|
@@ -1004,12 +1660,13 @@ function MessageBubble({
|
|
|
1004
1660
|
const isUser = message.role === "user";
|
|
1005
1661
|
const isCompact = viewportSize === "compact";
|
|
1006
1662
|
const isMedium = viewportSize === "medium";
|
|
1007
|
-
const [showSources, setShowSources] =
|
|
1008
|
-
const
|
|
1663
|
+
const [showSources, setShowSources] = React12.useState(false);
|
|
1664
|
+
const [showTrace, setShowTrace] = React12.useState(false);
|
|
1665
|
+
const structuredContent = React12.useMemo(
|
|
1009
1666
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
1010
1667
|
[isUser, message.content]
|
|
1011
1668
|
);
|
|
1012
|
-
const hasRichUI =
|
|
1669
|
+
const hasRichUI = React12.useMemo(() => {
|
|
1013
1670
|
if (message.uiTransformation) {
|
|
1014
1671
|
const type = message.uiTransformation.type;
|
|
1015
1672
|
if (type && !["text", "table"].includes(type)) return true;
|
|
@@ -1020,131 +1677,28 @@ function MessageBubble({
|
|
|
1020
1677
|
return false;
|
|
1021
1678
|
}, [message.uiTransformation, structuredContent.payload]);
|
|
1022
1679
|
const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
|
|
1023
|
-
const productsFromSources =
|
|
1024
|
-
|
|
1025
|
-
return sources.filter((s) => {
|
|
1026
|
-
var _a;
|
|
1027
|
-
const m = (_a = s.metadata) != null ? _a : {};
|
|
1028
|
-
const keys = Object.keys(m).map((k) => k.toLowerCase());
|
|
1029
|
-
const hasProductKey = keys.some(
|
|
1030
|
-
(k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
|
|
1031
|
-
);
|
|
1032
|
-
const hasPricePattern = /\$\s*\d+/.test(s.content);
|
|
1033
|
-
return hasProductKey || hasPricePattern || m.type === "product";
|
|
1034
|
-
}).map((s) => {
|
|
1035
|
-
var _a, _b;
|
|
1036
|
-
const m = (_a = s.metadata) != null ? _a : {};
|
|
1037
|
-
const name = getMetadataValue(m, ["name", "product", "title", "label", "item"]);
|
|
1038
|
-
const brand = getMetadataValue(m, ["brand", "manufacturer", "vendor", "make"]);
|
|
1039
|
-
const price = getMetadataValue(m, ["price", "cost", "amount", "msrp", "rate"]);
|
|
1040
|
-
return {
|
|
1041
|
-
id: s.id,
|
|
1042
|
-
name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
|
|
1043
|
-
brand,
|
|
1044
|
-
price,
|
|
1045
|
-
image: resolveImage(m),
|
|
1046
|
-
link: getMetadataValue(m, ["link", "url", "product_url"]),
|
|
1047
|
-
description: s.content
|
|
1048
|
-
};
|
|
1049
|
-
});
|
|
1680
|
+
const productsFromSources = React12.useMemo(() => {
|
|
1681
|
+
return extractProductsFromSources(sources, isUser);
|
|
1050
1682
|
}, [sources, isUser]);
|
|
1051
|
-
const { productsFromContent, cleanContent } =
|
|
1052
|
-
var _a, _b;
|
|
1683
|
+
const { productsFromContent, cleanContent } = React12.useMemo(() => {
|
|
1053
1684
|
if (isUser) {
|
|
1054
1685
|
return { productsFromContent: [], cleanContent: message.content };
|
|
1055
1686
|
}
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
...itemSet.map((item) => {
|
|
1067
|
-
var _a2;
|
|
1068
|
-
return __spreadProps(__spreadValues({}, item), {
|
|
1069
|
-
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1070
|
-
});
|
|
1071
|
-
})
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
} catch (e) {
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
|
|
1078
|
-
for (const match of Array.from(content.matchAll(jsonRegex))) {
|
|
1079
|
-
try {
|
|
1080
|
-
const data = JSON.parse(sanitizeJson(match[1]));
|
|
1081
|
-
if (data.type === "products" && Array.isArray(data.items)) {
|
|
1082
|
-
products.push(
|
|
1083
|
-
...data.items.map((item) => {
|
|
1084
|
-
var _a2;
|
|
1085
|
-
return __spreadProps(__spreadValues({}, item), {
|
|
1086
|
-
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1087
|
-
});
|
|
1088
|
-
})
|
|
1089
|
-
);
|
|
1090
|
-
content = content.replace(match[0], "");
|
|
1091
|
-
}
|
|
1092
|
-
} catch (e) {
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
const bulletRegex = /(?:[•*-]\s*)?([^•\n\-\$*–—\(]+?)(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gi;
|
|
1097
|
-
const matches = Array.from(content.matchAll(bulletRegex));
|
|
1098
|
-
if (matches.length >= 2) {
|
|
1099
|
-
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1100
|
-
for (const match of matches) {
|
|
1101
|
-
let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
|
|
1102
|
-
name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
|
|
1103
|
-
let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
|
|
1104
|
-
price = price.replace(/[,\s]+$/, "").trim();
|
|
1105
|
-
if (name && price) {
|
|
1106
|
-
const newProduct = {
|
|
1107
|
-
id: `text-prod-${name}-${price}`,
|
|
1108
|
-
name,
|
|
1109
|
-
price: `$${price}`
|
|
1110
|
-
};
|
|
1111
|
-
const normName = normalize(name);
|
|
1112
|
-
const sourceMatch = productsFromSources.find((s) => {
|
|
1113
|
-
const sn = normalize(s.name);
|
|
1114
|
-
return sn.includes(normName) || normName.includes(sn);
|
|
1115
|
-
});
|
|
1116
|
-
if (sourceMatch) {
|
|
1117
|
-
newProduct.image = sourceMatch.image;
|
|
1118
|
-
newProduct.brand = sourceMatch.brand;
|
|
1119
|
-
newProduct.link = sourceMatch.link;
|
|
1120
|
-
newProduct.description = sourceMatch.description;
|
|
1121
|
-
}
|
|
1122
|
-
products.push(newProduct);
|
|
1123
|
-
content = content.replace(match[0], `
|
|
1124
|
-
**${name}** \u2014 $${price}`);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
content = content.replace(/\n{3,}/g, "\n\n").trim();
|
|
1128
|
-
}
|
|
1129
|
-
return { productsFromContent: products, cleanContent: content.trim() };
|
|
1687
|
+
const payloadCandidates = [
|
|
1688
|
+
structuredContent.payload,
|
|
1689
|
+
...Array.from(structuredContent.text.matchAll(/```(?:json|ui)?\s*([\s\S]*?)\s*```/g)).map((m) => m[1])
|
|
1690
|
+
].filter(Boolean);
|
|
1691
|
+
return extractProductsFromContent(
|
|
1692
|
+
message.content,
|
|
1693
|
+
structuredContent.text,
|
|
1694
|
+
payloadCandidates,
|
|
1695
|
+
productsFromSources
|
|
1696
|
+
);
|
|
1130
1697
|
}, [message.content, isUser, structuredContent, productsFromSources]);
|
|
1131
|
-
const allProducts =
|
|
1132
|
-
|
|
1133
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1134
|
-
const contentLower = message.content.toLowerCase();
|
|
1135
|
-
return productsFromSources.filter((p) => {
|
|
1136
|
-
var _a;
|
|
1137
|
-
const id = String((_a = p.id) != null ? _a : p.name);
|
|
1138
|
-
if (seen.has(id)) return false;
|
|
1139
|
-
const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
|
|
1140
|
-
if (mentioned) {
|
|
1141
|
-
seen.add(id);
|
|
1142
|
-
return true;
|
|
1143
|
-
}
|
|
1144
|
-
return false;
|
|
1145
|
-
});
|
|
1698
|
+
const allProducts = React12.useMemo(() => {
|
|
1699
|
+
return deduplicateProducts(productsFromSources, productsFromContent, message.content);
|
|
1146
1700
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
1147
|
-
const processedMarkdown =
|
|
1701
|
+
const processedMarkdown = React12.useMemo(() => {
|
|
1148
1702
|
let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
|
|
1149
1703
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
1150
1704
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
@@ -1165,156 +1719,26 @@ ${match.trim()}
|
|
|
1165
1719
|
if (hasStructuredUiBlock) {
|
|
1166
1720
|
raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
|
|
1167
1721
|
}
|
|
1168
|
-
if (isStreaming) {
|
|
1169
|
-
raw = raw.replace(/((?:^|\n)\|.*)+/g, (match) => {
|
|
1170
|
-
return `
|
|
1171
|
-
|
|
1172
|
-
\`\`\`table-loading
|
|
1173
|
-
${match.trim()}
|
|
1174
|
-
\`\`\`
|
|
1175
|
-
|
|
1176
|
-
`;
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
1722
|
return raw;
|
|
1180
1723
|
}, [cleanContent, message.content, isStreaming, structuredContent]);
|
|
1181
|
-
const markdownComponents =
|
|
1182
|
-
() => ({
|
|
1183
|
-
// Wrap in not-prose so Tailwind Typography resets don't clobber our styles.
|
|
1184
|
-
// prose applies display:block and padding:0 to table/th/td — not-prose opts out.
|
|
1185
|
-
table: (_a) => {
|
|
1186
|
-
var props = __objRest(_a, []);
|
|
1187
|
-
if (isStreaming) {
|
|
1188
|
-
return /* @__PURE__ */ React6.createElement("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none" }, /* @__PURE__ */ React6.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1189
|
-
}
|
|
1190
|
-
return /* @__PURE__ */ React6.createElement("div", { className: "not-prose overflow-hidden my-5 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm bg-white dark:bg-slate-900/50" }, /* @__PURE__ */ React6.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React6.createElement(
|
|
1191
|
-
"table",
|
|
1192
|
-
__spreadValues({
|
|
1193
|
-
className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
|
|
1194
|
-
}, props)
|
|
1195
|
-
)));
|
|
1196
|
-
},
|
|
1197
|
-
thead: (_b) => {
|
|
1198
|
-
var props = __objRest(_b, []);
|
|
1199
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1200
|
-
"thead",
|
|
1201
|
-
__spreadValues({
|
|
1202
|
-
className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
|
|
1203
|
-
}, props)
|
|
1204
|
-
);
|
|
1205
|
-
},
|
|
1206
|
-
tbody: (_c) => {
|
|
1207
|
-
var props = __objRest(_c, []);
|
|
1208
|
-
return /* @__PURE__ */ React6.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
|
|
1209
|
-
},
|
|
1210
|
-
tr: (_d) => {
|
|
1211
|
-
var props = __objRest(_d, []);
|
|
1212
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1213
|
-
"tr",
|
|
1214
|
-
__spreadValues({
|
|
1215
|
-
className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
|
|
1216
|
-
}, props)
|
|
1217
|
-
);
|
|
1218
|
-
},
|
|
1219
|
-
th: (_e) => {
|
|
1220
|
-
var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
|
|
1221
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1222
|
-
"th",
|
|
1223
|
-
__spreadValues({
|
|
1224
|
-
className: "!table-cell px-4 py-3 font-bold text-slate-700 dark:text-white/90 whitespace-nowrap text-[11px] uppercase tracking-wider text-left"
|
|
1225
|
-
}, props),
|
|
1226
|
-
normaliseChild(children)
|
|
1227
|
-
);
|
|
1228
|
-
},
|
|
1229
|
-
td: (_g) => {
|
|
1230
|
-
var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
|
|
1231
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1232
|
-
"td",
|
|
1233
|
-
__spreadValues({
|
|
1234
|
-
className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
|
|
1235
|
-
}, props),
|
|
1236
|
-
normaliseChild(children)
|
|
1237
|
-
);
|
|
1238
|
-
},
|
|
1239
|
-
code(_i) {
|
|
1240
|
-
var _j = _i, {
|
|
1241
|
-
inline,
|
|
1242
|
-
className,
|
|
1243
|
-
children
|
|
1244
|
-
} = _j, props = __objRest(_j, [
|
|
1245
|
-
"inline",
|
|
1246
|
-
"className",
|
|
1247
|
-
"children"
|
|
1248
|
-
]);
|
|
1249
|
-
var _a;
|
|
1250
|
-
const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
|
|
1251
|
-
if (!inline && (lang === "ui" || lang === "chart")) {
|
|
1252
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1253
|
-
UIDispatcher,
|
|
1254
|
-
{
|
|
1255
|
-
rawContent: String(children != null ? children : "").trim(),
|
|
1256
|
-
primaryColor,
|
|
1257
|
-
accentColor,
|
|
1258
|
-
isStreaming,
|
|
1259
|
-
onAddToCart,
|
|
1260
|
-
viewportSize
|
|
1261
|
-
}
|
|
1262
|
-
);
|
|
1263
|
-
}
|
|
1264
|
-
if (!inline && lang === "json") {
|
|
1265
|
-
const content = String(children != null ? children : "").trim();
|
|
1266
|
-
if (looksLikeStructuredPayload(content)) {
|
|
1267
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1268
|
-
UIDispatcher,
|
|
1269
|
-
{
|
|
1270
|
-
rawContent: content,
|
|
1271
|
-
primaryColor,
|
|
1272
|
-
accentColor,
|
|
1273
|
-
isStreaming,
|
|
1274
|
-
onAddToCart,
|
|
1275
|
-
viewportSize
|
|
1276
|
-
}
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
if (!inline) {
|
|
1281
|
-
const content = String(children != null ? children : "").trim();
|
|
1282
|
-
if (looksLikeStructuredPayload(content)) {
|
|
1283
|
-
return /* @__PURE__ */ React6.createElement(
|
|
1284
|
-
UIDispatcher,
|
|
1285
|
-
{
|
|
1286
|
-
rawContent: content,
|
|
1287
|
-
primaryColor,
|
|
1288
|
-
accentColor,
|
|
1289
|
-
isStreaming,
|
|
1290
|
-
onAddToCart,
|
|
1291
|
-
viewportSize
|
|
1292
|
-
}
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
if (!inline && lang === "table-loading") {
|
|
1297
|
-
return /* @__PURE__ */ React6.createElement("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none" }, /* @__PURE__ */ React6.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1298
|
-
}
|
|
1299
|
-
return /* @__PURE__ */ React6.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
1300
|
-
}
|
|
1301
|
-
}),
|
|
1724
|
+
const markdownComponents = React12.useMemo(
|
|
1725
|
+
() => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
|
|
1302
1726
|
[primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
|
|
1303
1727
|
);
|
|
1304
|
-
return /* @__PURE__ */
|
|
1728
|
+
return /* @__PURE__ */ React12.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React12.createElement(
|
|
1305
1729
|
"div",
|
|
1306
1730
|
{
|
|
1307
1731
|
className: `flex-shrink-0 ${isCompact ? "w-7 h-7" : "w-8 h-8"} rounded-full flex items-center justify-center shadow-lg ${isUser ? "text-white" : "bg-slate-100 dark:bg-white/10 text-slate-700 dark:text-white/80"}`,
|
|
1308
1732
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
1309
1733
|
},
|
|
1310
|
-
isUser ? /* @__PURE__ */
|
|
1311
|
-
), /* @__PURE__ */
|
|
1734
|
+
isUser ? /* @__PURE__ */ React12.createElement(User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ React12.createElement(Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
|
|
1735
|
+
), /* @__PURE__ */ React12.createElement("div", { className: `flex flex-col gap-1 min-w-0 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ React12.createElement(
|
|
1312
1736
|
"div",
|
|
1313
1737
|
{
|
|
1314
1738
|
className: `relative ${isCompact ? "px-3 py-2.5 text-[13px]" : "px-4 py-3 text-sm"} rounded-2xl leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
|
|
1315
1739
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
1316
1740
|
},
|
|
1317
|
-
isStreaming && !message.content ? /* @__PURE__ */
|
|
1741
|
+
isStreaming && !message.content ? /* @__PURE__ */ React12.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React12.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, !isUser && structuredContent.payload && /* @__PURE__ */ React12.createElement(
|
|
1318
1742
|
UIDispatcher,
|
|
1319
1743
|
{
|
|
1320
1744
|
rawContent: structuredContent.payload,
|
|
@@ -1324,7 +1748,15 @@ ${match.trim()}
|
|
|
1324
1748
|
onAddToCart,
|
|
1325
1749
|
viewportSize
|
|
1326
1750
|
}
|
|
1327
|
-
), !shouldRenderStructuredOnly && /* @__PURE__ */
|
|
1751
|
+
), !shouldRenderStructuredOnly && /* @__PURE__ */ React12.createElement(ReactMarkdown, { components: markdownComponents }, processedMarkdown), isStreaming && /* @__PURE__ */ React12.createElement("span", { className: "inline-flex items-center gap-1.5 ml-2 text-emerald-500 animate-pulse align-middle text-xs font-medium" }, /* @__PURE__ */ React12.createElement(
|
|
1752
|
+
HourglassLoader_default,
|
|
1753
|
+
{
|
|
1754
|
+
size: 18,
|
|
1755
|
+
primaryColor,
|
|
1756
|
+
accentColor,
|
|
1757
|
+
glow: false
|
|
1758
|
+
}
|
|
1759
|
+
), "Thinking..."))
|
|
1328
1760
|
), (() => {
|
|
1329
1761
|
var _a, _b;
|
|
1330
1762
|
if (isUser || structuredContent.payload || !message.uiTransformation) return null;
|
|
@@ -1332,95 +1764,50 @@ ${match.trim()}
|
|
|
1332
1764
|
const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
|
|
1333
1765
|
const shouldShow = ui.type === "table" && allProducts.length === 0 || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase());
|
|
1334
1766
|
if (!shouldShow) return null;
|
|
1335
|
-
return /* @__PURE__ */
|
|
1767
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ React12.createElement(
|
|
1336
1768
|
VisualizationRenderer,
|
|
1337
1769
|
{
|
|
1338
1770
|
data: ui,
|
|
1339
1771
|
primaryColor,
|
|
1340
1772
|
onAddToCart,
|
|
1341
|
-
className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4"
|
|
1773
|
+
className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4",
|
|
1774
|
+
isStreaming
|
|
1342
1775
|
}
|
|
1343
1776
|
));
|
|
1344
|
-
})(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */
|
|
1777
|
+
})(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React12.createElement(
|
|
1345
1778
|
ProductCarousel,
|
|
1346
1779
|
{
|
|
1347
1780
|
products: allProducts,
|
|
1348
1781
|
primaryColor,
|
|
1349
1782
|
onAddToCart
|
|
1350
1783
|
}
|
|
1351
|
-
)), !isUser &&
|
|
1784
|
+
)), !isUser && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-4 mt-1 w-full flex-wrap" }, sources && sources.length > 0 && /* @__PURE__ */ React12.createElement(
|
|
1352
1785
|
"button",
|
|
1353
1786
|
{
|
|
1354
1787
|
onClick: () => setShowSources((s) => !s),
|
|
1355
|
-
className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1
|
|
1788
|
+
className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1"
|
|
1356
1789
|
},
|
|
1357
|
-
showSources ? /* @__PURE__ */
|
|
1790
|
+
showSources ? /* @__PURE__ */ React12.createElement(ChevronDown3, { className: "w-3 h-3" }) : /* @__PURE__ */ React12.createElement(ChevronRight3, { className: "w-3 h-3" }),
|
|
1358
1791
|
sources.length,
|
|
1359
1792
|
" source",
|
|
1360
1793
|
sources.length !== 1 ? "s" : "",
|
|
1361
1794
|
" used"
|
|
1362
|
-
),
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
ui: {
|
|
1372
|
-
title: "AI Assistant",
|
|
1373
|
-
subtitle: "Powered by RAG",
|
|
1374
|
-
primaryColor: "#6366f1",
|
|
1375
|
-
accentColor: "#8b5cf6",
|
|
1376
|
-
logoUrl: "",
|
|
1377
|
-
placeholder: "Ask me anything\u2026",
|
|
1378
|
-
showSources: true,
|
|
1379
|
-
welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
1380
|
-
showWidget: true,
|
|
1381
|
-
poweredBy: "RAG",
|
|
1382
|
-
visualStyle: "glass",
|
|
1383
|
-
borderRadius: "xl",
|
|
1384
|
-
allowUpload: true,
|
|
1385
|
-
allowResize: true,
|
|
1386
|
-
enableVoiceInput: true
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
var BORDER_RADIUS_MAP = {
|
|
1390
|
-
none: "rounded-none",
|
|
1391
|
-
sm: "rounded-sm",
|
|
1392
|
-
md: "rounded-md",
|
|
1393
|
-
lg: "rounded-lg",
|
|
1394
|
-
xl: "rounded-xl",
|
|
1395
|
-
full: "rounded-3xl"
|
|
1396
|
-
};
|
|
1397
|
-
var CHAT_SUGGESTIONS = [
|
|
1398
|
-
"What can you help me with?",
|
|
1399
|
-
"Summarise the key topics",
|
|
1400
|
-
"Show me an example"
|
|
1401
|
-
];
|
|
1402
|
-
|
|
1403
|
-
// src/components/ConfigProvider.tsx
|
|
1404
|
-
var ConfigContext = createContext(DEFAULT_CONFIG);
|
|
1405
|
-
function ConfigProvider({
|
|
1406
|
-
config,
|
|
1407
|
-
children
|
|
1408
|
-
}) {
|
|
1409
|
-
const merged = {
|
|
1410
|
-
projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
|
|
1411
|
-
ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
|
|
1412
|
-
};
|
|
1413
|
-
return /* @__PURE__ */ React7.createElement(ConfigContext.Provider, { value: merged }, children);
|
|
1414
|
-
}
|
|
1415
|
-
function useConfig() {
|
|
1416
|
-
return useContext(ConfigContext);
|
|
1795
|
+
), message.trace && /* @__PURE__ */ React12.createElement(
|
|
1796
|
+
"button",
|
|
1797
|
+
{
|
|
1798
|
+
onClick: () => setShowTrace((t) => !t),
|
|
1799
|
+
className: "text-[11px] text-emerald-500 hover:text-emerald-400 transition-colors flex items-center gap-1"
|
|
1800
|
+
},
|
|
1801
|
+
/* @__PURE__ */ React12.createElement(Activity, { className: "w-3 h-3" }),
|
|
1802
|
+
showTrace ? "Hide Trace" : "Trace"
|
|
1803
|
+
)), /* @__PURE__ */ React12.createElement("div", { className: "w-full flex flex-col gap-2 min-w-0" }, showSources && sources && sources.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "mt-1 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ React12.createElement(SourceCard, { key: src.id, source: src, index: i }))), showTrace && message.trace && /* @__PURE__ */ React12.createElement(ObservabilityPanel, { trace: message.trace, primaryColor }))));
|
|
1417
1804
|
}
|
|
1418
1805
|
|
|
1419
1806
|
// src/hooks/useRagChat.ts
|
|
1420
|
-
import { useState as
|
|
1807
|
+
import { useState as useState4, useCallback, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
1421
1808
|
|
|
1422
1809
|
// src/hooks/useStoredMessages.ts
|
|
1423
|
-
import * as
|
|
1810
|
+
import * as React13 from "react";
|
|
1424
1811
|
function readStoredMessages(storageKey) {
|
|
1425
1812
|
if (typeof window === "undefined") {
|
|
1426
1813
|
return [];
|
|
@@ -1437,8 +1824,8 @@ function readStoredMessages(storageKey) {
|
|
|
1437
1824
|
}
|
|
1438
1825
|
}
|
|
1439
1826
|
function useStoredMessages(storageKey, persist) {
|
|
1440
|
-
const [messages, setMessages] =
|
|
1441
|
-
|
|
1827
|
+
const [messages, setMessages] = React13.useState(() => persist ? readStoredMessages(storageKey) : []);
|
|
1828
|
+
React13.useEffect(() => {
|
|
1442
1829
|
if (!persist || typeof window === "undefined") {
|
|
1443
1830
|
return;
|
|
1444
1831
|
}
|
|
@@ -1478,10 +1865,10 @@ function useRagChat(projectId, options = {}) {
|
|
|
1478
1865
|
} = options;
|
|
1479
1866
|
const storageKey = `rag_chat_${projectId}`;
|
|
1480
1867
|
const [messages, setMessages] = useStoredMessages(storageKey, persist);
|
|
1481
|
-
const [isLoading, setIsLoading] =
|
|
1482
|
-
const [error, setError] =
|
|
1483
|
-
const lastInputRef =
|
|
1484
|
-
const messagesRef =
|
|
1868
|
+
const [isLoading, setIsLoading] = useState4(false);
|
|
1869
|
+
const [error, setError] = useState4(null);
|
|
1870
|
+
const lastInputRef = useRef3("");
|
|
1871
|
+
const messagesRef = useRef3(messages);
|
|
1485
1872
|
useEffect3(() => {
|
|
1486
1873
|
messagesRef.current = messages;
|
|
1487
1874
|
}, [messages]);
|
|
@@ -1525,6 +1912,7 @@ function useRagChat(projectId, options = {}) {
|
|
|
1525
1912
|
let assistantContent = "";
|
|
1526
1913
|
let sources = [];
|
|
1527
1914
|
let uiTransformation = null;
|
|
1915
|
+
let trace;
|
|
1528
1916
|
let buffer = "";
|
|
1529
1917
|
const assistantMessageId = `assistant_${Date.now()}`;
|
|
1530
1918
|
setMessages((prev) => [
|
|
@@ -1551,6 +1939,8 @@ function useRagChat(projectId, options = {}) {
|
|
|
1551
1939
|
sources = (_a = frame.sources) != null ? _a : [];
|
|
1552
1940
|
} else if (frame.type === "ui_transformation") {
|
|
1553
1941
|
uiTransformation = frame.data;
|
|
1942
|
+
} else if (frame.type === "observability") {
|
|
1943
|
+
trace = frame.data;
|
|
1554
1944
|
} else if (frame.type === "error") {
|
|
1555
1945
|
setError(frame.error || "Stream error");
|
|
1556
1946
|
}
|
|
@@ -1569,17 +1959,22 @@ function useRagChat(projectId, options = {}) {
|
|
|
1569
1959
|
for (const frame of parseSseChunk(buffer)) {
|
|
1570
1960
|
if (frame.type === "text" && frame.text) assistantContent += frame.text;
|
|
1571
1961
|
else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
|
|
1962
|
+
else if (frame.type === "observability") trace = frame.data;
|
|
1572
1963
|
}
|
|
1573
1964
|
}
|
|
1574
|
-
const
|
|
1965
|
+
const finalMsg = {
|
|
1575
1966
|
id: assistantMessageId,
|
|
1576
1967
|
role: "assistant",
|
|
1577
1968
|
content: assistantContent,
|
|
1578
1969
|
sources: sources.length > 0 ? sources : void 0,
|
|
1579
|
-
uiTransformation: uiTransformation
|
|
1970
|
+
uiTransformation: uiTransformation != null ? uiTransformation : void 0,
|
|
1971
|
+
trace,
|
|
1580
1972
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1581
1973
|
};
|
|
1582
|
-
|
|
1974
|
+
setMessages(
|
|
1975
|
+
(prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMsg : msg)
|
|
1976
|
+
);
|
|
1977
|
+
onReply == null ? void 0 : onReply(finalMsg);
|
|
1583
1978
|
} catch (err) {
|
|
1584
1979
|
const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
|
|
1585
1980
|
setError(msg);
|
|
@@ -1629,19 +2024,20 @@ function ChatWindow({
|
|
|
1629
2024
|
}) {
|
|
1630
2025
|
var _a;
|
|
1631
2026
|
const { ui, projectId } = useConfig();
|
|
1632
|
-
const [input, setInput] =
|
|
1633
|
-
const [mounted, setMounted] =
|
|
1634
|
-
const [viewportSize, setViewportSize] =
|
|
1635
|
-
const windowRef =
|
|
1636
|
-
const bottomRef =
|
|
1637
|
-
const inputRef =
|
|
2027
|
+
const [input, setInput] = useState5("");
|
|
2028
|
+
const [mounted, setMounted] = useState5(false);
|
|
2029
|
+
const [viewportSize, setViewportSize] = useState5("large");
|
|
2030
|
+
const windowRef = useRef4(null);
|
|
2031
|
+
const bottomRef = useRef4(null);
|
|
2032
|
+
const inputRef = useRef4(null);
|
|
1638
2033
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
1639
2034
|
namespace: projectId
|
|
1640
2035
|
});
|
|
1641
|
-
const [suggestions, setSuggestions] =
|
|
1642
|
-
const [isSuggesting, setIsSuggesting] =
|
|
1643
|
-
const [isListening, setIsListening] =
|
|
1644
|
-
const
|
|
2036
|
+
const [suggestions, setSuggestions] = useState5([]);
|
|
2037
|
+
const [isSuggesting, setIsSuggesting] = useState5(false);
|
|
2038
|
+
const [isListening, setIsListening] = useState5(false);
|
|
2039
|
+
const [isUploadModalOpen, setIsUploadModalOpen] = useState5(false);
|
|
2040
|
+
const recognitionRef = useRef4(null);
|
|
1645
2041
|
useEffect4(() => {
|
|
1646
2042
|
if (typeof window !== "undefined") {
|
|
1647
2043
|
const win = window;
|
|
@@ -1767,84 +2163,84 @@ function ChatWindow({
|
|
|
1767
2163
|
}, 800);
|
|
1768
2164
|
return () => clearTimeout(timer);
|
|
1769
2165
|
}, [input, projectId]);
|
|
1770
|
-
return /* @__PURE__ */
|
|
2166
|
+
return /* @__PURE__ */ React14.createElement(
|
|
1771
2167
|
"div",
|
|
1772
2168
|
{
|
|
1773
2169
|
ref: windowRef,
|
|
1774
2170
|
className: `relative flex flex-col border border-slate-200 dark:border-white/10 shadow-2xl transition-all duration-300 ${currentRadius} ${isGlass ? "bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl" : "bg-white dark:bg-[#0f0f1a]"} ${className}`,
|
|
1775
2171
|
style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
|
|
1776
2172
|
},
|
|
1777
|
-
onResizeStart && /* @__PURE__ */
|
|
2173
|
+
onResizeStart && /* @__PURE__ */ React14.createElement(
|
|
1778
2174
|
"div",
|
|
1779
2175
|
{
|
|
1780
2176
|
onMouseDown: onResizeStart,
|
|
1781
2177
|
className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
|
|
1782
2178
|
title: "Drag to resize"
|
|
1783
2179
|
},
|
|
1784
|
-
/* @__PURE__ */
|
|
2180
|
+
/* @__PURE__ */ React14.createElement("div", { className: "w-2.5 h-2.5 border-t-2 border-l-2 border-slate-300 dark:border-white/20 group-hover:border-slate-500 dark:group-hover:border-white/50 transition-colors rounded-tl-[2px]" })
|
|
1785
2181
|
),
|
|
1786
|
-
/* @__PURE__ */
|
|
2182
|
+
/* @__PURE__ */ React14.createElement(
|
|
1787
2183
|
"div",
|
|
1788
2184
|
{
|
|
1789
2185
|
className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
|
|
1790
2186
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
|
|
1791
2187
|
},
|
|
1792
|
-
/* @__PURE__ */
|
|
2188
|
+
/* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
|
|
1793
2189
|
// eslint-disable-next-line @next/next/no-img-element
|
|
1794
|
-
/* @__PURE__ */
|
|
1795
|
-
) : /* @__PURE__ */
|
|
2190
|
+
/* @__PURE__ */ React14.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
|
|
2191
|
+
) : /* @__PURE__ */ React14.createElement(
|
|
1796
2192
|
"div",
|
|
1797
2193
|
{
|
|
1798
2194
|
className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
|
|
1799
2195
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
1800
2196
|
},
|
|
1801
|
-
/* @__PURE__ */
|
|
1802
|
-
), /* @__PURE__ */
|
|
1803
|
-
/* @__PURE__ */
|
|
2197
|
+
/* @__PURE__ */ React14.createElement(Bot2, { className: "w-5 h-5 text-white" })
|
|
2198
|
+
), /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight" }, ui.title), ui.poweredBy && /* @__PURE__ */ React14.createElement("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight" }, `Powered by ${ui.poweredBy}`))),
|
|
2199
|
+
/* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React14.createElement("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2" }, /* @__PURE__ */ React14.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }), "Online"), /* @__PURE__ */ React14.createElement("div", { className: "flex items-center bg-slate-100/50 dark:bg-white/5 rounded-lg p-0.5 border border-slate-200/50 dark:border-white/5" }, mounted && messages.length > 0 && /* @__PURE__ */ React14.createElement(
|
|
1804
2200
|
"button",
|
|
1805
2201
|
{
|
|
1806
2202
|
onClick: clearHistory,
|
|
1807
2203
|
title: "Clear conversation",
|
|
1808
2204
|
className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-rose-500 dark:text-white/40 dark:hover:text-rose-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
|
|
1809
2205
|
},
|
|
1810
|
-
/* @__PURE__ */
|
|
1811
|
-
), isResized && onResetResize && /* @__PURE__ */
|
|
2206
|
+
/* @__PURE__ */ React14.createElement(Trash2, { className: "w-3.5 h-3.5" })
|
|
2207
|
+
), isResized && onResetResize && /* @__PURE__ */ React14.createElement(
|
|
1812
2208
|
"button",
|
|
1813
2209
|
{
|
|
1814
2210
|
onClick: onResetResize,
|
|
1815
2211
|
title: "Reset to default size",
|
|
1816
2212
|
className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-blue-500 dark:text-white/40 dark:hover:text-blue-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
|
|
1817
2213
|
},
|
|
1818
|
-
/* @__PURE__ */
|
|
1819
|
-
), onMaximize && /* @__PURE__ */
|
|
2214
|
+
/* @__PURE__ */ React14.createElement(RotateCcw, { className: "w-3.5 h-3.5" })
|
|
2215
|
+
), onMaximize && /* @__PURE__ */ React14.createElement(
|
|
1820
2216
|
"button",
|
|
1821
2217
|
{
|
|
1822
2218
|
onClick: onMaximize,
|
|
1823
2219
|
title: isMaximized ? "Minimize" : "Maximize",
|
|
1824
2220
|
className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-blue-500 dark:text-white/40 dark:hover:text-blue-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
|
|
1825
2221
|
},
|
|
1826
|
-
isMaximized ? /* @__PURE__ */
|
|
1827
|
-
), showClose && onClose && /* @__PURE__ */
|
|
2222
|
+
isMaximized ? /* @__PURE__ */ React14.createElement(Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ React14.createElement(Maximize2, { className: "w-3.5 h-3.5" })
|
|
2223
|
+
), showClose && onClose && /* @__PURE__ */ React14.createElement(
|
|
1828
2224
|
"button",
|
|
1829
2225
|
{
|
|
1830
2226
|
onClick: onClose,
|
|
1831
2227
|
title: "Close chat",
|
|
1832
2228
|
className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-slate-600 dark:text-white/40 dark:hover:text-white/70 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
|
|
1833
2229
|
},
|
|
1834
|
-
/* @__PURE__ */
|
|
2230
|
+
/* @__PURE__ */ React14.createElement(X2, { className: "w-4 h-4" })
|
|
1835
2231
|
)))
|
|
1836
2232
|
),
|
|
1837
|
-
/* @__PURE__ */
|
|
2233
|
+
/* @__PURE__ */ React14.createElement("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5 scrollbar-thin scrollbar-thumb-slate-200 dark:scrollbar-thumb-white/10 scrollbar-track-transparent min-h-0 bg-slate-50 dark:bg-transparent" }, !mounted || isEmpty ? (
|
|
1838
2234
|
/* Welcome state */
|
|
1839
|
-
/* @__PURE__ */
|
|
2235
|
+
/* @__PURE__ */ React14.createElement("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12" }, /* @__PURE__ */ React14.createElement(
|
|
1840
2236
|
"div",
|
|
1841
2237
|
{
|
|
1842
2238
|
className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
|
|
1843
2239
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
1844
2240
|
},
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
), /* @__PURE__ */
|
|
1847
|
-
(suggestion) => /* @__PURE__ */
|
|
2241
|
+
/* @__PURE__ */ React14.createElement(Sparkles, { className: "w-8 h-8 text-white" })
|
|
2242
|
+
), /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed" }, ui.welcomeMessage), /* @__PURE__ */ React14.createElement("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2" }, "Ask a question to get started")), /* @__PURE__ */ React14.createElement("div", { className: "flex flex-wrap gap-2 justify-center mt-2" }, CHAT_SUGGESTIONS.map(
|
|
2243
|
+
(suggestion) => /* @__PURE__ */ React14.createElement(
|
|
1848
2244
|
"button",
|
|
1849
2245
|
{
|
|
1850
2246
|
key: suggestion,
|
|
@@ -1858,7 +2254,7 @@ function ChatWindow({
|
|
|
1858
2254
|
suggestion
|
|
1859
2255
|
)
|
|
1860
2256
|
)))
|
|
1861
|
-
) : messages.map((msg, index) => /* @__PURE__ */
|
|
2257
|
+
) : messages.map((msg, index) => /* @__PURE__ */ React14.createElement(
|
|
1862
2258
|
MessageBubble,
|
|
1863
2259
|
{
|
|
1864
2260
|
key: msg.id,
|
|
@@ -1870,7 +2266,7 @@ function ChatWindow({
|
|
|
1870
2266
|
onAddToCart,
|
|
1871
2267
|
viewportSize
|
|
1872
2268
|
}
|
|
1873
|
-
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */
|
|
2269
|
+
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ React14.createElement(
|
|
1874
2270
|
MessageBubble,
|
|
1875
2271
|
{
|
|
1876
2272
|
message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
@@ -1880,8 +2276,22 @@ function ChatWindow({
|
|
|
1880
2276
|
onAddToCart,
|
|
1881
2277
|
viewportSize
|
|
1882
2278
|
}
|
|
1883
|
-
), error && /* @__PURE__ */
|
|
1884
|
-
/* @__PURE__ */
|
|
2279
|
+
), error && /* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3" }, /* @__PURE__ */ React14.createElement(TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ React14.createElement("span", null, error)), /* @__PURE__ */ React14.createElement("div", { ref: bottomRef })),
|
|
2280
|
+
isUploadModalOpen && /* @__PURE__ */ React14.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm rounded-inherit" }, /* @__PURE__ */ React14.createElement("div", { className: "relative w-full max-w-md bg-white dark:bg-[#0f0f1a] rounded-2xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React14.createElement(
|
|
2281
|
+
"button",
|
|
2282
|
+
{
|
|
2283
|
+
onClick: () => setIsUploadModalOpen(false),
|
|
2284
|
+
className: "absolute top-4 right-4 p-1.5 rounded-lg text-slate-400 hover:text-slate-600 hover:bg-slate-100 dark:hover:text-white/80 dark:hover:bg-white/10 transition-colors z-10"
|
|
2285
|
+
},
|
|
2286
|
+
/* @__PURE__ */ React14.createElement(X2, { className: "w-5 h-5" })
|
|
2287
|
+
), /* @__PURE__ */ React14.createElement(
|
|
2288
|
+
DocumentUpload,
|
|
2289
|
+
{
|
|
2290
|
+
namespace: projectId,
|
|
2291
|
+
onUploadComplete: () => setIsUploadModalOpen(false)
|
|
2292
|
+
}
|
|
2293
|
+
))),
|
|
2294
|
+
/* @__PURE__ */ React14.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ React14.createElement("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300" }, suggestions.map((suggestion) => /* @__PURE__ */ React14.createElement(
|
|
1885
2295
|
"button",
|
|
1886
2296
|
{
|
|
1887
2297
|
key: suggestion,
|
|
@@ -1893,9 +2303,9 @@ function ChatWindow({
|
|
|
1893
2303
|
},
|
|
1894
2304
|
className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/60 hover:text-slate-900 dark:hover:text-white/90 hover:border-slate-300 dark:hover:border-white/20 transition-all shadow-sm cursor-pointer group"
|
|
1895
2305
|
},
|
|
1896
|
-
/* @__PURE__ */
|
|
2306
|
+
/* @__PURE__ */ React14.createElement(Sparkles, { className: "w-3 h-3 text-amber-400" }),
|
|
1897
2307
|
suggestion
|
|
1898
|
-
)), isSuggesting && suggestions.length === 0 && /* @__PURE__ */
|
|
2308
|
+
)), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium text-slate-400 dark:text-white/30 animate-pulse" }, /* @__PURE__ */ React14.createElement(Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ React14.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ React14.createElement(
|
|
1899
2309
|
"textarea",
|
|
1900
2310
|
{
|
|
1901
2311
|
ref: inputRef,
|
|
@@ -1914,7 +2324,7 @@ function ChatWindow({
|
|
|
1914
2324
|
className: "flex-1 bg-transparent text-slate-900 dark:text-white/90 placeholder-slate-400 dark:placeholder-white/30 text-sm resize-none outline-none py-1.5 px-2 max-h-32 leading-relaxed disabled:opacity-50",
|
|
1915
2325
|
style: { scrollbarWidth: "none" }
|
|
1916
2326
|
}
|
|
1917
|
-
), ui.enableVoiceInput !== false && /* @__PURE__ */
|
|
2327
|
+
), ui.enableVoiceInput !== false && /* @__PURE__ */ React14.createElement(
|
|
1918
2328
|
"button",
|
|
1919
2329
|
{
|
|
1920
2330
|
type: "button",
|
|
@@ -1923,8 +2333,17 @@ function ChatWindow({
|
|
|
1923
2333
|
className: `flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100 ${isListening ? "bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20" : "text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10"}`,
|
|
1924
2334
|
title: isListening ? "Stop listening" : "Start voice input"
|
|
1925
2335
|
},
|
|
1926
|
-
isListening ? /* @__PURE__ */
|
|
1927
|
-
), /* @__PURE__ */
|
|
2336
|
+
isListening ? /* @__PURE__ */ React14.createElement(MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ React14.createElement(Mic, { className: "w-4 h-4" })
|
|
2337
|
+
), ui.allowUpload !== false && /* @__PURE__ */ React14.createElement(
|
|
2338
|
+
"button",
|
|
2339
|
+
{
|
|
2340
|
+
type: "button",
|
|
2341
|
+
onClick: () => setIsUploadModalOpen(true),
|
|
2342
|
+
className: "flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10",
|
|
2343
|
+
title: "Upload Document"
|
|
2344
|
+
},
|
|
2345
|
+
/* @__PURE__ */ React14.createElement(Paperclip, { className: "w-4 h-4" })
|
|
2346
|
+
), /* @__PURE__ */ React14.createElement(
|
|
1928
2347
|
"button",
|
|
1929
2348
|
{
|
|
1930
2349
|
onClick: sendMessage,
|
|
@@ -1935,8 +2354,8 @@ function ChatWindow({
|
|
|
1935
2354
|
// slate-400/20 for light mode disabled
|
|
1936
2355
|
}
|
|
1937
2356
|
},
|
|
1938
|
-
/* @__PURE__ */
|
|
1939
|
-
)), /* @__PURE__ */
|
|
2357
|
+
/* @__PURE__ */ React14.createElement(ArrowUp, { className: "w-4 h-4 text-white" })
|
|
2358
|
+
)), /* @__PURE__ */ React14.createElement("p", { className: "text-center text-[10px] text-slate-400 dark:text-white/20 mt-2" }, "Press Enter to send \xB7 Shift+Enter for new line"))
|
|
1940
2359
|
);
|
|
1941
2360
|
}
|
|
1942
2361
|
|
|
@@ -1944,12 +2363,12 @@ function ChatWindow({
|
|
|
1944
2363
|
var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
|
|
1945
2364
|
function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
1946
2365
|
const { ui } = useConfig();
|
|
1947
|
-
const [isOpen, setIsOpen] =
|
|
1948
|
-
const [hasUnread, setHasUnread] =
|
|
1949
|
-
const [dimensions, setDimensions] =
|
|
1950
|
-
const [isResizing, setIsResizing] =
|
|
1951
|
-
const [isMaximized, setIsMaximized] =
|
|
1952
|
-
const [prevDimensions, setPrevDimensions] =
|
|
2366
|
+
const [isOpen, setIsOpen] = useState6(false);
|
|
2367
|
+
const [hasUnread, setHasUnread] = useState6(false);
|
|
2368
|
+
const [dimensions, setDimensions] = useState6(DEFAULT_DIMENSIONS);
|
|
2369
|
+
const [isResizing, setIsResizing] = useState6(false);
|
|
2370
|
+
const [isMaximized, setIsMaximized] = useState6(false);
|
|
2371
|
+
const [prevDimensions, setPrevDimensions] = useState6(DEFAULT_DIMENSIONS);
|
|
1953
2372
|
if (ui.showWidget === false) return null;
|
|
1954
2373
|
const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
|
|
1955
2374
|
const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
|
|
@@ -1999,7 +2418,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
1999
2418
|
}
|
|
2000
2419
|
};
|
|
2001
2420
|
const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
|
|
2002
|
-
return /* @__PURE__ */
|
|
2421
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
|
|
2003
2422
|
"div",
|
|
2004
2423
|
{
|
|
2005
2424
|
className: `fixed z-[9998] max-w-[calc(100vw-3rem)] ease-in-out ${windowPositionClass} ${isOpen ? "opacity-100 translate-y-0 pointer-events-auto" : "opacity-0 translate-y-4 pointer-events-none"} ${isResizing ? "" : "transition-all duration-300"}`,
|
|
@@ -2009,7 +2428,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
2009
2428
|
maxHeight: "calc(100vh - 6rem)"
|
|
2010
2429
|
}
|
|
2011
2430
|
},
|
|
2012
|
-
/* @__PURE__ */
|
|
2431
|
+
/* @__PURE__ */ React15.createElement(
|
|
2013
2432
|
ChatWindow,
|
|
2014
2433
|
{
|
|
2015
2434
|
className: "h-full relative z-10",
|
|
@@ -2023,13 +2442,13 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
2023
2442
|
onAddToCart
|
|
2024
2443
|
}
|
|
2025
2444
|
),
|
|
2026
|
-
/* @__PURE__ */
|
|
2445
|
+
/* @__PURE__ */ React15.createElement(
|
|
2027
2446
|
"div",
|
|
2028
2447
|
{
|
|
2029
2448
|
className: `absolute -bottom-1.5 w-4 h-4 rotate-45 border-r border-b border-slate-200 dark:border-white/10 z-0 ${ui.visualStyle === "solid" ? "bg-white dark:bg-[#0f0f1a]" : "bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl"} ${position === "bottom-left" ? "left-5" : "right-5"}`
|
|
2030
2449
|
}
|
|
2031
2450
|
)
|
|
2032
|
-
), /* @__PURE__ */
|
|
2451
|
+
), /* @__PURE__ */ React15.createElement(
|
|
2033
2452
|
"button",
|
|
2034
2453
|
{
|
|
2035
2454
|
onClick: isOpen ? () => setIsOpen(false) : handleOpen,
|
|
@@ -2037,156 +2456,32 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
2037
2456
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
|
|
2038
2457
|
"aria-label": "Open chat"
|
|
2039
2458
|
},
|
|
2040
|
-
/* @__PURE__ */
|
|
2459
|
+
/* @__PURE__ */ React15.createElement(
|
|
2041
2460
|
"span",
|
|
2042
2461
|
{
|
|
2043
2462
|
className: "absolute inset-0 rounded-full animate-ping opacity-20",
|
|
2044
2463
|
style: { background: ui.primaryColor }
|
|
2045
2464
|
}
|
|
2046
2465
|
),
|
|
2047
|
-
/* @__PURE__ */
|
|
2466
|
+
/* @__PURE__ */ React15.createElement(
|
|
2048
2467
|
"span",
|
|
2049
2468
|
{
|
|
2050
2469
|
className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
|
|
2051
2470
|
},
|
|
2052
|
-
isOpen ? /* @__PURE__ */
|
|
2471
|
+
isOpen ? /* @__PURE__ */ React15.createElement(X3, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ React15.createElement(MessageSquare, { className: "w-6 h-6 text-white" })
|
|
2053
2472
|
),
|
|
2054
|
-
hasUnread && !isOpen && /* @__PURE__ */
|
|
2473
|
+
hasUnread && !isOpen && /* @__PURE__ */ React15.createElement("span", { className: "absolute top-0 right-0 w-4 h-4 bg-rose-500 rounded-full border-2 border-white flex items-center justify-center text-[9px] font-bold text-white" }, "1")
|
|
2055
2474
|
));
|
|
2056
2475
|
}
|
|
2057
|
-
|
|
2058
|
-
// src/components/DocumentUpload.tsx
|
|
2059
|
-
import React11, { useState as useState6, useRef as useRef4 } from "react";
|
|
2060
|
-
import { Upload, File, X as X3, CheckCircle, AlertCircle, Loader2 } from "lucide-react";
|
|
2061
|
-
function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
2062
|
-
const { ui } = useConfig();
|
|
2063
|
-
const [fileStates, setFileStates] = useState6([]);
|
|
2064
|
-
const [isDragging, setIsDragging] = useState6(false);
|
|
2065
|
-
const fileInputRef = useRef4(null);
|
|
2066
|
-
const addFiles = (files) => {
|
|
2067
|
-
const newStates = files.map((file) => ({ file, status: "idle" }));
|
|
2068
|
-
setFileStates((prev) => [...prev, ...newStates]);
|
|
2069
|
-
};
|
|
2070
|
-
const removeFile = (index) => {
|
|
2071
|
-
setFileStates((prev) => prev.filter((_, i) => i !== index));
|
|
2072
|
-
};
|
|
2073
|
-
const onDragOver = (e) => {
|
|
2074
|
-
e.preventDefault();
|
|
2075
|
-
setIsDragging(true);
|
|
2076
|
-
};
|
|
2077
|
-
const onDragLeave = () => {
|
|
2078
|
-
setIsDragging(false);
|
|
2079
|
-
};
|
|
2080
|
-
const onDrop = (e) => {
|
|
2081
|
-
e.preventDefault();
|
|
2082
|
-
setIsDragging(false);
|
|
2083
|
-
if (e.dataTransfer.files) {
|
|
2084
|
-
addFiles(Array.from(e.dataTransfer.files));
|
|
2085
|
-
}
|
|
2086
|
-
};
|
|
2087
|
-
const onFileSelect = (e) => {
|
|
2088
|
-
if (e.target.files) {
|
|
2089
|
-
addFiles(Array.from(e.target.files));
|
|
2090
|
-
}
|
|
2091
|
-
};
|
|
2092
|
-
const uploadFiles = async () => {
|
|
2093
|
-
const idleFiles = fileStates.filter((s) => s.status === "idle");
|
|
2094
|
-
if (idleFiles.length === 0) return;
|
|
2095
|
-
setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
|
|
2096
|
-
const formData = new FormData();
|
|
2097
|
-
idleFiles.forEach((s) => formData.append("files", s.file));
|
|
2098
|
-
if (namespace) formData.append("namespace", namespace);
|
|
2099
|
-
try {
|
|
2100
|
-
const response = await fetch("/api/upload", {
|
|
2101
|
-
method: "POST",
|
|
2102
|
-
body: formData
|
|
2103
|
-
});
|
|
2104
|
-
const result = await response.json();
|
|
2105
|
-
if (!response.ok) throw new Error(result.error || "Upload failed");
|
|
2106
|
-
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
|
|
2107
|
-
if (onUploadComplete) onUploadComplete(result);
|
|
2108
|
-
} catch (err) {
|
|
2109
|
-
const message = err instanceof Error ? err.message : "Upload failed";
|
|
2110
|
-
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
|
|
2111
|
-
}
|
|
2112
|
-
};
|
|
2113
|
-
const isUploading = fileStates.some((s) => s.status === "uploading");
|
|
2114
|
-
const hasIdle = fileStates.some((s) => s.status === "idle");
|
|
2115
|
-
return /* @__PURE__ */ React11.createElement(
|
|
2116
|
-
"div",
|
|
2117
|
-
{
|
|
2118
|
-
className: `p-6 border-2 border-dashed transition-all duration-300 rounded-2xl ${isDragging ? "border-emerald-500 bg-emerald-50/50 dark:bg-emerald-500/5" : "border-slate-200 dark:border-white/10 bg-white dark:bg-white/5"} ${className}`,
|
|
2119
|
-
onDragOver,
|
|
2120
|
-
onDragLeave,
|
|
2121
|
-
onDrop
|
|
2122
|
-
},
|
|
2123
|
-
/* @__PURE__ */ React11.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ React11.createElement(
|
|
2124
|
-
"div",
|
|
2125
|
-
{
|
|
2126
|
-
className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
|
|
2127
|
-
style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
|
|
2128
|
-
},
|
|
2129
|
-
/* @__PURE__ */ React11.createElement(Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
|
|
2130
|
-
), /* @__PURE__ */ React11.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ React11.createElement("p", { className: "text-slate-500 dark:text-white/40 text-sm mb-6 max-w-xs" }, "Drag and drop PDF, DOCX, TXT, or JSON files to train your AI on your own data."), /* @__PURE__ */ React11.createElement(
|
|
2131
|
-
"button",
|
|
2132
|
-
{
|
|
2133
|
-
onClick: () => {
|
|
2134
|
-
var _a;
|
|
2135
|
-
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
2136
|
-
},
|
|
2137
|
-
disabled: isUploading,
|
|
2138
|
-
className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
|
|
2139
|
-
style: {
|
|
2140
|
-
backgroundColor: `${ui.primaryColor}15`,
|
|
2141
|
-
color: ui.primaryColor,
|
|
2142
|
-
border: `1px solid ${ui.primaryColor}30`
|
|
2143
|
-
}
|
|
2144
|
-
},
|
|
2145
|
-
"Select Files"
|
|
2146
|
-
), /* @__PURE__ */ React11.createElement(
|
|
2147
|
-
"input",
|
|
2148
|
-
{
|
|
2149
|
-
ref: fileInputRef,
|
|
2150
|
-
type: "file",
|
|
2151
|
-
multiple: true,
|
|
2152
|
-
onChange: onFileSelect,
|
|
2153
|
-
className: "hidden",
|
|
2154
|
-
accept: ".pdf,.docx,.txt,.md,.json,.csv"
|
|
2155
|
-
}
|
|
2156
|
-
)),
|
|
2157
|
-
fileStates.length > 0 && /* @__PURE__ */ React11.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ React11.createElement(
|
|
2158
|
-
"div",
|
|
2159
|
-
{
|
|
2160
|
-
key: i,
|
|
2161
|
-
className: "flex items-center justify-between p-3 rounded-xl bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/10"
|
|
2162
|
-
},
|
|
2163
|
-
/* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ React11.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ React11.createElement(File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ React11.createElement("div", { className: "truncate" }, /* @__PURE__ */ React11.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ React11.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
|
|
2164
|
-
/* @__PURE__ */ React11.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ React11.createElement(Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ React11.createElement(CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ React11.createElement("div", { title: state.error }, /* @__PURE__ */ React11.createElement(AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ React11.createElement(
|
|
2165
|
-
"button",
|
|
2166
|
-
{
|
|
2167
|
-
onClick: () => removeFile(i),
|
|
2168
|
-
className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
2169
|
-
},
|
|
2170
|
-
/* @__PURE__ */ React11.createElement(X3, { className: "w-3.5 h-3.5 text-slate-400" })
|
|
2171
|
-
))
|
|
2172
|
-
)), hasIdle && !isUploading && /* @__PURE__ */ React11.createElement(
|
|
2173
|
-
"button",
|
|
2174
|
-
{
|
|
2175
|
-
onClick: uploadFiles,
|
|
2176
|
-
className: "w-full mt-4 py-2.5 rounded-xl text-sm font-semibold text-white shadow-lg transition-all hover:scale-[1.02] active:scale-[0.98]",
|
|
2177
|
-
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
2178
|
-
},
|
|
2179
|
-
"Start Upload"
|
|
2180
|
-
))
|
|
2181
|
-
);
|
|
2182
|
-
}
|
|
2183
2476
|
export {
|
|
2184
2477
|
ChatWidget,
|
|
2185
2478
|
ChatWindow,
|
|
2186
2479
|
ConfigProvider,
|
|
2187
2480
|
DocumentUpload,
|
|
2188
2481
|
MessageBubble,
|
|
2482
|
+
ObservabilityPanel,
|
|
2189
2483
|
SourceCard,
|
|
2484
|
+
addSynonyms,
|
|
2190
2485
|
useConfig,
|
|
2191
2486
|
useRagChat
|
|
2192
2487
|
};
|