@retrivora-ai/rag-engine 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/{ILLMProvider-BfRgI1Xh.d.mts → ILLMProvider-BOJFz3Na.d.mts} +47 -1
  2. package/dist/{ILLMProvider-BfRgI1Xh.d.ts → ILLMProvider-BOJFz3Na.d.ts} +47 -1
  3. package/dist/{MultiTablePostgresProvider-YY7LPNJK.mjs → MultiTablePostgresProvider-ZLGSKTJR.mjs} +1 -1
  4. package/dist/chunk-ICKRMZQK.mjs +76 -0
  5. package/dist/{chunk-BFYLQYQU.mjs → chunk-LZVVLSDN.mjs} +192 -100
  6. package/dist/{chunk-R3RGUMHE.mjs → chunk-OZFBG4BA.mjs} +121 -48
  7. package/dist/handlers/index.d.mts +2 -2
  8. package/dist/handlers/index.d.ts +2 -2
  9. package/dist/handlers/index.js +368 -147
  10. package/dist/handlers/index.mjs +4 -1
  11. package/dist/{index-BV0z5mb6.d.mts → index-BwpcaziY.d.ts} +4 -2
  12. package/dist/{index-1Z4GuYBi.d.ts → index-D3V9Et2M.d.mts} +4 -2
  13. package/dist/index.d.mts +23 -3
  14. package/dist/index.d.ts +23 -3
  15. package/dist/index.js +1143 -790
  16. package/dist/index.mjs +1065 -770
  17. package/dist/server.d.mts +15 -25
  18. package/dist/server.d.ts +15 -25
  19. package/dist/server.js +366 -147
  20. package/dist/server.mjs +3 -2
  21. package/package.json +4 -2
  22. package/src/app/api/upload/route.ts +4 -0
  23. package/src/app/constants.tsx +2 -2
  24. package/src/app/page.tsx +12 -321
  25. package/src/components/AmbientBackground.tsx +29 -0
  26. package/src/components/ArchitectureCard.tsx +17 -0
  27. package/src/components/ArchitectureCardsSection.tsx +15 -0
  28. package/src/components/ChatWindow.tsx +32 -0
  29. package/src/components/CodeViewer.tsx +51 -0
  30. package/src/components/ConfigProvider.tsx +1 -0
  31. package/src/components/DocViewer.tsx +37 -0
  32. package/src/components/DocumentUpload.tsx +44 -1
  33. package/src/components/Documentation.tsx +58 -0
  34. package/src/components/DynamicChart.tsx +27 -2
  35. package/src/components/Hero.tsx +59 -0
  36. package/src/components/HourglassLoader.tsx +87 -0
  37. package/src/components/Lifecycle.tsx +37 -0
  38. package/src/components/MarkdownComponents.tsx +140 -0
  39. package/src/components/MessageBubble.tsx +88 -1010
  40. package/src/components/Navbar.tsx +55 -0
  41. package/src/components/ObservabilityPanel.tsx +374 -0
  42. package/src/components/ProductCard.tsx +3 -1
  43. package/src/components/UIDispatcher.tsx +344 -0
  44. package/src/components/VisualizationRenderer.tsx +48 -26
  45. package/src/core/Pipeline.ts +186 -76
  46. package/src/handlers/index.ts +72 -12
  47. package/src/hooks/useRagChat.ts +19 -9
  48. package/src/index.ts +9 -1
  49. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  50. package/src/types/chat.ts +2 -0
  51. package/src/types/index.ts +52 -0
  52. package/src/types/props.ts +9 -1
  53. package/src/utils/ProductExtractor.ts +347 -0
  54. package/src/utils/UITransformer.ts +4 -53
  55. package/src/utils/synonyms.ts +78 -0
package/dist/index.js CHANGED
@@ -64,58 +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 import_react10 = __toESM(require("react"));
75
- var import_lucide_react6 = require("lucide-react");
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 import_react9 = __toESM(require("react"));
79
- var import_lucide_react5 = require("lucide-react");
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 import_react6 = __toESM(require("react"));
83
- var import_lucide_react4 = require("lucide-react");
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
309
 
86
310
  // src/components/SourceCard.tsx
87
- var import_react = __toESM(require("react"));
88
- var import_lucide_react = require("lucide-react");
311
+ var import_react3 = __toESM(require("react"));
312
+ var import_lucide_react2 = require("lucide-react");
89
313
  function SourceCard({ source, index }) {
90
314
  var _a, _b;
91
- const [expanded, setExpanded] = import_react.default.useState(false);
315
+ const [expanded, setExpanded] = import_react3.default.useState(false);
92
316
  const scorePercent = Math.round(source.score * 100);
93
317
  const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
94
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";
95
319
  const preview = source.content.slice(0, 120);
96
320
  const hasMore = source.content.length > 120;
97
- return /* @__PURE__ */ import_react.default.createElement(
321
+ return /* @__PURE__ */ import_react3.default.createElement(
98
322
  "div",
99
323
  {
100
324
  className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
101
325
  onClick: () => setExpanded((e) => !e)
102
326
  },
103
- /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.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_react.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_react.default.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
104
- /* @__PURE__ */ import_react.default.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
105
- hasMore && /* @__PURE__ */ import_react.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_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronDown, { className: "w-3 h-3" }), " Show more"))
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"))
106
330
  );
107
331
  }
108
332
 
109
333
  // src/components/ProductCarousel.tsx
110
- var import_react3 = __toESM(require("react"));
111
- var import_lucide_react3 = require("lucide-react");
334
+ var import_react5 = __toESM(require("react"));
335
+ var import_lucide_react4 = require("lucide-react");
112
336
 
113
337
  // src/components/ProductCard.tsx
114
- var import_react2 = __toESM(require("react"));
338
+ var import_react4 = __toESM(require("react"));
115
339
  var import_image = __toESM(require("next/image"));
116
- var import_lucide_react2 = require("lucide-react");
340
+ var import_lucide_react3 = require("lucide-react");
117
341
  function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
118
- return /* @__PURE__ */ import_react2.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_react2.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_react2.default.createElement(
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(
119
343
  import_image.default,
120
344
  {
121
345
  src: product.image,
@@ -124,16 +348,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
124
348
  unoptimized: true,
125
349
  className: "object-cover group-hover:scale-105 transition-transform duration-500"
126
350
  }
127
- ) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ import_react2.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_react2.default.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ import_react2.default.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ import_react2.default.createElement(
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(
128
352
  "button",
129
353
  {
130
354
  onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
131
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",
132
356
  style: { background: primaryColor }
133
357
  },
134
- /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ShoppingCart, { className: "w-3 h-3" }),
358
+ /* @__PURE__ */ import_react4.default.createElement(import_lucide_react3.ShoppingCart, { className: "w-3 h-3" }),
135
359
  "Add to Cart"
136
- ), product.link && /* @__PURE__ */ import_react2.default.createElement(
360
+ ), product.link && /* @__PURE__ */ import_react4.default.createElement(
137
361
  "a",
138
362
  {
139
363
  href: product.link,
@@ -141,15 +365,15 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
141
365
  rel: "noopener noreferrer",
142
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"
143
367
  },
144
- /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ExternalLink, { className: "w-3.5 h-3.5" })
368
+ /* @__PURE__ */ import_react4.default.createElement(import_lucide_react3.ExternalLink, { className: "w-3.5 h-3.5" })
145
369
  ))));
146
370
  }
147
371
 
148
372
  // src/components/ProductCarousel.tsx
149
373
  function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
150
- const scrollRef = (0, import_react3.useRef)(null);
151
- const [canScroll, setCanScroll] = (0, import_react3.useState)(false);
152
- (0, import_react3.useEffect)(() => {
374
+ const scrollRef = (0, import_react5.useRef)(null);
375
+ const [canScroll, setCanScroll] = (0, import_react5.useState)(false);
376
+ (0, import_react5.useEffect)(() => {
153
377
  const checkScroll = () => {
154
378
  if (scrollRef.current) {
155
379
  const hasOverflow = scrollRef.current.scrollWidth > scrollRef.current.clientWidth;
@@ -173,9 +397,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
173
397
  };
174
398
  if (!products || products.length === 0) return null;
175
399
  if (products.length === 1) {
176
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
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 }));
177
401
  }
178
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.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_react3.default.createElement(
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(
179
403
  "button",
180
404
  {
181
405
  onClick: (e) => {
@@ -185,8 +409,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
185
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",
186
410
  "aria-label": "Scroll left"
187
411
  },
188
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronLeft, { className: "w-5 h-5" })
189
- )), /* @__PURE__ */ import_react3.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_react3.default.createElement(
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(
190
414
  "button",
191
415
  {
192
416
  onClick: (e) => {
@@ -196,40 +420,45 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
196
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",
197
421
  "aria-label": "Scroll right"
198
422
  },
199
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronRight, { className: "w-5 h-5" })
200
- ))), /* @__PURE__ */ import_react3.default.createElement(
423
+ /* @__PURE__ */ import_react5.default.createElement(import_lucide_react4.ChevronRight, { className: "w-5 h-5" })
424
+ ))), /* @__PURE__ */ import_react5.default.createElement(
201
425
  "div",
202
426
  {
203
427
  ref: scrollRef,
204
428
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
205
429
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
206
430
  },
207
- products.map((product) => /* @__PURE__ */ import_react3.default.createElement(
431
+ products.map((product) => /* @__PURE__ */ import_react5.default.createElement(
208
432
  "div",
209
433
  {
210
434
  key: product.id,
211
435
  className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
212
436
  },
213
- /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product, primaryColor, onAddToCart })
437
+ /* @__PURE__ */ import_react5.default.createElement(ProductCard, { product, primaryColor, onAddToCart })
214
438
  ))
215
439
  ));
