@retrivora-ai/rag-engine 1.8.0 → 1.8.2

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