@retrivora-ai/rag-engine 1.8.0 → 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/DocumentChunker-Dh9TvmGG.d.mts +45 -0
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
- package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
- package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
- package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
- package/dist/chunk-ICKRMZQK.mjs +76 -0
- package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
- package/dist/chunk-OZFBG4BA.mjs +291 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1269 -656
- package/dist/handlers/index.mjs +4 -1
- package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
- package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
- package/dist/index.d.mts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +1354 -826
- package/dist/index.mjs +1284 -795
- package/dist/server.d.mts +47 -27
- package/dist/server.d.ts +47 -27
- package/dist/server.js +1417 -829
- package/dist/server.mjs +164 -176
- package/package.json +6 -2
- package/src/app/api/upload/route.ts +4 -0
- package/src/app/constants.tsx +2 -2
- package/src/app/page.tsx +12 -322
- 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 +124 -904
- package/src/components/Navbar.tsx +55 -0
- package/src/components/ObservabilityPanel.tsx +374 -0
- package/src/components/ProductCard.tsx +5 -3
- package/src/components/UIDispatcher.tsx +344 -0
- package/src/components/VisualizationRenderer.tsx +372 -250
- package/src/config/RagConfig.ts +5 -0
- package/src/config/serverConfig.ts +3 -1
- package/src/core/Pipeline.ts +240 -271
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/VectorPlugin.ts +9 -0
- package/src/handlers/index.ts +91 -15
- package/src/hooks/useRagChat.ts +21 -11
- package/src/index.ts +9 -1
- package/src/llm/LLMFactory.ts +54 -2
- package/src/llm/providers/AnthropicProvider.ts +12 -8
- package/src/llm/providers/GeminiProvider.ts +188 -143
- package/src/llm/providers/OllamaProvider.ts +7 -3
- package/src/llm/providers/OpenAIProvider.ts +12 -8
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
- package/src/types/chat.ts +8 -0
- package/src/types/index.ts +132 -0
- package/src/types/props.ts +9 -1
- package/src/utils/ProductExtractor.ts +347 -0
- package/src/utils/SchemaMapper.ts +129 -0
- package/src/utils/UITransformer.ts +470 -209
- package/src/utils/synonyms.ts +78 -0
- package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
- package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
- package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
- package/dist/chunk-FLOSGE6A.mjs +0 -202
package/dist/index.js
CHANGED
|
@@ -64,59 +64,282 @@ __export(index_exports, {
|
|
|
64
64
|
ConfigProvider: () => ConfigProvider,
|
|
65
65
|
DocumentUpload: () => DocumentUpload,
|
|
66
66
|
MessageBubble: () => MessageBubble,
|
|
67
|
+
ObservabilityPanel: () => ObservabilityPanel,
|
|
67
68
|
SourceCard: () => SourceCard,
|
|
69
|
+
addSynonyms: () => addSynonyms,
|
|
68
70
|
useConfig: () => useConfig,
|
|
69
71
|
useRagChat: () => useRagChat
|
|
70
72
|
});
|
|
71
73
|
module.exports = __toCommonJS(index_exports);
|
|
72
74
|
|
|
73
75
|
// src/components/ChatWidget.tsx
|
|
74
|
-
var
|
|
75
|
-
var
|
|
76
|
+
var import_react15 = __toESM(require("react"));
|
|
77
|
+
var import_lucide_react8 = require("lucide-react");
|
|
76
78
|
|
|
77
79
|
// src/components/ChatWindow.tsx
|
|
78
|
-
var
|
|
79
|
-
var
|
|
80
|
+
var import_react14 = __toESM(require("react"));
|
|
81
|
+
var import_lucide_react7 = require("lucide-react");
|
|
82
|
+
|
|
83
|
+
// src/components/DocumentUpload.tsx
|
|
84
|
+
var import_react2 = __toESM(require("react"));
|
|
85
|
+
var import_lucide_react = require("lucide-react");
|
|
86
|
+
|
|
87
|
+
// src/components/ConfigProvider.tsx
|
|
88
|
+
var import_react = __toESM(require("react"));
|
|
89
|
+
|
|
90
|
+
// src/utils/templateUtils.ts
|
|
91
|
+
function mergeDefined(base, override) {
|
|
92
|
+
const merged = __spreadValues({}, base || {});
|
|
93
|
+
if (!override) {
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
for (const [key, value] of Object.entries(override)) {
|
|
97
|
+
if (value !== void 0 && value !== null && value !== "") {
|
|
98
|
+
merged[key] = value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return merged;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/config/uiConstants.ts
|
|
105
|
+
var DEFAULT_CONFIG = {
|
|
106
|
+
projectId: "default",
|
|
107
|
+
ui: {
|
|
108
|
+
title: "AI Assistant",
|
|
109
|
+
subtitle: "Powered by RAG",
|
|
110
|
+
primaryColor: "#6366f1",
|
|
111
|
+
accentColor: "#8b5cf6",
|
|
112
|
+
logoUrl: "",
|
|
113
|
+
placeholder: "Ask me anything\u2026",
|
|
114
|
+
showSources: true,
|
|
115
|
+
welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
116
|
+
showWidget: true,
|
|
117
|
+
poweredBy: "RAG",
|
|
118
|
+
visualStyle: "glass",
|
|
119
|
+
borderRadius: "xl",
|
|
120
|
+
allowUpload: true,
|
|
121
|
+
allowResize: true,
|
|
122
|
+
enableVoiceInput: true
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var BORDER_RADIUS_MAP = {
|
|
126
|
+
none: "rounded-none",
|
|
127
|
+
sm: "rounded-sm",
|
|
128
|
+
md: "rounded-md",
|
|
129
|
+
lg: "rounded-lg",
|
|
130
|
+
xl: "rounded-xl",
|
|
131
|
+
full: "rounded-3xl"
|
|
132
|
+
};
|
|
133
|
+
var CHAT_SUGGESTIONS = [
|
|
134
|
+
"What can you help me with?",
|
|
135
|
+
"Summarise the key topics",
|
|
136
|
+
"Show me an example"
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
// src/components/ConfigProvider.tsx
|
|
140
|
+
var ConfigContext = (0, import_react.createContext)(DEFAULT_CONFIG);
|
|
141
|
+
function ConfigProvider({
|
|
142
|
+
config,
|
|
143
|
+
children
|
|
144
|
+
}) {
|
|
145
|
+
const merged = {
|
|
146
|
+
projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
|
|
147
|
+
ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui),
|
|
148
|
+
embedding: config == null ? void 0 : config.embedding
|
|
149
|
+
};
|
|
150
|
+
return /* @__PURE__ */ import_react.default.createElement(ConfigContext.Provider, { value: merged }, children);
|
|
151
|
+
}
|
|
152
|
+
function useConfig() {
|
|
153
|
+
return (0, import_react.useContext)(ConfigContext);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/components/DocumentUpload.tsx
|
|
157
|
+
function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
158
|
+
var _a;
|
|
159
|
+
const { ui, embedding } = useConfig();
|
|
160
|
+
const [fileStates, setFileStates] = (0, import_react2.useState)([]);
|
|
161
|
+
const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
|
|
162
|
+
const fileInputRef = (0, import_react2.useRef)(null);
|
|
163
|
+
const MULTI_DIMENSION_MODELS = {
|
|
164
|
+
"text-embedding-3-small": [512, 1536],
|
|
165
|
+
"text-embedding-3-large": [256, 1024, 3072],
|
|
166
|
+
"nomic-embed-text": [64, 128, 256, 512, 768]
|
|
167
|
+
};
|
|
168
|
+
const COMMON_DIMENSIONS = [64, 128, 256, 384, 512, 768, 1024, 1536, 3072];
|
|
169
|
+
const currentModel = (embedding == null ? void 0 : embedding.model) || "unknown";
|
|
170
|
+
const defaultDimension = (embedding == null ? void 0 : embedding.dimensions) || 1536;
|
|
171
|
+
let availableDimensions = (_a = Object.entries(MULTI_DIMENSION_MODELS).find(([k]) => currentModel.includes(k))) == null ? void 0 : _a[1];
|
|
172
|
+
if (!availableDimensions) {
|
|
173
|
+
availableDimensions = COMMON_DIMENSIONS.includes(defaultDimension) ? COMMON_DIMENSIONS : [...COMMON_DIMENSIONS, defaultDimension].sort((a, b) => a - b);
|
|
174
|
+
}
|
|
175
|
+
const isChangeable = true;
|
|
176
|
+
const [dimension, setDimension] = (0, import_react2.useState)(defaultDimension);
|
|
177
|
+
const addFiles = (files) => {
|
|
178
|
+
const newStates = files.map((file) => ({ file, status: "idle" }));
|
|
179
|
+
setFileStates((prev) => [...prev, ...newStates]);
|
|
180
|
+
};
|
|
181
|
+
const removeFile = (index) => {
|
|
182
|
+
setFileStates((prev) => prev.filter((_, i) => i !== index));
|
|
183
|
+
};
|
|
184
|
+
const onDragOver = (e) => {
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
setIsDragging(true);
|
|
187
|
+
};
|
|
188
|
+
const onDragLeave = () => {
|
|
189
|
+
setIsDragging(false);
|
|
190
|
+
};
|
|
191
|
+
const onDrop = (e) => {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
setIsDragging(false);
|
|
194
|
+
if (e.dataTransfer.files) {
|
|
195
|
+
addFiles(Array.from(e.dataTransfer.files));
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const onFileSelect = (e) => {
|
|
199
|
+
if (e.target.files) {
|
|
200
|
+
addFiles(Array.from(e.target.files));
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const uploadFiles = async () => {
|
|
204
|
+
const idleFiles = fileStates.filter((s) => s.status === "idle");
|
|
205
|
+
if (idleFiles.length === 0) return;
|
|
206
|
+
setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
|
|
207
|
+
const formData = new FormData();
|
|
208
|
+
idleFiles.forEach((s) => formData.append("files", s.file));
|
|
209
|
+
if (namespace) formData.append("namespace", namespace);
|
|
210
|
+
formData.append("dimension", dimension.toString());
|
|
211
|
+
try {
|
|
212
|
+
const response = await fetch("/api/upload", {
|
|
213
|
+
method: "POST",
|
|
214
|
+
body: formData
|
|
215
|
+
});
|
|
216
|
+
const result = await response.json();
|
|
217
|
+
if (!response.ok) throw new Error(result.error || "Upload failed");
|
|
218
|
+
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
|
|
219
|
+
if (onUploadComplete) onUploadComplete(result);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
const message = err instanceof Error ? err.message : "Upload failed";
|
|
222
|
+
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
const isUploading = fileStates.some((s) => s.status === "uploading");
|
|
226
|
+
const hasIdle = fileStates.some((s) => s.status === "idle");
|
|
227
|
+
return /* @__PURE__ */ import_react2.default.createElement(
|
|
228
|
+
"div",
|
|
229
|
+
{
|
|
230
|
+
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}`,
|
|
231
|
+
onDragOver,
|
|
232
|
+
onDragLeave,
|
|
233
|
+
onDrop
|
|
234
|
+
},
|
|
235
|
+
/* @__PURE__ */ import_react2.default.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
236
|
+
"div",
|
|
237
|
+
{
|
|
238
|
+
className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
|
|
239
|
+
style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
|
|
240
|
+
},
|
|
241
|
+
/* @__PURE__ */ import_react2.default.createElement(import_lucide_react.Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
|
|
242
|
+
), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement(
|
|
243
|
+
"button",
|
|
244
|
+
{
|
|
245
|
+
onClick: () => {
|
|
246
|
+
var _a2;
|
|
247
|
+
return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
|
|
248
|
+
},
|
|
249
|
+
disabled: isUploading,
|
|
250
|
+
className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
|
|
251
|
+
style: {
|
|
252
|
+
backgroundColor: `${ui.primaryColor}15`,
|
|
253
|
+
color: ui.primaryColor,
|
|
254
|
+
border: `1px solid ${ui.primaryColor}30`
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"Select Files"
|
|
258
|
+
), /* @__PURE__ */ import_react2.default.createElement(
|
|
259
|
+
"input",
|
|
260
|
+
{
|
|
261
|
+
ref: fileInputRef,
|
|
262
|
+
type: "file",
|
|
263
|
+
multiple: true,
|
|
264
|
+
onChange: onFileSelect,
|
|
265
|
+
className: "hidden",
|
|
266
|
+
accept: ".pdf,.docx,.txt,.md,.json,.csv"
|
|
267
|
+
}
|
|
268
|
+
), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mt-6 flex flex-col items-center gap-2 text-sm" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-slate-600 dark:text-white/60 font-medium" }, "Embedding Dimension"), isChangeable ? /* @__PURE__ */ import_react2.default.createElement(
|
|
269
|
+
"select",
|
|
270
|
+
{
|
|
271
|
+
value: dimension,
|
|
272
|
+
onChange: (e) => setDimension(Number(e.target.value)),
|
|
273
|
+
disabled: isUploading,
|
|
274
|
+
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"
|
|
275
|
+
},
|
|
276
|
+
availableDimensions.map((dim) => /* @__PURE__ */ import_react2.default.createElement("option", { key: dim, value: dim, className: "bg-white dark:bg-slate-900" }, dim))
|
|
277
|
+
) : /* @__PURE__ */ import_react2.default.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, ")"))),
|
|
278
|
+
fileStates.length > 0 && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ import_react2.default.createElement(
|
|
279
|
+
"div",
|
|
280
|
+
{
|
|
281
|
+
key: i,
|
|
282
|
+
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"
|
|
283
|
+
},
|
|
284
|
+
/* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ import_react2.default.createElement(import_lucide_react.File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ import_react2.default.createElement("div", { className: "truncate" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
|
|
285
|
+
/* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ import_react2.default.createElement(import_lucide_react.Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ import_react2.default.createElement(import_lucide_react.CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ import_react2.default.createElement("div", { title: state.error }, /* @__PURE__ */ import_react2.default.createElement(import_lucide_react.AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ import_react2.default.createElement(
|
|
286
|
+
"button",
|
|
287
|
+
{
|
|
288
|
+
onClick: () => removeFile(i),
|
|
289
|
+
className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
290
|
+
},
|
|
291
|
+
/* @__PURE__ */ import_react2.default.createElement(import_lucide_react.X, { className: "w-3.5 h-3.5 text-slate-400" })
|
|
292
|
+
))
|
|
293
|
+
)), hasIdle && !isUploading && /* @__PURE__ */ import_react2.default.createElement(
|
|
294
|
+
"button",
|
|
295
|
+
{
|
|
296
|
+
onClick: uploadFiles,
|
|
297
|
+
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]",
|
|
298
|
+
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
299
|
+
},
|
|
300
|
+
"Start Upload"
|
|
301
|
+
))
|
|
302
|
+
);
|
|
303
|
+
}
|
|
80
304
|
|
|
81
305
|
// src/components/MessageBubble.tsx
|
|
82
|
-
var
|
|
83
|
-
var
|
|
306
|
+
var import_react12 = __toESM(require("react"));
|
|
307
|
+
var import_lucide_react6 = require("lucide-react");
|
|
84
308
|
var import_react_markdown = __toESM(require("react-markdown"));
|
|
85
|
-
var import_remark_gfm = __toESM(require("remark-gfm"));
|
|
86
309
|
|
|
87
310
|
// src/components/SourceCard.tsx
|
|
88
|
-
var
|
|
89
|
-
var
|
|
311
|
+
var import_react3 = __toESM(require("react"));
|
|
312
|
+
var import_lucide_react2 = require("lucide-react");
|
|
90
313
|
function SourceCard({ source, index }) {
|
|
91
314
|
var _a, _b;
|
|
92
|
-
const [expanded, setExpanded] =
|
|
315
|
+
const [expanded, setExpanded] = import_react3.default.useState(false);
|
|
93
316
|
const scorePercent = Math.round(source.score * 100);
|
|
94
317
|
const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
|
|
95
318
|
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";
|
|
96
319
|
const preview = source.content.slice(0, 120);
|
|
97
320
|
const hasMore = source.content.length > 120;
|
|
98
|
-
return /* @__PURE__ */
|
|
321
|
+
return /* @__PURE__ */ import_react3.default.createElement(
|
|
99
322
|
"div",
|
|
100
323
|
{
|
|
101
324
|
className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
|
|
102
325
|
onClick: () => setExpanded((e) => !e)
|
|
103
326
|
},
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
/* @__PURE__ */
|
|
106
|
-
hasMore && /* @__PURE__ */
|
|
327
|
+
/* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react3.default.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__ */ import_react3.default.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__ */ import_react3.default.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
|
|
328
|
+
/* @__PURE__ */ import_react3.default.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
|
|
329
|
+
hasMore && /* @__PURE__ */ import_react3.default.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_lucide_react2.ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement(import_lucide_react2.ChevronDown, { className: "w-3 h-3" }), " Show more"))
|
|
107
330
|
);
|
|
108
331
|
}
|
|
109
332
|
|
|
110
333
|
// src/components/ProductCarousel.tsx
|
|
111
|
-
var
|
|
112
|
-
var
|
|
334
|
+
var import_react5 = __toESM(require("react"));
|
|
335
|
+
var import_lucide_react4 = require("lucide-react");
|
|
113
336
|
|
|
114
337
|
// src/components/ProductCard.tsx
|
|
115
|
-
var
|
|
338
|
+
var import_react4 = __toESM(require("react"));
|
|
116
339
|
var import_image = __toESM(require("next/image"));
|
|
117
|
-
var
|
|
340
|
+
var import_lucide_react3 = require("lucide-react");
|
|
118
341
|
function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
119
|
-
return /* @__PURE__ */
|
|
342
|
+
return /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.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__ */ import_react4.default.createElement(
|
|
120
343
|
import_image.default,
|
|
121
344
|
{
|
|
122
345
|
src: product.image,
|
|
@@ -125,16 +348,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
125
348
|
unoptimized: true,
|
|
126
349
|
className: "object-cover group-hover:scale-105 transition-transform duration-500"
|
|
127
350
|
}
|
|
128
|
-
) : /* @__PURE__ */
|
|
351
|
+
) : /* @__PURE__ */ import_react4.default.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ import_react4.default.createElement(import_lucide_react3.ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ import_react4.default.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ import_react4.default.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__ */ import_react4.default.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ import_react4.default.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ import_react4.default.createElement(
|
|
129
352
|
"button",
|
|
130
353
|
{
|
|
131
354
|
onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
|
|
132
355
|
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",
|
|
133
356
|
style: { background: primaryColor }
|
|
134
357
|
},
|
|
135
|
-
/* @__PURE__ */
|
|
358
|
+
/* @__PURE__ */ import_react4.default.createElement(import_lucide_react3.ShoppingCart, { className: "w-3 h-3" }),
|
|
136
359
|
"Add to Cart"
|
|
137
|
-
), product.link && /* @__PURE__ */
|
|
360
|
+
), product.link && /* @__PURE__ */ import_react4.default.createElement(
|
|
138
361
|
"a",
|
|
139
362
|
{
|
|
140
363
|
href: product.link,
|
|
@@ -142,15 +365,15 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
142
365
|
rel: "noopener noreferrer",
|
|
143
366
|
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"
|
|
144
367
|
},
|
|
145
|
-
/* @__PURE__ */
|
|
368
|
+
/* @__PURE__ */ import_react4.default.createElement(import_lucide_react3.ExternalLink, { className: "w-3.5 h-3.5" })
|
|
146
369
|
))));
|
|
147
370
|
}
|
|
148
371
|
|
|
149
372
|
// src/components/ProductCarousel.tsx
|
|
150
373
|
function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
151
|
-
const scrollRef = (0,
|
|
152
|
-
const [canScroll, setCanScroll] = (0,
|
|
153
|
-
(0,
|
|
374
|
+
const scrollRef = (0, import_react5.useRef)(null);
|
|
375
|
+
const [canScroll, setCanScroll] = (0, import_react5.useState)(false);
|
|
376
|
+
(0, import_react5.useEffect)(() => {
|
|
154
377
|
const checkScroll = () => {
|
|
155
378
|
if (scrollRef.current) {
|
|
156
379
|
const hasOverflow = scrollRef.current.scrollWidth > scrollRef.current.clientWidth;
|
|
@@ -174,9 +397,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
174
397
|
};
|
|
175
398
|
if (!products || products.length === 0) return null;
|
|
176
399
|
if (products.length === 1) {
|
|
177
|
-
return /* @__PURE__ */
|
|
400
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ import_react5.default.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
|
|
178
401
|
}
|
|
179
|
-
return /* @__PURE__ */
|
|
402
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.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__ */ import_react5.default.createElement(
|
|
180
403
|
"button",
|
|
181
404
|
{
|
|
182
405
|
onClick: (e) => {
|
|
@@ -186,8 +409,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
186
409
|
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",
|
|
187
410
|
"aria-label": "Scroll left"
|
|
188
411
|
},
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
)), /* @__PURE__ */
|
|
412
|
+
/* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.ChevronLeft, { className: "w-5 h-5" })
|
|
413
|
+
)), /* @__PURE__ */ import_react5.default.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__ */ import_react5.default.createElement(
|
|
191
414
|
"button",
|
|
192
415
|
{
|
|
193
416
|
onClick: (e) => {
|
|
@@ -197,40 +420,45 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
|
|
|
197
420
|
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",
|
|
198
421
|
"aria-label": "Scroll right"
|
|
199
422
|
},
|
|
200
|
-
/* @__PURE__ */
|
|
201
|
-
))), /* @__PURE__ */
|
|
423
|
+
/* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.ChevronRight, { className: "w-5 h-5" })
|
|
424
|
+
))), /* @__PURE__ */ import_react5.default.createElement(
|
|
202
425
|
"div",
|
|
203
426
|
{
|
|
204
427
|
ref: scrollRef,
|
|
205
428
|
className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
|
|
206
429
|
style: { scrollbarWidth: "none", msOverflowStyle: "none" }
|
|
207
430
|
},
|
|
208
|
-
products.map((product) => /* @__PURE__ */
|
|
431
|
+
products.map((product) => /* @__PURE__ */ import_react5.default.createElement(
|
|
209
432
|
"div",
|
|
210
433
|
{
|
|
211
434
|
key: product.id,
|
|
212
435
|
className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
|
|
213
436
|
},
|
|
214
|
-
/* @__PURE__ */
|
|
437
|
+
/* @__PURE__ */ import_react5.default.createElement(ProductCard, { product, primaryColor, onAddToCart })
|
|
215
438
|
))
|
|
216
439
|
));
|
|
217
440
|
}
|
|
218
441
|
|
|
442
|
+
// src/components/VisualizationRenderer.tsx
|
|
443
|
+
var import_react7 = __toESM(require("react"));
|
|
444
|
+
var import_recharts2 = require("recharts");
|
|
445
|
+
|
|
219
446
|
// src/components/DynamicChart.tsx
|
|
220
|
-
var
|
|
447
|
+
var import_react6 = __toESM(require("react"));
|
|
221
448
|
var import_recharts = require("recharts");
|
|
222
|
-
var
|
|
449
|
+
var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
|
|
223
450
|
function DynamicChart({
|
|
224
451
|
config,
|
|
225
452
|
primaryColor = "#6366f1",
|
|
226
453
|
accentColor = "#8b5cf6",
|
|
227
|
-
viewportSize = "large"
|
|
454
|
+
viewportSize = "large",
|
|
455
|
+
isStreaming = false
|
|
228
456
|
}) {
|
|
229
457
|
const { type, data } = config;
|
|
230
|
-
const colors = config.colors || [primaryColor, accentColor, ...
|
|
231
|
-
const containerRef =
|
|
232
|
-
const [containerWidth, setContainerWidth] =
|
|
233
|
-
|
|
458
|
+
const colors = config.colors || [primaryColor, accentColor, ...CHART_COLORS];
|
|
459
|
+
const containerRef = import_react6.default.useRef(null);
|
|
460
|
+
const [containerWidth, setContainerWidth] = import_react6.default.useState(0);
|
|
461
|
+
import_react6.default.useEffect(() => {
|
|
234
462
|
const element = containerRef.current;
|
|
235
463
|
if (!element || typeof ResizeObserver === "undefined") return;
|
|
236
464
|
const updateWidth = (width) => {
|
|
@@ -256,7 +484,7 @@ function DynamicChart({
|
|
|
256
484
|
return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
|
|
257
485
|
};
|
|
258
486
|
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 };
|
|
259
|
-
const sanitizedData =
|
|
487
|
+
const sanitizedData = import_react6.default.useMemo(() => {
|
|
260
488
|
if (!data) return [];
|
|
261
489
|
return data.map((item) => {
|
|
262
490
|
const newItem = {};
|
|
@@ -288,8 +516,19 @@ function DynamicChart({
|
|
|
288
516
|
return newItem;
|
|
289
517
|
});
|
|
290
518
|
}, [data]);
|
|
519
|
+
if (isStreaming) {
|
|
520
|
+
const bars = type === "pie" ? 6 : 5;
|
|
521
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, type === "pie" ? /* @__PURE__ */ import_react6.default.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }) : /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-end gap-2 h-32 px-2" }, Array.from({ length: bars }).map((_, i) => /* @__PURE__ */ import_react6.default.createElement(
|
|
522
|
+
"div",
|
|
523
|
+
{
|
|
524
|
+
key: i,
|
|
525
|
+
className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10",
|
|
526
|
+
style: { height: `${40 + i % 3 * 25}%` }
|
|
527
|
+
}
|
|
528
|
+
))));
|
|
529
|
+
}
|
|
291
530
|
if (!data || data.length === 0) {
|
|
292
|
-
return /* @__PURE__ */
|
|
531
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
|
|
293
532
|
}
|
|
294
533
|
const firstItem = sanitizedData[0];
|
|
295
534
|
const allKeys = Object.keys(firstItem);
|
|
@@ -314,13 +553,13 @@ function DynamicChart({
|
|
|
314
553
|
const renderTooltip = (row, label) => {
|
|
315
554
|
var _a, _b;
|
|
316
555
|
if (!row) return null;
|
|
317
|
-
return /* @__PURE__ */
|
|
556
|
+
return /* @__PURE__ */ import_react6.default.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__ */ import_react6.default.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]) => {
|
|
318
557
|
var _a2;
|
|
319
558
|
if (value === null || value === void 0 || key === finalXKey) return null;
|
|
320
559
|
if (typeof value === "object") return null;
|
|
321
560
|
const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
|
|
322
561
|
if (forbiddenKeys.includes(key)) return null;
|
|
323
|
-
return /* @__PURE__ */
|
|
562
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react6.default.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ import_react6.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
|
|
324
563
|
}));
|
|
325
564
|
};
|
|
326
565
|
const getTooltipRow = (payload) => {
|
|
@@ -332,7 +571,7 @@ function DynamicChart({
|
|
|
332
571
|
return row;
|
|
333
572
|
};
|
|
334
573
|
if (type === "pie") {
|
|
335
|
-
return /* @__PURE__ */
|
|
574
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react6.default.createElement(
|
|
336
575
|
import_recharts.Pie,
|
|
337
576
|
{
|
|
338
577
|
data: sanitizedData,
|
|
@@ -344,10 +583,10 @@ function DynamicChart({
|
|
|
344
583
|
label: false,
|
|
345
584
|
labelLine: false
|
|
346
585
|
},
|
|
347
|
-
sanitizedData.map((_entry, index) => /* @__PURE__ */
|
|
348
|
-
), /* @__PURE__ */
|
|
586
|
+
sanitizedData.map((_entry, index) => /* @__PURE__ */ import_react6.default.createElement(import_recharts.Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
|
|
587
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
|
|
349
588
|
}
|
|
350
|
-
return /* @__PURE__ */
|
|
589
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react6.default.createElement(
|
|
351
590
|
import_recharts.Bar,
|
|
352
591
|
{
|
|
353
592
|
key,
|
|
@@ -356,7 +595,7 @@ function DynamicChart({
|
|
|
356
595
|
radius: [4, 4, 0, 0],
|
|
357
596
|
barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
|
|
358
597
|
}
|
|
359
|
-
))) : /* @__PURE__ */
|
|
598
|
+
))) : /* @__PURE__ */ import_react6.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react6.default.createElement(
|
|
360
599
|
import_recharts.Line,
|
|
361
600
|
{
|
|
362
601
|
key,
|
|
@@ -371,178 +610,553 @@ function DynamicChart({
|
|
|
371
610
|
}
|
|
372
611
|
|
|
373
612
|
// src/components/VisualizationRenderer.tsx
|
|
374
|
-
|
|
613
|
+
function getColor(index) {
|
|
614
|
+
return CHART_COLORS[index % CHART_COLORS.length];
|
|
615
|
+
}
|
|
616
|
+
function CustomTooltip({
|
|
617
|
+
active,
|
|
618
|
+
payload,
|
|
619
|
+
label
|
|
620
|
+
}) {
|
|
621
|
+
if (!active || !payload || payload.length === 0) return null;
|
|
622
|
+
return /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ import_react7.default.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react7.default.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ import_react7.default.createElement(
|
|
623
|
+
"span",
|
|
624
|
+
{
|
|
625
|
+
className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
|
|
626
|
+
style: { background: entry.color }
|
|
627
|
+
}
|
|
628
|
+
), entry.name), /* @__PURE__ */ import_react7.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
|
|
629
|
+
}
|
|
375
630
|
function VisualizationRenderer({
|
|
376
631
|
data,
|
|
377
|
-
className = ""
|
|
632
|
+
className = "",
|
|
633
|
+
primaryColor,
|
|
634
|
+
onAddToCart,
|
|
635
|
+
isStreaming
|
|
378
636
|
}) {
|
|
637
|
+
var _a;
|
|
379
638
|
if (!data) {
|
|
380
|
-
return /* @__PURE__ */
|
|
639
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
|
|
381
640
|
}
|
|
382
|
-
return /* @__PURE__ */
|
|
641
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor, isStreaming));
|
|
383
642
|
}
|
|
384
|
-
function renderVisualization(type, data) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
643
|
+
function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
|
|
644
|
+
try {
|
|
645
|
+
switch (type) {
|
|
646
|
+
case "pie_chart":
|
|
647
|
+
return /* @__PURE__ */ import_react7.default.createElement(PieChartView, { data, isStreaming });
|
|
648
|
+
case "bar_chart":
|
|
649
|
+
return /* @__PURE__ */ import_react7.default.createElement(BarChartView, { data, primaryColor, isStreaming });
|
|
650
|
+
case "line_chart":
|
|
651
|
+
return /* @__PURE__ */ import_react7.default.createElement(LineChartView, { data, primaryColor, isStreaming });
|
|
652
|
+
case "radar_chart":
|
|
653
|
+
return /* @__PURE__ */ import_react7.default.createElement(RadarChartView, { data, isStreaming });
|
|
654
|
+
case "table":
|
|
655
|
+
return /* @__PURE__ */ import_react7.default.createElement(TableView, { data, isStreaming });
|
|
656
|
+
case "product_carousel":
|
|
657
|
+
case "carousel":
|
|
658
|
+
return /* @__PURE__ */ import_react7.default.createElement(CarouselView, { data, onAddToCart, primaryColor });
|
|
659
|
+
case "text":
|
|
660
|
+
default:
|
|
661
|
+
return /* @__PURE__ */ import_react7.default.createElement(TextView, { data });
|
|
662
|
+
}
|
|
663
|
+
} catch (error) {
|
|
664
|
+
console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
|
|
665
|
+
return /* @__PURE__ */ import_react7.default.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
|
|
399
666
|
}
|
|
400
667
|
}
|
|
401
|
-
function
|
|
402
|
-
if (
|
|
403
|
-
return /* @__PURE__ */
|
|
668
|
+
function ChartSkeleton({ type }) {
|
|
669
|
+
if (type === "pie") {
|
|
670
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }));
|
|
404
671
|
}
|
|
405
|
-
|
|
406
|
-
"div",
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
className: "flex items-center gap-2 p-2 bg-gray-50 dark:bg-gray-700 rounded"
|
|
410
|
-
},
|
|
411
|
-
/* @__PURE__ */ React.createElement(
|
|
412
|
-
"div",
|
|
413
|
-
{
|
|
414
|
-
className: "w-3 h-3 rounded-full",
|
|
415
|
-
style: { backgroundColor: generateColor(idx) }
|
|
416
|
-
}
|
|
417
|
-
),
|
|
418
|
-
/* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.label, ": ", item.value),
|
|
419
|
-
item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
|
|
420
|
-
"span",
|
|
421
|
-
{
|
|
422
|
-
className: `text-xs px-2 py-1 rounded ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
|
|
423
|
-
},
|
|
424
|
-
item.inStock ? "In Stock" : "Out of Stock"
|
|
425
|
-
)
|
|
426
|
-
))));
|
|
672
|
+
if (type === "table") {
|
|
673
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse space-y-2" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }));
|
|
674
|
+
}
|
|
675
|
+
return /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement("div", { key: i, className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10", style: { height: `${40 + i % 3 * 25}%` } })));
|
|
427
676
|
}
|
|
428
|
-
function
|
|
429
|
-
if (
|
|
430
|
-
|
|
677
|
+
function PieChartView({ data, isStreaming }) {
|
|
678
|
+
if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "pie" });
|
|
679
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
680
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
|
|
431
681
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
backgroundColor: generateColor(idx)
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
)), item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
|
|
442
|
-
"span",
|
|
682
|
+
const chartData = data.map((item) => ({
|
|
683
|
+
name: item.label,
|
|
684
|
+
value: item.value
|
|
685
|
+
}));
|
|
686
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 280 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.PieChart, null, /* @__PURE__ */ import_react7.default.createElement(
|
|
687
|
+
import_recharts2.Pie,
|
|
443
688
|
{
|
|
444
|
-
|
|
689
|
+
data: chartData,
|
|
690
|
+
dataKey: "value",
|
|
691
|
+
nameKey: "name",
|
|
692
|
+
cx: "50%",
|
|
693
|
+
cy: "50%",
|
|
694
|
+
outerRadius: "68%",
|
|
695
|
+
label: false,
|
|
696
|
+
labelLine: false
|
|
445
697
|
},
|
|
446
|
-
|
|
447
|
-
)))));
|
|
698
|
+
chartData.map((_entry, index) => /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Cell, { key: `cell-${index}`, fill: getColor(index) }))
|
|
699
|
+
), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react7.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
|
|
448
700
|
}
|
|
449
|
-
function
|
|
450
|
-
if (
|
|
451
|
-
|
|
701
|
+
function BarChartView({ data, primaryColor, isStreaming }) {
|
|
702
|
+
if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "bar" });
|
|
703
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
704
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
|
|
452
705
|
}
|
|
453
|
-
|
|
454
|
-
|
|
706
|
+
const chartData = data.map((item) => __spreadValues(__spreadValues({
|
|
707
|
+
category: item.category,
|
|
708
|
+
value: item.value
|
|
709
|
+
}, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
|
|
710
|
+
const barColor = primaryColor != null ? primaryColor : getColor(0);
|
|
711
|
+
const hasStock = chartData.some((d) => "inStockCount" in d);
|
|
712
|
+
return /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.BarChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react7.default.createElement(
|
|
713
|
+
import_recharts2.XAxis,
|
|
455
714
|
{
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
))));
|
|
715
|
+
dataKey: "category",
|
|
716
|
+
tick: { fontSize: 11, fill: "#64748b" },
|
|
717
|
+
tickLine: false,
|
|
718
|
+
axisLine: { stroke: "#cbd5e1" }
|
|
719
|
+
}
|
|
720
|
+
), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react7.default.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
|
|
462
721
|
}
|
|
463
|
-
function
|
|
464
|
-
if (
|
|
465
|
-
|
|
722
|
+
function LineChartView({ data, primaryColor, isStreaming }) {
|
|
723
|
+
if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "bar" });
|
|
724
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
725
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
|
|
466
726
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
{
|
|
727
|
+
const chartData = data.map((point) => {
|
|
728
|
+
var _a;
|
|
729
|
+
return {
|
|
730
|
+
label: String((_a = point.label) != null ? _a : point.timestamp),
|
|
731
|
+
value: Number(point.value)
|
|
732
|
+
};
|
|
733
|
+
});
|
|
734
|
+
const lineColor = primaryColor != null ? primaryColor : getColor(0);
|
|
735
|
+
return /* @__PURE__ */ import_react7.default.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__ */ import_react7.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.LineChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react7.default.createElement(
|
|
736
|
+
import_recharts2.XAxis,
|
|
737
|
+
{
|
|
738
|
+
dataKey: "label",
|
|
739
|
+
tick: { fontSize: 11, fill: "#64748b" },
|
|
740
|
+
tickLine: false,
|
|
741
|
+
axisLine: { stroke: "#cbd5e1" }
|
|
742
|
+
}
|
|
743
|
+
), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react7.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react7.default.createElement(
|
|
744
|
+
import_recharts2.Line,
|
|
745
|
+
{
|
|
746
|
+
type: "monotone",
|
|
747
|
+
dataKey: "value",
|
|
748
|
+
name: "Value",
|
|
749
|
+
stroke: lineColor,
|
|
750
|
+
strokeWidth: 2.5,
|
|
751
|
+
dot: { r: 3.5, strokeWidth: 2, fill: lineColor },
|
|
752
|
+
activeDot: { r: 5 }
|
|
753
|
+
}
|
|
754
|
+
))));
|
|
755
|
+
}
|
|
756
|
+
function RadarChartView({ data, isStreaming }) {
|
|
757
|
+
if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "pie" });
|
|
758
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
759
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
|
|
760
|
+
}
|
|
761
|
+
const productNames = Array.from(
|
|
762
|
+
new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
|
|
763
|
+
);
|
|
764
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 300 }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ import_react7.default.createElement(import_recharts2.PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ import_react7.default.createElement(
|
|
765
|
+
import_recharts2.Radar,
|
|
766
|
+
{
|
|
767
|
+
key: productName,
|
|
768
|
+
name: productName,
|
|
769
|
+
dataKey: productName,
|
|
770
|
+
stroke: getColor(index),
|
|
771
|
+
fill: getColor(index),
|
|
772
|
+
fillOpacity: 0.4
|
|
773
|
+
}
|
|
774
|
+
)), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react7.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react7.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }))));
|
|
775
|
+
}
|
|
776
|
+
function TableView({ data, isStreaming }) {
|
|
777
|
+
if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "table" });
|
|
778
|
+
if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
|
|
779
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
|
|
780
|
+
}
|
|
781
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react7.default.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ import_react7.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react7.default.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ import_react7.default.createElement(
|
|
782
|
+
"th",
|
|
783
|
+
{
|
|
470
784
|
key: idx,
|
|
471
|
-
className: "px-4 py-
|
|
785
|
+
className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
|
|
472
786
|
},
|
|
473
787
|
col
|
|
474
|
-
)))), /* @__PURE__ */
|
|
788
|
+
)))), /* @__PURE__ */ import_react7.default.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ import_react7.default.createElement(
|
|
475
789
|
"tr",
|
|
476
790
|
{
|
|
477
791
|
key: rowIdx,
|
|
478
|
-
className: "hover:bg-
|
|
792
|
+
className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
479
793
|
},
|
|
480
|
-
row.map((cell, cellIdx) => /* @__PURE__ */
|
|
794
|
+
row.map((cell, cellIdx) => /* @__PURE__ */ import_react7.default.createElement(
|
|
481
795
|
"td",
|
|
482
796
|
{
|
|
483
797
|
key: cellIdx,
|
|
484
|
-
className: "px-4 py-
|
|
798
|
+
className: "px-4 py-3 text-slate-600 dark:text-white/70 text-sm"
|
|
485
799
|
},
|
|
486
800
|
String(cell)
|
|
487
801
|
))
|
|
488
|
-
)))));
|
|
802
|
+
))))), /* @__PURE__ */ import_react7.default.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ import_react7.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
|
|
489
803
|
}
|
|
490
|
-
function
|
|
804
|
+
function CarouselView({
|
|
805
|
+
data,
|
|
806
|
+
onAddToCart,
|
|
807
|
+
primaryColor
|
|
808
|
+
}) {
|
|
491
809
|
if (!Array.isArray(data)) {
|
|
492
|
-
return /* @__PURE__ */
|
|
810
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
|
|
493
811
|
}
|
|
494
|
-
|
|
812
|
+
const products = data.map((product) => ({
|
|
813
|
+
id: product.id,
|
|
814
|
+
name: product.name,
|
|
815
|
+
brand: product.brand,
|
|
816
|
+
price: product.price,
|
|
817
|
+
image: product.image,
|
|
818
|
+
link: typeof product.link === "string" ? product.link : void 0,
|
|
819
|
+
description: product.description,
|
|
820
|
+
inStock: product.inStock
|
|
821
|
+
}));
|
|
822
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react7.default.createElement(
|
|
823
|
+
ProductCarousel,
|
|
824
|
+
{
|
|
825
|
+
products,
|
|
826
|
+
primaryColor: primaryColor != null ? primaryColor : "#6366f1",
|
|
827
|
+
onAddToCart: (p) => onAddToCart == null ? void 0 : onAddToCart(p)
|
|
828
|
+
}
|
|
829
|
+
));
|
|
830
|
+
}
|
|
831
|
+
function TextView({ data }) {
|
|
832
|
+
const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
|
|
833
|
+
return /* @__PURE__ */ import_react7.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react7.default.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// src/components/ObservabilityPanel.tsx
|
|
837
|
+
var import_react8 = __toESM(require("react"));
|
|
838
|
+
var import_lucide_react5 = require("lucide-react");
|
|
839
|
+
function fmtMs(ms) {
|
|
840
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
|
|
841
|
+
}
|
|
842
|
+
function fmtTokens(n) {
|
|
843
|
+
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
844
|
+
}
|
|
845
|
+
function ScoreBadge({ score }) {
|
|
846
|
+
const pct = Math.round(score * 100);
|
|
847
|
+
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";
|
|
848
|
+
return /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(import_lucide_react5.CheckCircle, { className: "w-3 h-3" }) : /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.AlertCircle, { className: "w-3 h-3" }), pct, "% hallucination risk");
|
|
849
|
+
}
|
|
850
|
+
function RetrievalTab({ trace }) {
|
|
851
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("span", { className: "ml-2 italic" }, "(query rewritten: ", /* @__PURE__ */ import_react8.default.createElement("em", { className: "text-indigo-500" }, trace.rewrittenQuery), ")")), /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-1.5 max-h-56 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => /* @__PURE__ */ import_react8.default.createElement(
|
|
495
852
|
"div",
|
|
496
853
|
{
|
|
497
|
-
key:
|
|
498
|
-
className: "
|
|
854
|
+
key: String(chunk.id),
|
|
855
|
+
className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5"
|
|
499
856
|
},
|
|
500
|
-
|
|
501
|
-
|
|
857
|
+
/* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center justify-between mb-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, "[", i + 1, "] ", String(chunk.id).slice(0, 20)), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, "score: ", chunk.score.toFixed(3))),
|
|
858
|
+
/* @__PURE__ */ import_react8.default.createElement("div", { className: "h-1 rounded-full bg-slate-200 dark:bg-white/10 mb-2 overflow-hidden" }, /* @__PURE__ */ import_react8.default.createElement(
|
|
859
|
+
"div",
|
|
502
860
|
{
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
fill: true,
|
|
506
|
-
className: "object-cover",
|
|
507
|
-
sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
|
861
|
+
className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
|
|
862
|
+
style: { width: `${Math.min(100, chunk.score * 100)}%` }
|
|
508
863
|
}
|
|
509
864
|
)),
|
|
510
|
-
/* @__PURE__ */
|
|
865
|
+
/* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 line-clamp-2 leading-relaxed" }, chunk.content)
|
|
866
|
+
))));
|
|
867
|
+
}
|
|
868
|
+
function LatencyTab({ trace }) {
|
|
869
|
+
const { latency } = trace;
|
|
870
|
+
const bars = [
|
|
871
|
+
{ label: "Embed", ms: latency.embedMs, color: "#6366f1" },
|
|
872
|
+
{ label: "Retrieve", ms: latency.retrieveMs, color: "#10b981" },
|
|
873
|
+
...latency.rerankMs != null ? [{ label: "Rerank", ms: latency.rerankMs, color: "#f59e0b" }] : [],
|
|
874
|
+
{ label: "Generate", ms: latency.generateMs, color: "#8b5cf6" }
|
|
875
|
+
];
|
|
876
|
+
const maxMs = Math.max(...bars.map((b) => b.ms), 1);
|
|
877
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] text-slate-500 dark:text-white/40" }, "Pipeline stages"), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] font-semibold text-slate-700 dark:text-white/70" }, "Total: ", fmtMs(latency.totalMs))), /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-2" }, bars.map((bar) => /* @__PURE__ */ import_react8.default.createElement("div", { key: bar.label, className: "space-y-0.5" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] text-slate-600 dark:text-white/60" }, bar.label), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, fmtMs(bar.ms))), /* @__PURE__ */ import_react8.default.createElement("div", { className: "h-2 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ import_react8.default.createElement(
|
|
878
|
+
"div",
|
|
879
|
+
{
|
|
880
|
+
className: "h-full rounded-full transition-all duration-700",
|
|
881
|
+
style: {
|
|
882
|
+
width: `${bar.ms / maxMs * 100}%`,
|
|
883
|
+
background: bar.color,
|
|
884
|
+
opacity: 0.85
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
))))));
|
|
888
|
+
}
|
|
889
|
+
function TokensTab({ trace }) {
|
|
890
|
+
const { tokens } = trace;
|
|
891
|
+
if (!tokens) {
|
|
892
|
+
return /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Token data not available.");
|
|
893
|
+
}
|
|
894
|
+
const stats = [
|
|
895
|
+
{ label: "Prompt Tokens", value: fmtTokens(tokens.promptTokens), color: "text-indigo-600 dark:text-indigo-400" },
|
|
896
|
+
{ label: "Completion Tokens", value: fmtTokens(tokens.completionTokens), color: "text-emerald-600 dark:text-emerald-400" },
|
|
897
|
+
{ label: "Total Tokens", value: fmtTokens(tokens.totalTokens), color: "text-slate-700 dark:text-white/80" }
|
|
898
|
+
];
|
|
899
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "grid grid-cols-3 gap-2" }, stats.map((s) => /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("p", { className: `text-lg font-bold ${s.color}` }, s.value), /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 mt-0.5" }, s.label)))), tokens.estimatedCostUsd != null && /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("span", { className: "text-[11px] text-emerald-700 dark:text-emerald-400 font-medium" }, "Estimated Cost"), /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("div", { className: "h-2 rounded-full overflow-hidden flex" }, /* @__PURE__ */ import_react8.default.createElement(
|
|
900
|
+
"div",
|
|
901
|
+
{
|
|
902
|
+
className: "h-full bg-indigo-500 dark:bg-indigo-400",
|
|
903
|
+
style: { width: `${tokens.promptTokens / tokens.totalTokens * 100}%` },
|
|
904
|
+
title: "Prompt tokens"
|
|
905
|
+
}
|
|
906
|
+
), /* @__PURE__ */ import_react8.default.createElement(
|
|
907
|
+
"div",
|
|
908
|
+
{
|
|
909
|
+
className: "h-full bg-emerald-500 dark:bg-emerald-400",
|
|
910
|
+
style: { width: `${tokens.completionTokens / tokens.totalTokens * 100}%` },
|
|
911
|
+
title: "Completion tokens"
|
|
912
|
+
}
|
|
913
|
+
)), /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex gap-3 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-indigo-500" }), " Prompt"), /* @__PURE__ */ import_react8.default.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " Completion")));
|
|
914
|
+
}
|
|
915
|
+
function HallucinationTab({ trace }) {
|
|
916
|
+
if (trace.hallucinationScore == null) {
|
|
917
|
+
return /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Hallucination scoring not available for this response.");
|
|
918
|
+
}
|
|
919
|
+
const score = trace.hallucinationScore;
|
|
920
|
+
const pct = Math.round(score * 100);
|
|
921
|
+
const barColor = pct <= 20 ? "#10b981" : pct <= 50 ? "#f59e0b" : "#ef4444";
|
|
922
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react8.default.createElement(ScoreBadge, { score }), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, pct, "/100")), /* @__PURE__ */ import_react8.default.createElement("div", { className: "h-3 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ import_react8.default.createElement(
|
|
923
|
+
"div",
|
|
924
|
+
{
|
|
925
|
+
className: "h-full rounded-full transition-all duration-700",
|
|
926
|
+
style: { width: `${pct}%`, background: barColor }
|
|
927
|
+
}
|
|
928
|
+
)), trace.hallucinationReason && /* @__PURE__ */ import_react8.default.createElement("div", { className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5" }, /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/40 font-medium mb-0.5" }, "AI self-assessment"), /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] text-slate-700 dark:text-white/70 leading-relaxed" }, trace.hallucinationReason)), /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex gap-2 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " 0\u201320%: Grounded"), /* @__PURE__ */ import_react8.default.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-amber-500" }), " 21\u201350%: Uncertain"), /* @__PURE__ */ import_react8.default.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react8.default.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-red-500" }), " 51\u2013100%: Risk")));
|
|
929
|
+
}
|
|
930
|
+
function PromptTab({ trace }) {
|
|
931
|
+
const [showSystem, setShowSystem] = import_react8.default.useState(false);
|
|
932
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ import_react8.default.createElement("div", null, /* @__PURE__ */ import_react8.default.createElement(
|
|
933
|
+
"button",
|
|
934
|
+
{
|
|
935
|
+
onClick: () => setShowSystem((s) => !s),
|
|
936
|
+
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"
|
|
937
|
+
},
|
|
938
|
+
showSystem ? /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.ChevronRight, { className: "w-3 h-3" }),
|
|
939
|
+
"System Prompt"
|
|
940
|
+
), showSystem && /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("div", null, /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[11px] font-semibold text-slate-600 dark:text-white/60 mb-1.5" }, "User Prompt"), /* @__PURE__ */ import_react8.default.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)));
|
|
941
|
+
}
|
|
942
|
+
function ChunksTab({ trace }) {
|
|
943
|
+
const [expanded, setExpanded] = import_react8.default.useState(/* @__PURE__ */ new Set());
|
|
944
|
+
const toggle = (i) => setExpanded((prev) => {
|
|
945
|
+
const next = new Set(prev);
|
|
946
|
+
if (next.has(i)) {
|
|
947
|
+
next.delete(i);
|
|
948
|
+
} else {
|
|
949
|
+
next.add(i);
|
|
950
|
+
}
|
|
951
|
+
return next;
|
|
952
|
+
});
|
|
953
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", { className: "space-y-1.5 max-h-64 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => {
|
|
954
|
+
const isOpen = expanded.has(i);
|
|
955
|
+
return /* @__PURE__ */ import_react8.default.createElement(
|
|
956
|
+
"div",
|
|
957
|
+
{
|
|
958
|
+
key: String(chunk.id),
|
|
959
|
+
className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 overflow-hidden"
|
|
960
|
+
},
|
|
961
|
+
/* @__PURE__ */ import_react8.default.createElement(
|
|
962
|
+
"button",
|
|
963
|
+
{
|
|
964
|
+
onClick: () => toggle(i),
|
|
965
|
+
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"
|
|
966
|
+
},
|
|
967
|
+
/* @__PURE__ */ import_react8.default.createElement("div", { className: "flex items-center gap-2" }, isOpen ? /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.ChevronDown, { className: "w-3 h-3 text-slate-400" }) : /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.ChevronRight, { className: "w-3 h-3 text-slate-400" }), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[11px] font-medium text-slate-700 dark:text-white/70" }, "Chunk ", i + 1), /* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, String(chunk.id).slice(0, 16))),
|
|
968
|
+
/* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, chunk.score.toFixed(3))
|
|
969
|
+
),
|
|
970
|
+
isOpen && /* @__PURE__ */ import_react8.default.createElement("div", { className: "px-3 pb-3 space-y-2 border-t border-slate-100 dark:border-white/5 pt-2" }, /* @__PURE__ */ import_react8.default.createElement("div", null, /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Content"), /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement("div", null, /* @__PURE__ */ import_react8.default.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Metadata"), /* @__PURE__ */ import_react8.default.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))))
|
|
971
|
+
);
|
|
972
|
+
}));
|
|
973
|
+
}
|
|
974
|
+
var TABS = [
|
|
975
|
+
{ id: "retrieval", label: "Retrieval", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.Layers, { className: "w-3 h-3" }) },
|
|
976
|
+
{ id: "latency", label: "Latency", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.Clock, { className: "w-3 h-3" }) },
|
|
977
|
+
{ id: "tokens", label: "Tokens", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.Cpu, { className: "w-3 h-3" }) },
|
|
978
|
+
{ id: "hallucination", label: "Grounding", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.AlertTriangle, { className: "w-3 h-3" }) },
|
|
979
|
+
{ id: "prompt", label: "Prompt", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.FileText, { className: "w-3 h-3" }) },
|
|
980
|
+
{ id: "chunks", label: "Chunks", icon: /* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.Zap, { className: "w-3 h-3" }) }
|
|
981
|
+
];
|
|
982
|
+
function ObservabilityPanel({ trace, primaryColor = "#6366f1" }) {
|
|
983
|
+
const [activeTab, setActiveTab] = import_react8.default.useState("retrieval");
|
|
984
|
+
return /* @__PURE__ */ import_react8.default.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__ */ import_react8.default.createElement(
|
|
985
|
+
"div",
|
|
986
|
+
{
|
|
987
|
+
className: "flex items-center gap-2 px-3 py-2 border-b border-slate-100 dark:border-white/8",
|
|
988
|
+
style: { background: `${primaryColor}08` }
|
|
989
|
+
},
|
|
990
|
+
/* @__PURE__ */ import_react8.default.createElement(
|
|
511
991
|
"span",
|
|
512
992
|
{
|
|
513
|
-
className:
|
|
993
|
+
className: "inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider",
|
|
994
|
+
style: { color: primaryColor }
|
|
514
995
|
},
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
996
|
+
/* @__PURE__ */ import_react8.default.createElement(import_lucide_react5.Zap, { className: "w-3 h-3" }),
|
|
997
|
+
"Observability Trace"
|
|
998
|
+
),
|
|
999
|
+
/* @__PURE__ */ import_react8.default.createElement("span", { className: "text-[10px] text-slate-400 dark:text-white/30 ml-auto font-mono" }, trace.requestId)
|
|
1000
|
+
), /* @__PURE__ */ import_react8.default.createElement("div", { className: "flex border-b border-slate-100 dark:border-white/8 overflow-x-auto" }, TABS.map((tab) => /* @__PURE__ */ import_react8.default.createElement(
|
|
1001
|
+
"button",
|
|
1002
|
+
{
|
|
1003
|
+
key: tab.id,
|
|
1004
|
+
onClick: () => setActiveTab(tab.id),
|
|
1005
|
+
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"}`,
|
|
1006
|
+
style: activeTab === tab.id ? { borderBottomColor: primaryColor, color: primaryColor } : {}
|
|
1007
|
+
},
|
|
1008
|
+
tab.icon,
|
|
1009
|
+
tab.label
|
|
1010
|
+
))), /* @__PURE__ */ import_react8.default.createElement("div", { className: "p-3" }, activeTab === "retrieval" && /* @__PURE__ */ import_react8.default.createElement(RetrievalTab, { trace }), activeTab === "latency" && /* @__PURE__ */ import_react8.default.createElement(LatencyTab, { trace }), activeTab === "tokens" && /* @__PURE__ */ import_react8.default.createElement(TokensTab, { trace }), activeTab === "hallucination" && /* @__PURE__ */ import_react8.default.createElement(HallucinationTab, { trace }), activeTab === "prompt" && /* @__PURE__ */ import_react8.default.createElement(PromptTab, { trace }), activeTab === "chunks" && /* @__PURE__ */ import_react8.default.createElement(ChunksTab, { trace })));
|
|
518
1011
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
1012
|
+
|
|
1013
|
+
// src/components/HourglassLoader.tsx
|
|
1014
|
+
var import_react9 = __toESM(require("react"));
|
|
1015
|
+
var HourglassLoader = ({
|
|
1016
|
+
size = 16,
|
|
1017
|
+
primaryColor = "#10b981",
|
|
1018
|
+
glow = false
|
|
1019
|
+
}) => {
|
|
1020
|
+
return /* @__PURE__ */ import_react9.default.createElement(
|
|
1021
|
+
"svg",
|
|
1022
|
+
{
|
|
1023
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1024
|
+
viewBox: "0 0 100 100",
|
|
1025
|
+
preserveAspectRatio: "xMidYMid",
|
|
1026
|
+
style: {
|
|
1027
|
+
width: size,
|
|
1028
|
+
height: size,
|
|
1029
|
+
background: "transparent",
|
|
1030
|
+
filter: glow ? `drop-shadow(0 0 4px ${primaryColor})` : "none"
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
/* @__PURE__ */ import_react9.default.createElement("g", null, /* @__PURE__ */ import_react9.default.createElement(
|
|
1034
|
+
"animateTransform",
|
|
1035
|
+
{
|
|
1036
|
+
attributeName: "transform",
|
|
1037
|
+
type: "rotate",
|
|
1038
|
+
calcMode: "spline",
|
|
1039
|
+
values: "0 50 50; 0 50 50; 180 50 50",
|
|
1040
|
+
keyTimes: "0; 0.8; 1",
|
|
1041
|
+
keySplines: "0 0 1 1; 0.4 0 0.2 1",
|
|
1042
|
+
dur: "2s",
|
|
1043
|
+
repeatCount: "indefinite"
|
|
1044
|
+
}
|
|
1045
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
1046
|
+
"path",
|
|
1047
|
+
{
|
|
1048
|
+
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",
|
|
1049
|
+
fill: "none",
|
|
1050
|
+
stroke: primaryColor,
|
|
1051
|
+
strokeWidth: "5",
|
|
1052
|
+
strokeLinejoin: "round",
|
|
1053
|
+
strokeLinecap: "round"
|
|
1054
|
+
}
|
|
1055
|
+
), /* @__PURE__ */ import_react9.default.createElement("path", { fill: primaryColor }, /* @__PURE__ */ import_react9.default.createElement(
|
|
1056
|
+
"animate",
|
|
1057
|
+
{
|
|
1058
|
+
attributeName: "d",
|
|
1059
|
+
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",
|
|
1060
|
+
keyTimes: "0; 0.8; 1",
|
|
1061
|
+
dur: "2s",
|
|
1062
|
+
repeatCount: "indefinite"
|
|
1063
|
+
}
|
|
1064
|
+
)), /* @__PURE__ */ import_react9.default.createElement("path", { fill: primaryColor }, /* @__PURE__ */ import_react9.default.createElement(
|
|
1065
|
+
"animate",
|
|
1066
|
+
{
|
|
1067
|
+
attributeName: "d",
|
|
1068
|
+
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",
|
|
1069
|
+
keyTimes: "0; 0.8; 1",
|
|
1070
|
+
dur: "2s",
|
|
1071
|
+
repeatCount: "indefinite"
|
|
1072
|
+
}
|
|
1073
|
+
)), /* @__PURE__ */ import_react9.default.createElement("line", { x1: "50", y1: "50", x2: "50", y2: "80", stroke: primaryColor, strokeWidth: "3" }, /* @__PURE__ */ import_react9.default.createElement(
|
|
1074
|
+
"animate",
|
|
1075
|
+
{
|
|
1076
|
+
attributeName: "opacity",
|
|
1077
|
+
values: "0; 1; 1; 0; 0",
|
|
1078
|
+
keyTimes: "0; 0.1; 0.7; 0.8; 1",
|
|
1079
|
+
dur: "2s",
|
|
1080
|
+
repeatCount: "indefinite"
|
|
1081
|
+
}
|
|
1082
|
+
)))
|
|
1083
|
+
);
|
|
1084
|
+
};
|
|
1085
|
+
var HourglassLoader_default = HourglassLoader;
|
|
1086
|
+
|
|
1087
|
+
// src/utils/synonyms.ts
|
|
1088
|
+
var FIELD_SYNONYMS = {
|
|
1089
|
+
name: ["product", "item", "title", "label", "heading", "subject", "product name", "item name"],
|
|
1090
|
+
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
1091
|
+
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
1092
|
+
image: [
|
|
1093
|
+
"imageUrl",
|
|
1094
|
+
"thumbnail",
|
|
1095
|
+
"img",
|
|
1096
|
+
"url",
|
|
1097
|
+
"photo",
|
|
1098
|
+
"picture",
|
|
1099
|
+
"media",
|
|
1100
|
+
"image_url",
|
|
1101
|
+
"main_image",
|
|
1102
|
+
"product_image",
|
|
1103
|
+
"thumb"
|
|
1104
|
+
],
|
|
1105
|
+
stock: [
|
|
1106
|
+
"inventory",
|
|
1107
|
+
"quantity",
|
|
1108
|
+
"count",
|
|
1109
|
+
"availability",
|
|
1110
|
+
"stock_level",
|
|
1111
|
+
"inStock",
|
|
1112
|
+
"is_available",
|
|
1113
|
+
"in stock",
|
|
1114
|
+
"status"
|
|
1115
|
+
],
|
|
1116
|
+
description: ["summary", "content", "body", "text", "info", "details"],
|
|
1117
|
+
link: ["url", "href", "product_url", "page_url", "link"]
|
|
1118
|
+
};
|
|
1119
|
+
function addSynonyms(customSynonyms) {
|
|
1120
|
+
for (const [key, aliases] of Object.entries(customSynonyms)) {
|
|
1121
|
+
if (!FIELD_SYNONYMS[key]) {
|
|
1122
|
+
FIELD_SYNONYMS[key] = [];
|
|
1123
|
+
}
|
|
1124
|
+
const existing = new Set(FIELD_SYNONYMS[key].map((s) => s.toLowerCase()));
|
|
1125
|
+
for (const alias of aliases) {
|
|
1126
|
+
if (!existing.has(alias.toLowerCase())) {
|
|
1127
|
+
FIELD_SYNONYMS[key].push(alias);
|
|
1128
|
+
existing.add(alias.toLowerCase());
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
522
1132
|
}
|
|
523
|
-
function
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
"
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1133
|
+
function resolveMetadataValue(meta, uiKey) {
|
|
1134
|
+
var _a;
|
|
1135
|
+
const synonyms = (_a = FIELD_SYNONYMS[uiKey]) != null ? _a : [];
|
|
1136
|
+
const keys = Object.keys(meta);
|
|
1137
|
+
const systemKeys = ["namespace", "filename", "filesize", "filetype", "docid", "uploadedat", "dimension", "chunkindex"];
|
|
1138
|
+
let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
|
|
1139
|
+
if (match !== void 0) return meta[match];
|
|
1140
|
+
match = keys.find((k) => synonyms.map((s) => s.toLowerCase()).includes(k.toLowerCase()));
|
|
1141
|
+
if (match !== void 0) return meta[match];
|
|
1142
|
+
const isBlacklisted = (kl) => {
|
|
1143
|
+
return systemKeys.includes(kl) || kl.startsWith("option1") || kl.startsWith("option2") || kl.startsWith("option3");
|
|
1144
|
+
};
|
|
1145
|
+
match = keys.find((k) => {
|
|
1146
|
+
const kl = k.toLowerCase();
|
|
1147
|
+
if (isBlacklisted(kl)) return false;
|
|
1148
|
+
return kl.includes(uiKey.toLowerCase());
|
|
1149
|
+
});
|
|
1150
|
+
if (match !== void 0) return meta[match];
|
|
1151
|
+
match = keys.find((k) => {
|
|
1152
|
+
const kl = k.toLowerCase();
|
|
1153
|
+
if (isBlacklisted(kl)) return false;
|
|
1154
|
+
return synonyms.some((sk) => kl.includes(sk.toLowerCase()) || sk.toLowerCase().includes(kl));
|
|
1155
|
+
});
|
|
1156
|
+
return match !== void 0 ? meta[match] : void 0;
|
|
543
1157
|
}
|
|
544
1158
|
|
|
545
|
-
// src/
|
|
1159
|
+
// src/utils/ProductExtractor.ts
|
|
546
1160
|
function sanitizeJson(raw) {
|
|
547
1161
|
let s = raw.trim();
|
|
548
1162
|
s = s.replace(
|
|
@@ -559,37 +1173,35 @@ function sanitizeJson(raw) {
|
|
|
559
1173
|
s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
|
|
560
1174
|
s = s.replace(/,\s*([}\]])/g, "$1");
|
|
561
1175
|
s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
continue;
|
|
570
|
-
}
|
|
571
|
-
if (ch === "\\" && inString) {
|
|
572
|
-
escape = true;
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
if (ch === '"') {
|
|
576
|
-
inString = !inString;
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
if (inString) continue;
|
|
580
|
-
if (ch === "{" || ch === "[") {
|
|
581
|
-
stack.push(ch);
|
|
582
|
-
} else if (ch === "}") {
|
|
583
|
-
if (stack[stack.length - 1] === "{") stack.pop();
|
|
584
|
-
} else if (ch === "]") {
|
|
585
|
-
if (stack[stack.length - 1] === "[") stack.pop();
|
|
586
|
-
}
|
|
1176
|
+
const stack = [];
|
|
1177
|
+
let inString = false;
|
|
1178
|
+
let escape = false;
|
|
1179
|
+
for (const ch of s) {
|
|
1180
|
+
if (escape) {
|
|
1181
|
+
escape = false;
|
|
1182
|
+
continue;
|
|
587
1183
|
}
|
|
588
|
-
if (inString)
|
|
589
|
-
|
|
590
|
-
|
|
1184
|
+
if (ch === "\\" && inString) {
|
|
1185
|
+
escape = true;
|
|
1186
|
+
continue;
|
|
1187
|
+
}
|
|
1188
|
+
if (ch === '"') {
|
|
1189
|
+
inString = !inString;
|
|
1190
|
+
continue;
|
|
1191
|
+
}
|
|
1192
|
+
if (inString) continue;
|
|
1193
|
+
if (ch === "{" || ch === "[") {
|
|
1194
|
+
stack.push(ch);
|
|
1195
|
+
} else if (ch === "}") {
|
|
1196
|
+
if (stack[stack.length - 1] === "{") stack.pop();
|
|
1197
|
+
} else if (ch === "]") {
|
|
1198
|
+
if (stack[stack.length - 1] === "[") stack.pop();
|
|
591
1199
|
}
|
|
592
1200
|
}
|
|
1201
|
+
if (inString) s += '"';
|
|
1202
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
1203
|
+
s += stack[i] === "{" ? "}" : "]";
|
|
1204
|
+
}
|
|
593
1205
|
const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
|
|
594
1206
|
if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
|
|
595
1207
|
s = s.substring(0, lastClose + 1);
|
|
@@ -623,41 +1235,51 @@ function stripStructuredUiLabels(raw) {
|
|
|
623
1235
|
);
|
|
624
1236
|
}
|
|
625
1237
|
function extractBareJsonObject(raw) {
|
|
626
|
-
|
|
1238
|
+
let start = raw.indexOf("{");
|
|
627
1239
|
if (start === -1) return null;
|
|
628
|
-
let
|
|
629
|
-
let
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
if (
|
|
650
|
-
|
|
1240
|
+
let bestJson = null;
|
|
1241
|
+
let maxLen = 0;
|
|
1242
|
+
while (start !== -1) {
|
|
1243
|
+
let depth = 0;
|
|
1244
|
+
let inString = false;
|
|
1245
|
+
let escape = false;
|
|
1246
|
+
for (let i = start; i < raw.length; i += 1) {
|
|
1247
|
+
const ch = raw[i];
|
|
1248
|
+
if (escape) {
|
|
1249
|
+
escape = false;
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
if (ch === "\\" && inString) {
|
|
1253
|
+
escape = true;
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
if (ch === '"') {
|
|
1257
|
+
inString = !inString;
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (inString) continue;
|
|
1261
|
+
if (ch === "{") depth += 1;
|
|
1262
|
+
else if (ch === "}") {
|
|
1263
|
+
depth -= 1;
|
|
1264
|
+
if (depth === 0) {
|
|
1265
|
+
const candidate = raw.slice(start, i + 1);
|
|
1266
|
+
if (candidate.length > maxLen) {
|
|
1267
|
+
maxLen = candidate.length;
|
|
1268
|
+
bestJson = candidate;
|
|
1269
|
+
}
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
651
1272
|
}
|
|
652
1273
|
}
|
|
1274
|
+
start = raw.indexOf("{", start + 1);
|
|
653
1275
|
}
|
|
654
|
-
return
|
|
1276
|
+
return bestJson;
|
|
655
1277
|
}
|
|
656
1278
|
function extractStructuredPayload(raw) {
|
|
657
1279
|
var _a, _b;
|
|
658
1280
|
let working = raw;
|
|
659
1281
|
const fenceRegex = /```(?:[a-z]+)?\s*([\s\S]*?)```/gi;
|
|
660
|
-
for (const match of raw.matchAll(fenceRegex)) {
|
|
1282
|
+
for (const match of Array.from(raw.matchAll(fenceRegex))) {
|
|
661
1283
|
const body = (_b = (_a = match[1]) == null ? void 0 : _a.trim()) != null ? _b : "";
|
|
662
1284
|
if (!looksLikeStructuredPayload(body)) continue;
|
|
663
1285
|
working = working.replace(match[0], "");
|
|
@@ -679,98 +1301,158 @@ function extractStructuredPayload(raw) {
|
|
|
679
1301
|
text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
|
|
680
1302
|
};
|
|
681
1303
|
}
|
|
682
|
-
function isLikelyUiOnlyMessage(text) {
|
|
683
|
-
const trimmed = text.trim();
|
|
684
|
-
return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed);
|
|
685
|
-
}
|
|
686
1304
|
function resolveImage(data) {
|
|
687
1305
|
const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
|
|
688
|
-
for (const key of ["image", "img", "thumbnail"]) {
|
|
1306
|
+
for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
|
|
689
1307
|
const v = data[key];
|
|
690
1308
|
if (typeof v === "string" && v && !isPlaceholder(v)) return v;
|
|
691
|
-
|
|
692
|
-
if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
|
|
693
|
-
return data.images[0];
|
|
1309
|
+
if (Array.isArray(v) && typeof v[0] === "string" && v[0] && !isPlaceholder(v[0])) return v[0];
|
|
694
1310
|
}
|
|
695
1311
|
return void 0;
|
|
696
1312
|
}
|
|
697
|
-
function
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
}
|
|
701
|
-
return children;
|
|
702
|
-
}
|
|
703
|
-
function normaliseFieldName(value) {
|
|
704
|
-
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
705
|
-
}
|
|
706
|
-
function formatColumnLabel(value) {
|
|
707
|
-
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
|
|
1313
|
+
function isLikelyUiOnlyMessage(text) {
|
|
1314
|
+
const trimmed = text.trim();
|
|
1315
|
+
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);
|
|
708
1316
|
}
|
|
709
|
-
function
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
)
|
|
1317
|
+
function extractProductsFromSources(sources, isUser) {
|
|
1318
|
+
if (isUser || !sources) return [];
|
|
1319
|
+
return sources.filter((s) => {
|
|
1320
|
+
var _a;
|
|
1321
|
+
const m = (_a = s.metadata) != null ? _a : {};
|
|
1322
|
+
const keys = Object.keys(m).map((k) => k.toLowerCase());
|
|
1323
|
+
const hasProductKey = keys.some(
|
|
1324
|
+
(k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
|
|
717
1325
|
);
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
1326
|
+
const hasPricePattern = /\$\s*\d+/.test(s.content);
|
|
1327
|
+
return hasProductKey || hasPricePattern || m.type === "product";
|
|
1328
|
+
}).map((s) => {
|
|
1329
|
+
var _a, _b;
|
|
1330
|
+
const m = (_a = s.metadata) != null ? _a : {};
|
|
1331
|
+
const name = resolveMetadataValue(m, "name");
|
|
1332
|
+
const brand = resolveMetadataValue(m, "brand");
|
|
1333
|
+
const price = resolveMetadataValue(m, "price");
|
|
1334
|
+
const description = resolveMetadataValue(m, "description");
|
|
1335
|
+
console.log("\u{1F680} ~ extractProductsFromSources ~ description:", s);
|
|
1336
|
+
return {
|
|
1337
|
+
id: s.id,
|
|
1338
|
+
name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
|
|
1339
|
+
brand,
|
|
1340
|
+
price,
|
|
1341
|
+
image: resolveImage(m),
|
|
1342
|
+
link: resolveMetadataValue(m, "link"),
|
|
1343
|
+
description: description ? description : ""
|
|
1344
|
+
//description ?? s.content,
|
|
725
1345
|
};
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
function extractProductsFromContent(content, payloadText, payloadCandidates, productsFromSources) {
|
|
1349
|
+
var _a, _b;
|
|
1350
|
+
const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
|
|
1351
|
+
const products = [];
|
|
1352
|
+
let cleanContent = payloadText;
|
|
1353
|
+
for (const candidate of payloadCandidates) {
|
|
1354
|
+
try {
|
|
1355
|
+
const data = JSON.parse(sanitizeJson(candidate));
|
|
1356
|
+
const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
|
|
1357
|
+
if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
|
|
1358
|
+
products.push(
|
|
1359
|
+
...itemSet.map((item) => {
|
|
1360
|
+
var _a2;
|
|
1361
|
+
return __spreadProps(__spreadValues({}, item), {
|
|
1362
|
+
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1363
|
+
});
|
|
1364
|
+
})
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1367
|
+
} catch (e) {
|
|
731
1368
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1369
|
+
}
|
|
1370
|
+
if (cleanContent.includes('"type": "products"') || cleanContent.includes('"type":"products"')) {
|
|
1371
|
+
for (const match of Array.from(cleanContent.matchAll(jsonRegex))) {
|
|
1372
|
+
try {
|
|
1373
|
+
const data = JSON.parse(sanitizeJson(match[1]));
|
|
1374
|
+
if (data.type === "products" && Array.isArray(data.items)) {
|
|
1375
|
+
products.push(
|
|
1376
|
+
...data.items.map((item) => {
|
|
1377
|
+
var _a2;
|
|
1378
|
+
return __spreadProps(__spreadValues({}, item), {
|
|
1379
|
+
image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
|
|
1380
|
+
});
|
|
1381
|
+
})
|
|
1382
|
+
);
|
|
1383
|
+
cleanContent = cleanContent.replace(match[0], "");
|
|
1384
|
+
}
|
|
1385
|
+
} catch (e) {
|
|
1386
|
+
}
|
|
738
1387
|
}
|
|
739
|
-
return rowKeys.map((key) => ({ label: formatColumnLabel(key), accessor: key }));
|
|
740
|
-
}, [config]);
|
|
741
|
-
if (!config.data.length || !columns.length) {
|
|
742
|
-
return /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
|
|
743
1388
|
}
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
1389
|
+
const bulletRegex = /^[ \t]*(?:[•*-]|\d+\.)\s+(?:\*\*)?([^\n\-\$–—\(]+?)(?:\*\*)?(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gim;
|
|
1390
|
+
const matches = Array.from(cleanContent.matchAll(bulletRegex));
|
|
1391
|
+
if (matches.length >= 2) {
|
|
1392
|
+
const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1393
|
+
for (const match of matches) {
|
|
1394
|
+
let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
|
|
1395
|
+
name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
|
|
1396
|
+
name = name.replace(/^\*\*|\*\*$/g, "").replace(/^\*|\*$/g, "").trim();
|
|
1397
|
+
let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
|
|
1398
|
+
price = price.replace(/[,\s]+$/, "").trim();
|
|
1399
|
+
if (name && price) {
|
|
1400
|
+
const newProduct = {
|
|
1401
|
+
id: `text-prod-${name}-${price}`,
|
|
1402
|
+
name,
|
|
1403
|
+
price: `$${price}`
|
|
1404
|
+
};
|
|
1405
|
+
const normName = normalize(name);
|
|
1406
|
+
const sourceMatch = productsFromSources.find((s) => {
|
|
1407
|
+
const sn = normalize(s.name);
|
|
1408
|
+
return sn.includes(normName) || normName.includes(sn);
|
|
1409
|
+
});
|
|
1410
|
+
if (sourceMatch) {
|
|
1411
|
+
newProduct.image = sourceMatch.image;
|
|
1412
|
+
newProduct.brand = sourceMatch.brand;
|
|
1413
|
+
newProduct.link = sourceMatch.link;
|
|
1414
|
+
newProduct.description = sourceMatch.description;
|
|
1415
|
+
}
|
|
1416
|
+
products.push(newProduct);
|
|
1417
|
+
cleanContent = cleanContent.replace(match[0], `
|
|
1418
|
+
**${name}** \u2014 $${price}`);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
cleanContent = cleanContent.replace(/\n{3,}/g, "\n\n").trim();
|
|
1422
|
+
}
|
|
1423
|
+
return { productsFromContent: products, cleanContent: cleanContent.trim() };
|
|
1424
|
+
}
|
|
1425
|
+
function deduplicateProducts(productsFromSources, productsFromContent, messageContent) {
|
|
1426
|
+
if (productsFromContent.length > 0) return productsFromContent;
|
|
1427
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1428
|
+
const contentLower = messageContent.toLowerCase();
|
|
1429
|
+
return productsFromSources.filter((p) => {
|
|
1430
|
+
var _a;
|
|
1431
|
+
const id = String((_a = p.id) != null ? _a : p.name);
|
|
1432
|
+
if (seen.has(id)) return false;
|
|
1433
|
+
const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
|
|
1434
|
+
if (mentioned) {
|
|
1435
|
+
seen.add(id);
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1438
|
+
return false;
|
|
1439
|
+
});
|
|
771
1440
|
}
|
|
1441
|
+
|
|
1442
|
+
// src/components/MarkdownComponents.tsx
|
|
1443
|
+
var import_react11 = __toESM(require("react"));
|
|
1444
|
+
|
|
1445
|
+
// src/components/UIDispatcher.tsx
|
|
1446
|
+
var import_react10 = __toESM(require("react"));
|
|
772
1447
|
function normalizeTabularRows(rows, columns) {
|
|
773
1448
|
if (!Array.isArray(rows)) return [];
|
|
1449
|
+
const SYNONYMS = {
|
|
1450
|
+
name: ["product", "item", "title", "label", "heading", "product name", "item name"],
|
|
1451
|
+
price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
|
|
1452
|
+
brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
|
|
1453
|
+
image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media"],
|
|
1454
|
+
stock: ["inventory", "quantity", "count", "availability", "stock_level", "in stock", "status"]
|
|
1455
|
+
};
|
|
774
1456
|
return rows.map((row) => {
|
|
775
1457
|
if (Array.isArray(row)) {
|
|
776
1458
|
const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
|
|
@@ -780,7 +1462,36 @@ function normalizeTabularRows(rows, columns) {
|
|
|
780
1462
|
}, {});
|
|
781
1463
|
}
|
|
782
1464
|
if (row && typeof row === "object") {
|
|
783
|
-
|
|
1465
|
+
const result = __spreadValues({}, row);
|
|
1466
|
+
if (Array.isArray(columns)) {
|
|
1467
|
+
columns.forEach((col) => {
|
|
1468
|
+
if (result[col] !== void 0) return;
|
|
1469
|
+
const rowKeys = Object.keys(row);
|
|
1470
|
+
const colLower = col.toLowerCase().replace(/_/g, " ");
|
|
1471
|
+
const foundKey = rowKeys.find((rk) => {
|
|
1472
|
+
const rkLower = rk.toLowerCase().replace(/_/g, " ");
|
|
1473
|
+
return rkLower === colLower || rkLower.includes(colLower) || colLower.includes(rkLower);
|
|
1474
|
+
});
|
|
1475
|
+
if (foundKey) {
|
|
1476
|
+
result[col] = row[foundKey];
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
for (const [target, aliases] of Object.entries(SYNONYMS)) {
|
|
1480
|
+
const isMatch = target === colLower || aliases.some((a) => colLower.includes(a) || a.includes(colLower));
|
|
1481
|
+
if (isMatch) {
|
|
1482
|
+
const mappedKey = rowKeys.find((rk) => {
|
|
1483
|
+
const rkL = rk.toLowerCase();
|
|
1484
|
+
return rkL === target || aliases.some((a) => rkL.includes(a) || a.includes(rkL));
|
|
1485
|
+
});
|
|
1486
|
+
if (mappedKey) {
|
|
1487
|
+
result[col] = row[mappedKey];
|
|
1488
|
+
break;
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
return result;
|
|
784
1495
|
}
|
|
785
1496
|
return { value: row };
|
|
786
1497
|
}).filter((row) => Object.keys(row).length > 0);
|
|
@@ -800,8 +1511,7 @@ function resolveStructuredRows(config) {
|
|
|
800
1511
|
}
|
|
801
1512
|
function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
|
|
802
1513
|
var _a;
|
|
803
|
-
const result =
|
|
804
|
-
if (isStreaming) return { loading: true };
|
|
1514
|
+
const result = import_react10.default.useMemo(() => {
|
|
805
1515
|
try {
|
|
806
1516
|
const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
|
|
807
1517
|
const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
|
|
@@ -830,10 +1540,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
830
1540
|
} 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)) {
|
|
831
1541
|
config2.view = "chart";
|
|
832
1542
|
config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
|
|
833
|
-
} else if (viewStr.includes("table")) {
|
|
1543
|
+
} else if (viewStr.includes("table") || config2.type === "table") {
|
|
834
1544
|
config2.view = "table";
|
|
835
1545
|
} else {
|
|
836
|
-
config2.view = "
|
|
1546
|
+
config2.view = "none";
|
|
837
1547
|
}
|
|
838
1548
|
}
|
|
839
1549
|
const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
|
|
@@ -841,22 +1551,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
841
1551
|
});
|
|
842
1552
|
return { config: normalizedConfig };
|
|
843
1553
|
} catch (err) {
|
|
1554
|
+
if (isStreaming) return { loading: true };
|
|
844
1555
|
return { error: String(err) };
|
|
845
1556
|
}
|
|
846
1557
|
}, [rawContent, isStreaming]);
|
|
847
1558
|
if ("loading" in result) {
|
|
848
|
-
return /* @__PURE__ */
|
|
1559
|
+
return /* @__PURE__ */ import_react10.default.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__ */ import_react10.default.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react10.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
|
|
849
1560
|
}
|
|
850
1561
|
if ("error" in result) {
|
|
851
|
-
return /* @__PURE__ */
|
|
1562
|
+
return /* @__PURE__ */ import_react10.default.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ import_react10.default.createElement("code", null, rawContent));
|
|
852
1563
|
}
|
|
853
1564
|
const { config } = result;
|
|
854
1565
|
const isCompact = viewportSize === "compact";
|
|
855
1566
|
const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
|
|
856
1567
|
const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
|
|
1568
|
+
console.log("\u{1F680} ~ UIDispatcher ~ description:", config.description);
|
|
1569
|
+
console.log("\u{1F680} ~ UIDispatcher ~ hasDescription:", hasDescription);
|
|
857
1570
|
switch (config.view) {
|
|
858
1571
|
case "chart":
|
|
859
|
-
return /* @__PURE__ */
|
|
1572
|
+
return /* @__PURE__ */ import_react10.default.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__ */ import_react10.default.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ import_react10.default.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react10.default.createElement(
|
|
860
1573
|
DynamicChart,
|
|
861
1574
|
{
|
|
862
1575
|
config: {
|
|
@@ -868,9 +1581,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
868
1581
|
},
|
|
869
1582
|
primaryColor,
|
|
870
1583
|
accentColor,
|
|
871
|
-
viewportSize
|
|
1584
|
+
viewportSize,
|
|
1585
|
+
isStreaming
|
|
872
1586
|
}
|
|
873
|
-
), hasInsights && /* @__PURE__ */
|
|
1587
|
+
), hasInsights && /* @__PURE__ */ import_react10.default.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ import_react10.default.createElement(
|
|
874
1588
|
"span",
|
|
875
1589
|
{
|
|
876
1590
|
key: `insight-${i}`,
|
|
@@ -879,26 +1593,151 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
|
|
|
879
1593
|
String(insight)
|
|
880
1594
|
))));
|
|
881
1595
|
case "carousel":
|
|
882
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ import_react10.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react10.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react10.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react10.default.createElement(ProductCarousel, { products: config.data.map((item) => {
|
|
883
1597
|
var _a2;
|
|
884
1598
|
return __spreadProps(__spreadValues({}, item), {
|
|
885
1599
|
image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
|
|
886
1600
|
});
|
|
887
1601
|
}), primaryColor, onAddToCart }));
|
|
888
1602
|
case "table":
|
|
889
|
-
return /* @__PURE__ */
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1603
|
+
return /* @__PURE__ */ import_react10.default.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__ */ import_react10.default.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ import_react10.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react10.default.createElement(
|
|
1604
|
+
VisualizationRenderer,
|
|
1605
|
+
{
|
|
1606
|
+
data: {
|
|
1607
|
+
type: "table",
|
|
1608
|
+
title: config.title || "",
|
|
1609
|
+
description: config.description,
|
|
1610
|
+
data: {
|
|
1611
|
+
columns: config.columns || Object.keys(config.data[0] || {}),
|
|
1612
|
+
rows: config.data.map((row) => Object.values(row))
|
|
1613
|
+
}
|
|
1614
|
+
},
|
|
1615
|
+
primaryColor,
|
|
1616
|
+
isStreaming
|
|
1617
|
+
}
|
|
1618
|
+
));
|
|
898
1619
|
default:
|
|
899
1620
|
return null;
|
|
900
1621
|
}
|
|
901
1622
|
}
|
|
1623
|
+
|
|
1624
|
+
// src/components/MarkdownComponents.tsx
|
|
1625
|
+
function createMarkdownComponents({
|
|
1626
|
+
primaryColor,
|
|
1627
|
+
accentColor,
|
|
1628
|
+
isStreaming,
|
|
1629
|
+
onAddToCart,
|
|
1630
|
+
viewportSize
|
|
1631
|
+
}) {
|
|
1632
|
+
return {
|
|
1633
|
+
table: (_a) => {
|
|
1634
|
+
var props = __objRest(_a, []);
|
|
1635
|
+
if (isStreaming) {
|
|
1636
|
+
return /* @__PURE__ */ import_react11.default.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__ */ import_react11.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1637
|
+
}
|
|
1638
|
+
return /* @__PURE__ */ import_react11.default.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__ */ import_react11.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react11.default.createElement(
|
|
1639
|
+
"table",
|
|
1640
|
+
__spreadValues({
|
|
1641
|
+
className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
|
|
1642
|
+
}, props)
|
|
1643
|
+
)));
|
|
1644
|
+
},
|
|
1645
|
+
thead: (_b) => {
|
|
1646
|
+
var props = __objRest(_b, []);
|
|
1647
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1648
|
+
"thead",
|
|
1649
|
+
__spreadValues({
|
|
1650
|
+
className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
|
|
1651
|
+
}, props)
|
|
1652
|
+
);
|
|
1653
|
+
},
|
|
1654
|
+
tbody: (_c) => {
|
|
1655
|
+
var props = __objRest(_c, []);
|
|
1656
|
+
return /* @__PURE__ */ import_react11.default.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
|
|
1657
|
+
},
|
|
1658
|
+
tr: (_d) => {
|
|
1659
|
+
var props = __objRest(_d, []);
|
|
1660
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1661
|
+
"tr",
|
|
1662
|
+
__spreadValues({
|
|
1663
|
+
className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
|
|
1664
|
+
}, props)
|
|
1665
|
+
);
|
|
1666
|
+
},
|
|
1667
|
+
th: (_e) => {
|
|
1668
|
+
var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
|
|
1669
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1670
|
+
"th",
|
|
1671
|
+
__spreadValues({
|
|
1672
|
+
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"
|
|
1673
|
+
}, props),
|
|
1674
|
+
normaliseChild(children)
|
|
1675
|
+
);
|
|
1676
|
+
},
|
|
1677
|
+
td: (_g) => {
|
|
1678
|
+
var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
|
|
1679
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1680
|
+
"td",
|
|
1681
|
+
__spreadValues({
|
|
1682
|
+
className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
|
|
1683
|
+
}, props),
|
|
1684
|
+
normaliseChild(children)
|
|
1685
|
+
);
|
|
1686
|
+
},
|
|
1687
|
+
code(_i) {
|
|
1688
|
+
var _j = _i, {
|
|
1689
|
+
inline,
|
|
1690
|
+
className,
|
|
1691
|
+
children
|
|
1692
|
+
} = _j, props = __objRest(_j, [
|
|
1693
|
+
"inline",
|
|
1694
|
+
"className",
|
|
1695
|
+
"children"
|
|
1696
|
+
]);
|
|
1697
|
+
var _a;
|
|
1698
|
+
const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
|
|
1699
|
+
if (!inline && (lang === "ui" || lang === "chart" || lang === "json")) {
|
|
1700
|
+
const content = String(children != null ? children : "").trim();
|
|
1701
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1702
|
+
UIDispatcher,
|
|
1703
|
+
{
|
|
1704
|
+
rawContent: content,
|
|
1705
|
+
primaryColor,
|
|
1706
|
+
accentColor,
|
|
1707
|
+
isStreaming,
|
|
1708
|
+
onAddToCart,
|
|
1709
|
+
viewportSize
|
|
1710
|
+
}
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
if (!inline) {
|
|
1714
|
+
const content = String(children != null ? children : "").trim();
|
|
1715
|
+
if (content.startsWith("{") && content.endsWith("}")) {
|
|
1716
|
+
return /* @__PURE__ */ import_react11.default.createElement(
|
|
1717
|
+
UIDispatcher,
|
|
1718
|
+
{
|
|
1719
|
+
rawContent: content,
|
|
1720
|
+
primaryColor,
|
|
1721
|
+
accentColor,
|
|
1722
|
+
isStreaming,
|
|
1723
|
+
onAddToCart,
|
|
1724
|
+
viewportSize
|
|
1725
|
+
}
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return /* @__PURE__ */ import_react11.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
function normaliseChild(children) {
|
|
1734
|
+
if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react11.default.isValidElement(children)) {
|
|
1735
|
+
return JSON.stringify(children);
|
|
1736
|
+
}
|
|
1737
|
+
return children;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/components/MessageBubble.tsx
|
|
902
1741
|
function MessageBubble({
|
|
903
1742
|
message,
|
|
904
1743
|
sources,
|
|
@@ -911,99 +1750,45 @@ function MessageBubble({
|
|
|
911
1750
|
const isUser = message.role === "user";
|
|
912
1751
|
const isCompact = viewportSize === "compact";
|
|
913
1752
|
const isMedium = viewportSize === "medium";
|
|
914
|
-
const [showSources, setShowSources] =
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
[message.content]
|
|
918
|
-
);
|
|
919
|
-
const structuredContent = import_react5.default.useMemo(
|
|
1753
|
+
const [showSources, setShowSources] = import_react12.default.useState(false);
|
|
1754
|
+
const [showTrace, setShowTrace] = import_react12.default.useState(false);
|
|
1755
|
+
const structuredContent = import_react12.default.useMemo(
|
|
920
1756
|
() => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
|
|
921
1757
|
[isUser, message.content]
|
|
922
1758
|
);
|
|
923
|
-
const
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
return
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
brand: m.brand,
|
|
937
|
-
price: m.price,
|
|
938
|
-
image: resolveImage(m),
|
|
939
|
-
link: m.link,
|
|
940
|
-
description: s.content
|
|
941
|
-
};
|
|
942
|
-
});
|
|
1759
|
+
const hasRichUI = import_react12.default.useMemo(() => {
|
|
1760
|
+
if (message.uiTransformation) {
|
|
1761
|
+
const type = message.uiTransformation.type;
|
|
1762
|
+
if (type && !["text", "table"].includes(type)) return true;
|
|
1763
|
+
}
|
|
1764
|
+
if (structuredContent.payload) {
|
|
1765
|
+
return /"?(?:view|type|chartType)"?\s*:\s*"?(?:chart|carousel|pie|bar|line|product_carousel)"?/i.test(structuredContent.payload);
|
|
1766
|
+
}
|
|
1767
|
+
return false;
|
|
1768
|
+
}, [message.uiTransformation, structuredContent.payload]);
|
|
1769
|
+
const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
|
|
1770
|
+
const productsFromSources = import_react12.default.useMemo(() => {
|
|
1771
|
+
return extractProductsFromSources(sources, isUser);
|
|
943
1772
|
}, [sources, isUser]);
|
|
944
|
-
const { productsFromContent, cleanContent } =
|
|
1773
|
+
const { productsFromContent, cleanContent } = import_react12.default.useMemo(() => {
|
|
945
1774
|
if (isUser) {
|
|
946
1775
|
return { productsFromContent: [], cleanContent: message.content };
|
|
947
1776
|
}
|
|
948
|
-
const
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
image: (_a = item.image) != null ? _a : resolveImage(item)
|
|
962
|
-
});
|
|
963
|
-
})
|
|
964
|
-
);
|
|
965
|
-
}
|
|
966
|
-
} catch (e) {
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
|
|
970
|
-
for (const match of content.matchAll(jsonRegex)) {
|
|
971
|
-
try {
|
|
972
|
-
const data = JSON.parse(sanitizeJson(match[1]));
|
|
973
|
-
if (data.type === "products" && Array.isArray(data.items)) {
|
|
974
|
-
products.push(
|
|
975
|
-
...data.items.map((item) => {
|
|
976
|
-
var _a;
|
|
977
|
-
return __spreadProps(__spreadValues({}, item), {
|
|
978
|
-
image: (_a = item.image) != null ? _a : resolveImage(item)
|
|
979
|
-
});
|
|
980
|
-
})
|
|
981
|
-
);
|
|
982
|
-
content = content.replace(match[0], "");
|
|
983
|
-
}
|
|
984
|
-
} catch (e) {
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
return { productsFromContent: products, cleanContent: content.trim() };
|
|
989
|
-
}, [message.content, isUser, structuredContent]);
|
|
990
|
-
const allProducts = import_react5.default.useMemo(() => {
|
|
991
|
-
if (productsFromContent.length > 0) return productsFromContent;
|
|
992
|
-
const seen = /* @__PURE__ */ new Set();
|
|
993
|
-
const contentLower = message.content.toLowerCase();
|
|
994
|
-
return productsFromSources.filter((p) => {
|
|
995
|
-
var _a;
|
|
996
|
-
const id = String((_a = p.id) != null ? _a : p.name);
|
|
997
|
-
if (seen.has(id)) return false;
|
|
998
|
-
const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
|
|
999
|
-
if (mentioned) {
|
|
1000
|
-
seen.add(id);
|
|
1001
|
-
return true;
|
|
1002
|
-
}
|
|
1003
|
-
return false;
|
|
1004
|
-
});
|
|
1777
|
+
const payloadCandidates = [
|
|
1778
|
+
structuredContent.payload,
|
|
1779
|
+
...Array.from(structuredContent.text.matchAll(/```(?:json|ui)?\s*([\s\S]*?)\s*```/g)).map((m) => m[1])
|
|
1780
|
+
].filter(Boolean);
|
|
1781
|
+
return extractProductsFromContent(
|
|
1782
|
+
message.content,
|
|
1783
|
+
structuredContent.text,
|
|
1784
|
+
payloadCandidates,
|
|
1785
|
+
productsFromSources
|
|
1786
|
+
);
|
|
1787
|
+
}, [message.content, isUser, structuredContent, productsFromSources]);
|
|
1788
|
+
const allProducts = import_react12.default.useMemo(() => {
|
|
1789
|
+
return deduplicateProducts(productsFromSources, productsFromContent, message.content);
|
|
1005
1790
|
}, [productsFromSources, productsFromContent, message.content]);
|
|
1006
|
-
const processedMarkdown =
|
|
1791
|
+
const processedMarkdown = import_react12.default.useMemo(() => {
|
|
1007
1792
|
let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
|
|
1008
1793
|
const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
|
|
1009
1794
|
raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
|
|
@@ -1024,156 +1809,26 @@ ${match.trim()}
|
|
|
1024
1809
|
if (hasStructuredUiBlock) {
|
|
1025
1810
|
raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
|
|
1026
1811
|
}
|
|
1027
|
-
if (isStreaming) {
|
|
1028
|
-
raw = raw.replace(/((?:^|\n)\|.*)+/g, (match) => {
|
|
1029
|
-
return `
|
|
1030
|
-
|
|
1031
|
-
\`\`\`table-loading
|
|
1032
|
-
${match.trim()}
|
|
1033
|
-
\`\`\`
|
|
1034
|
-
|
|
1035
|
-
`;
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
1812
|
return raw;
|
|
1039
1813
|
}, [cleanContent, message.content, isStreaming, structuredContent]);
|
|
1040
|
-
const markdownComponents =
|
|
1041
|
-
() => ({
|
|
1042
|
-
// Wrap in not-prose so Tailwind Typography resets don't clobber our styles.
|
|
1043
|
-
// prose applies display:block and padding:0 to table/th/td — not-prose opts out.
|
|
1044
|
-
table: (_a) => {
|
|
1045
|
-
var props = __objRest(_a, []);
|
|
1046
|
-
if (isStreaming) {
|
|
1047
|
-
return /* @__PURE__ */ import_react5.default.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__ */ import_react5.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1048
|
-
}
|
|
1049
|
-
return /* @__PURE__ */ import_react5.default.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__ */ import_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
1050
|
-
"table",
|
|
1051
|
-
__spreadValues({
|
|
1052
|
-
className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
|
|
1053
|
-
}, props)
|
|
1054
|
-
)));
|
|
1055
|
-
},
|
|
1056
|
-
thead: (_b) => {
|
|
1057
|
-
var props = __objRest(_b, []);
|
|
1058
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1059
|
-
"thead",
|
|
1060
|
-
__spreadValues({
|
|
1061
|
-
className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
|
|
1062
|
-
}, props)
|
|
1063
|
-
);
|
|
1064
|
-
},
|
|
1065
|
-
tbody: (_c) => {
|
|
1066
|
-
var props = __objRest(_c, []);
|
|
1067
|
-
return /* @__PURE__ */ import_react5.default.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
|
|
1068
|
-
},
|
|
1069
|
-
tr: (_d) => {
|
|
1070
|
-
var props = __objRest(_d, []);
|
|
1071
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1072
|
-
"tr",
|
|
1073
|
-
__spreadValues({
|
|
1074
|
-
className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
|
|
1075
|
-
}, props)
|
|
1076
|
-
);
|
|
1077
|
-
},
|
|
1078
|
-
th: (_e) => {
|
|
1079
|
-
var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
|
|
1080
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1081
|
-
"th",
|
|
1082
|
-
__spreadValues({
|
|
1083
|
-
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"
|
|
1084
|
-
}, props),
|
|
1085
|
-
normaliseChild(children)
|
|
1086
|
-
);
|
|
1087
|
-
},
|
|
1088
|
-
td: (_g) => {
|
|
1089
|
-
var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
|
|
1090
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1091
|
-
"td",
|
|
1092
|
-
__spreadValues({
|
|
1093
|
-
className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
|
|
1094
|
-
}, props),
|
|
1095
|
-
normaliseChild(children)
|
|
1096
|
-
);
|
|
1097
|
-
},
|
|
1098
|
-
code(_i) {
|
|
1099
|
-
var _j = _i, {
|
|
1100
|
-
inline,
|
|
1101
|
-
className,
|
|
1102
|
-
children
|
|
1103
|
-
} = _j, props = __objRest(_j, [
|
|
1104
|
-
"inline",
|
|
1105
|
-
"className",
|
|
1106
|
-
"children"
|
|
1107
|
-
]);
|
|
1108
|
-
var _a;
|
|
1109
|
-
const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
|
|
1110
|
-
if (!inline && (lang === "ui" || lang === "chart")) {
|
|
1111
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1112
|
-
UIDispatcher,
|
|
1113
|
-
{
|
|
1114
|
-
rawContent: String(children != null ? children : "").trim(),
|
|
1115
|
-
primaryColor,
|
|
1116
|
-
accentColor,
|
|
1117
|
-
isStreaming,
|
|
1118
|
-
onAddToCart,
|
|
1119
|
-
viewportSize
|
|
1120
|
-
}
|
|
1121
|
-
);
|
|
1122
|
-
}
|
|
1123
|
-
if (!inline && lang === "json") {
|
|
1124
|
-
const content = String(children != null ? children : "").trim();
|
|
1125
|
-
if (looksLikeStructuredPayload(content)) {
|
|
1126
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1127
|
-
UIDispatcher,
|
|
1128
|
-
{
|
|
1129
|
-
rawContent: content,
|
|
1130
|
-
primaryColor,
|
|
1131
|
-
accentColor,
|
|
1132
|
-
isStreaming,
|
|
1133
|
-
onAddToCart,
|
|
1134
|
-
viewportSize
|
|
1135
|
-
}
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
if (!inline) {
|
|
1140
|
-
const content = String(children != null ? children : "").trim();
|
|
1141
|
-
if (looksLikeStructuredPayload(content)) {
|
|
1142
|
-
return /* @__PURE__ */ import_react5.default.createElement(
|
|
1143
|
-
UIDispatcher,
|
|
1144
|
-
{
|
|
1145
|
-
rawContent: content,
|
|
1146
|
-
primaryColor,
|
|
1147
|
-
accentColor,
|
|
1148
|
-
isStreaming,
|
|
1149
|
-
onAddToCart,
|
|
1150
|
-
viewportSize
|
|
1151
|
-
}
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
if (!inline && lang === "table-loading") {
|
|
1156
|
-
return /* @__PURE__ */ import_react5.default.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__ */ import_react5.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
|
|
1157
|
-
}
|
|
1158
|
-
return /* @__PURE__ */ import_react5.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
|
|
1159
|
-
}
|
|
1160
|
-
}),
|
|
1814
|
+
const markdownComponents = import_react12.default.useMemo(
|
|
1815
|
+
() => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
|
|
1161
1816
|
[primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
|
|
1162
1817
|
);
|
|
1163
|
-
return /* @__PURE__ */
|
|
1818
|
+
return /* @__PURE__ */ import_react12.default.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react12.default.createElement(
|
|
1164
1819
|
"div",
|
|
1165
1820
|
{
|
|
1166
1821
|
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"}`,
|
|
1167
1822
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
1168
1823
|
},
|
|
1169
|
-
isUser ? /* @__PURE__ */
|
|
1170
|
-
), /* @__PURE__ */
|
|
1824
|
+
isUser ? /* @__PURE__ */ import_react12.default.createElement(import_lucide_react6.User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ import_react12.default.createElement(import_lucide_react6.Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
|
|
1825
|
+
), /* @__PURE__ */ import_react12.default.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__ */ import_react12.default.createElement(
|
|
1171
1826
|
"div",
|
|
1172
1827
|
{
|
|
1173
1828
|
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"}`,
|
|
1174
1829
|
style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
|
|
1175
1830
|
},
|
|
1176
|
-
isStreaming && !message.content ? /* @__PURE__ */
|
|
1831
|
+
isStreaming && !message.content ? /* @__PURE__ */ import_react12.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react12.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react12.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react12.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react12.default.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, !isUser && structuredContent.payload && /* @__PURE__ */ import_react12.default.createElement(
|
|
1177
1832
|
UIDispatcher,
|
|
1178
1833
|
{
|
|
1179
1834
|
rawContent: structuredContent.payload,
|
|
@@ -1183,101 +1838,66 @@ ${match.trim()}
|
|
|
1183
1838
|
onAddToCart,
|
|
1184
1839
|
viewportSize
|
|
1185
1840
|
}
|
|
1186
|
-
), !shouldRenderStructuredOnly && /* @__PURE__ */
|
|
1187
|
-
|
|
1841
|
+
), !shouldRenderStructuredOnly && /* @__PURE__ */ import_react12.default.createElement(import_react_markdown.default, { components: markdownComponents }, processedMarkdown), isStreaming && /* @__PURE__ */ import_react12.default.createElement("span", { className: "inline-flex items-center gap-1.5 ml-2 text-emerald-500 animate-pulse align-middle text-xs font-medium" }, /* @__PURE__ */ import_react12.default.createElement(
|
|
1842
|
+
HourglassLoader_default,
|
|
1843
|
+
{
|
|
1844
|
+
size: 18,
|
|
1845
|
+
primaryColor,
|
|
1846
|
+
accentColor,
|
|
1847
|
+
glow: false
|
|
1848
|
+
}
|
|
1849
|
+
), "Thinking..."))
|
|
1850
|
+
), (() => {
|
|
1851
|
+
var _a, _b;
|
|
1852
|
+
if (isUser || structuredContent.payload || !message.uiTransformation) return null;
|
|
1853
|
+
const ui = message.uiTransformation;
|
|
1854
|
+
const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
|
|
1855
|
+
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());
|
|
1856
|
+
if (!shouldShow) return null;
|
|
1857
|
+
return /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ import_react12.default.createElement(
|
|
1858
|
+
VisualizationRenderer,
|
|
1859
|
+
{
|
|
1860
|
+
data: ui,
|
|
1861
|
+
primaryColor,
|
|
1862
|
+
onAddToCart,
|
|
1863
|
+
className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4",
|
|
1864
|
+
isStreaming
|
|
1865
|
+
}
|
|
1866
|
+
));
|
|
1867
|
+
})(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react12.default.createElement(
|
|
1188
1868
|
ProductCarousel,
|
|
1189
1869
|
{
|
|
1190
1870
|
products: allProducts,
|
|
1191
1871
|
primaryColor,
|
|
1192
1872
|
onAddToCart
|
|
1193
1873
|
}
|
|
1194
|
-
)), !isUser &&
|
|
1874
|
+
)), !isUser && /* @__PURE__ */ import_react12.default.createElement("div", { className: "flex items-center gap-4 mt-1 w-full flex-wrap" }, sources && sources.length > 0 && /* @__PURE__ */ import_react12.default.createElement(
|
|
1195
1875
|
"button",
|
|
1196
1876
|
{
|
|
1197
1877
|
onClick: () => setShowSources((s) => !s),
|
|
1198
|
-
className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1
|
|
1878
|
+
className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1"
|
|
1199
1879
|
},
|
|
1200
|
-
showSources ? /* @__PURE__ */
|
|
1880
|
+
showSources ? /* @__PURE__ */ import_react12.default.createElement(import_lucide_react6.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ import_react12.default.createElement(import_lucide_react6.ChevronRight, { className: "w-3 h-3" }),
|
|
1201
1881
|
sources.length,
|
|
1202
1882
|
" source",
|
|
1203
1883
|
sources.length !== 1 ? "s" : "",
|
|
1204
1884
|
" used"
|
|
1205
|
-
),
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
if (!override) {
|
|
1215
|
-
return merged;
|
|
1216
|
-
}
|
|
1217
|
-
for (const [key, value] of Object.entries(override)) {
|
|
1218
|
-
if (value !== void 0 && value !== null && value !== "") {
|
|
1219
|
-
merged[key] = value;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
return merged;
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
// src/config/uiConstants.ts
|
|
1226
|
-
var DEFAULT_CONFIG = {
|
|
1227
|
-
projectId: "default",
|
|
1228
|
-
ui: {
|
|
1229
|
-
title: "AI Assistant",
|
|
1230
|
-
subtitle: "Powered by RAG",
|
|
1231
|
-
primaryColor: "#6366f1",
|
|
1232
|
-
accentColor: "#8b5cf6",
|
|
1233
|
-
logoUrl: "",
|
|
1234
|
-
placeholder: "Ask me anything\u2026",
|
|
1235
|
-
showSources: true,
|
|
1236
|
-
welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
|
|
1237
|
-
showWidget: true,
|
|
1238
|
-
poweredBy: "RAG",
|
|
1239
|
-
visualStyle: "glass",
|
|
1240
|
-
borderRadius: "xl",
|
|
1241
|
-
allowUpload: true,
|
|
1242
|
-
allowResize: true,
|
|
1243
|
-
enableVoiceInput: true
|
|
1244
|
-
}
|
|
1245
|
-
};
|
|
1246
|
-
var BORDER_RADIUS_MAP = {
|
|
1247
|
-
none: "rounded-none",
|
|
1248
|
-
sm: "rounded-sm",
|
|
1249
|
-
md: "rounded-md",
|
|
1250
|
-
lg: "rounded-lg",
|
|
1251
|
-
xl: "rounded-xl",
|
|
1252
|
-
full: "rounded-3xl"
|
|
1253
|
-
};
|
|
1254
|
-
var CHAT_SUGGESTIONS = [
|
|
1255
|
-
"What can you help me with?",
|
|
1256
|
-
"Summarise the key topics",
|
|
1257
|
-
"Show me an example"
|
|
1258
|
-
];
|
|
1259
|
-
|
|
1260
|
-
// src/components/ConfigProvider.tsx
|
|
1261
|
-
var ConfigContext = (0, import_react6.createContext)(DEFAULT_CONFIG);
|
|
1262
|
-
function ConfigProvider({
|
|
1263
|
-
config,
|
|
1264
|
-
children
|
|
1265
|
-
}) {
|
|
1266
|
-
const merged = {
|
|
1267
|
-
projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
|
|
1268
|
-
ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
|
|
1269
|
-
};
|
|
1270
|
-
return /* @__PURE__ */ import_react6.default.createElement(ConfigContext.Provider, { value: merged }, children);
|
|
1271
|
-
}
|
|
1272
|
-
function useConfig() {
|
|
1273
|
-
return (0, import_react6.useContext)(ConfigContext);
|
|
1885
|
+
), message.trace && /* @__PURE__ */ import_react12.default.createElement(
|
|
1886
|
+
"button",
|
|
1887
|
+
{
|
|
1888
|
+
onClick: () => setShowTrace((t) => !t),
|
|
1889
|
+
className: "text-[11px] text-emerald-500 hover:text-emerald-400 transition-colors flex items-center gap-1"
|
|
1890
|
+
},
|
|
1891
|
+
/* @__PURE__ */ import_react12.default.createElement(import_lucide_react6.Activity, { className: "w-3 h-3" }),
|
|
1892
|
+
showTrace ? "Hide Trace" : "Trace"
|
|
1893
|
+
)), /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-full flex flex-col gap-2 min-w-0" }, showSources && sources && sources.length > 0 && /* @__PURE__ */ import_react12.default.createElement("div", { className: "mt-1 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ import_react12.default.createElement(SourceCard, { key: src.id, source: src, index: i }))), showTrace && message.trace && /* @__PURE__ */ import_react12.default.createElement(ObservabilityPanel, { trace: message.trace, primaryColor }))));
|
|
1274
1894
|
}
|
|
1275
1895
|
|
|
1276
1896
|
// src/hooks/useRagChat.ts
|
|
1277
|
-
var
|
|
1897
|
+
var import_react13 = require("react");
|
|
1278
1898
|
|
|
1279
1899
|
// src/hooks/useStoredMessages.ts
|
|
1280
|
-
var
|
|
1900
|
+
var React13 = __toESM(require("react"));
|
|
1281
1901
|
function readStoredMessages(storageKey) {
|
|
1282
1902
|
if (typeof window === "undefined") {
|
|
1283
1903
|
return [];
|
|
@@ -1294,8 +1914,8 @@ function readStoredMessages(storageKey) {
|
|
|
1294
1914
|
}
|
|
1295
1915
|
}
|
|
1296
1916
|
function useStoredMessages(storageKey, persist) {
|
|
1297
|
-
const [messages, setMessages] =
|
|
1298
|
-
|
|
1917
|
+
const [messages, setMessages] = React13.useState(() => persist ? readStoredMessages(storageKey) : []);
|
|
1918
|
+
React13.useEffect(() => {
|
|
1299
1919
|
if (!persist || typeof window === "undefined") {
|
|
1300
1920
|
return;
|
|
1301
1921
|
}
|
|
@@ -1335,14 +1955,14 @@ function useRagChat(projectId, options = {}) {
|
|
|
1335
1955
|
} = options;
|
|
1336
1956
|
const storageKey = `rag_chat_${projectId}`;
|
|
1337
1957
|
const [messages, setMessages] = useStoredMessages(storageKey, persist);
|
|
1338
|
-
const [isLoading, setIsLoading] = (0,
|
|
1339
|
-
const [error, setError] = (0,
|
|
1340
|
-
const lastInputRef = (0,
|
|
1341
|
-
const messagesRef = (0,
|
|
1342
|
-
(0,
|
|
1958
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(false);
|
|
1959
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
1960
|
+
const lastInputRef = (0, import_react13.useRef)("");
|
|
1961
|
+
const messagesRef = (0, import_react13.useRef)(messages);
|
|
1962
|
+
(0, import_react13.useEffect)(() => {
|
|
1343
1963
|
messagesRef.current = messages;
|
|
1344
1964
|
}, [messages]);
|
|
1345
|
-
const sendMessage = (0,
|
|
1965
|
+
const sendMessage = (0, import_react13.useCallback)(
|
|
1346
1966
|
async (text, opts) => {
|
|
1347
1967
|
var _a, _b;
|
|
1348
1968
|
const trimmed = text.trim();
|
|
@@ -1382,6 +2002,7 @@ function useRagChat(projectId, options = {}) {
|
|
|
1382
2002
|
let assistantContent = "";
|
|
1383
2003
|
let sources = [];
|
|
1384
2004
|
let uiTransformation = null;
|
|
2005
|
+
let trace;
|
|
1385
2006
|
let buffer = "";
|
|
1386
2007
|
const assistantMessageId = `assistant_${Date.now()}`;
|
|
1387
2008
|
setMessages((prev) => [
|
|
@@ -1402,12 +2023,14 @@ function useRagChat(projectId, options = {}) {
|
|
|
1402
2023
|
const complete = buffer.slice(0, lastBoundary + 2);
|
|
1403
2024
|
buffer = buffer.slice(lastBoundary + 2);
|
|
1404
2025
|
for (const frame of parseSseChunk(complete)) {
|
|
1405
|
-
if (frame.type === "text") {
|
|
2026
|
+
if (frame.type === "text" && frame.text) {
|
|
1406
2027
|
assistantContent += frame.text;
|
|
1407
2028
|
} else if (frame.type === "metadata") {
|
|
1408
2029
|
sources = (_a = frame.sources) != null ? _a : [];
|
|
1409
2030
|
} else if (frame.type === "ui_transformation") {
|
|
1410
2031
|
uiTransformation = frame.data;
|
|
2032
|
+
} else if (frame.type === "observability") {
|
|
2033
|
+
trace = frame.data;
|
|
1411
2034
|
} else if (frame.type === "error") {
|
|
1412
2035
|
setError(frame.error || "Stream error");
|
|
1413
2036
|
}
|
|
@@ -1424,19 +2047,24 @@ function useRagChat(projectId, options = {}) {
|
|
|
1424
2047
|
}
|
|
1425
2048
|
if (buffer.trim()) {
|
|
1426
2049
|
for (const frame of parseSseChunk(buffer)) {
|
|
1427
|
-
if (frame.type === "text") assistantContent += frame.text;
|
|
2050
|
+
if (frame.type === "text" && frame.text) assistantContent += frame.text;
|
|
1428
2051
|
else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
|
|
2052
|
+
else if (frame.type === "observability") trace = frame.data;
|
|
1429
2053
|
}
|
|
1430
2054
|
}
|
|
1431
|
-
const
|
|
2055
|
+
const finalMsg = {
|
|
1432
2056
|
id: assistantMessageId,
|
|
1433
2057
|
role: "assistant",
|
|
1434
2058
|
content: assistantContent,
|
|
1435
2059
|
sources: sources.length > 0 ? sources : void 0,
|
|
1436
|
-
uiTransformation: uiTransformation
|
|
2060
|
+
uiTransformation: uiTransformation != null ? uiTransformation : void 0,
|
|
2061
|
+
trace,
|
|
1437
2062
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1438
2063
|
};
|
|
1439
|
-
|
|
2064
|
+
setMessages(
|
|
2065
|
+
(prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMsg : msg)
|
|
2066
|
+
);
|
|
2067
|
+
onReply == null ? void 0 : onReply(finalMsg);
|
|
1440
2068
|
} catch (err) {
|
|
1441
2069
|
const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
|
|
1442
2070
|
setError(msg);
|
|
@@ -1447,12 +2075,12 @@ function useRagChat(projectId, options = {}) {
|
|
|
1447
2075
|
},
|
|
1448
2076
|
[apiUrl, isLoading, namespace, onError, onReply, projectId, setMessages]
|
|
1449
2077
|
);
|
|
1450
|
-
const clear = (0,
|
|
2078
|
+
const clear = (0, import_react13.useCallback)(() => {
|
|
1451
2079
|
setMessages([]);
|
|
1452
2080
|
setError(null);
|
|
1453
2081
|
if (persist) localStorage.removeItem(storageKey);
|
|
1454
2082
|
}, [persist, setMessages, storageKey]);
|
|
1455
|
-
const retry = (0,
|
|
2083
|
+
const retry = (0, import_react13.useCallback)(async () => {
|
|
1456
2084
|
if (lastInputRef.current) {
|
|
1457
2085
|
const latestMessage = messagesRef.current[messagesRef.current.length - 1];
|
|
1458
2086
|
await sendMessage(lastInputRef.current, {
|
|
@@ -1486,20 +2114,21 @@ function ChatWindow({
|
|
|
1486
2114
|
}) {
|
|
1487
2115
|
var _a;
|
|
1488
2116
|
const { ui, projectId } = useConfig();
|
|
1489
|
-
const [input, setInput] = (0,
|
|
1490
|
-
const [mounted, setMounted] = (0,
|
|
1491
|
-
const [viewportSize, setViewportSize] = (0,
|
|
1492
|
-
const windowRef = (0,
|
|
1493
|
-
const bottomRef = (0,
|
|
1494
|
-
const inputRef = (0,
|
|
2117
|
+
const [input, setInput] = (0, import_react14.useState)("");
|
|
2118
|
+
const [mounted, setMounted] = (0, import_react14.useState)(false);
|
|
2119
|
+
const [viewportSize, setViewportSize] = (0, import_react14.useState)("large");
|
|
2120
|
+
const windowRef = (0, import_react14.useRef)(null);
|
|
2121
|
+
const bottomRef = (0, import_react14.useRef)(null);
|
|
2122
|
+
const inputRef = (0, import_react14.useRef)(null);
|
|
1495
2123
|
const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
|
|
1496
2124
|
namespace: projectId
|
|
1497
2125
|
});
|
|
1498
|
-
const [suggestions, setSuggestions] = (0,
|
|
1499
|
-
const [isSuggesting, setIsSuggesting] = (0,
|
|
1500
|
-
const [isListening, setIsListening] = (0,
|
|
1501
|
-
const
|
|
1502
|
-
(0,
|
|
2126
|
+
const [suggestions, setSuggestions] = (0, import_react14.useState)([]);
|
|
2127
|
+
const [isSuggesting, setIsSuggesting] = (0, import_react14.useState)(false);
|
|
2128
|
+
const [isListening, setIsListening] = (0, import_react14.useState)(false);
|
|
2129
|
+
const [isUploadModalOpen, setIsUploadModalOpen] = (0, import_react14.useState)(false);
|
|
2130
|
+
const recognitionRef = (0, import_react14.useRef)(null);
|
|
2131
|
+
(0, import_react14.useEffect)(() => {
|
|
1503
2132
|
if (typeof window !== "undefined") {
|
|
1504
2133
|
const win = window;
|
|
1505
2134
|
const SpeechRecognition = win.SpeechRecognition || win.webkitSpeechRecognition;
|
|
@@ -1545,10 +2174,10 @@ function ChatWindow({
|
|
|
1545
2174
|
}
|
|
1546
2175
|
}
|
|
1547
2176
|
};
|
|
1548
|
-
(0,
|
|
2177
|
+
(0, import_react14.useEffect)(() => {
|
|
1549
2178
|
setMounted(true);
|
|
1550
2179
|
}, []);
|
|
1551
|
-
(0,
|
|
2180
|
+
(0, import_react14.useEffect)(() => {
|
|
1552
2181
|
const element = windowRef.current;
|
|
1553
2182
|
if (!element || typeof ResizeObserver === "undefined") return;
|
|
1554
2183
|
const resolveViewportSize = (width) => {
|
|
@@ -1564,13 +2193,13 @@ function ChatWindow({
|
|
|
1564
2193
|
observer.observe(element);
|
|
1565
2194
|
return () => observer.disconnect();
|
|
1566
2195
|
}, []);
|
|
1567
|
-
(0,
|
|
2196
|
+
(0, import_react14.useEffect)(() => {
|
|
1568
2197
|
var _a2;
|
|
1569
2198
|
if (messages.length > 0 || isLoading) {
|
|
1570
2199
|
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1571
2200
|
}
|
|
1572
2201
|
}, [messages, isLoading]);
|
|
1573
|
-
const sendMessage = (0,
|
|
2202
|
+
const sendMessage = (0, import_react14.useCallback)(async () => {
|
|
1574
2203
|
var _a2;
|
|
1575
2204
|
const text = input.trim();
|
|
1576
2205
|
if (!text || isLoading) return;
|
|
@@ -1600,7 +2229,7 @@ function ChatWindow({
|
|
|
1600
2229
|
const isEmpty = messages.length === 0;
|
|
1601
2230
|
const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || "xl"];
|
|
1602
2231
|
const isGlass = ui.visualStyle !== "solid";
|
|
1603
|
-
(0,
|
|
2232
|
+
(0, import_react14.useEffect)(() => {
|
|
1604
2233
|
if (input.trim().length < 3) {
|
|
1605
2234
|
return;
|
|
1606
2235
|
}
|
|
@@ -1624,84 +2253,84 @@ function ChatWindow({
|
|
|
1624
2253
|
}, 800);
|
|
1625
2254
|
return () => clearTimeout(timer);
|
|
1626
2255
|
}, [input, projectId]);
|
|
1627
|
-
return /* @__PURE__ */
|
|
2256
|
+
return /* @__PURE__ */ import_react14.default.createElement(
|
|
1628
2257
|
"div",
|
|
1629
2258
|
{
|
|
1630
2259
|
ref: windowRef,
|
|
1631
2260
|
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}`,
|
|
1632
2261
|
style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
|
|
1633
2262
|
},
|
|
1634
|
-
onResizeStart && /* @__PURE__ */
|
|
2263
|
+
onResizeStart && /* @__PURE__ */ import_react14.default.createElement(
|
|
1635
2264
|
"div",
|
|
1636
2265
|
{
|
|
1637
2266
|
onMouseDown: onResizeStart,
|
|
1638
2267
|
className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
|
|
1639
2268
|
title: "Drag to resize"
|
|
1640
2269
|
},
|
|
1641
|
-
/* @__PURE__ */
|
|
2270
|
+
/* @__PURE__ */ import_react14.default.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]" })
|
|
1642
2271
|
),
|
|
1643
|
-
/* @__PURE__ */
|
|
2272
|
+
/* @__PURE__ */ import_react14.default.createElement(
|
|
1644
2273
|
"div",
|
|
1645
2274
|
{
|
|
1646
2275
|
className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
|
|
1647
2276
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
|
|
1648
2277
|
},
|
|
1649
|
-
/* @__PURE__ */
|
|
2278
|
+
/* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
|
|
1650
2279
|
// eslint-disable-next-line @next/next/no-img-element
|
|
1651
|
-
/* @__PURE__ */
|
|
1652
|
-
) : /* @__PURE__ */
|
|
2280
|
+
/* @__PURE__ */ import_react14.default.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
|
|
2281
|
+
) : /* @__PURE__ */ import_react14.default.createElement(
|
|
1653
2282
|
"div",
|
|
1654
2283
|
{
|
|
1655
2284
|
className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
|
|
1656
2285
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
1657
2286
|
},
|
|
1658
|
-
/* @__PURE__ */
|
|
1659
|
-
), /* @__PURE__ */
|
|
1660
|
-
/* @__PURE__ */
|
|
2287
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Bot, { className: "w-5 h-5 text-white" })
|
|
2288
|
+
), /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight" }, ui.title), ui.poweredBy && /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight" }, `Powered by ${ui.poweredBy}`))),
|
|
2289
|
+
/* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2" }, /* @__PURE__ */ import_react14.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }), "Online"), /* @__PURE__ */ import_react14.default.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__ */ import_react14.default.createElement(
|
|
1661
2290
|
"button",
|
|
1662
2291
|
{
|
|
1663
2292
|
onClick: clearHistory,
|
|
1664
2293
|
title: "Clear conversation",
|
|
1665
2294
|
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"
|
|
1666
2295
|
},
|
|
1667
|
-
/* @__PURE__ */
|
|
1668
|
-
), isResized && onResetResize && /* @__PURE__ */
|
|
2296
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Trash2, { className: "w-3.5 h-3.5" })
|
|
2297
|
+
), isResized && onResetResize && /* @__PURE__ */ import_react14.default.createElement(
|
|
1669
2298
|
"button",
|
|
1670
2299
|
{
|
|
1671
2300
|
onClick: onResetResize,
|
|
1672
2301
|
title: "Reset to default size",
|
|
1673
2302
|
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"
|
|
1674
2303
|
},
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
), onMaximize && /* @__PURE__ */
|
|
2304
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.RotateCcw, { className: "w-3.5 h-3.5" })
|
|
2305
|
+
), onMaximize && /* @__PURE__ */ import_react14.default.createElement(
|
|
1677
2306
|
"button",
|
|
1678
2307
|
{
|
|
1679
2308
|
onClick: onMaximize,
|
|
1680
2309
|
title: isMaximized ? "Minimize" : "Maximize",
|
|
1681
2310
|
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"
|
|
1682
2311
|
},
|
|
1683
|
-
isMaximized ? /* @__PURE__ */
|
|
1684
|
-
), showClose && onClose && /* @__PURE__ */
|
|
2312
|
+
isMaximized ? /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Maximize2, { className: "w-3.5 h-3.5" })
|
|
2313
|
+
), showClose && onClose && /* @__PURE__ */ import_react14.default.createElement(
|
|
1685
2314
|
"button",
|
|
1686
2315
|
{
|
|
1687
2316
|
onClick: onClose,
|
|
1688
2317
|
title: "Close chat",
|
|
1689
2318
|
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"
|
|
1690
2319
|
},
|
|
1691
|
-
/* @__PURE__ */
|
|
2320
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-4 h-4" })
|
|
1692
2321
|
)))
|
|
1693
2322
|
),
|
|
1694
|
-
/* @__PURE__ */
|
|
2323
|
+
/* @__PURE__ */ import_react14.default.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 ? (
|
|
1695
2324
|
/* Welcome state */
|
|
1696
|
-
/* @__PURE__ */
|
|
2325
|
+
/* @__PURE__ */ import_react14.default.createElement("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12" }, /* @__PURE__ */ import_react14.default.createElement(
|
|
1697
2326
|
"div",
|
|
1698
2327
|
{
|
|
1699
2328
|
className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
|
|
1700
2329
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
1701
2330
|
},
|
|
1702
|
-
/* @__PURE__ */
|
|
1703
|
-
), /* @__PURE__ */
|
|
1704
|
-
(suggestion) => /* @__PURE__ */
|
|
2331
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Sparkles, { className: "w-8 h-8 text-white" })
|
|
2332
|
+
), /* @__PURE__ */ import_react14.default.createElement("div", null, /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed" }, ui.welcomeMessage), /* @__PURE__ */ import_react14.default.createElement("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2" }, "Ask a question to get started")), /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex flex-wrap gap-2 justify-center mt-2" }, CHAT_SUGGESTIONS.map(
|
|
2333
|
+
(suggestion) => /* @__PURE__ */ import_react14.default.createElement(
|
|
1705
2334
|
"button",
|
|
1706
2335
|
{
|
|
1707
2336
|
key: suggestion,
|
|
@@ -1715,7 +2344,7 @@ function ChatWindow({
|
|
|
1715
2344
|
suggestion
|
|
1716
2345
|
)
|
|
1717
2346
|
)))
|
|
1718
|
-
) : messages.map((msg, index) => /* @__PURE__ */
|
|
2347
|
+
) : messages.map((msg, index) => /* @__PURE__ */ import_react14.default.createElement(
|
|
1719
2348
|
MessageBubble,
|
|
1720
2349
|
{
|
|
1721
2350
|
key: msg.id,
|
|
@@ -1727,7 +2356,7 @@ function ChatWindow({
|
|
|
1727
2356
|
onAddToCart,
|
|
1728
2357
|
viewportSize
|
|
1729
2358
|
}
|
|
1730
|
-
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */
|
|
2359
|
+
)), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react14.default.createElement(
|
|
1731
2360
|
MessageBubble,
|
|
1732
2361
|
{
|
|
1733
2362
|
message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
@@ -1737,8 +2366,22 @@ function ChatWindow({
|
|
|
1737
2366
|
onAddToCart,
|
|
1738
2367
|
viewportSize
|
|
1739
2368
|
}
|
|
1740
|
-
), error && /* @__PURE__ */
|
|
1741
|
-
/* @__PURE__ */
|
|
2369
|
+
), error && /* @__PURE__ */ import_react14.default.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__ */ import_react14.default.createElement(import_lucide_react7.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ import_react14.default.createElement("span", null, error)), /* @__PURE__ */ import_react14.default.createElement("div", { ref: bottomRef })),
|
|
2370
|
+
isUploadModalOpen && /* @__PURE__ */ import_react14.default.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__ */ import_react14.default.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__ */ import_react14.default.createElement(
|
|
2371
|
+
"button",
|
|
2372
|
+
{
|
|
2373
|
+
onClick: () => setIsUploadModalOpen(false),
|
|
2374
|
+
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"
|
|
2375
|
+
},
|
|
2376
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-5 h-5" })
|
|
2377
|
+
), /* @__PURE__ */ import_react14.default.createElement(
|
|
2378
|
+
DocumentUpload,
|
|
2379
|
+
{
|
|
2380
|
+
namespace: projectId,
|
|
2381
|
+
onUploadComplete: () => setIsUploadModalOpen(false)
|
|
2382
|
+
}
|
|
2383
|
+
))),
|
|
2384
|
+
/* @__PURE__ */ import_react14.default.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__ */ import_react14.default.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__ */ import_react14.default.createElement(
|
|
1742
2385
|
"button",
|
|
1743
2386
|
{
|
|
1744
2387
|
key: suggestion,
|
|
@@ -1750,9 +2393,9 @@ function ChatWindow({
|
|
|
1750
2393
|
},
|
|
1751
2394
|
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"
|
|
1752
2395
|
},
|
|
1753
|
-
/* @__PURE__ */
|
|
2396
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Sparkles, { className: "w-3 h-3 text-amber-400" }),
|
|
1754
2397
|
suggestion
|
|
1755
|
-
)), isSuggesting && suggestions.length === 0 && /* @__PURE__ */
|
|
2398
|
+
)), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ import_react14.default.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__ */ import_react14.default.createElement(import_lucide_react7.Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ import_react14.default.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__ */ import_react14.default.createElement(
|
|
1756
2399
|
"textarea",
|
|
1757
2400
|
{
|
|
1758
2401
|
ref: inputRef,
|
|
@@ -1771,7 +2414,7 @@ function ChatWindow({
|
|
|
1771
2414
|
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",
|
|
1772
2415
|
style: { scrollbarWidth: "none" }
|
|
1773
2416
|
}
|
|
1774
|
-
), ui.enableVoiceInput !== false && /* @__PURE__ */
|
|
2417
|
+
), ui.enableVoiceInput !== false && /* @__PURE__ */ import_react14.default.createElement(
|
|
1775
2418
|
"button",
|
|
1776
2419
|
{
|
|
1777
2420
|
type: "button",
|
|
@@ -1780,8 +2423,17 @@ function ChatWindow({
|
|
|
1780
2423
|
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"}`,
|
|
1781
2424
|
title: isListening ? "Stop listening" : "Start voice input"
|
|
1782
2425
|
},
|
|
1783
|
-
isListening ? /* @__PURE__ */
|
|
1784
|
-
), /* @__PURE__ */
|
|
2426
|
+
isListening ? /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Mic, { className: "w-4 h-4" })
|
|
2427
|
+
), ui.allowUpload !== false && /* @__PURE__ */ import_react14.default.createElement(
|
|
2428
|
+
"button",
|
|
2429
|
+
{
|
|
2430
|
+
type: "button",
|
|
2431
|
+
onClick: () => setIsUploadModalOpen(true),
|
|
2432
|
+
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",
|
|
2433
|
+
title: "Upload Document"
|
|
2434
|
+
},
|
|
2435
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Paperclip, { className: "w-4 h-4" })
|
|
2436
|
+
), /* @__PURE__ */ import_react14.default.createElement(
|
|
1785
2437
|
"button",
|
|
1786
2438
|
{
|
|
1787
2439
|
onClick: sendMessage,
|
|
@@ -1792,8 +2444,8 @@ function ChatWindow({
|
|
|
1792
2444
|
// slate-400/20 for light mode disabled
|
|
1793
2445
|
}
|
|
1794
2446
|
},
|
|
1795
|
-
/* @__PURE__ */
|
|
1796
|
-
)), /* @__PURE__ */
|
|
2447
|
+
/* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.ArrowUp, { className: "w-4 h-4 text-white" })
|
|
2448
|
+
)), /* @__PURE__ */ import_react14.default.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"))
|
|
1797
2449
|
);
|
|
1798
2450
|
}
|
|
1799
2451
|
|
|
@@ -1801,12 +2453,12 @@ function ChatWindow({
|
|
|
1801
2453
|
var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
|
|
1802
2454
|
function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
1803
2455
|
const { ui } = useConfig();
|
|
1804
|
-
const [isOpen, setIsOpen] = (0,
|
|
1805
|
-
const [hasUnread, setHasUnread] = (0,
|
|
1806
|
-
const [dimensions, setDimensions] = (0,
|
|
1807
|
-
const [isResizing, setIsResizing] = (0,
|
|
1808
|
-
const [isMaximized, setIsMaximized] = (0,
|
|
1809
|
-
const [prevDimensions, setPrevDimensions] = (0,
|
|
2456
|
+
const [isOpen, setIsOpen] = (0, import_react15.useState)(false);
|
|
2457
|
+
const [hasUnread, setHasUnread] = (0, import_react15.useState)(false);
|
|
2458
|
+
const [dimensions, setDimensions] = (0, import_react15.useState)(DEFAULT_DIMENSIONS);
|
|
2459
|
+
const [isResizing, setIsResizing] = (0, import_react15.useState)(false);
|
|
2460
|
+
const [isMaximized, setIsMaximized] = (0, import_react15.useState)(false);
|
|
2461
|
+
const [prevDimensions, setPrevDimensions] = (0, import_react15.useState)(DEFAULT_DIMENSIONS);
|
|
1810
2462
|
if (ui.showWidget === false) return null;
|
|
1811
2463
|
const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
|
|
1812
2464
|
const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
|
|
@@ -1856,7 +2508,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
1856
2508
|
}
|
|
1857
2509
|
};
|
|
1858
2510
|
const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
|
|
1859
|
-
return /* @__PURE__ */
|
|
2511
|
+
return /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
|
|
1860
2512
|
"div",
|
|
1861
2513
|
{
|
|
1862
2514
|
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"}`,
|
|
@@ -1866,7 +2518,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
1866
2518
|
maxHeight: "calc(100vh - 6rem)"
|
|
1867
2519
|
}
|
|
1868
2520
|
},
|
|
1869
|
-
/* @__PURE__ */
|
|
2521
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
1870
2522
|
ChatWindow,
|
|
1871
2523
|
{
|
|
1872
2524
|
className: "h-full relative z-10",
|
|
@@ -1880,13 +2532,13 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
1880
2532
|
onAddToCart
|
|
1881
2533
|
}
|
|
1882
2534
|
),
|
|
1883
|
-
/* @__PURE__ */
|
|
2535
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
1884
2536
|
"div",
|
|
1885
2537
|
{
|
|
1886
2538
|
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"}`
|
|
1887
2539
|
}
|
|
1888
2540
|
)
|
|
1889
|
-
), /* @__PURE__ */
|
|
2541
|
+
), /* @__PURE__ */ import_react15.default.createElement(
|
|
1890
2542
|
"button",
|
|
1891
2543
|
{
|
|
1892
2544
|
onClick: isOpen ? () => setIsOpen(false) : handleOpen,
|
|
@@ -1894,149 +2546,23 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
|
|
|
1894
2546
|
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
|
|
1895
2547
|
"aria-label": "Open chat"
|
|
1896
2548
|
},
|
|
1897
|
-
/* @__PURE__ */
|
|
2549
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
1898
2550
|
"span",
|
|
1899
2551
|
{
|
|
1900
2552
|
className: "absolute inset-0 rounded-full animate-ping opacity-20",
|
|
1901
2553
|
style: { background: ui.primaryColor }
|
|
1902
2554
|
}
|
|
1903
2555
|
),
|
|
1904
|
-
/* @__PURE__ */
|
|
2556
|
+
/* @__PURE__ */ import_react15.default.createElement(
|
|
1905
2557
|
"span",
|
|
1906
2558
|
{
|
|
1907
2559
|
className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
|
|
1908
2560
|
},
|
|
1909
|
-
isOpen ? /* @__PURE__ */
|
|
2561
|
+
isOpen ? /* @__PURE__ */ import_react15.default.createElement(import_lucide_react8.X, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ import_react15.default.createElement(import_lucide_react8.MessageSquare, { className: "w-6 h-6 text-white" })
|
|
1910
2562
|
),
|
|
1911
|
-
hasUnread && !isOpen && /* @__PURE__ */
|
|
2563
|
+
hasUnread && !isOpen && /* @__PURE__ */ import_react15.default.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")
|
|
1912
2564
|
));
|
|
1913
2565
|
}
|
|
1914
|
-
|
|
1915
|
-
// src/components/DocumentUpload.tsx
|
|
1916
|
-
var import_react10 = __toESM(require("react"));
|
|
1917
|
-
var import_lucide_react7 = require("lucide-react");
|
|
1918
|
-
function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
1919
|
-
const { ui } = useConfig();
|
|
1920
|
-
const [fileStates, setFileStates] = (0, import_react10.useState)([]);
|
|
1921
|
-
const [isDragging, setIsDragging] = (0, import_react10.useState)(false);
|
|
1922
|
-
const fileInputRef = (0, import_react10.useRef)(null);
|
|
1923
|
-
const addFiles = (files) => {
|
|
1924
|
-
const newStates = files.map((file) => ({ file, status: "idle" }));
|
|
1925
|
-
setFileStates((prev) => [...prev, ...newStates]);
|
|
1926
|
-
};
|
|
1927
|
-
const removeFile = (index) => {
|
|
1928
|
-
setFileStates((prev) => prev.filter((_, i) => i !== index));
|
|
1929
|
-
};
|
|
1930
|
-
const onDragOver = (e) => {
|
|
1931
|
-
e.preventDefault();
|
|
1932
|
-
setIsDragging(true);
|
|
1933
|
-
};
|
|
1934
|
-
const onDragLeave = () => {
|
|
1935
|
-
setIsDragging(false);
|
|
1936
|
-
};
|
|
1937
|
-
const onDrop = (e) => {
|
|
1938
|
-
e.preventDefault();
|
|
1939
|
-
setIsDragging(false);
|
|
1940
|
-
if (e.dataTransfer.files) {
|
|
1941
|
-
addFiles(Array.from(e.dataTransfer.files));
|
|
1942
|
-
}
|
|
1943
|
-
};
|
|
1944
|
-
const onFileSelect = (e) => {
|
|
1945
|
-
if (e.target.files) {
|
|
1946
|
-
addFiles(Array.from(e.target.files));
|
|
1947
|
-
}
|
|
1948
|
-
};
|
|
1949
|
-
const uploadFiles = async () => {
|
|
1950
|
-
const idleFiles = fileStates.filter((s) => s.status === "idle");
|
|
1951
|
-
if (idleFiles.length === 0) return;
|
|
1952
|
-
setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
|
|
1953
|
-
const formData = new FormData();
|
|
1954
|
-
idleFiles.forEach((s) => formData.append("files", s.file));
|
|
1955
|
-
if (namespace) formData.append("namespace", namespace);
|
|
1956
|
-
try {
|
|
1957
|
-
const response = await fetch("/api/upload", {
|
|
1958
|
-
method: "POST",
|
|
1959
|
-
body: formData
|
|
1960
|
-
});
|
|
1961
|
-
const result = await response.json();
|
|
1962
|
-
if (!response.ok) throw new Error(result.error || "Upload failed");
|
|
1963
|
-
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
|
|
1964
|
-
if (onUploadComplete) onUploadComplete(result);
|
|
1965
|
-
} catch (err) {
|
|
1966
|
-
const message = err instanceof Error ? err.message : "Upload failed";
|
|
1967
|
-
setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
|
|
1968
|
-
}
|
|
1969
|
-
};
|
|
1970
|
-
const isUploading = fileStates.some((s) => s.status === "uploading");
|
|
1971
|
-
const hasIdle = fileStates.some((s) => s.status === "idle");
|
|
1972
|
-
return /* @__PURE__ */ import_react10.default.createElement(
|
|
1973
|
-
"div",
|
|
1974
|
-
{
|
|
1975
|
-
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}`,
|
|
1976
|
-
onDragOver,
|
|
1977
|
-
onDragLeave,
|
|
1978
|
-
onDrop
|
|
1979
|
-
},
|
|
1980
|
-
/* @__PURE__ */ import_react10.default.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
1981
|
-
"div",
|
|
1982
|
-
{
|
|
1983
|
-
className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
|
|
1984
|
-
style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
|
|
1985
|
-
},
|
|
1986
|
-
/* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
|
|
1987
|
-
), /* @__PURE__ */ import_react10.default.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ import_react10.default.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__ */ import_react10.default.createElement(
|
|
1988
|
-
"button",
|
|
1989
|
-
{
|
|
1990
|
-
onClick: () => {
|
|
1991
|
-
var _a;
|
|
1992
|
-
return (_a = fileInputRef.current) == null ? void 0 : _a.click();
|
|
1993
|
-
},
|
|
1994
|
-
disabled: isUploading,
|
|
1995
|
-
className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
|
|
1996
|
-
style: {
|
|
1997
|
-
backgroundColor: `${ui.primaryColor}15`,
|
|
1998
|
-
color: ui.primaryColor,
|
|
1999
|
-
border: `1px solid ${ui.primaryColor}30`
|
|
2000
|
-
}
|
|
2001
|
-
},
|
|
2002
|
-
"Select Files"
|
|
2003
|
-
), /* @__PURE__ */ import_react10.default.createElement(
|
|
2004
|
-
"input",
|
|
2005
|
-
{
|
|
2006
|
-
ref: fileInputRef,
|
|
2007
|
-
type: "file",
|
|
2008
|
-
multiple: true,
|
|
2009
|
-
onChange: onFileSelect,
|
|
2010
|
-
className: "hidden",
|
|
2011
|
-
accept: ".pdf,.docx,.txt,.md,.json,.csv"
|
|
2012
|
-
}
|
|
2013
|
-
)),
|
|
2014
|
-
fileStates.length > 0 && /* @__PURE__ */ import_react10.default.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ import_react10.default.createElement(
|
|
2015
|
-
"div",
|
|
2016
|
-
{
|
|
2017
|
-
key: i,
|
|
2018
|
-
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"
|
|
2019
|
-
},
|
|
2020
|
-
/* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ import_react10.default.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ import_react10.default.createElement("div", { className: "truncate" }, /* @__PURE__ */ import_react10.default.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ import_react10.default.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
|
|
2021
|
-
/* @__PURE__ */ import_react10.default.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ import_react10.default.createElement("div", { title: state.error }, /* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ import_react10.default.createElement(
|
|
2022
|
-
"button",
|
|
2023
|
-
{
|
|
2024
|
-
onClick: () => removeFile(i),
|
|
2025
|
-
className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
|
|
2026
|
-
},
|
|
2027
|
-
/* @__PURE__ */ import_react10.default.createElement(import_lucide_react7.X, { className: "w-3.5 h-3.5 text-slate-400" })
|
|
2028
|
-
))
|
|
2029
|
-
)), hasIdle && !isUploading && /* @__PURE__ */ import_react10.default.createElement(
|
|
2030
|
-
"button",
|
|
2031
|
-
{
|
|
2032
|
-
onClick: uploadFiles,
|
|
2033
|
-
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]",
|
|
2034
|
-
style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
|
|
2035
|
-
},
|
|
2036
|
-
"Start Upload"
|
|
2037
|
-
))
|
|
2038
|
-
);
|
|
2039
|
-
}
|
|
2040
2566
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2041
2567
|
0 && (module.exports = {
|
|
2042
2568
|
ChatWidget,
|
|
@@ -2044,7 +2570,9 @@ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
|
|
|
2044
2570
|
ConfigProvider,
|
|
2045
2571
|
DocumentUpload,
|
|
2046
2572
|
MessageBubble,
|
|
2573
|
+
ObservabilityPanel,
|
|
2047
2574
|
SourceCard,
|
|
2575
|
+
addSynonyms,
|
|
2048
2576
|
useConfig,
|
|
2049
2577
|
useRagChat
|
|
2050
2578
|
});
|