216
440
  }
217
441
 
442
+ // src/components/VisualizationRenderer.tsx
443
+ var import_react7 = __toESM(require("react"));
444
+ var import_recharts2 = require("recharts");
445
+
218
446
  // src/components/DynamicChart.tsx
219
- var import_react4 = __toESM(require("react"));
447
+ var import_react6 = __toESM(require("react"));
220
448
  var import_recharts = require("recharts");
221
- var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
449
+ var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
222
450
  function DynamicChart({
223
451
  config,
224
452
  primaryColor = "#6366f1",
225
453
  accentColor = "#8b5cf6",
226
- viewportSize = "large"
454
+ viewportSize = "large",
455
+ isStreaming = false
227
456
  }) {
228
457
  const { type, data } = config;
229
- const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
230
- const containerRef = import_react4.default.useRef(null);
231
- const [containerWidth, setContainerWidth] = import_react4.default.useState(0);
232
- import_react4.default.useEffect(() => {
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(() => {
233
462
  const element = containerRef.current;
234
463
  if (!element || typeof ResizeObserver === "undefined") return;
235
464
  const updateWidth = (width) => {
@@ -255,7 +484,7 @@ function DynamicChart({
255
484
  return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
256
485
  };
257
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 };
258
- const sanitizedData = import_react4.default.useMemo(() => {
487
+ const sanitizedData = import_react6.default.useMemo(() => {
259
488
  if (!data) return [];
260
489
  return data.map((item) => {
261
490
  const newItem = {};
@@ -287,8 +516,19 @@ function DynamicChart({
287
516
  return newItem;
288
517
  });
289
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
+ }
290
530
  if (!data || data.length === 0) {
291
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
531
+ return /* @__PURE__ */ import_react6.default.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
292
532
  }
293
533
  const firstItem = sanitizedData[0];
294
534
  const allKeys = Object.keys(firstItem);
@@ -313,13 +553,13 @@ function DynamicChart({
313
553
  const renderTooltip = (row, label) => {
314
554
  var _a, _b;
315
555
  if (!row) return null;
316
- return /* @__PURE__ */ import_react4.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_react4.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]) => {
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]) => {
317
557
  var _a2;
318
558
  if (value === null || value === void 0 || key === finalXKey) return null;
319
559
  if (typeof value === "object") return null;
320
560
  const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
321
561
  if (forbiddenKeys.includes(key)) return null;
322
- return /* @__PURE__ */ import_react4.default.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react4.default.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ import_react4.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
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)));
323
563
  }));
324
564
  };
325
565
  const getTooltipRow = (payload) => {
@@ -331,7 +571,7 @@ function DynamicChart({
331
571
  return row;
332
572
  };
333
573
  if (type === "pie") {
334
- return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react4.default.createElement(
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(
335
575
  import_recharts.Pie,
336
576
  {
337
577
  data: sanitizedData,
@@ -343,10 +583,10 @@ function DynamicChart({
343
583
  label: false,
344
584
  labelLine: false
345
585
  },
346
- sanitizedData.map((_entry, index) => /* @__PURE__ */ import_react4.default.createElement(import_recharts.Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
347
- ), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
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" })))));
348
588
  }
349
- return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.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_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.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_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
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(
350
590
  import_recharts.Bar,
351
591
  {
352
592
  key,
@@ -355,7 +595,7 @@ function DynamicChart({
355
595
  radius: [4, 4, 0, 0],
356
596
  barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
357
597
  }
358
- ))) : /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.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_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
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(
359
599
  import_recharts.Line,
360
600
  {
361
601
  key,
@@ -370,26 +610,6 @@ function DynamicChart({
370
610
  }
371
611
 
372
612
  // src/components/VisualizationRenderer.tsx
373
- var import_react5 = __toESM(require("react"));
374
- var import_recharts2 = require("recharts");
375
- var CHART_COLORS = [
376
- "#6366f1",
377
- // indigo
378
- "#10b981",
379
- // emerald
380
- "#f59e0b",
381
- // amber
382
- "#ef4444",
383
- // red
384
- "#8b5cf6",
385
- // purple
386
- "#ec4899",
387
- // pink
388
- "#06b6d4",
389
- // cyan
390
- "#f97316"
391
- // orange
392
- ];
393
613
  function getColor(index) {
394
614
  return CHART_COLORS[index % CHART_COLORS.length];
395
615
  }
@@ -399,60 +619,71 @@ function CustomTooltip({
399
619
  label
400
620
  }) {
401
621
  if (!active || !payload || payload.length === 0) return null;
402
- return /* @__PURE__ */ import_react5.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_react5.default.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ import_react5.default.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ import_react5.default.createElement(
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(
403
623
  "span",
404
624
  {
405
625
  className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
406
626
  style: { background: entry.color }
407
627
  }
408
- ), entry.name), /* @__PURE__ */ import_react5.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
628
+ ), entry.name), /* @__PURE__ */ import_react7.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
409
629
  }
410
630
  function VisualizationRenderer({
411
631
  data,
412
632
  className = "",
413
633
  primaryColor,
414
- onAddToCart
634
+ onAddToCart,
635
+ isStreaming
415
636
  }) {
416
637
  var _a;
417
638
  if (!data) {
418
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
639
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
419
640
  }
420
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react5.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_react5.default.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor));
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));
421
642
  }
422
- function renderVisualization(type, data, onAddToCart, primaryColor) {
643
+ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
423
644
  try {
424
645
  switch (type) {
425
646
  case "pie_chart":
426
- return /* @__PURE__ */ import_react5.default.createElement(PieChartView, { data });
647
+ return /* @__PURE__ */ import_react7.default.createElement(PieChartView, { data, isStreaming });
427
648
  case "bar_chart":
428
- return /* @__PURE__ */ import_react5.default.createElement(BarChartView, { data, primaryColor });
649
+ return /* @__PURE__ */ import_react7.default.createElement(BarChartView, { data, primaryColor, isStreaming });
429
650
  case "line_chart":
430
- return /* @__PURE__ */ import_react5.default.createElement(LineChartView, { data, primaryColor });
651
+ return /* @__PURE__ */ import_react7.default.createElement(LineChartView, { data, primaryColor, isStreaming });
431
652
  case "radar_chart":
432
- return /* @__PURE__ */ import_react5.default.createElement(RadarChartView, { data });
653
+ return /* @__PURE__ */ import_react7.default.createElement(RadarChartView, { data, isStreaming });
433
654
  case "table":
434
- return /* @__PURE__ */ import_react5.default.createElement(TableView, { data });
655
+ return /* @__PURE__ */ import_react7.default.createElement(TableView, { data, isStreaming });
435
656
  case "product_carousel":
436
657
  case "carousel":
437
- return /* @__PURE__ */ import_react5.default.createElement(CarouselView, { data, onAddToCart, primaryColor });
658
+ return /* @__PURE__ */ import_react7.default.createElement(CarouselView, { data, onAddToCart, primaryColor });
438
659
  case "text":
439
660
  default:
440
- return /* @__PURE__ */ import_react5.default.createElement(TextView, { data });
661
+ return /* @__PURE__ */ import_react7.default.createElement(TextView, { data });
441
662
  }
442
663
  } catch (error) {
443
664
  console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
444
- return /* @__PURE__ */ import_react5.default.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
665
+ return /* @__PURE__ */ import_react7.default.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
445
666
  }
446
667
  }
447
- function PieChartView({ data }) {
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" }));
671
+ }
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}%` } })));
676
+ }
677
+ function PieChartView({ data, isStreaming }) {
678
+ if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "pie" });
448
679
  if (!Array.isArray(data) || data.length === 0) {
449
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
680
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
450
681
  }
451
682
  const chartData = data.map((item) => ({
452
683
  name: item.label,
453
684
  value: item.value
454
685
  }));
455
- return /* @__PURE__ */ import_react5.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_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 280 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PieChart, null, /* @__PURE__ */ import_react5.default.createElement(
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(
456
687
  import_recharts2.Pie,
457
688
  {
458
689
  data: chartData,
@@ -464,12 +695,13 @@ function PieChartView({ data }) {
464
695
  label: false,
465
696
  labelLine: false
466
697
  },
467
- chartData.map((_entry, index) => /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Cell, { key: `cell-${index}`, fill: getColor(index) }))
468
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
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" }))));
469
700
  }
470
- function BarChartView({ data, primaryColor }) {
701
+ function BarChartView({ data, primaryColor, isStreaming }) {
702
+ if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "bar" });
471
703
  if (!Array.isArray(data) || data.length === 0) {
472
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
704
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
473
705
  }
474
706
  const chartData = data.map((item) => __spreadValues(__spreadValues({
475
707
  category: item.category,
@@ -477,7 +709,7 @@ function BarChartView({ data, primaryColor }) {
477
709
  }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
478
710
  const barColor = primaryColor != null ? primaryColor : getColor(0);
479
711
  const hasStock = chartData.some((d) => "inStockCount" in d);
480
- return /* @__PURE__ */ import_react5.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_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.BarChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(
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(
481
713
  import_recharts2.XAxis,
482
714
  {
483
715
  dataKey: "category",
@@ -485,11 +717,12 @@ function BarChartView({ data, primaryColor }) {
485
717
  tickLine: false,
486
718
  axisLine: { stroke: "#cbd5e1" }
487
719
  }
488
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
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] }))));
489
721
  }
490
- function LineChartView({ data, primaryColor }) {
722
+ function LineChartView({ data, primaryColor, isStreaming }) {
723
+ if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "bar" });
491
724
  if (!Array.isArray(data) || data.length === 0) {
492
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
725
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
493
726
  }
494
727
  const chartData = data.map((point) => {
495
728
  var _a;
@@ -499,7 +732,7 @@ function LineChartView({ data, primaryColor }) {
499
732
  };
500
733
  });
501
734
  const lineColor = primaryColor != null ? primaryColor : getColor(0);
502
- return /* @__PURE__ */ import_react5.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_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.LineChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(
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(
503
736
  import_recharts2.XAxis,
504
737
  {
505
738
  dataKey: "label",
@@ -507,7 +740,7 @@ function LineChartView({ data, primaryColor }) {
507
740
  tickLine: false,
508
741
  axisLine: { stroke: "#cbd5e1" }
509
742
  }
510
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(
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(
511
744
  import_recharts2.Line,
512
745
  {
513
746
  type: "monotone",
@@ -520,14 +753,15 @@ function LineChartView({ data, primaryColor }) {
520
753
  }
521
754
  ))));
522
755
  }
523
- function RadarChartView({ data }) {
756
+ function RadarChartView({ data, isStreaming }) {
757
+ if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "pie" });
524
758
  if (!Array.isArray(data) || data.length === 0) {
525
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
759
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
526
760
  }
527
761
  const productNames = Array.from(
528
762
  new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
529
763
  );
530
- return /* @__PURE__ */ import_react5.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_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 300 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ import_react5.default.createElement(
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(
531
765
  import_recharts2.Radar,
532
766
  {
533
767
  key: productName,
@@ -537,26 +771,27 @@ function RadarChartView({ data }) {
537
771
  fill: getColor(index),
538
772
  fillOpacity: 0.4
539
773
  }
540
- )), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }))));
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 } }))));
541
775
  }
542
- function TableView({ data }) {
776
+ function TableView({ data, isStreaming }) {
777
+ if (isStreaming) return /* @__PURE__ */ import_react7.default.createElement(ChartSkeleton, { type: "table" });
543
778
  if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
544
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
779
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
545
780
  }
546
- return /* @__PURE__ */ import_react5.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_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ import_react5.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ import_react5.default.createElement(
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(
547
782
  "th",
548
783
  {
549
784
  key: idx,
550
785
  className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
551
786
  },
552
787
  col
553
- )))), /* @__PURE__ */ import_react5.default.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ import_react5.default.createElement(
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(
554
789
  "tr",
555
790
  {
556
791
  key: rowIdx,
557
792
  className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
558
793
  },
559
- row.map((cell, cellIdx) => /* @__PURE__ */ import_react5.default.createElement(
794
+ row.map((cell, cellIdx) => /* @__PURE__ */ import_react7.default.createElement(
560
795
  "td",
561
796
  {
562
797
  key: cellIdx,
@@ -564,7 +799,7 @@ function TableView({ data }) {
564
799
  },
565
800
  String(cell)
566
801
  ))
567
- ))))), /* @__PURE__ */ import_react5.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_react5.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
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" : "")));
568
803
  }
569
804
  function CarouselView({
570
805
  data,
@@ -572,7 +807,7 @@ function CarouselView({
572
807
  primaryColor
573
808
  }) {
574
809
  if (!Array.isArray(data)) {
575
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
810
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
576
811
  }
577
812
  const products = data.map((product) => ({
578
813
  id: product.id,
@@ -584,7 +819,7 @@ function CarouselView({
584
819
  description: product.description,
585
820
  inStock: product.inStock
586
821
  }));
587
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react5.default.createElement(
822
+ return /* @__PURE__ */ import_react7.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react7.default.createElement(
588
823
  ProductCarousel,
589
824
  {
590
825
  products,
@@ -595,10 +830,333 @@ function CarouselView({
595
830
  }
596
831
  function TextView({ data }) {
597
832
  const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
598
- return /* @__PURE__ */ import_react5.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_react5.default.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
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));
599
834
  }
600
835
 
601
- // src/components/MessageBubble.tsx
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(
852
+ "div",
853
+ {
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"
856
+ },
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",
860
+ {
861
+ className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
862
+ style: { width: `${Math.min(100, chunk.score * 100)}%` }
863
+ }
864
+ )),
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(
991
+ "span",
992
+ {
993
+ className: "inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider",
994
+ style: { color: primaryColor }
995
+ },
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 })));
1011
+ }
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
+ }
1132
+ }
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;
1157
+ }
1158
+
1159
+ // src/utils/ProductExtractor.ts
602
1160
  function sanitizeJson(raw) {
603
1161
  let s = raw.trim();
604
1162
  s = s.replace(
@@ -615,37 +1173,35 @@ function sanitizeJson(raw) {
615
1173
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
616
1174
  s = s.replace(/,\s*([}\]])/g, "$1");
617
1175
  s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
618
- {
619
- const stack = [];
620
- let inString = false;
621
- let escape = false;
622
- for (const ch of s) {
623
- if (escape) {
624
- escape = false;
625
- continue;
626
- }
627
- if (ch === "\\" && inString) {
628
- escape = true;
629
- continue;
630
- }
631
- if (ch === '"') {
632
- inString = !inString;
633
- continue;
634
- }
635
- if (inString) continue;
636
- if (ch === "{" || ch === "[") {
637
- stack.push(ch);
638
- } else if (ch === "}") {
639
- if (stack[stack.length - 1] === "{") stack.pop();
640
- } else if (ch === "]") {
641
- if (stack[stack.length - 1] === "[") stack.pop();
642
- }
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;
1183
+ }
1184
+ if (ch === "\\" && inString) {
1185
+ escape = true;
1186
+ continue;
1187
+ }
1188
+ if (ch === '"') {
1189
+ inString = !inString;
1190
+ continue;
643
1191
  }
644
- if (inString) s += '"';
645
- for (let i = stack.length - 1; i >= 0; i--) {
646
- s += stack[i] === "{" ? "}" : "]";
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();
647
1199
  }
648
1200
  }
1201
+ if (inString) s += '"';
1202
+ for (let i = stack.length - 1; i >= 0; i--) {
1203
+ s += stack[i] === "{" ? "}" : "]";
1204
+ }
649
1205
  const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
650
1206
  if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
651
1207
  s = s.substring(0, lastClose + 1);
@@ -745,10 +1301,6 @@ function extractStructuredPayload(raw) {
745
1301
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
746
1302
  };
747
1303
  }
748
- function isLikelyUiOnlyMessage(text) {
749
- const trimmed = text.trim();
750
- 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);
751
- }
752
1304
  function resolveImage(data) {
753
1305
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
754
1306
  for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
@@ -758,92 +1310,140 @@ function resolveImage(data) {
758
1310
  }
759
1311
  return void 0;
760
1312
  }
761
- function getMetadataValue(meta, keys) {
762
- const searchKeys = keys.map((k) => k.toLowerCase());
763
- const metaKeys = Object.keys(meta);
764
- const foundKey = metaKeys.find((k) => searchKeys.includes(k.toLowerCase()));
765
- if (foundKey) return meta[foundKey];
766
- const partialKey = metaKeys.find((k) => {
767
- const kLow = k.toLowerCase();
768
- return searchKeys.some((sk) => kLow.includes(sk) || sk.includes(kLow));
769
- });
770
- return partialKey ? meta[partialKey] : void 0;
771
- }
772
- function normaliseChild(children) {
773
- if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react6.default.isValidElement(children)) {
774
- return JSON.stringify(children);
775
- }
776
- return children;
777
- }
778
- function normaliseFieldName(value) {
779
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
780
- }
781
- function formatColumnLabel(value) {
782
- 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);
783
1316
  }
784
- function DataTable({ config, viewportSize = "large" }) {
785
- const isCompact = viewportSize === "compact";
786
- const isMedium = viewportSize === "medium";
787
- const columns = import_react6.default.useMemo(() => {
788
- const rowKeys = Array.from(
789
- new Set(
790
- config.data.flatMap((row) => Object.keys(row))
791
- )
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)
792
1325
  );
793
- const findAccessor = (label) => {
794
- const exact = rowKeys.find((key) => key === label);
795
- if (exact) return exact;
796
- const normalisedLabel = normaliseFieldName(label);
797
- const normalisedMatch = rowKeys.find((key) => normaliseFieldName(key) === normalisedLabel);
798
- if (normalisedMatch) return normalisedMatch;
799
- return label;
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,
800
1345
  };
801
- if (Array.isArray(config.columns) && config.columns.length) {
802
- return config.columns.map((label) => ({
803
- label: formatColumnLabel(label),
804
- accessor: findAccessor(label)
805
- }));
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) {
806
1368
  }
807
- if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
808
- const keys = config.xAxisKey && !config.dataKeys.includes(config.xAxisKey) ? [config.xAxisKey, ...config.dataKeys] : config.dataKeys;
809
- return keys.map((key) => ({
810
- label: formatColumnLabel(key),
811
- accessor: findAccessor(key)
812
- }));
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
+ }
813
1387
  }
814
- return rowKeys.map((key) => ({ label: formatColumnLabel(key), accessor: key }));
815
- }, [config]);
816
- if (!config.data.length || !columns.length) {
817
- return /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
818
1388
  }
819
- const formatCell = (val) => {
820
- if (val === null || val === void 0) return "\u2014";
821
- if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
822
- return String(val);
823
- };
824
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react6.default.createElement("table", { className: `w-full text-left border-collapse ${isCompact ? "min-w-[240px] text-xs" : isMedium ? "min-w-[280px] text-[13px]" : "min-w-[320px] text-sm"}` }, /* @__PURE__ */ import_react6.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react6.default.createElement("tr", null, columns.map((column) => /* @__PURE__ */ import_react6.default.createElement(
825
- "th",
826
- {
827
- key: column.label,
828
- className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap`
829
- },
830
- column.label
831
- )))), /* @__PURE__ */ import_react6.default.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ import_react6.default.createElement(
832
- "tr",
833
- {
834
- key: ri,
835
- className: "border-b border-slate-100 dark:border-white/5 last:border-0 hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
836
- },
837
- columns.map((column) => /* @__PURE__ */ import_react6.default.createElement(
838
- "td",
839
- {
840
- key: column.label,
841
- className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} text-slate-600 dark:text-white/70 ${column.accessor.toLowerCase().includes("description") ? "whitespace-normal break-words" : "whitespace-nowrap"}`
842
- },
843
- formatCell(row[column.accessor])
844
- ))
845
- ))))), /* @__PURE__ */ import_react6.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_react6.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
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
+ });
846
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"));
847
1447
  function normalizeTabularRows(rows, columns) {
848
1448
  if (!Array.isArray(rows)) return [];
849
1449
  const SYNONYMS = {
@@ -911,8 +1511,7 @@ function resolveStructuredRows(config) {
911
1511
  }
912
1512
  function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
913
1513
  var _a;
914
- const result = import_react6.default.useMemo(() => {
915
- if (isStreaming) return { loading: true };
1514
+ const result = import_react10.default.useMemo(() => {
916
1515
  try {
917
1516
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
918
1517
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
@@ -941,10 +1540,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
941
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)) {
942
1541
  config2.view = "chart";
943
1542
  config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
944
- } else if (viewStr.includes("table")) {
1543
+ } else if (viewStr.includes("table") || config2.type === "table") {
945
1544
  config2.view = "table";
946
1545
  } else {
947
- config2.view = "table";
1546
+ config2.view = "none";
948
1547
  }
949
1548
  }
950
1549
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
@@ -952,22 +1551,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
952
1551
  });
953
1552
  return { config: normalizedConfig };
954
1553
  } catch (err) {
1554
+ if (isStreaming) return { loading: true };
955
1555
  return { error: String(err) };
956
1556
  }
957
1557
  }, [rawContent, isStreaming]);
958
1558
  if ("loading" in result) {
959
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
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..."));
960
1560
  }
961
1561
  if ("error" in result) {
962
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("code", null, rawContent));
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));
963
1563
  }
964
1564
  const { config } = result;
965
1565
  const isCompact = viewportSize === "compact";
966
1566
  const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
967
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);
968
1570
  switch (config.view) {
969
1571
  case "chart":
970
- return /* @__PURE__ */ import_react6.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_react6.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_react6.default.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(
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(
971
1573
  DynamicChart,
972
1574
  {
973
1575
  config: {
@@ -979,9 +1581,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
979
1581
  },
980
1582
  primaryColor,
981
1583
  accentColor,
982
- viewportSize
1584
+ viewportSize,
1585
+ isStreaming
983
1586
  }
984
- ), hasInsights && /* @__PURE__ */ import_react6.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_react6.default.createElement(
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(
985
1588
  "span",
986
1589
  {
987
1590
  key: `insight-${i}`,
@@ -990,26 +1593,151 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
990
1593
  String(insight)
991
1594
  ))));
992
1595
  case "carousel":
993
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react6.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react6.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(ProductCarousel, { products: config.data.map((item) => {
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) => {
994
1597
  var _a2;
995
1598
  return __spreadProps(__spreadValues({}, item), {
996
1599
  image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
997
1600
  });
998
1601
  }), primaryColor, onAddToCart }));
999
1602
  case "table":
1000
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ import_react6.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(DataTable, { config: {
1001
- type: "table",
1002
- title: config.title,
1003
- description: config.description,
1004
- columns: config.columns,
1005
- dataKeys: config.dataKeys,
1006
- xAxisKey: config.xAxisKey,
1007
- data: config.data
1008
- }, viewportSize }));
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
+ ));
1009
1619
  default:
1010
1620
  return null;
1011
1621
  }
1012
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
1013
1741
  function MessageBubble({
1014
1742
  message,
1015
1743
  sources,
@@ -1022,147 +1750,45 @@ function MessageBubble({
1022
1750
  const isUser = message.role === "user";
1023
1751
  const isCompact = viewportSize === "compact";
1024
1752
  const isMedium = viewportSize === "medium";
1025
- const [showSources, setShowSources] = import_react6.default.useState(false);
1026
- const structuredContent = import_react6.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(
1027
1756
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
1028
1757
  [isUser, message.content]
1029
- );
1030
- const hasRichUI = import_react6.default.useMemo(() => {
1031
- if (message.uiTransformation) {
1032
- const type = message.uiTransformation.type;
1033
- if (type && !["text", "table"].includes(type)) return true;
1034
- }
1035
- if (structuredContent.payload) {
1036
- return /"?(?:view|type|chartType)"?\s*:\s*"?(?:chart|carousel|pie|bar|line|product_carousel)"?/i.test(structuredContent.payload);
1037
- }
1038
- return false;
1039
- }, [message.uiTransformation, structuredContent.payload]);
1040
- const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
1041
- const productsFromSources = import_react6.default.useMemo(() => {
1042
- if (isUser || !sources) return [];
1043
- return sources.filter((s) => {
1044
- var _a;
1045
- const m = (_a = s.metadata) != null ? _a : {};
1046
- const keys = Object.keys(m).map((k) => k.toLowerCase());
1047
- const hasProductKey = keys.some(
1048
- (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
1049
- );
1050
- const hasPricePattern = /\$\s*\d+/.test(s.content);
1051
- return hasProductKey || hasPricePattern || m.type === "product";
1052
- }).map((s) => {
1053
- var _a, _b;
1054
- const m = (_a = s.metadata) != null ? _a : {};
1055
- const name = getMetadataValue(m, ["name", "product", "title", "label", "item"]);
1056
- const brand = getMetadataValue(m, ["brand", "manufacturer", "vendor", "make"]);
1057
- const price = getMetadataValue(m, ["price", "cost", "amount", "msrp", "rate"]);
1058
- return {
1059
- id: s.id,
1060
- name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1061
- brand,
1062
- price,
1063
- image: resolveImage(m),
1064
- link: getMetadataValue(m, ["link", "url", "product_url"]),
1065
- description: s.content
1066
- };
1067
- });
1068
- }, [sources, isUser]);
1069
- const { productsFromContent, cleanContent } = import_react6.default.useMemo(() => {
1070
- var _a, _b;
1071
- if (isUser) {
1072
- return { productsFromContent: [], cleanContent: message.content };
1073
- }
1074
- const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
1075
- const products = [];
1076
- let content = structuredContent.text;
1077
- const payloadCandidates = [structuredContent.payload, ...Array.from(content.matchAll(jsonRegex)).map((match) => match[1])].filter(Boolean);
1078
- for (const candidate of payloadCandidates) {
1079
- try {
1080
- const data = JSON.parse(sanitizeJson(candidate));
1081
- const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
1082
- if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
1083
- products.push(
1084
- ...itemSet.map((item) => {
1085
- var _a2;
1086
- return __spreadProps(__spreadValues({}, item), {
1087
- image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1088
- });
1089
- })
1090
- );
1091
- }
1092
- } catch (e) {
1093
- }
1758
+ );
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;
1094
1763
  }
1095
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
1096
- for (const match of Array.from(content.matchAll(jsonRegex))) {
1097
- try {
1098
- const data = JSON.parse(sanitizeJson(match[1]));
1099
- if (data.type === "products" && Array.isArray(data.items)) {
1100
- products.push(
1101
- ...data.items.map((item) => {
1102
- var _a2;
1103
- return __spreadProps(__spreadValues({}, item), {
1104
- image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1105
- });
1106
- })
1107
- );
1108
- content = content.replace(match[0], "");
1109
- }
1110
- } catch (e) {
1111
- }
1112
- }
1764
+ if (structuredContent.payload) {
1765
+ return /"?(?:view|type|chartType)"?\s*:\s*"?(?:chart|carousel|pie|bar|line|product_carousel)"?/i.test(structuredContent.payload);
1113
1766
  }
1114
- const bulletRegex = /(?:[•*-]\s*)?([^•\n\-\$*–—\(]+?)(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gi;
1115
- const matches = Array.from(content.matchAll(bulletRegex));
1116
- if (matches.length >= 2) {
1117
- const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1118
- for (const match of matches) {
1119
- let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
1120
- name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
1121
- let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
1122
- price = price.replace(/[,\s]+$/, "").trim();
1123
- if (name && price) {
1124
- const newProduct = {
1125
- id: `text-prod-${name}-${price}`,
1126
- name,
1127
- price: `$${price}`
1128
- };
1129
- const normName = normalize(name);
1130
- const sourceMatch = productsFromSources.find((s) => {
1131
- const sn = normalize(s.name);
1132
- return sn.includes(normName) || normName.includes(sn);
1133
- });
1134
- if (sourceMatch) {
1135
- newProduct.image = sourceMatch.image;
1136
- newProduct.brand = sourceMatch.brand;
1137
- newProduct.link = sourceMatch.link;
1138
- newProduct.description = sourceMatch.description;
1139
- }
1140
- products.push(newProduct);
1141
- content = content.replace(match[0], `
1142
- **${name}** \u2014 $${price}`);
1143
- }
1144
- }
1145
- content = content.replace(/\n{3,}/g, "\n\n").trim();
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);
1772
+ }, [sources, isUser]);
1773
+ const { productsFromContent, cleanContent } = import_react12.default.useMemo(() => {
1774
+ if (isUser) {
1775
+ return { productsFromContent: [], cleanContent: message.content };
1146
1776
  }
1147
- return { productsFromContent: products, cleanContent: content.trim() };
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
+ );
1148
1787
  }, [message.content, isUser, structuredContent, productsFromSources]);
1149
- const allProducts = import_react6.default.useMemo(() => {
1150
- if (productsFromContent.length > 0) return productsFromContent;
1151
- const seen = /* @__PURE__ */ new Set();
1152
- const contentLower = message.content.toLowerCase();
1153
- return productsFromSources.filter((p) => {
1154
- var _a;
1155
- const id = String((_a = p.id) != null ? _a : p.name);
1156
- if (seen.has(id)) return false;
1157
- const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
1158
- if (mentioned) {
1159
- seen.add(id);
1160
- return true;
1161
- }
1162
- return false;
1163
- });
1788
+ const allProducts = import_react12.default.useMemo(() => {
1789
+ return deduplicateProducts(productsFromSources, productsFromContent, message.content);
1164
1790
  }, [productsFromSources, productsFromContent, message.content]);
1165
- const processedMarkdown = import_react6.default.useMemo(() => {
1791
+ const processedMarkdown = import_react12.default.useMemo(() => {
1166
1792
  let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
1167
1793
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
1168
1794
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
@@ -1183,156 +1809,26 @@ ${match.trim()}
1183
1809
  if (hasStructuredUiBlock) {
1184
1810
  raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
1185
1811
  }
1186
- if (isStreaming) {
1187
- raw = raw.replace(/((?:^|\n)\|.*)+/g, (match) => {
1188
- return `
1189
-
1190
- \`\`\`table-loading
1191
- ${match.trim()}
1192
- \`\`\`
1193
-
1194
- `;
1195
- });
1196
- }
1197
1812
  return raw;
1198
1813
  }, [cleanContent, message.content, isStreaming, structuredContent]);
1199
- const markdownComponents = import_react6.default.useMemo(
1200
- () => ({
1201
- // Wrap in not-prose so Tailwind Typography resets don't clobber our styles.
1202
- // prose applies display:block and padding:0 to table/th/td — not-prose opts out.
1203
- table: (_a) => {
1204
- var props = __objRest(_a, []);
1205
- if (isStreaming) {
1206
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
1207
- }
1208
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react6.default.createElement(
1209
- "table",
1210
- __spreadValues({
1211
- className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
1212
- }, props)
1213
- )));
1214
- },
1215
- thead: (_b) => {
1216
- var props = __objRest(_b, []);
1217
- return /* @__PURE__ */ import_react6.default.createElement(
1218
- "thead",
1219
- __spreadValues({
1220
- className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
1221
- }, props)
1222
- );
1223
- },
1224
- tbody: (_c) => {
1225
- var props = __objRest(_c, []);
1226
- return /* @__PURE__ */ import_react6.default.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1227
- },
1228
- tr: (_d) => {
1229
- var props = __objRest(_d, []);
1230
- return /* @__PURE__ */ import_react6.default.createElement(
1231
- "tr",
1232
- __spreadValues({
1233
- className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
1234
- }, props)
1235
- );
1236
- },
1237
- th: (_e) => {
1238
- var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
1239
- return /* @__PURE__ */ import_react6.default.createElement(
1240
- "th",
1241
- __spreadValues({
1242
- 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"
1243
- }, props),
1244
- normaliseChild(children)
1245
- );
1246
- },
1247
- td: (_g) => {
1248
- var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
1249
- return /* @__PURE__ */ import_react6.default.createElement(
1250
- "td",
1251
- __spreadValues({
1252
- className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
1253
- }, props),
1254
- normaliseChild(children)
1255
- );
1256
- },
1257
- code(_i) {
1258
- var _j = _i, {
1259
- inline,
1260
- className,
1261
- children
1262
- } = _j, props = __objRest(_j, [
1263
- "inline",
1264
- "className",
1265
- "children"
1266
- ]);
1267
- var _a;
1268
- const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
1269
- if (!inline && (lang === "ui" || lang === "chart")) {
1270
- return /* @__PURE__ */ import_react6.default.createElement(
1271
- UIDispatcher,
1272
- {
1273
- rawContent: String(children != null ? children : "").trim(),
1274
- primaryColor,
1275
- accentColor,
1276
- isStreaming,
1277
- onAddToCart,
1278
- viewportSize
1279
- }
1280
- );
1281
- }
1282
- if (!inline && lang === "json") {
1283
- const content = String(children != null ? children : "").trim();
1284
- if (looksLikeStructuredPayload(content)) {
1285
- return /* @__PURE__ */ import_react6.default.createElement(
1286
- UIDispatcher,
1287
- {
1288
- rawContent: content,
1289
- primaryColor,
1290
- accentColor,
1291
- isStreaming,
1292
- onAddToCart,
1293
- viewportSize
1294
- }
1295
- );
1296
- }
1297
- }
1298
- if (!inline) {
1299
- const content = String(children != null ? children : "").trim();
1300
- if (looksLikeStructuredPayload(content)) {
1301
- return /* @__PURE__ */ import_react6.default.createElement(
1302
- UIDispatcher,
1303
- {
1304
- rawContent: content,
1305
- primaryColor,
1306
- accentColor,
1307
- isStreaming,
1308
- onAddToCart,
1309
- viewportSize
1310
- }
1311
- );
1312
- }
1313
- }
1314
- if (!inline && lang === "table-loading") {
1315
- return /* @__PURE__ */ import_react6.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_react6.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
1316
- }
1317
- return /* @__PURE__ */ import_react6.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
1318
- }
1319
- }),
1814
+ const markdownComponents = import_react12.default.useMemo(
1815
+ () => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
1320
1816
  [primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
1321
1817
  );
1322
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react6.default.createElement(
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(
1323
1819
  "div",
1324
1820
  {
1325
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"}`,
1326
1822
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1327
1823
  },
1328
- isUser ? /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
1329
- ), /* @__PURE__ */ import_react6.default.createElement("div", { className: `flex flex-col gap-1 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react6.default.createElement(
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(
1330
1826
  "div",
1331
1827
  {
1332
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"}`,
1333
1829
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1334
1830
  },
1335
- isStreaming && !message.content ? /* @__PURE__ */ import_react6.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react6.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_react6.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_react6.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_react6.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_react6.default.createElement(
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(
1336
1832
  UIDispatcher,
1337
1833
  {
1338
1834
  rawContent: structuredContent.payload,
@@ -1342,7 +1838,15 @@ ${match.trim()}
1342
1838
  onAddToCart,
1343
1839
  viewportSize
1344
1840
  }
1345
- ), !shouldRenderStructuredOnly && /* @__PURE__ */ import_react6.default.createElement(import_react_markdown.default, { components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
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..."))
1346
1850
  ), (() => {
1347
1851
  var _a, _b;
1348
1852
  if (isUser || structuredContent.payload || !message.uiTransformation) return null;
@@ -1350,109 +1854,50 @@ ${match.trim()}
1350
1854
  const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
1351
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());
1352
1856
  if (!shouldShow) return null;
1353
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ import_react6.default.createElement(
1857
+ return /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ import_react12.default.createElement(
1354
1858
  VisualizationRenderer,
1355
1859
  {
1356
1860
  data: ui,
1357
1861
  primaryColor,
1358
1862
  onAddToCart,
1359
- className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4"
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
1360
1865
  }
1361
1866
  ));
1362
- })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react6.default.createElement(
1867
+ })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ import_react12.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react12.default.createElement(
1363
1868
  ProductCarousel,
1364
1869
  {
1365
1870
  products: allProducts,
1366
1871
  primaryColor,
1367
1872
  onAddToCart
1368
1873
  }
1369
- )), !isUser && sources && sources.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react6.default.createElement(
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(
1370
1875
  "button",
1371
1876
  {
1372
1877
  onClick: () => setShowSources((s) => !s),
1373
- className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1 mt-1"
1878
+ className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1"
1374
1879
  },
1375
- showSources ? /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.ChevronRight, { className: "w-3 h-3" }),
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" }),
1376
1881
  sources.length,
1377
1882
  " source",
1378
1883
  sources.length !== 1 ? "s" : "",
1379
1884
  " used"
1380
- ), showSources && /* @__PURE__ */ import_react6.default.createElement("div", { className: "mt-2 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ import_react6.default.createElement(SourceCard, { key: src.id, source: src, index: i }))))));
1381
- }
1382
-
1383
- // src/components/ConfigProvider.tsx
1384
- var import_react7 = __toESM(require("react"));
1385
-
1386
- // src/utils/templateUtils.ts
1387
- function mergeDefined(base, override) {
1388
- const merged = __spreadValues({}, base || {});
1389
- if (!override) {
1390
- return merged;
1391
- }
1392
- for (const [key, value] of Object.entries(override)) {
1393
- if (value !== void 0 && value !== null && value !== "") {
1394
- merged[key] = value;
1395
- }
1396
- }
1397
- return merged;
1398
- }
1399
-
1400
- // src/config/uiConstants.ts
1401
- var DEFAULT_CONFIG = {
1402
- projectId: "default",
1403
- ui: {
1404
- title: "AI Assistant",
1405
- subtitle: "Powered by RAG",
1406
- primaryColor: "#6366f1",
1407
- accentColor: "#8b5cf6",
1408
- logoUrl: "",
1409
- placeholder: "Ask me anything\u2026",
1410
- showSources: true,
1411
- welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
1412
- showWidget: true,
1413
- poweredBy: "RAG",
1414
- visualStyle: "glass",
1415
- borderRadius: "xl",
1416
- allowUpload: true,
1417
- allowResize: true,
1418
- enableVoiceInput: true
1419
- }
1420
- };
1421
- var BORDER_RADIUS_MAP = {
1422
- none: "rounded-none",
1423
- sm: "rounded-sm",
1424
- md: "rounded-md",
1425
- lg: "rounded-lg",
1426
- xl: "rounded-xl",
1427
- full: "rounded-3xl"
1428
- };
1429
- var CHAT_SUGGESTIONS = [
1430
- "What can you help me with?",
1431
- "Summarise the key topics",
1432
- "Show me an example"
1433
- ];
1434
-
1435
- // src/components/ConfigProvider.tsx
1436
- var ConfigContext = (0, import_react7.createContext)(DEFAULT_CONFIG);
1437
- function ConfigProvider({
1438
- config,
1439
- children
1440
- }) {
1441
- const merged = {
1442
- projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
1443
- ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
1444
- };
1445
- return /* @__PURE__ */ import_react7.default.createElement(ConfigContext.Provider, { value: merged }, children);
1446
- }
1447
- function useConfig() {
1448
- return (0, import_react7.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 }))));
1449
1894
  }
1450
1895
 
1451
1896
  // src/hooks/useRagChat.ts
1452
- var import_react8 = require("react");
1897
+ var import_react13 = require("react");
1453
1898
 
1454
1899
  // src/hooks/useStoredMessages.ts
1455
- var React8 = __toESM(require("react"));
1900
+ var React13 = __toESM(require("react"));
1456
1901
  function readStoredMessages(storageKey) {
1457
1902
  if (typeof window === "undefined") {
1458
1903
  return [];
@@ -1469,8 +1914,8 @@ function readStoredMessages(storageKey) {
1469
1914
  }
1470
1915
  }
1471
1916
  function useStoredMessages(storageKey, persist) {
1472
- const [messages, setMessages] = React8.useState(() => persist ? readStoredMessages(storageKey) : []);
1473
- React8.useEffect(() => {
1917
+ const [messages, setMessages] = React13.useState(() => persist ? readStoredMessages(storageKey) : []);
1918
+ React13.useEffect(() => {
1474
1919
  if (!persist || typeof window === "undefined") {
1475
1920
  return;
1476
1921
  }
@@ -1510,14 +1955,14 @@ function useRagChat(projectId, options = {}) {
1510
1955
  } = options;
1511
1956
  const storageKey = `rag_chat_${projectId}`;
1512
1957
  const [messages, setMessages] = useStoredMessages(storageKey, persist);
1513
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
1514
- const [error, setError] = (0, import_react8.useState)(null);
1515
- const lastInputRef = (0, import_react8.useRef)("");
1516
- const messagesRef = (0, import_react8.useRef)(messages);
1517
- (0, import_react8.useEffect)(() => {
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)(() => {
1518
1963
  messagesRef.current = messages;
1519
1964
  }, [messages]);
1520
- const sendMessage = (0, import_react8.useCallback)(
1965
+ const sendMessage = (0, import_react13.useCallback)(
1521
1966
  async (text, opts) => {
1522
1967
  var _a, _b;
1523
1968
  const trimmed = text.trim();
@@ -1557,6 +2002,7 @@ function useRagChat(projectId, options = {}) {
1557
2002
  let assistantContent = "";
1558
2003
  let sources = [];
1559
2004
  let uiTransformation = null;
2005
+ let trace;
1560
2006
  let buffer = "";
1561
2007
  const assistantMessageId = `assistant_${Date.now()}`;
1562
2008
  setMessages((prev) => [
@@ -1583,6 +2029,8 @@ function useRagChat(projectId, options = {}) {
1583
2029
  sources = (_a = frame.sources) != null ? _a : [];
1584
2030
  } else if (frame.type === "ui_transformation") {
1585
2031
  uiTransformation = frame.data;
2032
+ } else if (frame.type === "observability") {
2033
+ trace = frame.data;
1586
2034
  } else if (frame.type === "error") {
1587
2035
  setError(frame.error || "Stream error");
1588
2036
  }
@@ -1601,17 +2049,22 @@ function useRagChat(projectId, options = {}) {
1601
2049
  for (const frame of parseSseChunk(buffer)) {
1602
2050
  if (frame.type === "text" && frame.text) assistantContent += frame.text;
1603
2051
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
2052
+ else if (frame.type === "observability") trace = frame.data;
1604
2053
  }
1605
2054
  }
1606
- const finalAssistantMessage = {
2055
+ const finalMsg = {
1607
2056
  id: assistantMessageId,
1608
2057
  role: "assistant",
1609
2058
  content: assistantContent,
1610
2059
  sources: sources.length > 0 ? sources : void 0,
1611
- uiTransformation: uiTransformation || void 0,
2060
+ uiTransformation: uiTransformation != null ? uiTransformation : void 0,
2061
+ trace,
1612
2062
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1613
2063
  };
1614
- onReply == null ? void 0 : onReply(finalAssistantMessage);
2064
+ setMessages(
2065
+ (prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMsg : msg)
2066
+ );
2067
+ onReply == null ? void 0 : onReply(finalMsg);
1615
2068
  } catch (err) {
1616
2069
  const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
1617
2070
  setError(msg);
@@ -1622,12 +2075,12 @@ function useRagChat(projectId, options = {}) {
1622
2075
  },
1623
2076
  [apiUrl, isLoading, namespace, onError, onReply, projectId, setMessages]
1624
2077
  );
1625
- const clear = (0, import_react8.useCallback)(() => {
2078
+ const clear = (0, import_react13.useCallback)(() => {
1626
2079
  setMessages([]);
1627
2080
  setError(null);
1628
2081
  if (persist) localStorage.removeItem(storageKey);
1629
2082
  }, [persist, setMessages, storageKey]);
1630
- const retry = (0, import_react8.useCallback)(async () => {
2083
+ const retry = (0, import_react13.useCallback)(async () => {
1631
2084
  if (lastInputRef.current) {
1632
2085
  const latestMessage = messagesRef.current[messagesRef.current.length - 1];
1633
2086
  await sendMessage(lastInputRef.current, {
@@ -1661,20 +2114,21 @@ function ChatWindow({
1661
2114
  }) {
1662
2115
  var _a;
1663
2116
  const { ui, projectId } = useConfig();
1664
- const [input, setInput] = (0, import_react9.useState)("");
1665
- const [mounted, setMounted] = (0, import_react9.useState)(false);
1666
- const [viewportSize, setViewportSize] = (0, import_react9.useState)("large");
1667
- const windowRef = (0, import_react9.useRef)(null);
1668
- const bottomRef = (0, import_react9.useRef)(null);
1669
- const inputRef = (0, import_react9.useRef)(null);
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);
1670
2123
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
1671
2124
  namespace: projectId
1672
2125
  });
1673
- const [suggestions, setSuggestions] = (0, import_react9.useState)([]);
1674
- const [isSuggesting, setIsSuggesting] = (0, import_react9.useState)(false);
1675
- const [isListening, setIsListening] = (0, import_react9.useState)(false);
1676
- const recognitionRef = (0, import_react9.useRef)(null);
1677
- (0, import_react9.useEffect)(() => {
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)(() => {
1678
2132
  if (typeof window !== "undefined") {
1679
2133
  const win = window;
1680
2134
  const SpeechRecognition = win.SpeechRecognition || win.webkitSpeechRecognition;
@@ -1720,10 +2174,10 @@ function ChatWindow({
1720
2174
  }
1721
2175
  }
1722
2176
  };
1723
- (0, import_react9.useEffect)(() => {
2177
+ (0, import_react14.useEffect)(() => {
1724
2178
  setMounted(true);
1725
2179
  }, []);
1726
- (0, import_react9.useEffect)(() => {
2180
+ (0, import_react14.useEffect)(() => {
1727
2181
  const element = windowRef.current;
1728
2182
  if (!element || typeof ResizeObserver === "undefined") return;
1729
2183
  const resolveViewportSize = (width) => {
@@ -1739,13 +2193,13 @@ function ChatWindow({
1739
2193
  observer.observe(element);
1740
2194
  return () => observer.disconnect();
1741
2195
  }, []);
1742
- (0, import_react9.useEffect)(() => {
2196
+ (0, import_react14.useEffect)(() => {
1743
2197
  var _a2;
1744
2198
  if (messages.length > 0 || isLoading) {
1745
2199
  (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
1746
2200
  }
1747
2201
  }, [messages, isLoading]);
1748
- const sendMessage = (0, import_react9.useCallback)(async () => {
2202
+ const sendMessage = (0, import_react14.useCallback)(async () => {
1749
2203
  var _a2;
1750
2204
  const text = input.trim();
1751
2205
  if (!text || isLoading) return;
@@ -1775,7 +2229,7 @@ function ChatWindow({
1775
2229
  const isEmpty = messages.length === 0;
1776
2230
  const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || "xl"];
1777
2231
  const isGlass = ui.visualStyle !== "solid";
1778
- (0, import_react9.useEffect)(() => {
2232
+ (0, import_react14.useEffect)(() => {
1779
2233
  if (input.trim().length < 3) {
1780
2234
  return;
1781
2235
  }
@@ -1799,84 +2253,84 @@ function ChatWindow({
1799
2253
  }, 800);
1800
2254
  return () => clearTimeout(timer);
1801
2255
  }, [input, projectId]);
1802
- return /* @__PURE__ */ import_react9.default.createElement(
2256
+ return /* @__PURE__ */ import_react14.default.createElement(
1803
2257
  "div",
1804
2258
  {
1805
2259
  ref: windowRef,
1806
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}`,
1807
2261
  style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
1808
2262
  },
1809
- onResizeStart && /* @__PURE__ */ import_react9.default.createElement(
2263
+ onResizeStart && /* @__PURE__ */ import_react14.default.createElement(
1810
2264
  "div",
1811
2265
  {
1812
2266
  onMouseDown: onResizeStart,
1813
2267
  className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
1814
2268
  title: "Drag to resize"
1815
2269
  },
1816
- /* @__PURE__ */ import_react9.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]" })
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]" })
1817
2271
  ),
1818
- /* @__PURE__ */ import_react9.default.createElement(
2272
+ /* @__PURE__ */ import_react14.default.createElement(
1819
2273
  "div",
1820
2274
  {
1821
2275
  className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
1822
2276
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
1823
2277
  },
1824
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
2278
+ /* @__PURE__ */ import_react14.default.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
1825
2279
  // eslint-disable-next-line @next/next/no-img-element
1826
- /* @__PURE__ */ import_react9.default.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
1827
- ) : /* @__PURE__ */ import_react9.default.createElement(
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(
1828
2282
  "div",
1829
2283
  {
1830
2284
  className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
1831
2285
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1832
2286
  },
1833
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Bot, { className: "w-5 h-5 text-white" })
1834
- ), /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight" }, ui.title), ui.poweredBy && /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight" }, `Powered by ${ui.poweredBy}`))),
1835
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }), "Online"), /* @__PURE__ */ import_react9.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_react9.default.createElement(
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(
1836
2290
  "button",
1837
2291
  {
1838
2292
  onClick: clearHistory,
1839
2293
  title: "Clear conversation",
1840
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"
1841
2295
  },
1842
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Trash2, { className: "w-3.5 h-3.5" })
1843
- ), isResized && onResetResize && /* @__PURE__ */ import_react9.default.createElement(
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(
1844
2298
  "button",
1845
2299
  {
1846
2300
  onClick: onResetResize,
1847
2301
  title: "Reset to default size",
1848
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"
1849
2303
  },
1850
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.RotateCcw, { className: "w-3.5 h-3.5" })
1851
- ), onMaximize && /* @__PURE__ */ import_react9.default.createElement(
2304
+ /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.RotateCcw, { className: "w-3.5 h-3.5" })
2305
+ ), onMaximize && /* @__PURE__ */ import_react14.default.createElement(
1852
2306
  "button",
1853
2307
  {
1854
2308
  onClick: onMaximize,
1855
2309
  title: isMaximized ? "Minimize" : "Maximize",
1856
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"
1857
2311
  },
1858
- isMaximized ? /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Maximize2, { className: "w-3.5 h-3.5" })
1859
- ), showClose && onClose && /* @__PURE__ */ import_react9.default.createElement(
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(
1860
2314
  "button",
1861
2315
  {
1862
2316
  onClick: onClose,
1863
2317
  title: "Close chat",
1864
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"
1865
2319
  },
1866
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.X, { className: "w-4 h-4" })
2320
+ /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.X, { className: "w-4 h-4" })
1867
2321
  )))
1868
2322
  ),
1869
- /* @__PURE__ */ import_react9.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 ? (
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 ? (
1870
2324
  /* Welcome state */
1871
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12" }, /* @__PURE__ */ import_react9.default.createElement(
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(
1872
2326
  "div",
1873
2327
  {
1874
2328
  className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
1875
2329
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1876
2330
  },
1877
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-8 h-8 text-white" })
1878
- ), /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed" }, ui.welcomeMessage), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2" }, "Ask a question to get started")), /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex flex-wrap gap-2 justify-center mt-2" }, CHAT_SUGGESTIONS.map(
1879
- (suggestion) => /* @__PURE__ */ import_react9.default.createElement(
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(
1880
2334
  "button",
1881
2335
  {
1882
2336
  key: suggestion,
@@ -1890,7 +2344,7 @@ function ChatWindow({
1890
2344
  suggestion
1891
2345
  )
1892
2346
  )))
1893
- ) : messages.map((msg, index) => /* @__PURE__ */ import_react9.default.createElement(
2347
+ ) : messages.map((msg, index) => /* @__PURE__ */ import_react14.default.createElement(
1894
2348
  MessageBubble,
1895
2349
  {
1896
2350
  key: msg.id,
@@ -1902,7 +2356,7 @@ function ChatWindow({
1902
2356
  onAddToCart,
1903
2357
  viewportSize
1904
2358
  }
1905
- )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react9.default.createElement(
2359
+ )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react14.default.createElement(
1906
2360
  MessageBubble,
1907
2361
  {
1908
2362
  message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
@@ -1912,8 +2366,22 @@ function ChatWindow({
1912
2366
  onAddToCart,
1913
2367
  viewportSize
1914
2368
  }
1915
- ), error && /* @__PURE__ */ import_react9.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_react9.default.createElement(import_lucide_react5.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ import_react9.default.createElement("span", null, error)), /* @__PURE__ */ import_react9.default.createElement("div", { ref: bottomRef })),
1916
- /* @__PURE__ */ import_react9.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_react9.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_react9.default.createElement(
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(
1917
2385
  "button",
1918
2386
  {
1919
2387
  key: suggestion,
@@ -1925,9 +2393,9 @@ function ChatWindow({
1925
2393
  },
1926
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"
1927
2395
  },
1928
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3 text-amber-400" }),
2396
+ /* @__PURE__ */ import_react14.default.createElement(import_lucide_react7.Sparkles, { className: "w-3 h-3 text-amber-400" }),
1929
2397
  suggestion
1930
- )), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ import_react9.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_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ import_react9.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_react9.default.createElement(
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(
1931
2399
  "textarea",
1932
2400
  {
1933
2401
  ref: inputRef,
@@ -1946,7 +2414,7 @@ function ChatWindow({
1946
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",
1947
2415
  style: { scrollbarWidth: "none" }
1948
2416
  }
1949
- ), ui.enableVoiceInput !== false && /* @__PURE__ */ import_react9.default.createElement(
2417
+ ), ui.enableVoiceInput !== false && /* @__PURE__ */ import_react14.default.createElement(
1950
2418
  "button",
1951
2419
  {
1952
2420
  type: "button",
@@ -1955,8 +2423,17 @@ function ChatWindow({
1955
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"}`,
1956
2424
  title: isListening ? "Stop listening" : "Start voice input"
1957
2425
  },
1958
- isListening ? /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Mic, { className: "w-4 h-4" })
1959
- ), /* @__PURE__ */ import_react9.default.createElement(
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(
1960
2437
  "button",
1961
2438
  {
1962
2439
  onClick: sendMessage,
@@ -1967,8 +2444,8 @@ function ChatWindow({
1967
2444
  // slate-400/20 for light mode disabled
1968
2445
  }
1969
2446
  },
1970
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.ArrowUp, { className: "w-4 h-4 text-white" })
1971
- )), /* @__PURE__ */ import_react9.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"))
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"))
1972
2449
  );
1973
2450
  }
1974
2451
 
@@ -1976,12 +2453,12 @@ function ChatWindow({
1976
2453
  var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
1977
2454
  function ChatWidget({ position = "bottom-right", onAddToCart }) {
1978
2455
  const { ui } = useConfig();
1979
- const [isOpen, setIsOpen] = (0, import_react10.useState)(false);
1980
- const [hasUnread, setHasUnread] = (0, import_react10.useState)(false);
1981
- const [dimensions, setDimensions] = (0, import_react10.useState)(DEFAULT_DIMENSIONS);
1982
- const [isResizing, setIsResizing] = (0, import_react10.useState)(false);
1983
- const [isMaximized, setIsMaximized] = (0, import_react10.useState)(false);
1984
- const [prevDimensions, setPrevDimensions] = (0, import_react10.useState)(DEFAULT_DIMENSIONS);
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);
1985
2462
  if (ui.showWidget === false) return null;
1986
2463
  const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
1987
2464
  const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
@@ -2031,7 +2508,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2031
2508
  }
2032
2509
  };
2033
2510
  const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
2034
- return /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(
2511
+ return /* @__PURE__ */ import_react15.default.createElement(import_react15.default.Fragment, null, /* @__PURE__ */ import_react15.default.createElement(
2035
2512
  "div",
2036
2513
  {
2037
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"}`,
@@ -2041,7 +2518,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2041
2518
  maxHeight: "calc(100vh - 6rem)"
2042
2519
  }
2043
2520
  },
2044
- /* @__PURE__ */ import_react10.default.createElement(
2521
+ /* @__PURE__ */ import_react15.default.createElement(
2045
2522
  ChatWindow,
2046
2523
  {
2047
2524
  className: "h-full relative z-10",
@@ -2055,13 +2532,13 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2055
2532
  onAddToCart
2056
2533
  }
2057
2534
  ),
2058
- /* @__PURE__ */ import_react10.default.createElement(
2535
+ /* @__PURE__ */ import_react15.default.createElement(
2059
2536
  "div",
2060
2537
  {
2061
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"}`
2062
2539
  }
2063
2540
  )
2064
- ), /* @__PURE__ */ import_react10.default.createElement(
2541
+ ), /* @__PURE__ */ import_react15.default.createElement(
2065
2542
  "button",
2066
2543
  {
2067
2544
  onClick: isOpen ? () => setIsOpen(false) : handleOpen,
@@ -2069,149 +2546,23 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2069
2546
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2070
2547
  "aria-label": "Open chat"
2071
2548
  },
2072
- /* @__PURE__ */ import_react10.default.createElement(
2549
+ /* @__PURE__ */ import_react15.default.createElement(
2073
2550
  "span",
2074
2551
  {
2075
2552
  className: "absolute inset-0 rounded-full animate-ping opacity-20",
2076
2553
  style: { background: ui.primaryColor }
2077
2554
  }
2078
2555
  ),
2079
- /* @__PURE__ */ import_react10.default.createElement(
2556
+ /* @__PURE__ */ import_react15.default.createElement(
2080
2557
  "span",
2081
2558
  {
2082
2559
  className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
2083
2560
  },
2084
- isOpen ? /* @__PURE__ */ import_react10.default.createElement(import_lucide_react6.X, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ import_react10.default.createElement(import_lucide_react6.MessageSquare, { className: "w-6 h-6 text-white" })
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" })
2085
2562
  ),
2086
- hasUnread && !isOpen && /* @__PURE__ */ import_react10.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")
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")
2087
2564
  ));
2088
2565
  }
2089
-
2090
- // src/components/DocumentUpload.tsx
2091
- var import_react11 = __toESM(require("react"));
2092
- var import_lucide_react7 = require("lucide-react");
2093
- function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
2094
- const { ui } = useConfig();
2095
- const [fileStates, setFileStates] = (0, import_react11.useState)([]);
2096
- const [isDragging, setIsDragging] = (0, import_react11.useState)(false);
2097
- const fileInputRef = (0, import_react11.useRef)(null);
2098
- const addFiles = (files) => {
2099
- const newStates = files.map((file) => ({ file, status: "idle" }));
2100
- setFileStates((prev) => [...prev, ...newStates]);
2101
- };
2102
- const removeFile = (index) => {
2103
- setFileStates((prev) => prev.filter((_, i) => i !== index));
2104
- };
2105
- const onDragOver = (e) => {
2106
- e.preventDefault();
2107
- setIsDragging(true);
2108
- };
2109
- const onDragLeave = () => {
2110
- setIsDragging(false);
2111
- };
2112
- const onDrop = (e) => {
2113
- e.preventDefault();
2114
- setIsDragging(false);
2115
- if (e.dataTransfer.files) {
2116
- addFiles(Array.from(e.dataTransfer.files));
2117
- }
2118
- };
2119
- const onFileSelect = (e) => {
2120
- if (e.target.files) {
2121
- addFiles(Array.from(e.target.files));
2122
- }
2123
- };
2124
- const uploadFiles = async () => {
2125
- const idleFiles = fileStates.filter((s) => s.status === "idle");
2126
- if (idleFiles.length === 0) return;
2127
- setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
2128
- const formData = new FormData();
2129
- idleFiles.forEach((s) => formData.append("files", s.file));
2130
- if (namespace) formData.append("namespace", namespace);
2131
- try {
2132
- const response = await fetch("/api/upload", {
2133
- method: "POST",
2134
- body: formData
2135
- });
2136
- const result = await response.json();
2137
- if (!response.ok) throw new Error(result.error || "Upload failed");
2138
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
2139
- if (onUploadComplete) onUploadComplete(result);
2140
- } catch (err) {
2141
- const message = err instanceof Error ? err.message : "Upload failed";
2142
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
2143
- }
2144
- };
2145
- const isUploading = fileStates.some((s) => s.status === "uploading");
2146
- const hasIdle = fileStates.some((s) => s.status === "idle");
2147
- return /* @__PURE__ */ import_react11.default.createElement(
2148
- "div",
2149
- {
2150
- 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}`,
2151
- onDragOver,
2152
- onDragLeave,
2153
- onDrop
2154
- },
2155
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ import_react11.default.createElement(
2156
- "div",
2157
- {
2158
- className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
2159
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
2160
- },
2161
- /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
2162
- ), /* @__PURE__ */ import_react11.default.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ import_react11.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_react11.default.createElement(
2163
- "button",
2164
- {
2165
- onClick: () => {
2166
- var _a;
2167
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
2168
- },
2169
- disabled: isUploading,
2170
- className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
2171
- style: {
2172
- backgroundColor: `${ui.primaryColor}15`,
2173
- color: ui.primaryColor,
2174
- border: `1px solid ${ui.primaryColor}30`
2175
- }
2176
- },
2177
- "Select Files"
2178
- ), /* @__PURE__ */ import_react11.default.createElement(
2179
- "input",
2180
- {
2181
- ref: fileInputRef,
2182
- type: "file",
2183
- multiple: true,
2184
- onChange: onFileSelect,
2185
- className: "hidden",
2186
- accept: ".pdf,.docx,.txt,.md,.json,.csv"
2187
- }
2188
- )),
2189
- fileStates.length > 0 && /* @__PURE__ */ import_react11.default.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ import_react11.default.createElement(
2190
- "div",
2191
- {
2192
- key: i,
2193
- 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"
2194
- },
2195
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ import_react11.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_react11.default.createElement(import_lucide_react7.File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ import_react11.default.createElement("div", { className: "truncate" }, /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
2196
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ import_react11.default.createElement("div", { title: state.error }, /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ import_react11.default.createElement(
2197
- "button",
2198
- {
2199
- onClick: () => removeFile(i),
2200
- className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
2201
- },
2202
- /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.X, { className: "w-3.5 h-3.5 text-slate-400" })
2203
- ))
2204
- )), hasIdle && !isUploading && /* @__PURE__ */ import_react11.default.createElement(
2205
- "button",
2206
- {
2207
- onClick: uploadFiles,
2208
- 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]",
2209
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
2210
- },
2211
- "Start Upload"
2212
- ))
2213
- );
2214
- }
2215
2566
  // Annotate the CommonJS export names for ESM import in node:
2216
2567
  0 && (module.exports = {
2217
2568
  ChatWidget,
@@ -2219,7 +2570,9 @@ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
2219
2570
  ConfigProvider,
2220
2571
  DocumentUpload,
2221
2572
  MessageBubble,
2573
+ ObservabilityPanel,
2222
2574
  SourceCard,
2575
+ addSynonyms,
2223
2576
  useConfig,
2224
2577
  useRagChat
2225
2578
  });