@retrivora-ai/rag-engine 1.8.1 → 1.8.3

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 (80) hide show
  1. package/dist/{ILLMProvider-BfRgI1Xh.d.mts → ILLMProvider-BOJFz3Na.d.mts} +47 -1
  2. package/dist/{ILLMProvider-BfRgI1Xh.d.ts → ILLMProvider-BOJFz3Na.d.ts} +47 -1
  3. package/dist/handlers/index.d.mts +2 -2
  4. package/dist/handlers/index.d.ts +2 -2
  5. package/dist/handlers/index.js +509 -162
  6. package/dist/handlers/index.mjs +5849 -15
  7. package/dist/{index-1Z4GuYBi.d.ts → index-BwpcaziY.d.ts} +4 -2
  8. package/dist/{index-BV0z5mb6.d.mts → index-D3V9Et2M.d.mts} +4 -2
  9. package/dist/index.d.mts +30 -16
  10. package/dist/index.d.ts +30 -16
  11. package/dist/index.js +1999 -1210
  12. package/dist/index.mjs +2041 -1205
  13. package/dist/server.d.mts +21 -25
  14. package/dist/server.d.ts +21 -25
  15. package/dist/server.js +536 -163
  16. package/dist/server.mjs +5923 -67
  17. package/package.json +9 -7
  18. package/src/app/api/upload/route.ts +4 -0
  19. package/src/app/constants.tsx +2 -2
  20. package/src/app/globals.css +35 -11
  21. package/src/app/page.tsx +12 -321
  22. package/src/components/AmbientBackground.tsx +29 -0
  23. package/src/components/ArchitectureCard.tsx +17 -0
  24. package/src/components/ArchitectureCardsSection.tsx +15 -0
  25. package/src/components/ChatWidget.tsx +0 -1
  26. package/src/components/ChatWindow.tsx +32 -0
  27. package/src/components/CodeViewer.tsx +51 -0
  28. package/src/components/ConfigProvider.tsx +1 -0
  29. package/src/components/DocViewer.tsx +37 -0
  30. package/src/components/DocumentUpload.tsx +44 -1
  31. package/src/components/Documentation.tsx +58 -0
  32. package/src/components/DynamicChart.tsx +27 -2
  33. package/src/components/Hero.tsx +59 -0
  34. package/src/components/HourglassLoader.tsx +87 -0
  35. package/src/components/Lifecycle.tsx +37 -0
  36. package/src/components/MarkdownComponents.tsx +143 -0
  37. package/src/components/MessageBubble.tsx +91 -1012
  38. package/src/components/Navbar.tsx +55 -0
  39. package/src/components/ObservabilityPanel.tsx +374 -0
  40. package/src/components/ProductCard.tsx +3 -1
  41. package/src/components/UIDispatcher.tsx +341 -0
  42. package/src/components/VisualizationRenderer.tsx +48 -26
  43. package/src/config/ConfigBuilder.ts +38 -1
  44. package/src/core/LangChainAgent.ts +1 -4
  45. package/src/core/Pipeline.ts +209 -86
  46. package/src/core/QueryProcessor.ts +65 -0
  47. package/src/handlers/index.ts +72 -12
  48. package/src/hooks/useRagChat.ts +19 -9
  49. package/src/index.ts +9 -1
  50. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  51. package/src/rag/Reranker.ts +99 -6
  52. package/src/types/chat.ts +2 -0
  53. package/src/types/index.ts +52 -0
  54. package/src/types/props.ts +9 -1
  55. package/src/utils/ProductExtractor.ts +347 -0
  56. package/src/utils/UITransformer.ts +4 -53
  57. package/src/utils/synonyms.ts +78 -0
  58. package/dist/ChromaDBProvider-MIDOR4FW.mjs +0 -8
  59. package/dist/MilvusProvider-U7SKC27V.mjs +0 -8
  60. package/dist/MongoDBProvider-YNKC7EJ6.mjs +0 -8
  61. package/dist/MultiTablePostgresProvider-YY7LPNJK.mjs +0 -8
  62. package/dist/PineconeProvider-QZNRKTN2.mjs +0 -8
  63. package/dist/QdrantProvider-RLJTNGPY.mjs +0 -8
  64. package/dist/RedisProvider-SR65SCKV.mjs +0 -8
  65. package/dist/SimpleGraphProvider-SLOXO4M7.mjs +0 -62
  66. package/dist/UniversalVectorProvider-IN67OS56.mjs +0 -9
  67. package/dist/WeaviateProvider-5FWDFITI.mjs +0 -8
  68. package/dist/chunk-5AJ4XHLW.mjs +0 -201
  69. package/dist/chunk-5YGUXK7Z.mjs +0 -80
  70. package/dist/chunk-BFYLQYQU.mjs +0 -3985
  71. package/dist/chunk-CFVEZTBJ.mjs +0 -102
  72. package/dist/chunk-IMP6FUCY.mjs +0 -30
  73. package/dist/chunk-LR3VMDVK.mjs +0 -157
  74. package/dist/chunk-M6JSPGAR.mjs +0 -117
  75. package/dist/chunk-PSFPZXHX.mjs +0 -245
  76. package/dist/chunk-R3RGUMHE.mjs +0 -218
  77. package/dist/chunk-U55XRW3U.mjs +0 -96
  78. package/dist/chunk-VUQJVIJT.mjs +0 -148
  79. package/dist/chunk-X4TOT24V.mjs +0 -89
  80. package/dist/chunk-YLTMFW4M.mjs +0 -49
package/dist/index.js CHANGED
@@ -64,92 +64,391 @@ __export(index_exports, {
64
64
  ConfigProvider: () => ConfigProvider,
65
65
  DocumentUpload: () => DocumentUpload,
66
66
  MessageBubble: () => MessageBubble,
67
+ ObservabilityPanel: () => ObservabilityPanel,
67
68
  SourceCard: () => SourceCard,
69
+ addSynonyms: () => addSynonyms,
68
70
  useConfig: () => useConfig,
69
71
  useRagChat: () => useRagChat
70
72
  });
71
73
  module.exports = __toCommonJS(index_exports);
72
74
 
73
75
  // src/components/ChatWidget.tsx
74
- var import_react10 = __toESM(require("react"));
75
- var import_lucide_react6 = require("lucide-react");
76
+ var import_react12 = require("react");
77
+ var import_lucide_react8 = require("lucide-react");
76
78
 
77
79
  // src/components/ChatWindow.tsx
78
- var import_react9 = __toESM(require("react"));
79
- var import_lucide_react5 = require("lucide-react");
80
+ var import_react11 = require("react");
81
+ var import_lucide_react7 = require("lucide-react");
82
+
83
+ // src/components/DocumentUpload.tsx
84
+ var import_react2 = require("react");
85
+ var import_lucide_react = require("lucide-react");
86
+
87
+ // src/components/ConfigProvider.tsx
88
+ var import_react = 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 import_jsx_runtime = require("react/jsx-runtime");
141
+ var ConfigContext = (0, import_react.createContext)(DEFAULT_CONFIG);
142
+ function ConfigProvider({
143
+ config,
144
+ children
145
+ }) {
146
+ const merged = {
147
+ projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
148
+ ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui),
149
+ embedding: config == null ? void 0 : config.embedding
150
+ };
151
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConfigContext.Provider, { value: merged, children });
152
+ }
153
+ function useConfig() {
154
+ return (0, import_react.useContext)(ConfigContext);
155
+ }
156
+
157
+ // src/components/DocumentUpload.tsx
158
+ var import_jsx_runtime2 = require("react/jsx-runtime");
159
+ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
160
+ var _a;
161
+ const { ui, embedding } = useConfig();
162
+ const [fileStates, setFileStates] = (0, import_react2.useState)([]);
163
+ const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
164
+ const fileInputRef = (0, import_react2.useRef)(null);
165
+ const MULTI_DIMENSION_MODELS = {
166
+ "text-embedding-3-small": [512, 1536],
167
+ "text-embedding-3-large": [256, 1024, 3072],
168
+ "nomic-embed-text": [64, 128, 256, 512, 768]
169
+ };
170
+ const COMMON_DIMENSIONS = [64, 128, 256, 384, 512, 768, 1024, 1536, 3072];
171
+ const currentModel = (embedding == null ? void 0 : embedding.model) || "unknown";
172
+ const defaultDimension = (embedding == null ? void 0 : embedding.dimensions) || 1536;
173
+ let availableDimensions = (_a = Object.entries(MULTI_DIMENSION_MODELS).find(([k]) => currentModel.includes(k))) == null ? void 0 : _a[1];
174
+ if (!availableDimensions) {
175
+ availableDimensions = COMMON_DIMENSIONS.includes(defaultDimension) ? COMMON_DIMENSIONS : [...COMMON_DIMENSIONS, defaultDimension].sort((a, b) => a - b);
176
+ }
177
+ const isChangeable = true;
178
+ const [dimension, setDimension] = (0, import_react2.useState)(defaultDimension);
179
+ const addFiles = (files) => {
180
+ const newStates = files.map((file) => ({ file, status: "idle" }));
181
+ setFileStates((prev) => [...prev, ...newStates]);
182
+ };
183
+ const removeFile = (index) => {
184
+ setFileStates((prev) => prev.filter((_, i) => i !== index));
185
+ };
186
+ const onDragOver = (e) => {
187
+ e.preventDefault();
188
+ setIsDragging(true);
189
+ };
190
+ const onDragLeave = () => {
191
+ setIsDragging(false);
192
+ };
193
+ const onDrop = (e) => {
194
+ e.preventDefault();
195
+ setIsDragging(false);
196
+ if (e.dataTransfer.files) {
197
+ addFiles(Array.from(e.dataTransfer.files));
198
+ }
199
+ };
200
+ const onFileSelect = (e) => {
201
+ if (e.target.files) {
202
+ addFiles(Array.from(e.target.files));
203
+ }
204
+ };
205
+ const uploadFiles = async () => {
206
+ const idleFiles = fileStates.filter((s) => s.status === "idle");
207
+ if (idleFiles.length === 0) return;
208
+ setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
209
+ const formData = new FormData();
210
+ idleFiles.forEach((s) => formData.append("files", s.file));
211
+ if (namespace) formData.append("namespace", namespace);
212
+ formData.append("dimension", dimension.toString());
213
+ try {
214
+ const response = await fetch("/api/upload", {
215
+ method: "POST",
216
+ body: formData
217
+ });
218
+ const result = await response.json();
219
+ if (!response.ok) throw new Error(result.error || "Upload failed");
220
+ setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
221
+ if (onUploadComplete) onUploadComplete(result);
222
+ } catch (err) {
223
+ const message = err instanceof Error ? err.message : "Upload failed";
224
+ setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
225
+ }
226
+ };
227
+ const isUploading = fileStates.some((s) => s.status === "uploading");
228
+ const hasIdle = fileStates.some((s) => s.status === "idle");
229
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
230
+ "div",
231
+ {
232
+ 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}`,
233
+ onDragOver,
234
+ onDragLeave,
235
+ onDrop,
236
+ children: [
237
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center justify-center text-center", children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
239
+ "div",
240
+ {
241
+ className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
242
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` },
243
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
244
+ }
245
+ ),
246
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1", children: "Upload Documents" }),
247
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-slate-500 dark:text-white/40 text-sm mb-6 max-w-xs", children: "Drag and drop PDF, DOCX, TXT, or JSON files to train your AI on your own data." }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
249
+ "button",
250
+ {
251
+ onClick: () => {
252
+ var _a2;
253
+ return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
254
+ },
255
+ disabled: isUploading,
256
+ className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
257
+ style: {
258
+ backgroundColor: `${ui.primaryColor}15`,
259
+ color: ui.primaryColor,
260
+ border: `1px solid ${ui.primaryColor}30`
261
+ },
262
+ children: "Select Files"
263
+ }
264
+ ),
265
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
266
+ "input",
267
+ {
268
+ ref: fileInputRef,
269
+ type: "file",
270
+ multiple: true,
271
+ onChange: onFileSelect,
272
+ className: "hidden",
273
+ accept: ".pdf,.docx,.txt,.md,.json,.csv"
274
+ }
275
+ ),
276
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-6 flex flex-col items-center gap-2 text-sm", children: [
277
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "text-slate-600 dark:text-white/60 font-medium", children: "Embedding Dimension" }),
278
+ isChangeable ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
279
+ "select",
280
+ {
281
+ value: dimension,
282
+ onChange: (e) => setDimension(Number(e.target.value)),
283
+ disabled: isUploading,
284
+ 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",
285
+ children: availableDimensions.map((dim) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: dim, className: "bg-white dark:bg-slate-900", children: dim }, dim))
286
+ }
287
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("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", children: [
288
+ dimension,
289
+ " (Fixed for ",
290
+ currentModel,
291
+ ")"
292
+ ] })
293
+ ] })
294
+ ] }),
295
+ fileStates.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-8 space-y-3", children: [
296
+ fileStates.map((state, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
297
+ "div",
298
+ {
299
+ 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",
300
+ children: [
301
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-3 overflow-hidden", children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.File, { className: "w-4 h-4 text-slate-400" }) }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "truncate", children: [
304
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate", children: state.file.name }),
305
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-[10px] text-slate-400", children: [
306
+ (state.file.size / 1024).toFixed(1),
307
+ " KB"
308
+ ] })
309
+ ] })
310
+ ] }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
312
+ state.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }),
313
+ state.status === "success" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.CheckCircle, { className: "w-4 h-4 text-emerald-500" }),
314
+ state.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { title: state.error, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.AlertCircle, { className: "w-4 h-4 text-rose-500" }) }),
315
+ state.status !== "uploading" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
316
+ "button",
317
+ {
318
+ onClick: () => removeFile(i),
319
+ className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors",
320
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.X, { className: "w-3.5 h-3.5 text-slate-400" })
321
+ }
322
+ )
323
+ ] })
324
+ ]
325
+ },
326
+ i
327
+ )),
328
+ hasIdle && !isUploading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
329
+ "button",
330
+ {
331
+ onClick: uploadFiles,
332
+ 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]",
333
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
334
+ children: "Start Upload"
335
+ }
336
+ )
337
+ ] })
338
+ ]
339
+ }
340
+ );
341
+ }
80
342
 
81
343
  // src/components/MessageBubble.tsx
82
- var import_react6 = __toESM(require("react"));
83
- var import_lucide_react4 = require("lucide-react");
344
+ var import_react9 = __toESM(require("react"));
345
+ var import_lucide_react6 = require("lucide-react");
84
346
  var import_react_markdown = __toESM(require("react-markdown"));
85
347
 
86
348
  // src/components/SourceCard.tsx
87
- var import_react = __toESM(require("react"));
88
- var import_lucide_react = require("lucide-react");
349
+ var import_react3 = __toESM(require("react"));
350
+ var import_lucide_react2 = require("lucide-react");
351
+ var import_jsx_runtime3 = require("react/jsx-runtime");
89
352
  function SourceCard({ source, index }) {
90
353
  var _a, _b;
91
- const [expanded, setExpanded] = import_react.default.useState(false);
354
+ const [expanded, setExpanded] = import_react3.default.useState(false);
92
355
  const scorePercent = Math.round(source.score * 100);
93
356
  const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
94
357
  const scoreBg = source.score >= 0.8 ? "bg-emerald-50 border-emerald-100 dark:bg-emerald-400/10 dark:border-emerald-400/20" : source.score >= 0.6 ? "bg-amber-50 border-amber-100 dark:bg-amber-400/10 dark:border-amber-400/20" : "bg-rose-50 border-rose-100 dark:bg-rose-400/10 dark:border-rose-400/20";
95
358
  const preview = source.content.slice(0, 120);
96
359
  const hasMore = source.content.length > 120;
97
- return /* @__PURE__ */ import_react.default.createElement(
360
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
98
361
  "div",
99
362
  {
100
363
  className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
101
- onClick: () => setExpanded((e) => !e)
102
- },
103
- /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-shrink-0 w-5 h-5 rounded-full bg-slate-200 dark:bg-white/10 flex items-center justify-center text-[10px] font-bold text-slate-500 dark:text-white/60" }, index + 1), /* @__PURE__ */ import_react.default.createElement("span", { className: "text-xs text-slate-500 dark:text-white/50 truncate font-mono" }, (_b = (_a = source.metadata) == null ? void 0 : _a.docId) != null ? _b : source.id)), /* @__PURE__ */ import_react.default.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
104
- /* @__PURE__ */ import_react.default.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
105
- hasMore && /* @__PURE__ */ import_react.default.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(import_lucide_react.ChevronDown, { className: "w-3 h-3" }), " Show more"))
364
+ onClick: () => setExpanded((e) => !e),
365
+ children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
367
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
368
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("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", children: index + 1 }),
369
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-xs text-slate-500 dark:text-white/50 truncate font-mono", children: (_b = (_a = source.metadata) == null ? void 0 : _a.docId) != null ? _b : source.id })
370
+ ] }),
371
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums`, children: [
372
+ scorePercent,
373
+ "%"
374
+ ] })
375
+ ] }),
376
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed", children: expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}` }),
377
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5", children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.ChevronUp, { className: "w-3 h-3" }),
379
+ " Show less"
380
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.ChevronDown, { className: "w-3 h-3" }),
382
+ " Show more"
383
+ ] }) })
384
+ ]
385
+ }
106
386
  );
107
387
  }
108
388
 
109
389
  // src/components/ProductCarousel.tsx
110
- var import_react3 = __toESM(require("react"));
111
- var import_lucide_react3 = require("lucide-react");
390
+ var import_react4 = require("react");
391
+ var import_lucide_react4 = require("lucide-react");
112
392
 
113
393
  // src/components/ProductCard.tsx
114
- var import_react2 = __toESM(require("react"));
115
394
  var import_image = __toESM(require("next/image"));
116
- var import_lucide_react2 = require("lucide-react");
395
+ var import_lucide_react3 = require("lucide-react");
396
+ var import_jsx_runtime4 = require("react/jsx-runtime");
117
397
  function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
118
- return /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex-shrink-0 w-full bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-xl overflow-hidden shadow-sm hover:shadow-md transition-all duration-300 group" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden" }, product.image ? /* @__PURE__ */ import_react2.default.createElement(
119
- import_image.default,
120
- {
121
- src: product.image,
122
- alt: product.name,
123
- fill: true,
124
- unoptimized: true,
125
- className: "object-cover group-hover:scale-105 transition-transform duration-500"
126
- }
127
- ) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute top-2 left-2 px-2 py-1 bg-white/90 dark:bg-black/60 backdrop-blur-md rounded-md text-[10px] font-bold uppercase tracking-wider text-slate-700 dark:text-white/90 shadow-sm" }, product.brand)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ import_react2.default.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ import_react2.default.createElement(
128
- "button",
129
- {
130
- onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
131
- className: "flex-1 py-2 px-3 rounded-lg text-[11px] font-medium text-white shadow-sm hover:opacity-90 transition-opacity flex items-center justify-center gap-1.5",
132
- style: { background: primaryColor }
133
- },
134
- /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ShoppingCart, { className: "w-3 h-3" }),
135
- "Add to Cart"
136
- ), product.link && /* @__PURE__ */ import_react2.default.createElement(
137
- "a",
138
- {
139
- href: product.link,
140
- target: "_blank",
141
- rel: "noopener noreferrer",
142
- className: "p-2 rounded-lg bg-slate-100 dark:bg-white/10 text-slate-600 dark:text-white/70 hover:bg-slate-200 dark:hover:bg-white/20 transition-colors"
143
- },
144
- /* @__PURE__ */ import_react2.default.createElement(import_lucide_react2.ExternalLink, { className: "w-3.5 h-3.5" })
145
- ))));
398
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("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", children: [
399
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative h-40 w-full bg-slate-100 dark:bg-white/5 flex items-center justify-center overflow-hidden", children: [
400
+ product.image ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
401
+ import_image.default,
402
+ {
403
+ src: product.image,
404
+ alt: product.name,
405
+ fill: true,
406
+ unoptimized: true,
407
+ className: "object-cover group-hover:scale-105 transition-transform duration-500"
408
+ }
409
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-slate-400 dark:text-white/20", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ShoppingCart, { className: "w-12 h-12" }) }),
410
+ product.brand && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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", children: product.brand })
411
+ ] }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "p-4 flex flex-col gap-2", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-between items-start gap-2", children: [
414
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1", children: product.name }),
415
+ product.price && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sm font-bold", style: { color: primaryColor }, children: typeof product.price === "number" ? `$${product.price}` : String(product.price).match(/^[\d.]/) ? `$${product.price}` : product.price })
416
+ ] }),
417
+ product.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed", children: product.description }),
418
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "mt-2 flex gap-2", children: [
419
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
420
+ "button",
421
+ {
422
+ onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
423
+ 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",
424
+ style: { background: primaryColor },
425
+ children: [
426
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ShoppingCart, { className: "w-3 h-3" }),
427
+ "Add to Cart"
428
+ ]
429
+ }
430
+ ),
431
+ product.link && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
432
+ "a",
433
+ {
434
+ href: product.link,
435
+ target: "_blank",
436
+ rel: "noopener noreferrer",
437
+ 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",
438
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ExternalLink, { className: "w-3.5 h-3.5" })
439
+ }
440
+ )
441
+ ] })
442
+ ] })
443
+ ] });
146
444
  }
147
445
 
148
446
  // src/components/ProductCarousel.tsx
447
+ var import_jsx_runtime5 = require("react/jsx-runtime");
149
448
  function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
150
- const scrollRef = (0, import_react3.useRef)(null);
151
- const [canScroll, setCanScroll] = (0, import_react3.useState)(false);
152
- (0, import_react3.useEffect)(() => {
449
+ const scrollRef = (0, import_react4.useRef)(null);
450
+ const [canScroll, setCanScroll] = (0, import_react4.useState)(false);
451
+ (0, import_react4.useEffect)(() => {
153
452
  const checkScroll = () => {
154
453
  if (scrollRef.current) {
155
454
  const hasOverflow = scrollRef.current.scrollWidth > scrollRef.current.clientWidth;
@@ -173,63 +472,74 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
173
472
  };
174
473
  if (!products || products.length === 0) return null;
175
474
  if (products.length === 1) {
176
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
475
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProductCard, { product: products[0], primaryColor, onAddToCart }) });
177
476
  }
178
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -left-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
179
- "button",
180
- {
181
- onClick: (e) => {
182
- e.stopPropagation();
183
- scroll("left");
184
- },
185
- className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
186
- "aria-label": "Scroll left"
187
- },
188
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronLeft, { className: "w-5 h-5" })
189
- )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute -right-4 top-1/2 -translate-y-1/2 z-20 opacity-100 md:opacity-0 md:group-hover/carousel:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react3.default.createElement(
190
- "button",
191
- {
192
- onClick: (e) => {
193
- e.stopPropagation();
194
- scroll("right");
195
- },
196
- className: "p-2 rounded-full bg-white dark:bg-slate-800 shadow-xl border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/80 hover:text-indigo-500 dark:hover:text-indigo-400 transition-all hover:scale-110 active:scale-95",
197
- "aria-label": "Scroll right"
198
- },
199
- /* @__PURE__ */ import_react3.default.createElement(import_lucide_react3.ChevronRight, { className: "w-5 h-5" })
200
- ))), /* @__PURE__ */ import_react3.default.createElement(
201
- "div",
202
- {
203
- ref: scrollRef,
204
- className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
205
- style: { scrollbarWidth: "none", msOverflowStyle: "none" }
206
- },
207
- products.map((product) => /* @__PURE__ */ import_react3.default.createElement(
477
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up", children: [
478
+ canScroll && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
479
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
480
+ "button",
481
+ {
482
+ onClick: (e) => {
483
+ e.stopPropagation();
484
+ scroll("left");
485
+ },
486
+ 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",
487
+ "aria-label": "Scroll left",
488
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ChevronLeft, { className: "w-5 h-5" })
489
+ }
490
+ ) }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
492
+ "button",
493
+ {
494
+ onClick: (e) => {
495
+ e.stopPropagation();
496
+ scroll("right");
497
+ },
498
+ 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",
499
+ "aria-label": "Scroll right",
500
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ChevronRight, { className: "w-5 h-5" })
501
+ }
502
+ ) })
503
+ ] }),
504
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
208
505
  "div",
209
506
  {
210
- key: product.id,
211
- className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
212
- },
213
- /* @__PURE__ */ import_react3.default.createElement(ProductCard, { product, primaryColor, onAddToCart })
214
- ))
215
- ));
507
+ ref: scrollRef,
508
+ className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
509
+ style: { scrollbarWidth: "none", msOverflowStyle: "none" },
510
+ children: products.map((product) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
511
+ "div",
512
+ {
513
+ className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full",
514
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ProductCard, { product, primaryColor, onAddToCart })
515
+ },
516
+ product.id
517
+ ))
518
+ }
519
+ )
520
+ ] });
216
521
  }
217
522
 
523
+ // src/components/VisualizationRenderer.tsx
524
+ var import_recharts2 = require("recharts");
525
+
218
526
  // src/components/DynamicChart.tsx
219
- var import_react4 = __toESM(require("react"));
527
+ var import_react5 = __toESM(require("react"));
220
528
  var import_recharts = require("recharts");
221
- var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
529
+ var import_jsx_runtime6 = require("react/jsx-runtime");
530
+ var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
222
531
  function DynamicChart({
223
532
  config,
224
533
  primaryColor = "#6366f1",
225
534
  accentColor = "#8b5cf6",
226
- viewportSize = "large"
535
+ viewportSize = "large",
536
+ isStreaming = false
227
537
  }) {
228
538
  const { type, data } = config;
229
- const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
230
- const containerRef = import_react4.default.useRef(null);
231
- const [containerWidth, setContainerWidth] = import_react4.default.useState(0);
232
- import_react4.default.useEffect(() => {
539
+ const colors = config.colors || [primaryColor, accentColor, ...CHART_COLORS];
540
+ const containerRef = import_react5.default.useRef(null);
541
+ const [containerWidth, setContainerWidth] = import_react5.default.useState(0);
542
+ import_react5.default.useEffect(() => {
233
543
  const element = containerRef.current;
234
544
  if (!element || typeof ResizeObserver === "undefined") return;
235
545
  const updateWidth = (width) => {
@@ -255,7 +565,7 @@ function DynamicChart({
255
565
  return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
256
566
  };
257
567
  const chartMargin = isTiny ? { top: 8, right: 4, left: -28, bottom: 0 } : isCompact ? { top: 10, right: 6, left: -24, bottom: 0 } : { top: 10, right: 10, left: -20, bottom: 0 };
258
- const sanitizedData = import_react4.default.useMemo(() => {
568
+ const sanitizedData = import_react5.default.useMemo(() => {
259
569
  if (!data) return [];
260
570
  return data.map((item) => {
261
571
  const newItem = {};
@@ -287,8 +597,19 @@ function DynamicChart({
287
597
  return newItem;
288
598
  });
289
599
  }, [data]);
600
+ if (isStreaming) {
601
+ const bars = type === "pie" ? 6 : 5;
602
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-full mt-4 mb-2 animate-pulse", children: type === "pie" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-end gap-2 h-32 px-2", children: Array.from({ length: bars }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
603
+ "div",
604
+ {
605
+ className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10",
606
+ style: { height: `${40 + i % 3 * 25}%` }
607
+ },
608
+ i
609
+ )) }) });
610
+ }
290
611
  if (!data || data.length === 0) {
291
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
612
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-4 text-sm text-slate-500", children: "No data available for chart." });
292
613
  }
293
614
  const firstItem = sanitizedData[0];
294
615
  const allKeys = Object.keys(firstItem);
@@ -313,14 +634,20 @@ function DynamicChart({
313
634
  const renderTooltip = (row, label) => {
314
635
  var _a, _b;
315
636
  if (!row) return null;
316
- return /* @__PURE__ */ import_react4.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, /* @__PURE__ */ import_react4.default.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, String((_b = (_a = row[finalXKey]) != null ? _a : label) != null ? _b : "Details")), Object.entries(row).map(([key, value]) => {
317
- var _a2;
318
- if (value === null || value === void 0 || key === finalXKey) return null;
319
- if (typeof value === "object") return null;
320
- const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
321
- if (forbiddenKeys.includes(key)) return null;
322
- return /* @__PURE__ */ import_react4.default.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react4.default.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ import_react4.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
323
- }));
637
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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", children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90", children: String((_b = (_a = row[finalXKey]) != null ? _a : label) != null ? _b : "Details") }),
639
+ Object.entries(row).map(([key, value]) => {
640
+ var _a2;
641
+ if (value === null || value === void 0 || key === finalXKey) return null;
642
+ if (typeof value === "object") return null;
643
+ const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
644
+ if (forbiddenKeys.includes(key)) return null;
645
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70", children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: (_a2 = tooltipLabelMap[key]) != null ? _a2 : key }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "font-medium text-slate-900 dark:text-white/90", children: String(value) })
648
+ ] }, key);
649
+ })
650
+ ] });
324
651
  };
325
652
  const getTooltipRow = (payload) => {
326
653
  if (!Array.isArray(payload) || payload.length === 0) return null;
@@ -331,65 +658,64 @@ function DynamicChart({
331
658
  return row;
332
659
  };
333
660
  if (type === "pie") {
334
- return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react4.default.createElement(
335
- import_recharts.Pie,
661
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-auto w-full max-w-full aspect-square", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_recharts.PieChart, { children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
663
+ import_recharts.Pie,
664
+ {
665
+ data: sanitizedData,
666
+ dataKey: pieDataKey,
667
+ nameKey: finalXKey,
668
+ cx: "50%",
669
+ cy: "50%",
670
+ outerRadius: pieOuterRadius,
671
+ label: false,
672
+ labelLine: false,
673
+ children: sanitizedData.map((_entry, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Cell, { fill: colors[index % colors.length] }, `cell-${index}`))
674
+ }
675
+ ),
676
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }),
677
+ showLegend && !isCompact && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })
678
+ ] }) }) }) });
679
+ }
680
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "w-full aspect-[4/3]", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.ResponsiveContainer, { width: "100%", height: "100%", children: type === "bar" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_recharts.BarChart, { data: sanitizedData, margin: chartMargin, children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }),
683
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }),
684
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }),
685
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }),
686
+ finalDataKeys.map((key, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
687
+ import_recharts.Bar,
336
688
  {
337
- data: sanitizedData,
338
- dataKey: pieDataKey,
339
- nameKey: finalXKey,
340
- cx: "50%",
341
- cy: "50%",
342
- outerRadius: pieOuterRadius,
343
- label: false,
344
- labelLine: false
689
+ dataKey: key,
690
+ fill: colors[index % colors.length],
691
+ radius: [4, 4, 0, 0],
692
+ barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
345
693
  },
346
- sanitizedData.map((_entry, index) => /* @__PURE__ */ import_react4.default.createElement(import_recharts.Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
347
- ), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
348
- }
349
- return /* @__PURE__ */ import_react4.default.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ import_react4.default.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ import_react4.default.createElement(import_recharts.BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
350
- import_recharts.Bar,
351
- {
352
- key,
353
- dataKey: key,
354
- fill: colors[index % colors.length],
355
- radius: [4, 4, 0, 0],
356
- barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
357
- }
358
- ))) : /* @__PURE__ */ import_react4.default.createElement(import_recharts.LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ import_react4.default.createElement(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react4.default.createElement(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ import_react4.default.createElement(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ import_react4.default.createElement(
359
- import_recharts.Line,
360
- {
361
- key,
362
- type: "monotone",
363
- dataKey: key,
364
- stroke: colors[index % colors.length],
365
- strokeWidth: isTiny ? 1.75 : isCompact ? 2 : 3,
366
- dot: { r: isTiny ? 2.5 : isCompact ? 3 : 4, strokeWidth: 2 },
367
- activeDot: { r: isTiny ? 4 : isCompact ? 5 : 6 }
368
- }
369
- ))))));
694
+ key
695
+ ))
696
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_recharts.LineChart, { data: sanitizedData, margin: chartMargin, children: [
697
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }),
699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.YAxis, { tick: axisTick, tickLine: false, axisLine: false }),
700
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }),
701
+ showLegend && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_recharts.Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }),
702
+ finalDataKeys.map((key, index) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
703
+ import_recharts.Line,
704
+ {
705
+ type: "monotone",
706
+ dataKey: key,
707
+ stroke: colors[index % colors.length],
708
+ strokeWidth: isTiny ? 1.75 : isCompact ? 2 : 3,
709
+ dot: { r: isTiny ? 2.5 : isCompact ? 3 : 4, strokeWidth: 2 },
710
+ activeDot: { r: isTiny ? 4 : isCompact ? 5 : 6 }
711
+ },
712
+ key
713
+ ))
714
+ ] }) }) }) });
370
715
  }
371
716
 
372
717
  // src/components/VisualizationRenderer.tsx
373
- var import_react5 = __toESM(require("react"));
374
- var import_recharts2 = require("recharts");
375
- var CHART_COLORS = [
376
- "#6366f1",
377
- // indigo
378
- "#10b981",
379
- // emerald
380
- "#f59e0b",
381
- // amber
382
- "#ef4444",
383
- // red
384
- "#8b5cf6",
385
- // purple
386
- "#ec4899",
387
- // pink
388
- "#06b6d4",
389
- // cyan
390
- "#f97316"
391
- // orange
392
- ];
718
+ var import_jsx_runtime7 = require("react/jsx-runtime");
393
719
  function getColor(index) {
394
720
  return CHART_COLORS[index % CHART_COLORS.length];
395
721
  }
@@ -399,77 +725,112 @@ function CustomTooltip({
399
725
  label
400
726
  }) {
401
727
  if (!active || !payload || payload.length === 0) return null;
402
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white px-3 py-2 text-xs shadow-xl dark:border-white/10 dark:bg-slate-950" }, label && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ import_react5.default.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ import_react5.default.createElement(
403
- "span",
404
- {
405
- className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
406
- style: { background: entry.color }
407
- }
408
- ), entry.name), /* @__PURE__ */ import_react5.default.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
728
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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", children: [
729
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90", children: label }),
730
+ payload.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70", children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "flex items-center gap-1.5", children: [
732
+ entry.color && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
733
+ "span",
734
+ {
735
+ className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
736
+ style: { background: entry.color }
737
+ }
738
+ ),
739
+ entry.name
740
+ ] }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-medium text-slate-900 dark:text-white/90", children: entry.value })
742
+ ] }, i))
743
+ ] });
409
744
  }
410
745
  function VisualizationRenderer({
411
746
  data,
412
747
  className = "",
413
748
  primaryColor,
414
- onAddToCart
749
+ onAddToCart,
750
+ isStreaming
415
751
  }) {
416
752
  var _a;
417
753
  if (!data) {
418
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
754
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `p-4 text-gray-500 ${className}`, children: "No data available for visualization" });
419
755
  }
420
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react5.default.createElement("h3", { className: "text-sm font-semibold text-slate-900 dark:text-white" }, data.title), data.description && data.description !== ((_a = data.data) == null ? void 0 : _a.content) && /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor));
756
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: `space-y-4 ${className}`, children: [
757
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "space-y-1", children: [
758
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-sm font-semibold text-slate-900 dark:text-white", children: data.title }),
759
+ data.description && data.description !== ((_a = data.data) == null ? void 0 : _a.content) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-xs text-slate-500 dark:text-slate-400", children: data.description })
760
+ ] }),
761
+ renderVisualization(data.type, data.data, onAddToCart, primaryColor, isStreaming)
762
+ ] });
421
763
  }
422
- function renderVisualization(type, data, onAddToCart, primaryColor) {
764
+ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
423
765
  try {
424
766
  switch (type) {
425
767
  case "pie_chart":
426
- return /* @__PURE__ */ import_react5.default.createElement(PieChartView, { data });
768
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PieChartView, { data, isStreaming });
427
769
  case "bar_chart":
428
- return /* @__PURE__ */ import_react5.default.createElement(BarChartView, { data, primaryColor });
770
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BarChartView, { data, primaryColor, isStreaming });
429
771
  case "line_chart":
430
- return /* @__PURE__ */ import_react5.default.createElement(LineChartView, { data, primaryColor });
772
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LineChartView, { data, primaryColor, isStreaming });
431
773
  case "radar_chart":
432
- return /* @__PURE__ */ import_react5.default.createElement(RadarChartView, { data });
774
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(RadarChartView, { data, isStreaming });
433
775
  case "table":
434
- return /* @__PURE__ */ import_react5.default.createElement(TableView, { data });
776
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TableView, { data, isStreaming });
435
777
  case "product_carousel":
436
778
  case "carousel":
437
- return /* @__PURE__ */ import_react5.default.createElement(CarouselView, { data, onAddToCart, primaryColor });
779
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CarouselView, { data, onAddToCart, primaryColor });
438
780
  case "text":
439
781
  default:
440
- return /* @__PURE__ */ import_react5.default.createElement(TextView, { data });
782
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextView, { data });
441
783
  }
442
784
  } catch (error) {
443
785
  console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
444
- return /* @__PURE__ */ import_react5.default.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
786
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TextView, { data: { content: "Unable to render the requested visualization." } });
787
+ }
788
+ }
789
+ function ChartSkeleton({ type }) {
790
+ if (type === "pie") {
791
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-full mt-4 mb-2 animate-pulse", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }) });
445
792
  }
793
+ if (type === "table") {
794
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "w-full mt-4 mb-2 animate-pulse space-y-2", children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" })
798
+ ] });
799
+ }
800
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-full mt-4 mb-2 animate-pulse flex items-end gap-2 h-32 px-2", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10", style: { height: `${40 + i % 3 * 25}%` } }, i)) });
446
801
  }
447
- function PieChartView({ data }) {
802
+ function PieChartView({ data, isStreaming }) {
803
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "pie" });
448
804
  if (!Array.isArray(data) || data.length === 0) {
449
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
805
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid pie chart data" });
450
806
  }
451
807
  const chartData = data.map((item) => ({
452
808
  name: item.label,
453
809
  value: item.value
454
810
  }));
455
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 280 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PieChart, null, /* @__PURE__ */ import_react5.default.createElement(
456
- import_recharts2.Pie,
457
- {
458
- data: chartData,
459
- dataKey: "value",
460
- nameKey: "name",
461
- cx: "50%",
462
- cy: "50%",
463
- outerRadius: "68%",
464
- label: false,
465
- labelLine: false
466
- },
467
- chartData.map((_entry, index) => /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Cell, { key: `cell-${index}`, fill: getColor(index) }))
468
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
811
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.PieChart, { children: [
812
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
813
+ import_recharts2.Pie,
814
+ {
815
+ data: chartData,
816
+ dataKey: "value",
817
+ nameKey: "name",
818
+ cx: "50%",
819
+ cy: "50%",
820
+ outerRadius: "68%",
821
+ label: false,
822
+ labelLine: false,
823
+ children: chartData.map((_entry, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Cell, { fill: getColor(index) }, `cell-${index}`))
824
+ }
825
+ ),
826
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}) }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })
828
+ ] }) }) });
469
829
  }
470
- function BarChartView({ data, primaryColor }) {
830
+ function BarChartView({ data, primaryColor, isStreaming }) {
831
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
471
832
  if (!Array.isArray(data) || data.length === 0) {
472
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
833
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid bar chart data" });
473
834
  }
474
835
  const chartData = data.map((item) => __spreadValues(__spreadValues({
475
836
  category: item.category,
@@ -477,19 +838,30 @@ function BarChartView({ data, primaryColor }) {
477
838
  }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
478
839
  const barColor = primaryColor != null ? primaryColor : getColor(0);
479
840
  const hasStock = chartData.some((d) => "inStockCount" in d);
480
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.BarChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(
481
- import_recharts2.XAxis,
482
- {
483
- dataKey: "category",
484
- tick: { fontSize: 11, fill: "#64748b" },
485
- tickLine: false,
486
- axisLine: { stroke: "#cbd5e1" }
487
- }
488
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
841
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 260, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.BarChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 }, children: [
842
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
843
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
844
+ import_recharts2.XAxis,
845
+ {
846
+ dataKey: "category",
847
+ tick: { fontSize: 11, fill: "#64748b" },
848
+ tickLine: false,
849
+ axisLine: { stroke: "#cbd5e1" }
850
+ }
851
+ ),
852
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }),
853
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}), cursor: { fill: "#f1f5f9" } }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }),
855
+ hasStock ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
856
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }),
857
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })
858
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Bar, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] })
859
+ ] }) }) });
489
860
  }
490
- function LineChartView({ data, primaryColor }) {
861
+ function LineChartView({ data, primaryColor, isStreaming }) {
862
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "bar" });
491
863
  if (!Array.isArray(data) || data.length === 0) {
492
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
864
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid line chart data" });
493
865
  }
494
866
  const chartData = data.map((point) => {
495
867
  var _a;
@@ -499,72 +871,97 @@ function LineChartView({ data, primaryColor }) {
499
871
  };
500
872
  });
501
873
  const lineColor = primaryColor != null ? primaryColor : getColor(0);
502
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 260 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.LineChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(
503
- import_recharts2.XAxis,
504
- {
505
- dataKey: "label",
506
- tick: { fontSize: 11, fill: "#64748b" },
507
- tickLine: false,
508
- axisLine: { stroke: "#cbd5e1" }
509
- }
510
- ), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(
511
- import_recharts2.Line,
512
- {
513
- type: "monotone",
514
- dataKey: "value",
515
- name: "Value",
516
- stroke: lineColor,
517
- strokeWidth: 2.5,
518
- dot: { r: 3.5, strokeWidth: 2, fill: lineColor },
519
- activeDot: { r: 5 }
520
- }
521
- ))));
522
- }
523
- function RadarChartView({ data }) {
524
- if (!Array.isArray(data) || data.length === 0) {
525
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
526
- }
527
- const productNames = Array.from(
874
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 260, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.LineChart, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 }, children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
877
+ import_recharts2.XAxis,
878
+ {
879
+ dataKey: "label",
880
+ tick: { fontSize: 11, fill: "#64748b" },
881
+ tickLine: false,
882
+ axisLine: { stroke: "#cbd5e1" }
883
+ }
884
+ ),
885
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.YAxis, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}) }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
888
+ import_recharts2.Line,
889
+ {
890
+ type: "monotone",
891
+ dataKey: "value",
892
+ name: "Value",
893
+ stroke: lineColor,
894
+ strokeWidth: 2.5,
895
+ dot: { r: 3.5, strokeWidth: 2, fill: lineColor },
896
+ activeDot: { r: 5 }
897
+ }
898
+ )
899
+ ] }) }) });
900
+ }
901
+ function RadarChartView({ data, isStreaming }) {
902
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "pie" });
903
+ if (!Array.isArray(data) || data.length === 0) {
904
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid radar chart data" });
905
+ }
906
+ const productNames = Array.from(
528
907
  new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
529
908
  );
530
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.ResponsiveContainer, { width: "100%", height: 300 }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ import_react5.default.createElement(
531
- import_recharts2.Radar,
532
- {
533
- key: productName,
534
- name: productName,
535
- dataKey: productName,
536
- stroke: getColor(index),
537
- fill: getColor(index),
538
- fillOpacity: 0.4
539
- }
540
- )), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Tooltip, { content: /* @__PURE__ */ import_react5.default.createElement(CustomTooltip, null) }), /* @__PURE__ */ import_react5.default.createElement(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } }))));
909
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.ResponsiveContainer, { width: "100%", height: 300, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_recharts2.RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data, children: [
910
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.PolarGrid, { stroke: "#e2e8f0" }),
911
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }),
912
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }),
913
+ productNames.map((productName, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
914
+ import_recharts2.Radar,
915
+ {
916
+ name: productName,
917
+ dataKey: productName,
918
+ stroke: getColor(index),
919
+ fill: getColor(index),
920
+ fillOpacity: 0.4
921
+ },
922
+ productName
923
+ )),
924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Tooltip, { content: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(CustomTooltip, {}) }),
925
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_recharts2.Legend, { wrapperStyle: { fontSize: 12 } })
926
+ ] }) }) });
541
927
  }
542
- function TableView({ data }) {
928
+ function TableView({ data, isStreaming }) {
929
+ if (isStreaming) return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChartSkeleton, { type: "table" });
543
930
  if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
544
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
931
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid table data" });
545
932
  }
546
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react5.default.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ import_react5.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react5.default.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ import_react5.default.createElement(
547
- "th",
548
- {
549
- key: idx,
550
- className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
551
- },
552
- col
553
- )))), /* @__PURE__ */ import_react5.default.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ import_react5.default.createElement(
554
- "tr",
555
- {
556
- key: rowIdx,
557
- className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
558
- },
559
- row.map((cell, cellIdx) => /* @__PURE__ */ import_react5.default.createElement(
560
- "td",
561
- {
562
- key: cellIdx,
563
- className: "px-4 py-3 text-slate-600 dark:text-white/70 text-sm"
564
- },
565
- String(cell)
566
- ))
567
- ))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
933
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden", children: [
934
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { className: "w-full text-sm border-collapse", children: [
935
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: data.columns.map((col, idx) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
936
+ "th",
937
+ {
938
+ className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider",
939
+ children: col
940
+ },
941
+ idx
942
+ )) }) }),
943
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5", children: data.rows.map((row, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
944
+ "tr",
945
+ {
946
+ className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors",
947
+ children: row.map((cell, cellIdx) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
948
+ "td",
949
+ {
950
+ className: "px-4 py-3 text-slate-600 dark:text-white/70 text-sm",
951
+ children: String(cell)
952
+ },
953
+ cellIdx
954
+ ))
955
+ },
956
+ rowIdx
957
+ )) })
958
+ ] }) }),
959
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-[11px] text-slate-400 dark:text-white/30", children: [
960
+ data.rows.length,
961
+ " row",
962
+ data.rows.length !== 1 ? "s" : ""
963
+ ] }) })
964
+ ] });
568
965
  }
569
966
  function CarouselView({
570
967
  data,
@@ -572,7 +969,7 @@ function CarouselView({
572
969
  primaryColor
573
970
  }) {
574
971
  if (!Array.isArray(data)) {
575
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
972
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-xs text-red-500", children: "Invalid carousel data" });
576
973
  }
577
974
  const products = data.map((product) => ({
578
975
  id: product.id,
@@ -584,21 +981,498 @@ function CarouselView({
584
981
  description: product.description,
585
982
  inStock: product.inStock
586
983
  }));
587
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react5.default.createElement(
984
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
588
985
  ProductCarousel,
589
986
  {
590
987
  products,
591
988
  primaryColor: primaryColor != null ? primaryColor : "#6366f1",
592
989
  onAddToCart: (p) => onAddToCart == null ? void 0 : onAddToCart(p)
593
990
  }
594
- ));
991
+ ) });
595
992
  }
596
993
  function TextView({ data }) {
597
994
  const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
598
- return /* @__PURE__ */ import_react5.default.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
995
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed", children: content }) });
599
996
  }
600
997
 
601
- // src/components/MessageBubble.tsx
998
+ // src/components/ObservabilityPanel.tsx
999
+ var import_react6 = __toESM(require("react"));
1000
+ var import_lucide_react5 = require("lucide-react");
1001
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1002
+ function fmtMs(ms) {
1003
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
1004
+ }
1005
+ function fmtTokens(n) {
1006
+ return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
1007
+ }
1008
+ function ScoreBadge({ score }) {
1009
+ const pct = Math.round(score * 100);
1010
+ 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";
1011
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: `inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold border ${color}`, children: [
1012
+ pct <= 20 ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.CheckCircle, { className: "w-3 h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.AlertCircle, { className: "w-3 h-3" }),
1013
+ pct,
1014
+ "% hallucination risk"
1015
+ ] });
1016
+ }
1017
+ function RetrievalTab({ trace }) {
1018
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-2", children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { className: "text-[11px] text-slate-500 dark:text-white/40", children: [
1020
+ trace.chunks.length,
1021
+ " chunk",
1022
+ trace.chunks.length !== 1 ? "s" : "",
1023
+ " retrieved",
1024
+ trace.rewrittenQuery && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "ml-2 italic", children: [
1025
+ "(query rewritten: ",
1026
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("em", { className: "text-indigo-500", children: trace.rewrittenQuery }),
1027
+ ")"
1028
+ ] })
1029
+ ] }),
1030
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-1.5 max-h-56 overflow-y-auto pr-1 custom-scrollbar", children: trace.chunks.map((chunk, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1031
+ "div",
1032
+ {
1033
+ className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5",
1034
+ children: [
1035
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
1036
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30", children: [
1037
+ "[",
1038
+ i + 1,
1039
+ "] ",
1040
+ String(chunk.id).slice(0, 20)
1041
+ ] }),
1042
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400", children: [
1043
+ "score: ",
1044
+ chunk.score.toFixed(3)
1045
+ ] })
1046
+ ] }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-1 rounded-full bg-slate-200 dark:bg-white/10 mb-2 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1048
+ "div",
1049
+ {
1050
+ className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
1051
+ style: { width: `${Math.min(100, chunk.score * 100)}%` }
1052
+ }
1053
+ ) }),
1054
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-600 dark:text-white/60 line-clamp-2 leading-relaxed", children: chunk.content })
1055
+ ]
1056
+ },
1057
+ String(chunk.id)
1058
+ )) })
1059
+ ] });
1060
+ }
1061
+ function LatencyTab({ trace }) {
1062
+ const { latency } = trace;
1063
+ const bars = [
1064
+ { label: "Embed", ms: latency.embedMs, color: "#6366f1" },
1065
+ { label: "Retrieve", ms: latency.retrieveMs, color: "#10b981" },
1066
+ ...latency.rerankMs != null ? [{ label: "Rerank", ms: latency.rerankMs, color: "#f59e0b" }] : [],
1067
+ { label: "Generate", ms: latency.generateMs, color: "#8b5cf6" }
1068
+ ];
1069
+ const maxMs = Math.max(...bars.map((b) => b.ms), 1);
1070
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
1071
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
1072
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-slate-500 dark:text-white/40", children: "Pipeline stages" }),
1073
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[11px] font-semibold text-slate-700 dark:text-white/70", children: [
1074
+ "Total: ",
1075
+ fmtMs(latency.totalMs)
1076
+ ] })
1077
+ ] }),
1078
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-2", children: bars.map((bar) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-0.5", children: [
1079
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
1080
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-slate-600 dark:text-white/60", children: bar.label }),
1081
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40", children: fmtMs(bar.ms) })
1082
+ ] }),
1083
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-2 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1084
+ "div",
1085
+ {
1086
+ className: "h-full rounded-full transition-all duration-700",
1087
+ style: {
1088
+ width: `${bar.ms / maxMs * 100}%`,
1089
+ background: bar.color,
1090
+ opacity: 0.85
1091
+ }
1092
+ }
1093
+ ) })
1094
+ ] }, bar.label)) })
1095
+ ] });
1096
+ }
1097
+ function TokensTab({ trace }) {
1098
+ const { tokens } = trace;
1099
+ if (!tokens) {
1100
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic", children: "Token data not available." });
1101
+ }
1102
+ const stats = [
1103
+ { label: "Prompt Tokens", value: fmtTokens(tokens.promptTokens), color: "text-indigo-600 dark:text-indigo-400" },
1104
+ { label: "Completion Tokens", value: fmtTokens(tokens.completionTokens), color: "text-emerald-600 dark:text-emerald-400" },
1105
+ { label: "Total Tokens", value: fmtTokens(tokens.totalTokens), color: "text-slate-700 dark:text-white/80" }
1106
+ ];
1107
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-3 gap-2", children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5 text-center", children: [
1109
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: `text-lg font-bold ${s.color}`, children: s.value }),
1110
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] text-slate-400 dark:text-white/30 mt-0.5", children: s.label })
1111
+ ] }, s.label)) }),
1112
+ tokens.estimatedCostUsd != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("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", children: [
1113
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[11px] text-emerald-700 dark:text-emerald-400 font-medium", children: "Estimated Cost" }),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[11px] font-bold text-emerald-700 dark:text-emerald-400", children: [
1115
+ "$",
1116
+ tokens.estimatedCostUsd < 1e-4 ? tokens.estimatedCostUsd.toExponential(2) : tokens.estimatedCostUsd.toFixed(5),
1117
+ " USD"
1118
+ ] })
1119
+ ] }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "h-2 rounded-full overflow-hidden flex", children: [
1121
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1122
+ "div",
1123
+ {
1124
+ className: "h-full bg-indigo-500 dark:bg-indigo-400",
1125
+ style: { width: `${tokens.promptTokens / tokens.totalTokens * 100}%` },
1126
+ title: "Prompt tokens"
1127
+ }
1128
+ ),
1129
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1130
+ "div",
1131
+ {
1132
+ className: "h-full bg-emerald-500 dark:bg-emerald-400",
1133
+ style: { width: `${tokens.completionTokens / tokens.totalTokens * 100}%` },
1134
+ title: "Completion tokens"
1135
+ }
1136
+ )
1137
+ ] }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-3 text-[10px] text-slate-400 dark:text-white/30", children: [
1139
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-1", children: [
1140
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block w-2 h-2 rounded-full bg-indigo-500" }),
1141
+ " Prompt"
1142
+ ] }),
1143
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-1", children: [
1144
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }),
1145
+ " Completion"
1146
+ ] })
1147
+ ] })
1148
+ ] });
1149
+ }
1150
+ function HallucinationTab({ trace }) {
1151
+ if (trace.hallucinationScore == null) {
1152
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic", children: "Hallucination scoring not available for this response." });
1153
+ }
1154
+ const score = trace.hallucinationScore;
1155
+ const pct = Math.round(score * 100);
1156
+ const barColor = pct <= 20 ? "#10b981" : pct <= 50 ? "#f59e0b" : "#ef4444";
1157
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
1158
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
1159
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ScoreBadge, { score }),
1160
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40", children: [
1161
+ pct,
1162
+ "/100"
1163
+ ] })
1164
+ ] }),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "h-3 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1166
+ "div",
1167
+ {
1168
+ className: "h-full rounded-full transition-all duration-700",
1169
+ style: { width: `${pct}%`, background: barColor }
1170
+ }
1171
+ ) }),
1172
+ trace.hallucinationReason && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5", children: [
1173
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-500 dark:text-white/40 font-medium mb-0.5", children: "AI self-assessment" }),
1174
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-700 dark:text-white/70 leading-relaxed", children: trace.hallucinationReason })
1175
+ ] }),
1176
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex gap-2 text-[10px] text-slate-400 dark:text-white/30", children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-1", children: [
1178
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }),
1179
+ " 0\u201320%: Grounded"
1180
+ ] }),
1181
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-1", children: [
1182
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block w-2 h-2 rounded-full bg-amber-500" }),
1183
+ " 21\u201350%: Uncertain"
1184
+ ] }),
1185
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex items-center gap-1", children: [
1186
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "inline-block w-2 h-2 rounded-full bg-red-500" }),
1187
+ " 51\u2013100%: Risk"
1188
+ ] })
1189
+ ] })
1190
+ ] });
1191
+ }
1192
+ function PromptTab({ trace }) {
1193
+ const [showSystem, setShowSystem] = import_react6.default.useState(false);
1194
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "space-y-3", children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1197
+ "button",
1198
+ {
1199
+ onClick: () => setShowSystem((s) => !s),
1200
+ 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",
1201
+ children: [
1202
+ showSystem ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronRight, { className: "w-3 h-3" }),
1203
+ "System Prompt"
1204
+ ]
1205
+ }
1206
+ ),
1207
+ showSystem && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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", children: trace.systemPrompt || "(empty)" })
1208
+ ] }),
1209
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] font-semibold text-slate-600 dark:text-white/60 mb-1.5", children: "User Prompt" }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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", children: trace.userPrompt })
1212
+ ] })
1213
+ ] });
1214
+ }
1215
+ function ChunksTab({ trace }) {
1216
+ const [expanded, setExpanded] = import_react6.default.useState(/* @__PURE__ */ new Set());
1217
+ const toggle = (i) => setExpanded((prev) => {
1218
+ const next = new Set(prev);
1219
+ if (next.has(i)) {
1220
+ next.delete(i);
1221
+ } else {
1222
+ next.add(i);
1223
+ }
1224
+ return next;
1225
+ });
1226
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "space-y-1.5 max-h-64 overflow-y-auto pr-1 custom-scrollbar", children: trace.chunks.map((chunk, i) => {
1227
+ const isOpen = expanded.has(i);
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1229
+ "div",
1230
+ {
1231
+ className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 overflow-hidden",
1232
+ children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1234
+ "button",
1235
+ {
1236
+ onClick: () => toggle(i),
1237
+ 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",
1238
+ children: [
1239
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
1240
+ isOpen ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronDown, { className: "w-3 h-3 text-slate-400" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronRight, { className: "w-3 h-3 text-slate-400" }),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[11px] font-medium text-slate-700 dark:text-white/70", children: [
1242
+ "Chunk ",
1243
+ i + 1
1244
+ ] }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30", children: String(chunk.id).slice(0, 16) })
1246
+ ] }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400", children: chunk.score.toFixed(3) })
1248
+ ]
1249
+ }
1250
+ ),
1251
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "px-3 pb-3 space-y-2 border-t border-slate-100 dark:border-white/5 pt-2", children: [
1252
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1", children: "Content" }),
1254
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[11px] text-slate-600 dark:text-white/60 leading-relaxed whitespace-pre-wrap", children: chunk.content })
1255
+ ] }),
1256
+ Object.keys(chunk.metadata).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
1257
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1", children: "Metadata" }),
1258
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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", children: JSON.stringify(chunk.metadata, null, 2) })
1259
+ ] })
1260
+ ] })
1261
+ ]
1262
+ },
1263
+ String(chunk.id)
1264
+ );
1265
+ }) });
1266
+ }
1267
+ var TABS = [
1268
+ { id: "retrieval", label: "Retrieval", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Layers, { className: "w-3 h-3" }) },
1269
+ { id: "latency", label: "Latency", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Clock, { className: "w-3 h-3" }) },
1270
+ { id: "tokens", label: "Tokens", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Cpu, { className: "w-3 h-3" }) },
1271
+ { id: "hallucination", label: "Grounding", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.AlertTriangle, { className: "w-3 h-3" }) },
1272
+ { id: "prompt", label: "Prompt", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.FileText, { className: "w-3 h-3" }) },
1273
+ { id: "chunks", label: "Chunks", icon: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Zap, { className: "w-3 h-3" }) }
1274
+ ];
1275
+ function ObservabilityPanel({ trace, primaryColor = "#6366f1" }) {
1276
+ const [activeTab, setActiveTab] = import_react6.default.useState("retrieval");
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mt-2 w-[500px] max-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]", children: [
1278
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1279
+ "div",
1280
+ {
1281
+ className: "flex items-center gap-2 px-3 py-2 border-b border-slate-100 dark:border-white/8",
1282
+ style: { background: `${primaryColor}08` },
1283
+ children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1285
+ "span",
1286
+ {
1287
+ className: "inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider",
1288
+ style: { color: primaryColor },
1289
+ children: [
1290
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Zap, { className: "w-3 h-3" }),
1291
+ "Observability Trace"
1292
+ ]
1293
+ }
1294
+ ),
1295
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] text-slate-400 dark:text-white/30 ml-auto font-mono", children: trace.requestId })
1296
+ ]
1297
+ }
1298
+ ),
1299
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex border-b border-slate-100 dark:border-white/8 overflow-x-auto", children: TABS.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1300
+ "button",
1301
+ {
1302
+ onClick: () => setActiveTab(tab.id),
1303
+ 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"}`,
1304
+ style: activeTab === tab.id ? { borderBottomColor: primaryColor, color: primaryColor } : {},
1305
+ children: [
1306
+ tab.icon,
1307
+ tab.label
1308
+ ]
1309
+ },
1310
+ tab.id
1311
+ )) }),
1312
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "p-3", children: [
1313
+ activeTab === "retrieval" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RetrievalTab, { trace }),
1314
+ activeTab === "latency" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(LatencyTab, { trace }),
1315
+ activeTab === "tokens" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TokensTab, { trace }),
1316
+ activeTab === "hallucination" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(HallucinationTab, { trace }),
1317
+ activeTab === "prompt" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PromptTab, { trace }),
1318
+ activeTab === "chunks" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChunksTab, { trace })
1319
+ ] })
1320
+ ] });
1321
+ }
1322
+
1323
+ // src/components/HourglassLoader.tsx
1324
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1325
+ var HourglassLoader = ({
1326
+ size = 16,
1327
+ primaryColor = "#10b981",
1328
+ glow = false
1329
+ }) => {
1330
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1331
+ "svg",
1332
+ {
1333
+ xmlns: "http://www.w3.org/2000/svg",
1334
+ viewBox: "0 0 100 100",
1335
+ preserveAspectRatio: "xMidYMid",
1336
+ style: {
1337
+ width: size,
1338
+ height: size,
1339
+ background: "transparent",
1340
+ filter: glow ? `drop-shadow(0 0 4px ${primaryColor})` : "none"
1341
+ },
1342
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
1343
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1344
+ "animateTransform",
1345
+ {
1346
+ attributeName: "transform",
1347
+ type: "rotate",
1348
+ calcMode: "spline",
1349
+ values: "0 50 50; 0 50 50; 180 50 50",
1350
+ keyTimes: "0; 0.8; 1",
1351
+ keySplines: "0 0 1 1; 0.4 0 0.2 1",
1352
+ dur: "2s",
1353
+ repeatCount: "indefinite"
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1357
+ "path",
1358
+ {
1359
+ 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",
1360
+ fill: "none",
1361
+ stroke: primaryColor,
1362
+ strokeWidth: "5",
1363
+ strokeLinejoin: "round",
1364
+ strokeLinecap: "round"
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { fill: primaryColor, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1368
+ "animate",
1369
+ {
1370
+ attributeName: "d",
1371
+ 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",
1372
+ keyTimes: "0; 0.8; 1",
1373
+ dur: "2s",
1374
+ repeatCount: "indefinite"
1375
+ }
1376
+ ) }),
1377
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { fill: primaryColor, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1378
+ "animate",
1379
+ {
1380
+ attributeName: "d",
1381
+ 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",
1382
+ keyTimes: "0; 0.8; 1",
1383
+ dur: "2s",
1384
+ repeatCount: "indefinite"
1385
+ }
1386
+ ) }),
1387
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "50", y1: "50", x2: "50", y2: "80", stroke: primaryColor, strokeWidth: "3", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1388
+ "animate",
1389
+ {
1390
+ attributeName: "opacity",
1391
+ values: "0; 1; 1; 0; 0",
1392
+ keyTimes: "0; 0.1; 0.7; 0.8; 1",
1393
+ dur: "2s",
1394
+ repeatCount: "indefinite"
1395
+ }
1396
+ ) })
1397
+ ] })
1398
+ }
1399
+ );
1400
+ };
1401
+ var HourglassLoader_default = HourglassLoader;
1402
+
1403
+ // src/utils/synonyms.ts
1404
+ var FIELD_SYNONYMS = {
1405
+ name: ["product", "item", "title", "label", "heading", "subject", "product name", "item name"],
1406
+ price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
1407
+ brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
1408
+ image: [
1409
+ "imageUrl",
1410
+ "thumbnail",
1411
+ "img",
1412
+ "url",
1413
+ "photo",
1414
+ "picture",
1415
+ "media",
1416
+ "image_url",
1417
+ "main_image",
1418
+ "product_image",
1419
+ "thumb"
1420
+ ],
1421
+ stock: [
1422
+ "inventory",
1423
+ "quantity",
1424
+ "count",
1425
+ "availability",
1426
+ "stock_level",
1427
+ "inStock",
1428
+ "is_available",
1429
+ "in stock",
1430
+ "status"
1431
+ ],
1432
+ description: ["summary", "content", "body", "text", "info", "details"],
1433
+ link: ["url", "href", "product_url", "page_url", "link"]
1434
+ };
1435
+ function addSynonyms(customSynonyms) {
1436
+ for (const [key, aliases] of Object.entries(customSynonyms)) {
1437
+ if (!FIELD_SYNONYMS[key]) {
1438
+ FIELD_SYNONYMS[key] = [];
1439
+ }
1440
+ const existing = new Set(FIELD_SYNONYMS[key].map((s) => s.toLowerCase()));
1441
+ for (const alias of aliases) {
1442
+ if (!existing.has(alias.toLowerCase())) {
1443
+ FIELD_SYNONYMS[key].push(alias);
1444
+ existing.add(alias.toLowerCase());
1445
+ }
1446
+ }
1447
+ }
1448
+ }
1449
+ function resolveMetadataValue(meta, uiKey) {
1450
+ var _a;
1451
+ const synonyms = (_a = FIELD_SYNONYMS[uiKey]) != null ? _a : [];
1452
+ const keys = Object.keys(meta);
1453
+ const systemKeys = ["namespace", "filename", "filesize", "filetype", "docid", "uploadedat", "dimension", "chunkindex"];
1454
+ let match = keys.find((k) => k.toLowerCase() === uiKey.toLowerCase());
1455
+ if (match !== void 0) return meta[match];
1456
+ match = keys.find((k) => synonyms.map((s) => s.toLowerCase()).includes(k.toLowerCase()));
1457
+ if (match !== void 0) return meta[match];
1458
+ const isBlacklisted = (kl) => {
1459
+ return systemKeys.includes(kl) || kl.startsWith("option1") || kl.startsWith("option2") || kl.startsWith("option3");
1460
+ };
1461
+ match = keys.find((k) => {
1462
+ const kl = k.toLowerCase();
1463
+ if (isBlacklisted(kl)) return false;
1464
+ return kl.includes(uiKey.toLowerCase());
1465
+ });
1466
+ if (match !== void 0) return meta[match];
1467
+ match = keys.find((k) => {
1468
+ const kl = k.toLowerCase();
1469
+ if (isBlacklisted(kl)) return false;
1470
+ return synonyms.some((sk) => kl.includes(sk.toLowerCase()) || sk.toLowerCase().includes(kl));
1471
+ });
1472
+ return match !== void 0 ? meta[match] : void 0;
1473
+ }
1474
+
1475
+ // src/utils/ProductExtractor.ts
602
1476
  function sanitizeJson(raw) {
603
1477
  let s = raw.trim();
604
1478
  s = s.replace(
@@ -615,36 +1489,34 @@ function sanitizeJson(raw) {
615
1489
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
616
1490
  s = s.replace(/,\s*([}\]])/g, "$1");
617
1491
  s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
618
- {
619
- const stack = [];
620
- let inString = false;
621
- let escape = false;
622
- for (const ch of s) {
623
- if (escape) {
624
- escape = false;
625
- continue;
626
- }
627
- if (ch === "\\" && inString) {
628
- escape = true;
629
- continue;
630
- }
631
- if (ch === '"') {
632
- inString = !inString;
633
- continue;
634
- }
635
- if (inString) continue;
636
- if (ch === "{" || ch === "[") {
637
- stack.push(ch);
638
- } else if (ch === "}") {
639
- if (stack[stack.length - 1] === "{") stack.pop();
640
- } else if (ch === "]") {
641
- if (stack[stack.length - 1] === "[") stack.pop();
642
- }
1492
+ const stack = [];
1493
+ let inString = false;
1494
+ let escape = false;
1495
+ for (const ch of s) {
1496
+ if (escape) {
1497
+ escape = false;
1498
+ continue;
1499
+ }
1500
+ if (ch === "\\" && inString) {
1501
+ escape = true;
1502
+ continue;
643
1503
  }
644
- if (inString) s += '"';
645
- for (let i = stack.length - 1; i >= 0; i--) {
646
- s += stack[i] === "{" ? "}" : "]";
1504
+ if (ch === '"') {
1505
+ inString = !inString;
1506
+ continue;
647
1507
  }
1508
+ if (inString) continue;
1509
+ if (ch === "{" || ch === "[") {
1510
+ stack.push(ch);
1511
+ } else if (ch === "}") {
1512
+ if (stack[stack.length - 1] === "{") stack.pop();
1513
+ } else if (ch === "]") {
1514
+ if (stack[stack.length - 1] === "[") stack.pop();
1515
+ }
1516
+ }
1517
+ if (inString) s += '"';
1518
+ for (let i = stack.length - 1; i >= 0; i--) {
1519
+ s += stack[i] === "{" ? "}" : "]";
648
1520
  }
649
1521
  const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
650
1522
  if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
@@ -745,10 +1617,6 @@ function extractStructuredPayload(raw) {
745
1617
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
746
1618
  };
747
1619
  }
748
- function isLikelyUiOnlyMessage(text) {
749
- const trimmed = text.trim();
750
- return trimmed.length === 0 || trimmed.length < 30 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary|availability|inventory status|details)\s*:?\s*$/i.test(trimmed);
751
- }
752
1620
  function resolveImage(data) {
753
1621
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
754
1622
  for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
@@ -758,92 +1626,140 @@ function resolveImage(data) {
758
1626
  }
759
1627
  return void 0;
760
1628
  }
761
- function getMetadataValue(meta, keys) {
762
- const searchKeys = keys.map((k) => k.toLowerCase());
763
- const metaKeys = Object.keys(meta);
764
- const foundKey = metaKeys.find((k) => searchKeys.includes(k.toLowerCase()));
765
- if (foundKey) return meta[foundKey];
766
- const partialKey = metaKeys.find((k) => {
767
- const kLow = k.toLowerCase();
768
- return searchKeys.some((sk) => kLow.includes(sk) || sk.includes(kLow));
769
- });
770
- return partialKey ? meta[partialKey] : void 0;
771
- }
772
- function normaliseChild(children) {
773
- if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react6.default.isValidElement(children)) {
774
- return JSON.stringify(children);
775
- }
776
- return children;
777
- }
778
- function normaliseFieldName(value) {
779
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
780
- }
781
- function formatColumnLabel(value) {
782
- return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (char) => char.toUpperCase());
1629
+ function isLikelyUiOnlyMessage(text) {
1630
+ const trimmed = text.trim();
1631
+ return trimmed.length === 0 || trimmed.length < 30 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary|availability|inventory status|details)\s*:?\s*$/i.test(trimmed);
783
1632
  }
784
- function DataTable({ config, viewportSize = "large" }) {
785
- const isCompact = viewportSize === "compact";
786
- const isMedium = viewportSize === "medium";
787
- const columns = import_react6.default.useMemo(() => {
788
- const rowKeys = Array.from(
789
- new Set(
790
- config.data.flatMap((row) => Object.keys(row))
791
- )
1633
+ function extractProductsFromSources(sources, isUser) {
1634
+ if (isUser || !sources) return [];
1635
+ return sources.filter((s) => {
1636
+ var _a;
1637
+ const m = (_a = s.metadata) != null ? _a : {};
1638
+ const keys = Object.keys(m).map((k) => k.toLowerCase());
1639
+ const hasProductKey = keys.some(
1640
+ (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
792
1641
  );
793
- const findAccessor = (label) => {
794
- const exact = rowKeys.find((key) => key === label);
795
- if (exact) return exact;
796
- const normalisedLabel = normaliseFieldName(label);
797
- const normalisedMatch = rowKeys.find((key) => normaliseFieldName(key) === normalisedLabel);
798
- if (normalisedMatch) return normalisedMatch;
799
- return label;
1642
+ const hasPricePattern = /\$\s*\d+/.test(s.content);
1643
+ return hasProductKey || hasPricePattern || m.type === "product";
1644
+ }).map((s) => {
1645
+ var _a, _b;
1646
+ const m = (_a = s.metadata) != null ? _a : {};
1647
+ const name = resolveMetadataValue(m, "name");
1648
+ const brand = resolveMetadataValue(m, "brand");
1649
+ const price = resolveMetadataValue(m, "price");
1650
+ const description = resolveMetadataValue(m, "description");
1651
+ return {
1652
+ id: s.id,
1653
+ name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1654
+ brand,
1655
+ price,
1656
+ image: resolveImage(m),
1657
+ link: resolveMetadataValue(m, "link"),
1658
+ description: description ? description : ""
1659
+ //description ?? s.content,
800
1660
  };
801
- if (Array.isArray(config.columns) && config.columns.length) {
802
- return config.columns.map((label) => ({
803
- label: formatColumnLabel(label),
804
- accessor: findAccessor(label)
805
- }));
1661
+ });
1662
+ }
1663
+ function extractProductsFromContent(content, payloadText, payloadCandidates, productsFromSources, isStreaming = false) {
1664
+ var _a, _b;
1665
+ const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
1666
+ const products = [];
1667
+ let cleanContent = payloadText;
1668
+ for (const candidate of payloadCandidates) {
1669
+ try {
1670
+ const data = JSON.parse(sanitizeJson(candidate));
1671
+ const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
1672
+ if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
1673
+ products.push(
1674
+ ...itemSet.map((item) => {
1675
+ var _a2;
1676
+ return __spreadProps(__spreadValues({}, item), {
1677
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1678
+ });
1679
+ })
1680
+ );
1681
+ }
1682
+ } catch (e) {
1683
+ }
1684
+ }
1685
+ if (cleanContent.includes('"type": "products"') || cleanContent.includes('"type":"products"')) {
1686
+ for (const match of Array.from(cleanContent.matchAll(jsonRegex))) {
1687
+ try {
1688
+ const data = JSON.parse(sanitizeJson(match[1]));
1689
+ if (data.type === "products" && Array.isArray(data.items)) {
1690
+ products.push(
1691
+ ...data.items.map((item) => {
1692
+ var _a2;
1693
+ return __spreadProps(__spreadValues({}, item), {
1694
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1695
+ });
1696
+ })
1697
+ );
1698
+ cleanContent = cleanContent.replace(match[0], "");
1699
+ }
1700
+ } catch (e) {
1701
+ }
806
1702
  }
807
- if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
808
- const keys = config.xAxisKey && !config.dataKeys.includes(config.xAxisKey) ? [config.xAxisKey, ...config.dataKeys] : config.dataKeys;
809
- return keys.map((key) => ({
810
- label: formatColumnLabel(key),
811
- accessor: findAccessor(key)
812
- }));
1703
+ }
1704
+ const bulletRegex = /^[ \t]*(?:[•*-]|\d+\.)\s+(?:\*\*)?([^\n\-\$–—\(]+?)(?:\*\*)?(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gim;
1705
+ const matches = Array.from(cleanContent.matchAll(bulletRegex));
1706
+ if (!isStreaming && matches.length >= 2) {
1707
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1708
+ for (const match of matches) {
1709
+ let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
1710
+ name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
1711
+ name = name.replace(/^\*\*|\*\*$/g, "").replace(/^\*|\*$/g, "").trim();
1712
+ let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
1713
+ price = price.replace(/[,\s]+$/, "").trim();
1714
+ if (name && price) {
1715
+ const newProduct = {
1716
+ id: `text-prod-${name}-${price}`,
1717
+ name,
1718
+ price: `$${price}`
1719
+ };
1720
+ const normName = normalize(name);
1721
+ const sourceMatch = productsFromSources.find((s) => {
1722
+ const sn = normalize(s.name);
1723
+ return sn.includes(normName) || normName.includes(sn);
1724
+ });
1725
+ if (sourceMatch) {
1726
+ newProduct.image = sourceMatch.image;
1727
+ newProduct.brand = sourceMatch.brand;
1728
+ newProduct.link = sourceMatch.link;
1729
+ newProduct.description = sourceMatch.description;
1730
+ }
1731
+ products.push(newProduct);
1732
+ cleanContent = cleanContent.replace(match[0], `
1733
+ **${name}** \u2014 $${price}`);
1734
+ }
813
1735
  }
814
- return rowKeys.map((key) => ({ label: formatColumnLabel(key), accessor: key }));
815
- }, [config]);
816
- if (!config.data.length || !columns.length) {
817
- return /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
1736
+ cleanContent = cleanContent.replace(/\n{3,}/g, "\n\n").trim();
818
1737
  }
819
- const formatCell = (val) => {
820
- if (val === null || val === void 0) return "\u2014";
821
- if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
822
- return String(val);
823
- };
824
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-4 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, config.title && /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-4 py-2.5 bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs font-semibold text-slate-600 dark:text-white/70 uppercase tracking-wide" }, config.title)), /* @__PURE__ */ import_react6.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react6.default.createElement("table", { className: `w-full text-left border-collapse ${isCompact ? "min-w-[240px] text-xs" : isMedium ? "min-w-[280px] text-[13px]" : "min-w-[320px] text-sm"}` }, /* @__PURE__ */ import_react6.default.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ import_react6.default.createElement("tr", null, columns.map((column) => /* @__PURE__ */ import_react6.default.createElement(
825
- "th",
826
- {
827
- key: column.label,
828
- className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap`
829
- },
830
- column.label
831
- )))), /* @__PURE__ */ import_react6.default.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ import_react6.default.createElement(
832
- "tr",
833
- {
834
- key: ri,
835
- className: "border-b border-slate-100 dark:border-white/5 last:border-0 hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
836
- },
837
- columns.map((column) => /* @__PURE__ */ import_react6.default.createElement(
838
- "td",
839
- {
840
- key: column.label,
841
- className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} text-slate-600 dark:text-white/70 ${column.accessor.toLowerCase().includes("description") ? "whitespace-normal break-words" : "whitespace-nowrap"}`
842
- },
843
- formatCell(row[column.accessor])
844
- ))
845
- ))))), /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, config.data.length, " row", config.data.length !== 1 ? "s" : "")));
1738
+ return { productsFromContent: products, cleanContent: cleanContent.trim() };
1739
+ }
1740
+ function deduplicateProducts(productsFromSources, productsFromContent, messageContent) {
1741
+ if (productsFromContent.length > 0) return productsFromContent;
1742
+ const seen = /* @__PURE__ */ new Set();
1743
+ const contentLower = messageContent.toLowerCase();
1744
+ return productsFromSources.filter((p) => {
1745
+ var _a;
1746
+ const id = String((_a = p.id) != null ? _a : p.name);
1747
+ if (seen.has(id)) return false;
1748
+ const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
1749
+ if (mentioned) {
1750
+ seen.add(id);
1751
+ return true;
1752
+ }
1753
+ return false;
1754
+ });
846
1755
  }
1756
+
1757
+ // src/components/MarkdownComponents.tsx
1758
+ var import_react8 = __toESM(require("react"));
1759
+
1760
+ // src/components/UIDispatcher.tsx
1761
+ var import_react7 = __toESM(require("react"));
1762
+ var import_jsx_runtime10 = require("react/jsx-runtime");
847
1763
  function normalizeTabularRows(rows, columns) {
848
1764
  if (!Array.isArray(rows)) return [];
849
1765
  const SYNONYMS = {
@@ -911,8 +1827,7 @@ function resolveStructuredRows(config) {
911
1827
  }
912
1828
  function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
913
1829
  var _a;
914
- const result = import_react6.default.useMemo(() => {
915
- if (isStreaming) return { loading: true };
1830
+ const result = import_react7.default.useMemo(() => {
916
1831
  try {
917
1832
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
918
1833
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
@@ -941,10 +1856,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
941
1856
  } else if (viewStr.includes("chart") || viewStr.includes("pie") || viewStr.includes("bar") || viewStr.includes("line") || hasAggregationLikeData || typeof config2.type === "string" && ["pie", "bar", "line"].includes(config2.type) || typeof config2.chartType === "string" && ["pie", "bar", "line"].includes(config2.chartType)) {
942
1857
  config2.view = "chart";
943
1858
  config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
944
- } else if (viewStr.includes("table")) {
1859
+ } else if (viewStr.includes("table") || config2.type === "table") {
945
1860
  config2.view = "table";
946
1861
  } else {
947
- config2.view = "table";
1862
+ config2.view = "none";
948
1863
  }
949
1864
  }
950
1865
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
@@ -952,14 +1867,18 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
952
1867
  });
953
1868
  return { config: normalizedConfig };
954
1869
  } catch (err) {
1870
+ if (isStreaming) return { loading: true };
955
1871
  return { error: String(err) };
956
1872
  }
957
1873
  }, [rawContent, isStreaming]);
958
1874
  if ("loading" in result) {
959
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-4 p-8 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex flex-col items-center justify-center gap-3 animate-pulse" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
1875
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("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", children: [
1876
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }),
1877
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-xs text-slate-500 font-medium italic", children: "Preparing view..." })
1878
+ ] });
960
1879
  }
961
1880
  if ("error" in result) {
962
- return /* @__PURE__ */ import_react6.default.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ import_react6.default.createElement("code", null, rawContent));
1881
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("code", { children: rawContent }) });
963
1882
  }
964
1883
  const { config } = result;
965
1884
  const isCompact = viewportSize === "compact";
@@ -967,49 +1886,199 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
967
1886
  const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
968
1887
  switch (config.view) {
969
1888
  case "chart":
970
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: `${isCompact ? "my-4 p-3" : "my-6 p-4"} bg-white dark:bg-slate-900 rounded-2xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden` }, config.title && /* @__PURE__ */ import_react6.default.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ import_react6.default.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(
971
- DynamicChart,
972
- {
973
- config: {
974
- type: config.chartType || "bar",
975
- data: config.data,
976
- xAxisKey: config.xAxisKey,
977
- dataKeys: config.dataKeys,
978
- colors: config.colors
1889
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("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`, children: [
1890
+ config.title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2`, children: config.title }),
1891
+ hasDescription && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60`, children: config.description }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1893
+ DynamicChart,
1894
+ {
1895
+ config: {
1896
+ type: config.chartType || "bar",
1897
+ data: config.data,
1898
+ xAxisKey: config.xAxisKey,
1899
+ dataKeys: config.dataKeys,
1900
+ colors: config.colors
1901
+ },
1902
+ primaryColor,
1903
+ accentColor,
1904
+ viewportSize,
1905
+ isStreaming
1906
+ }
1907
+ ),
1908
+ hasInsights && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-4 flex flex-wrap gap-2 px-2", children: (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1909
+ "span",
1910
+ {
1911
+ className: `rounded-full border border-emerald-200 bg-emerald-50 ${isCompact ? "px-2.5 py-1 text-[10px]" : "px-3 py-1 text-[11px]"} font-medium text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300`,
1912
+ children: String(insight)
979
1913
  },
980
- primaryColor,
981
- accentColor,
982
- viewportSize
983
- }
984
- ), hasInsights && /* @__PURE__ */ import_react6.default.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ import_react6.default.createElement(
985
- "span",
986
- {
987
- key: `insight-${i}`,
988
- className: `rounded-full border border-emerald-200 bg-emerald-50 ${isCompact ? "px-2.5 py-1 text-[10px]" : "px-3 py-1 text-[11px]"} font-medium text-emerald-700 dark:border-emerald-500/20 dark:bg-emerald-500/10 dark:text-emerald-300`
989
- },
990
- String(insight)
991
- ))));
1914
+ `insight-${i}`
1915
+ )) })
1916
+ ] });
992
1917
  case "carousel":
993
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ import_react6.default.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ import_react6.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(ProductCarousel, { products: config.data.map((item) => {
994
- var _a2;
995
- return __spreadProps(__spreadValues({}, item), {
996
- image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
997
- });
998
- }), primaryColor, onAddToCart }));
1918
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "my-4", children: [
1919
+ config.title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500`, children: config.title }),
1920
+ hasDescription && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60`, children: config.description }),
1921
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ProductCarousel, { products: config.data.map((item) => {
1922
+ var _a2;
1923
+ return __spreadProps(__spreadValues({}, item), {
1924
+ image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
1925
+ });
1926
+ }), primaryColor, onAddToCart })
1927
+ ] });
999
1928
  case "table":
1000
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: `${isCompact ? "my-3 p-3 max-h-[320px]" : "my-4 p-4 max-h-[400px]"} bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-auto` }, config.title && /* @__PURE__ */ import_react6.default.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ import_react6.default.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ import_react6.default.createElement(DataTable, { config: {
1001
- type: "table",
1002
- title: config.title,
1003
- description: config.description,
1004
- columns: config.columns,
1005
- dataKeys: config.dataKeys,
1006
- xAxisKey: config.xAxisKey,
1007
- data: config.data
1008
- }, viewportSize }));
1929
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("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`, children: [
1930
+ config.title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2`, children: config.title }),
1931
+ hasDescription && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60`, children: config.description }),
1932
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1933
+ VisualizationRenderer,
1934
+ {
1935
+ data: {
1936
+ type: "table",
1937
+ title: config.title || "",
1938
+ description: config.description,
1939
+ data: {
1940
+ columns: config.columns || Object.keys(config.data[0] || {}),
1941
+ rows: config.data.map((row) => Object.values(row))
1942
+ }
1943
+ },
1944
+ primaryColor,
1945
+ isStreaming
1946
+ }
1947
+ )
1948
+ ] });
1009
1949
  default:
1010
1950
  return null;
1011
1951
  }
1012
1952
  }
1953
+
1954
+ // src/components/MarkdownComponents.tsx
1955
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1956
+ function createMarkdownComponents({
1957
+ primaryColor,
1958
+ accentColor,
1959
+ isStreaming,
1960
+ onAddToCart,
1961
+ viewportSize
1962
+ }) {
1963
+ return {
1964
+ p: (_a) => {
1965
+ var props = __objRest(_a, []);
1966
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", __spreadValues({ className: "whitespace-pre-line" }, props));
1967
+ },
1968
+ table: (_b) => {
1969
+ var props = __objRest(_b, []);
1970
+ if (isStreaming) {
1971
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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", children: [
1972
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }),
1973
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse", children: "Generating data table..." })
1974
+ ] });
1975
+ }
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("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", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1977
+ "table",
1978
+ __spreadValues({
1979
+ className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
1980
+ }, props)
1981
+ ) }) });
1982
+ },
1983
+ thead: (_c) => {
1984
+ var props = __objRest(_c, []);
1985
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1986
+ "thead",
1987
+ __spreadValues({
1988
+ className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
1989
+ }, props)
1990
+ );
1991
+ },
1992
+ tbody: (_d) => {
1993
+ var props = __objRest(_d, []);
1994
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1995
+ },
1996
+ tr: (_e) => {
1997
+ var props = __objRest(_e, []);
1998
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1999
+ "tr",
2000
+ __spreadValues({
2001
+ className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
2002
+ }, props)
2003
+ );
2004
+ },
2005
+ th: (_f) => {
2006
+ var _g = _f, { children } = _g, props = __objRest(_g, ["children"]);
2007
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2008
+ "th",
2009
+ __spreadProps(__spreadValues({
2010
+ 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"
2011
+ }, props), {
2012
+ children: normaliseChild(children)
2013
+ })
2014
+ );
2015
+ },
2016
+ td: (_h) => {
2017
+ var _i = _h, { children } = _i, props = __objRest(_i, ["children"]);
2018
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2019
+ "td",
2020
+ __spreadProps(__spreadValues({
2021
+ className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
2022
+ }, props), {
2023
+ children: normaliseChild(children)
2024
+ })
2025
+ );
2026
+ },
2027
+ code(_j) {
2028
+ var _k = _j, {
2029
+ inline,
2030
+ className,
2031
+ children
2032
+ } = _k, props = __objRest(_k, [
2033
+ "inline",
2034
+ "className",
2035
+ "children"
2036
+ ]);
2037
+ var _a;
2038
+ const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
2039
+ if (!inline && (lang === "ui" || lang === "chart" || lang === "json")) {
2040
+ const content = String(children != null ? children : "").trim();
2041
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2042
+ UIDispatcher,
2043
+ {
2044
+ rawContent: content,
2045
+ primaryColor,
2046
+ accentColor,
2047
+ isStreaming,
2048
+ onAddToCart,
2049
+ viewportSize
2050
+ }
2051
+ );
2052
+ }
2053
+ if (!inline) {
2054
+ const content = String(children != null ? children : "").trim();
2055
+ if (content.startsWith("{") && content.endsWith("}")) {
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2057
+ UIDispatcher,
2058
+ {
2059
+ rawContent: content,
2060
+ primaryColor,
2061
+ accentColor,
2062
+ isStreaming,
2063
+ onAddToCart,
2064
+ viewportSize
2065
+ }
2066
+ );
2067
+ }
2068
+ }
2069
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("code", __spreadProps(__spreadValues({ className }, props), { children: typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children) }));
2070
+ }
2071
+ };
2072
+ }
2073
+ function normaliseChild(children) {
2074
+ if (typeof children === "object" && children !== null && !Array.isArray(children) && !import_react8.default.isValidElement(children)) {
2075
+ return JSON.stringify(children);
2076
+ }
2077
+ return children;
2078
+ }
2079
+
2080
+ // src/components/MessageBubble.tsx
2081
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1013
2082
  function MessageBubble({
1014
2083
  message,
1015
2084
  sources,
@@ -1022,12 +2091,13 @@ function MessageBubble({
1022
2091
  const isUser = message.role === "user";
1023
2092
  const isCompact = viewportSize === "compact";
1024
2093
  const isMedium = viewportSize === "medium";
1025
- const [showSources, setShowSources] = import_react6.default.useState(false);
1026
- const structuredContent = import_react6.default.useMemo(
2094
+ const [showSources, setShowSources] = import_react9.default.useState(false);
2095
+ const [showTrace, setShowTrace] = import_react9.default.useState(false);
2096
+ const structuredContent = import_react9.default.useMemo(
1027
2097
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
1028
2098
  [isUser, message.content]
1029
2099
  );
1030
- const hasRichUI = import_react6.default.useMemo(() => {
2100
+ const hasRichUI = import_react9.default.useMemo(() => {
1031
2101
  if (message.uiTransformation) {
1032
2102
  const type = message.uiTransformation.type;
1033
2103
  if (type && !["text", "table"].includes(type)) return true;
@@ -1038,421 +2108,168 @@ function MessageBubble({
1038
2108
  return false;
1039
2109
  }, [message.uiTransformation, structuredContent.payload]);
1040
2110
  const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
1041
- const productsFromSources = import_react6.default.useMemo(() => {
1042
- if (isUser || !sources) return [];
1043
- return sources.filter((s) => {
1044
- var _a;
1045
- const m = (_a = s.metadata) != null ? _a : {};
1046
- const keys = Object.keys(m).map((k) => k.toLowerCase());
1047
- const hasProductKey = keys.some(
1048
- (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
1049
- );
1050
- const hasPricePattern = /\$\s*\d+/.test(s.content);
1051
- return hasProductKey || hasPricePattern || m.type === "product";
1052
- }).map((s) => {
1053
- var _a, _b;
1054
- const m = (_a = s.metadata) != null ? _a : {};
1055
- const name = getMetadataValue(m, ["name", "product", "title", "label", "item"]);
1056
- const brand = getMetadataValue(m, ["brand", "manufacturer", "vendor", "make"]);
1057
- const price = getMetadataValue(m, ["price", "cost", "amount", "msrp", "rate"]);
1058
- return {
1059
- id: s.id,
1060
- name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1061
- brand,
1062
- price,
1063
- image: resolveImage(m),
1064
- link: getMetadataValue(m, ["link", "url", "product_url"]),
1065
- description: s.content
1066
- };
1067
- });
1068
- }, [sources, isUser]);
1069
- const { productsFromContent, cleanContent } = import_react6.default.useMemo(() => {
1070
- var _a, _b;
1071
- if (isUser) {
1072
- return { productsFromContent: [], cleanContent: message.content };
1073
- }
1074
- const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
1075
- const products = [];
1076
- let content = structuredContent.text;
1077
- const payloadCandidates = [structuredContent.payload, ...Array.from(content.matchAll(jsonRegex)).map((match) => match[1])].filter(Boolean);
1078
- for (const candidate of payloadCandidates) {
1079
- try {
1080
- const data = JSON.parse(sanitizeJson(candidate));
1081
- const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
1082
- if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
1083
- products.push(
1084
- ...itemSet.map((item) => {
1085
- var _a2;
1086
- return __spreadProps(__spreadValues({}, item), {
1087
- image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1088
- });
1089
- })
1090
- );
1091
- }
1092
- } catch (e) {
1093
- }
1094
- }
1095
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
1096
- for (const match of Array.from(content.matchAll(jsonRegex))) {
1097
- try {
1098
- const data = JSON.parse(sanitizeJson(match[1]));
1099
- if (data.type === "products" && Array.isArray(data.items)) {
1100
- products.push(
1101
- ...data.items.map((item) => {
1102
- var _a2;
1103
- return __spreadProps(__spreadValues({}, item), {
1104
- image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1105
- });
1106
- })
1107
- );
1108
- content = content.replace(match[0], "");
1109
- }
1110
- } catch (e) {
1111
- }
1112
- }
1113
- }
1114
- const bulletRegex = /(?:[•*-]\s*)?([^•\n\-\$*–—\(]+?)(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gi;
1115
- const matches = Array.from(content.matchAll(bulletRegex));
1116
- if (matches.length >= 2) {
1117
- const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1118
- for (const match of matches) {
1119
- let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
1120
- name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
1121
- let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
1122
- price = price.replace(/[,\s]+$/, "").trim();
1123
- if (name && price) {
1124
- const newProduct = {
1125
- id: `text-prod-${name}-${price}`,
1126
- name,
1127
- price: `$${price}`
1128
- };
1129
- const normName = normalize(name);
1130
- const sourceMatch = productsFromSources.find((s) => {
1131
- const sn = normalize(s.name);
1132
- return sn.includes(normName) || normName.includes(sn);
1133
- });
1134
- if (sourceMatch) {
1135
- newProduct.image = sourceMatch.image;
1136
- newProduct.brand = sourceMatch.brand;
1137
- newProduct.link = sourceMatch.link;
1138
- newProduct.description = sourceMatch.description;
1139
- }
1140
- products.push(newProduct);
1141
- content = content.replace(match[0], `
1142
- **${name}** \u2014 $${price}`);
1143
- }
1144
- }
1145
- content = content.replace(/\n{3,}/g, "\n\n").trim();
1146
- }
1147
- return { productsFromContent: products, cleanContent: content.trim() };
1148
- }, [message.content, isUser, structuredContent, productsFromSources]);
1149
- const allProducts = import_react6.default.useMemo(() => {
1150
- if (productsFromContent.length > 0) return productsFromContent;
1151
- const seen = /* @__PURE__ */ new Set();
1152
- const contentLower = message.content.toLowerCase();
1153
- return productsFromSources.filter((p) => {
1154
- var _a;
1155
- const id = String((_a = p.id) != null ? _a : p.name);
1156
- if (seen.has(id)) return false;
1157
- const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
1158
- if (mentioned) {
1159
- seen.add(id);
1160
- return true;
1161
- }
1162
- return false;
1163
- });
1164
- }, [productsFromSources, productsFromContent, message.content]);
1165
- const processedMarkdown = import_react6.default.useMemo(() => {
1166
- let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
1167
- const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
1168
- raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
1169
- if (!isStreaming) {
1170
- raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
1171
- if (match.includes("```")) return match;
1172
- if (!looksLikeStructuredPayload(match)) return match;
1173
- const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
1174
- return `
1175
-
1176
- \`\`\`${type}
1177
- ${match.trim()}
1178
- \`\`\`
1179
-
1180
- `;
1181
- });
1182
- }
1183
- if (hasStructuredUiBlock) {
1184
- raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
1185
- }
1186
- if (isStreaming) {
1187
- raw = raw.replace(/((?:^|\n)\|.*)+/g, (match) => {
1188
- return `
1189
-
1190
- \`\`\`table-loading
1191
- ${match.trim()}
1192
- \`\`\`
1193
-
1194
- `;
1195
- });
1196
- }
1197
- return raw;
1198
- }, [cleanContent, message.content, isStreaming, structuredContent]);
1199
- const markdownComponents = import_react6.default.useMemo(
1200
- () => ({
1201
- // Wrap in not-prose so Tailwind Typography resets don't clobber our styles.
1202
- // prose applies display:block and padding:0 to table/th/td — not-prose opts out.
1203
- table: (_a) => {
1204
- var props = __objRest(_a, []);
1205
- if (isStreaming) {
1206
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
1207
- }
1208
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "not-prose overflow-hidden my-5 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm bg-white dark:bg-slate-900/50" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ import_react6.default.createElement(
1209
- "table",
1210
- __spreadValues({
1211
- className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
1212
- }, props)
1213
- )));
1214
- },
1215
- thead: (_b) => {
1216
- var props = __objRest(_b, []);
1217
- return /* @__PURE__ */ import_react6.default.createElement(
1218
- "thead",
1219
- __spreadValues({
1220
- className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
1221
- }, props)
1222
- );
1223
- },
1224
- tbody: (_c) => {
1225
- var props = __objRest(_c, []);
1226
- return /* @__PURE__ */ import_react6.default.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1227
- },
1228
- tr: (_d) => {
1229
- var props = __objRest(_d, []);
1230
- return /* @__PURE__ */ import_react6.default.createElement(
1231
- "tr",
1232
- __spreadValues({
1233
- className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
1234
- }, props)
1235
- );
1236
- },
1237
- th: (_e) => {
1238
- var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
1239
- return /* @__PURE__ */ import_react6.default.createElement(
1240
- "th",
1241
- __spreadValues({
1242
- className: "!table-cell px-4 py-3 font-bold text-slate-700 dark:text-white/90 whitespace-nowrap text-[11px] uppercase tracking-wider text-left"
1243
- }, props),
1244
- normaliseChild(children)
1245
- );
1246
- },
1247
- td: (_g) => {
1248
- var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
1249
- return /* @__PURE__ */ import_react6.default.createElement(
1250
- "td",
1251
- __spreadValues({
1252
- className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
1253
- }, props),
1254
- normaliseChild(children)
1255
- );
1256
- },
1257
- code(_i) {
1258
- var _j = _i, {
1259
- inline,
1260
- className,
1261
- children
1262
- } = _j, props = __objRest(_j, [
1263
- "inline",
1264
- "className",
1265
- "children"
1266
- ]);
1267
- var _a;
1268
- const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
1269
- if (!inline && (lang === "ui" || lang === "chart")) {
1270
- return /* @__PURE__ */ import_react6.default.createElement(
1271
- UIDispatcher,
1272
- {
1273
- rawContent: String(children != null ? children : "").trim(),
1274
- primaryColor,
1275
- accentColor,
1276
- isStreaming,
1277
- onAddToCart,
1278
- viewportSize
1279
- }
1280
- );
1281
- }
1282
- if (!inline && lang === "json") {
1283
- const content = String(children != null ? children : "").trim();
1284
- if (looksLikeStructuredPayload(content)) {
1285
- return /* @__PURE__ */ import_react6.default.createElement(
1286
- UIDispatcher,
1287
- {
1288
- rawContent: content,
1289
- primaryColor,
1290
- accentColor,
1291
- isStreaming,
1292
- onAddToCart,
1293
- viewportSize
1294
- }
1295
- );
1296
- }
1297
- }
1298
- if (!inline) {
1299
- const content = String(children != null ? children : "").trim();
1300
- if (looksLikeStructuredPayload(content)) {
1301
- return /* @__PURE__ */ import_react6.default.createElement(
1302
- UIDispatcher,
1303
- {
1304
- rawContent: content,
1305
- primaryColor,
1306
- accentColor,
1307
- isStreaming,
1308
- onAddToCart,
1309
- viewportSize
1310
- }
1311
- );
1312
- }
1313
- }
1314
- if (!inline && lang === "table-loading") {
1315
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "my-5 p-6 bg-slate-50 dark:bg-white/5 rounded-xl border border-dashed border-slate-300 dark:border-white/10 flex items-center justify-center gap-3 select-none" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
1316
- }
1317
- return /* @__PURE__ */ import_react6.default.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
1318
- }
1319
- }),
1320
- [primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
1321
- );
1322
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ import_react6.default.createElement(
1323
- "div",
1324
- {
1325
- className: `flex-shrink-0 ${isCompact ? "w-7 h-7" : "w-8 h-8"} rounded-full flex items-center justify-center shadow-lg ${isUser ? "text-white" : "bg-slate-100 dark:bg-white/10 text-slate-700 dark:text-white/80"}`,
1326
- style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1327
- },
1328
- isUser ? /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
1329
- ), /* @__PURE__ */ import_react6.default.createElement("div", { className: `flex flex-col gap-1 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ import_react6.default.createElement(
1330
- "div",
1331
- {
1332
- className: `relative ${isCompact ? "px-3 py-2.5 text-[13px]" : "px-4 py-3 text-sm"} rounded-2xl leading-relaxed shadow-sm dark:shadow-lg ${isUser ? "text-white rounded-tr-sm" : "bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-800 dark:text-white/90 rounded-tl-sm"}`,
1333
- style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1334
- },
1335
- isStreaming && !message.content ? /* @__PURE__ */ import_react6.default.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ import_react6.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ import_react6.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ import_react6.default.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, !isUser && structuredContent.payload && /* @__PURE__ */ import_react6.default.createElement(
1336
- UIDispatcher,
1337
- {
1338
- rawContent: structuredContent.payload,
1339
- primaryColor,
1340
- accentColor,
1341
- isStreaming,
1342
- onAddToCart,
1343
- viewportSize
1344
- }
1345
- ), !shouldRenderStructuredOnly && /* @__PURE__ */ import_react6.default.createElement(import_react_markdown.default, { components: markdownComponents }, processedMarkdown), isStreaming && message.content && /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-block w-1.5 h-3.5 ml-1 bg-emerald-500 animate-pulse align-middle" }))
1346
- ), (() => {
1347
- var _a, _b;
1348
- if (isUser || structuredContent.payload || !message.uiTransformation) return null;
1349
- const ui = message.uiTransformation;
1350
- const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
1351
- const shouldShow = ui.type === "table" && allProducts.length === 0 || !["text", "table"].includes(ui.type) || ui.type === "text" && !hasRichUI && allProducts.length === 0 && textContent && !processedMarkdown.toLowerCase().includes(textContent.trim().toLowerCase());
1352
- if (!shouldShow) return null;
1353
- return /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ import_react6.default.createElement(
1354
- VisualizationRenderer,
1355
- {
1356
- data: ui,
1357
- primaryColor,
1358
- onAddToCart,
1359
- className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4"
1360
- }
1361
- ));
1362
- })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ import_react6.default.createElement(
1363
- ProductCarousel,
1364
- {
1365
- products: allProducts,
1366
- primaryColor,
1367
- onAddToCart
1368
- }
1369
- )), !isUser && sources && sources.length > 0 && /* @__PURE__ */ import_react6.default.createElement("div", { className: "w-full" }, /* @__PURE__ */ import_react6.default.createElement(
1370
- "button",
1371
- {
1372
- onClick: () => setShowSources((s) => !s),
1373
- className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1 mt-1"
1374
- },
1375
- showSources ? /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ import_react6.default.createElement(import_lucide_react4.ChevronRight, { className: "w-3 h-3" }),
1376
- sources.length,
1377
- " source",
1378
- sources.length !== 1 ? "s" : "",
1379
- " used"
1380
- ), showSources && /* @__PURE__ */ import_react6.default.createElement("div", { className: "mt-2 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ import_react6.default.createElement(SourceCard, { key: src.id, source: src, index: i }))))));
1381
- }
1382
-
1383
- // src/components/ConfigProvider.tsx
1384
- var import_react7 = __toESM(require("react"));
1385
-
1386
- // src/utils/templateUtils.ts
1387
- function mergeDefined(base, override) {
1388
- const merged = __spreadValues({}, base || {});
1389
- if (!override) {
1390
- return merged;
1391
- }
1392
- for (const [key, value] of Object.entries(override)) {
1393
- if (value !== void 0 && value !== null && value !== "") {
1394
- merged[key] = value;
1395
- }
1396
- }
1397
- return merged;
1398
- }
1399
-
1400
- // src/config/uiConstants.ts
1401
- var DEFAULT_CONFIG = {
1402
- projectId: "default",
1403
- ui: {
1404
- title: "AI Assistant",
1405
- subtitle: "Powered by RAG",
1406
- primaryColor: "#6366f1",
1407
- accentColor: "#8b5cf6",
1408
- logoUrl: "",
1409
- placeholder: "Ask me anything\u2026",
1410
- showSources: true,
1411
- welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
1412
- showWidget: true,
1413
- poweredBy: "RAG",
1414
- visualStyle: "glass",
1415
- borderRadius: "xl",
1416
- allowUpload: true,
1417
- allowResize: true,
1418
- enableVoiceInput: true
1419
- }
1420
- };
1421
- var BORDER_RADIUS_MAP = {
1422
- none: "rounded-none",
1423
- sm: "rounded-sm",
1424
- md: "rounded-md",
1425
- lg: "rounded-lg",
1426
- xl: "rounded-xl",
1427
- full: "rounded-3xl"
1428
- };
1429
- var CHAT_SUGGESTIONS = [
1430
- "What can you help me with?",
1431
- "Summarise the key topics",
1432
- "Show me an example"
1433
- ];
2111
+ const productsFromSources = import_react9.default.useMemo(() => {
2112
+ return extractProductsFromSources(sources, isUser);
2113
+ }, [sources, isUser]);
2114
+ const { productsFromContent, cleanContent } = import_react9.default.useMemo(() => {
2115
+ if (isUser) {
2116
+ return { productsFromContent: [], cleanContent: message.content };
2117
+ }
2118
+ const payloadCandidates = [
2119
+ structuredContent.payload,
2120
+ ...Array.from(structuredContent.text.matchAll(/```(?:json|ui)?\s*([\s\S]*?)\s*```/g)).map((m) => m[1])
2121
+ ].filter(Boolean);
2122
+ return extractProductsFromContent(
2123
+ message.content,
2124
+ structuredContent.text,
2125
+ payloadCandidates,
2126
+ productsFromSources,
2127
+ isStreaming
2128
+ );
2129
+ }, [message.content, isUser, structuredContent, productsFromSources, isStreaming]);
2130
+ const allProducts = import_react9.default.useMemo(() => {
2131
+ return deduplicateProducts(productsFromSources, productsFromContent, message.content);
2132
+ }, [productsFromSources, productsFromContent, message.content]);
2133
+ const processedMarkdown = import_react9.default.useMemo(() => {
2134
+ let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
2135
+ const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
2136
+ raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
2137
+ if (!isStreaming) {
2138
+ raw = raw.replace(/(?:^|\n)\s*\{[\s\S]*\}\s*(?:\n|$)/g, (match) => {
2139
+ if (match.includes("```")) return match;
2140
+ if (!looksLikeStructuredPayload(match)) return match;
2141
+ const type = match.includes('"view"') || match.includes('"chartType"') || match.includes('"type": "pie"') || match.includes('"type":"pie"') || match.includes('"type": "bar"') || match.includes('"type":"bar"') || match.includes('"type": "line"') || match.includes('"type":"line"') ? "ui" : "json";
2142
+ return `
1434
2143
 
1435
- // src/components/ConfigProvider.tsx
1436
- var ConfigContext = (0, import_react7.createContext)(DEFAULT_CONFIG);
1437
- function ConfigProvider({
1438
- config,
1439
- children
1440
- }) {
1441
- const merged = {
1442
- projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
1443
- ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
1444
- };
1445
- return /* @__PURE__ */ import_react7.default.createElement(ConfigContext.Provider, { value: merged }, children);
1446
- }
1447
- function useConfig() {
1448
- return (0, import_react7.useContext)(ConfigContext);
2144
+ \`\`\`${type}
2145
+ ${match.trim()}
2146
+ \`\`\`
2147
+
2148
+ `;
2149
+ });
2150
+ }
2151
+ if (hasStructuredUiBlock) {
2152
+ raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
2153
+ }
2154
+ return raw;
2155
+ }, [cleanContent, message.content, isStreaming, structuredContent]);
2156
+ const markdownComponents = import_react9.default.useMemo(
2157
+ () => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
2158
+ [primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
2159
+ );
2160
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start`, children: [
2161
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2162
+ "div",
2163
+ {
2164
+ 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"}`,
2165
+ style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {},
2166
+ children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
2167
+ }
2168
+ ),
2169
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("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"}`, children: [
2170
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2171
+ "div",
2172
+ {
2173
+ 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"}`,
2174
+ style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {},
2175
+ children: isStreaming && !message.content ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "flex items-center gap-1 py-1", children: [
2176
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }),
2177
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }),
2178
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })
2179
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}`, children: [
2180
+ !isUser && structuredContent.payload && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2181
+ UIDispatcher,
2182
+ {
2183
+ rawContent: structuredContent.payload,
2184
+ primaryColor,
2185
+ accentColor,
2186
+ isStreaming,
2187
+ onAddToCart,
2188
+ viewportSize
2189
+ }
2190
+ ),
2191
+ !shouldRenderStructuredOnly && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_markdown.default, { components: markdownComponents, children: processedMarkdown }),
2192
+ isStreaming && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "inline-flex items-center gap-1.5 ml-2 text-emerald-500 animate-pulse align-middle text-xs font-medium", children: [
2193
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2194
+ HourglassLoader_default,
2195
+ {
2196
+ size: 18,
2197
+ primaryColor,
2198
+ accentColor,
2199
+ glow: false
2200
+ }
2201
+ ),
2202
+ "Thinking..."
2203
+ ] })
2204
+ ] })
2205
+ }
2206
+ ),
2207
+ (() => {
2208
+ var _a, _b;
2209
+ if (isUser || structuredContent.payload || !message.uiTransformation) return null;
2210
+ const ui = message.uiTransformation;
2211
+ const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
2212
+ 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());
2213
+ if (!shouldShow) return null;
2214
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2215
+ VisualizationRenderer,
2216
+ {
2217
+ data: ui,
2218
+ primaryColor,
2219
+ onAddToCart,
2220
+ className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4",
2221
+ isStreaming
2222
+ }
2223
+ ) });
2224
+ })(),
2225
+ !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full mt-1", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2226
+ ProductCarousel,
2227
+ {
2228
+ products: allProducts,
2229
+ primaryColor,
2230
+ onAddToCart
2231
+ }
2232
+ ) }),
2233
+ !isUser && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-4 mt-1 w-full flex-wrap", children: [
2234
+ sources && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2235
+ "button",
2236
+ {
2237
+ onClick: () => setShowSources((s) => !s),
2238
+ className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1",
2239
+ children: [
2240
+ showSources ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.ChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.ChevronRight, { className: "w-3 h-3" }),
2241
+ sources.length,
2242
+ " source",
2243
+ sources.length !== 1 ? "s" : "",
2244
+ " used"
2245
+ ]
2246
+ }
2247
+ ),
2248
+ message.trace && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2249
+ "button",
2250
+ {
2251
+ onClick: () => setShowTrace((t) => !t),
2252
+ className: "text-[11px] text-emerald-500 hover:text-emerald-400 transition-colors flex items-center gap-1",
2253
+ children: [
2254
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react6.Activity, { className: "w-3 h-3" }),
2255
+ showTrace ? "Hide Trace" : "Trace"
2256
+ ]
2257
+ }
2258
+ )
2259
+ ] }),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "w-full flex flex-col gap-2 min-w-0", children: [
2261
+ showSources && sources && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 flex flex-col gap-2", children: sources.map((src, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SourceCard, { source: src, index: i }, src.id)) }),
2262
+ showTrace && message.trace && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ObservabilityPanel, { trace: message.trace, primaryColor })
2263
+ ] })
2264
+ ] })
2265
+ ] });
1449
2266
  }
1450
2267
 
1451
2268
  // src/hooks/useRagChat.ts
1452
- var import_react8 = require("react");
2269
+ var import_react10 = require("react");
1453
2270
 
1454
2271
  // src/hooks/useStoredMessages.ts
1455
- var React8 = __toESM(require("react"));
2272
+ var React10 = __toESM(require("react"));
1456
2273
  function readStoredMessages(storageKey) {
1457
2274
  if (typeof window === "undefined") {
1458
2275
  return [];
@@ -1469,8 +2286,8 @@ function readStoredMessages(storageKey) {
1469
2286
  }
1470
2287
  }
1471
2288
  function useStoredMessages(storageKey, persist) {
1472
- const [messages, setMessages] = React8.useState(() => persist ? readStoredMessages(storageKey) : []);
1473
- React8.useEffect(() => {
2289
+ const [messages, setMessages] = React10.useState(() => persist ? readStoredMessages(storageKey) : []);
2290
+ React10.useEffect(() => {
1474
2291
  if (!persist || typeof window === "undefined") {
1475
2292
  return;
1476
2293
  }
@@ -1510,14 +2327,14 @@ function useRagChat(projectId, options = {}) {
1510
2327
  } = options;
1511
2328
  const storageKey = `rag_chat_${projectId}`;
1512
2329
  const [messages, setMessages] = useStoredMessages(storageKey, persist);
1513
- const [isLoading, setIsLoading] = (0, import_react8.useState)(false);
1514
- const [error, setError] = (0, import_react8.useState)(null);
1515
- const lastInputRef = (0, import_react8.useRef)("");
1516
- const messagesRef = (0, import_react8.useRef)(messages);
1517
- (0, import_react8.useEffect)(() => {
2330
+ const [isLoading, setIsLoading] = (0, import_react10.useState)(false);
2331
+ const [error, setError] = (0, import_react10.useState)(null);
2332
+ const lastInputRef = (0, import_react10.useRef)("");
2333
+ const messagesRef = (0, import_react10.useRef)(messages);
2334
+ (0, import_react10.useEffect)(() => {
1518
2335
  messagesRef.current = messages;
1519
2336
  }, [messages]);
1520
- const sendMessage = (0, import_react8.useCallback)(
2337
+ const sendMessage = (0, import_react10.useCallback)(
1521
2338
  async (text, opts) => {
1522
2339
  var _a, _b;
1523
2340
  const trimmed = text.trim();
@@ -1557,6 +2374,7 @@ function useRagChat(projectId, options = {}) {
1557
2374
  let assistantContent = "";
1558
2375
  let sources = [];
1559
2376
  let uiTransformation = null;
2377
+ let trace;
1560
2378
  let buffer = "";
1561
2379
  const assistantMessageId = `assistant_${Date.now()}`;
1562
2380
  setMessages((prev) => [
@@ -1583,6 +2401,8 @@ function useRagChat(projectId, options = {}) {
1583
2401
  sources = (_a = frame.sources) != null ? _a : [];
1584
2402
  } else if (frame.type === "ui_transformation") {
1585
2403
  uiTransformation = frame.data;
2404
+ } else if (frame.type === "observability") {
2405
+ trace = frame.data;
1586
2406
  } else if (frame.type === "error") {
1587
2407
  setError(frame.error || "Stream error");
1588
2408
  }
@@ -1601,17 +2421,22 @@ function useRagChat(projectId, options = {}) {
1601
2421
  for (const frame of parseSseChunk(buffer)) {
1602
2422
  if (frame.type === "text" && frame.text) assistantContent += frame.text;
1603
2423
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
2424
+ else if (frame.type === "observability") trace = frame.data;
1604
2425
  }
1605
2426
  }
1606
- const finalAssistantMessage = {
2427
+ const finalMsg = {
1607
2428
  id: assistantMessageId,
1608
2429
  role: "assistant",
1609
2430
  content: assistantContent,
1610
2431
  sources: sources.length > 0 ? sources : void 0,
1611
- uiTransformation: uiTransformation || void 0,
2432
+ uiTransformation: uiTransformation != null ? uiTransformation : void 0,
2433
+ trace,
1612
2434
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1613
2435
  };
1614
- onReply == null ? void 0 : onReply(finalAssistantMessage);
2436
+ setMessages(
2437
+ (prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMsg : msg)
2438
+ );
2439
+ onReply == null ? void 0 : onReply(finalMsg);
1615
2440
  } catch (err) {
1616
2441
  const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
1617
2442
  setError(msg);
@@ -1622,12 +2447,12 @@ function useRagChat(projectId, options = {}) {
1622
2447
  },
1623
2448
  [apiUrl, isLoading, namespace, onError, onReply, projectId, setMessages]
1624
2449
  );
1625
- const clear = (0, import_react8.useCallback)(() => {
2450
+ const clear = (0, import_react10.useCallback)(() => {
1626
2451
  setMessages([]);
1627
2452
  setError(null);
1628
2453
  if (persist) localStorage.removeItem(storageKey);
1629
2454
  }, [persist, setMessages, storageKey]);
1630
- const retry = (0, import_react8.useCallback)(async () => {
2455
+ const retry = (0, import_react10.useCallback)(async () => {
1631
2456
  if (lastInputRef.current) {
1632
2457
  const latestMessage = messagesRef.current[messagesRef.current.length - 1];
1633
2458
  await sendMessage(lastInputRef.current, {
@@ -1647,6 +2472,7 @@ function useRagChat(projectId, options = {}) {
1647
2472
  }
1648
2473
 
1649
2474
  // src/components/ChatWindow.tsx
2475
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1650
2476
  function ChatWindow({
1651
2477
  className = "",
1652
2478
  style,
@@ -1661,20 +2487,21 @@ function ChatWindow({
1661
2487
  }) {
1662
2488
  var _a;
1663
2489
  const { ui, projectId } = useConfig();
1664
- const [input, setInput] = (0, import_react9.useState)("");
1665
- const [mounted, setMounted] = (0, import_react9.useState)(false);
1666
- const [viewportSize, setViewportSize] = (0, import_react9.useState)("large");
1667
- const windowRef = (0, import_react9.useRef)(null);
1668
- const bottomRef = (0, import_react9.useRef)(null);
1669
- const inputRef = (0, import_react9.useRef)(null);
2490
+ const [input, setInput] = (0, import_react11.useState)("");
2491
+ const [mounted, setMounted] = (0, import_react11.useState)(false);
2492
+ const [viewportSize, setViewportSize] = (0, import_react11.useState)("large");
2493
+ const windowRef = (0, import_react11.useRef)(null);
2494
+ const bottomRef = (0, import_react11.useRef)(null);
2495
+ const inputRef = (0, import_react11.useRef)(null);
1670
2496
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
1671
2497
  namespace: projectId
1672
2498
  });
1673
- const [suggestions, setSuggestions] = (0, import_react9.useState)([]);
1674
- const [isSuggesting, setIsSuggesting] = (0, import_react9.useState)(false);
1675
- const [isListening, setIsListening] = (0, import_react9.useState)(false);
1676
- const recognitionRef = (0, import_react9.useRef)(null);
1677
- (0, import_react9.useEffect)(() => {
2499
+ const [suggestions, setSuggestions] = (0, import_react11.useState)([]);
2500
+ const [isSuggesting, setIsSuggesting] = (0, import_react11.useState)(false);
2501
+ const [isListening, setIsListening] = (0, import_react11.useState)(false);
2502
+ const [isUploadModalOpen, setIsUploadModalOpen] = (0, import_react11.useState)(false);
2503
+ const recognitionRef = (0, import_react11.useRef)(null);
2504
+ (0, import_react11.useEffect)(() => {
1678
2505
  if (typeof window !== "undefined") {
1679
2506
  const win = window;
1680
2507
  const SpeechRecognition = win.SpeechRecognition || win.webkitSpeechRecognition;
@@ -1720,10 +2547,10 @@ function ChatWindow({
1720
2547
  }
1721
2548
  }
1722
2549
  };
1723
- (0, import_react9.useEffect)(() => {
2550
+ (0, import_react11.useEffect)(() => {
1724
2551
  setMounted(true);
1725
2552
  }, []);
1726
- (0, import_react9.useEffect)(() => {
2553
+ (0, import_react11.useEffect)(() => {
1727
2554
  const element = windowRef.current;
1728
2555
  if (!element || typeof ResizeObserver === "undefined") return;
1729
2556
  const resolveViewportSize = (width) => {
@@ -1739,13 +2566,13 @@ function ChatWindow({
1739
2566
  observer.observe(element);
1740
2567
  return () => observer.disconnect();
1741
2568
  }, []);
1742
- (0, import_react9.useEffect)(() => {
2569
+ (0, import_react11.useEffect)(() => {
1743
2570
  var _a2;
1744
2571
  if (messages.length > 0 || isLoading) {
1745
2572
  (_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
1746
2573
  }
1747
2574
  }, [messages, isLoading]);
1748
- const sendMessage = (0, import_react9.useCallback)(async () => {
2575
+ const sendMessage = (0, import_react11.useCallback)(async () => {
1749
2576
  var _a2;
1750
2577
  const text = input.trim();
1751
2578
  if (!text || isLoading) return;
@@ -1775,7 +2602,7 @@ function ChatWindow({
1775
2602
  const isEmpty = messages.length === 0;
1776
2603
  const currentRadius = BORDER_RADIUS_MAP[ui.borderRadius || "xl"];
1777
2604
  const isGlass = ui.visualStyle !== "solid";
1778
- (0, import_react9.useEffect)(() => {
2605
+ (0, import_react11.useEffect)(() => {
1779
2606
  if (input.trim().length < 3) {
1780
2607
  return;
1781
2608
  }
@@ -1799,194 +2626,273 @@ function ChatWindow({
1799
2626
  }, 800);
1800
2627
  return () => clearTimeout(timer);
1801
2628
  }, [input, projectId]);
1802
- return /* @__PURE__ */ import_react9.default.createElement(
2629
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1803
2630
  "div",
1804
2631
  {
1805
2632
  ref: windowRef,
1806
2633
  className: `relative flex flex-col border border-slate-200 dark:border-white/10 shadow-2xl transition-all duration-300 ${currentRadius} ${isGlass ? "bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl" : "bg-white dark:bg-[#0f0f1a]"} ${className}`,
1807
- style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
1808
- },
1809
- onResizeStart && /* @__PURE__ */ import_react9.default.createElement(
1810
- "div",
1811
- {
1812
- onMouseDown: onResizeStart,
1813
- className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
1814
- title: "Drag to resize"
1815
- },
1816
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "w-2.5 h-2.5 border-t-2 border-l-2 border-slate-300 dark:border-white/20 group-hover:border-slate-500 dark:group-hover:border-white/50 transition-colors rounded-tl-[2px]" })
1817
- ),
1818
- /* @__PURE__ */ import_react9.default.createElement(
1819
- "div",
1820
- {
1821
- className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
1822
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
1823
- },
1824
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
1825
- // eslint-disable-next-line @next/next/no-img-element
1826
- /* @__PURE__ */ import_react9.default.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
1827
- ) : /* @__PURE__ */ import_react9.default.createElement(
1828
- "div",
1829
- {
1830
- className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
1831
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1832
- },
1833
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Bot, { className: "w-5 h-5 text-white" })
1834
- ), /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight" }, ui.title), ui.poweredBy && /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight" }, `Powered by ${ui.poweredBy}`))),
1835
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }), "Online"), /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center bg-slate-100/50 dark:bg-white/5 rounded-lg p-0.5 border border-slate-200/50 dark:border-white/5" }, mounted && messages.length > 0 && /* @__PURE__ */ import_react9.default.createElement(
1836
- "button",
1837
- {
1838
- onClick: clearHistory,
1839
- title: "Clear conversation",
1840
- className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-rose-500 dark:text-white/40 dark:hover:text-rose-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
1841
- },
1842
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Trash2, { className: "w-3.5 h-3.5" })
1843
- ), isResized && onResetResize && /* @__PURE__ */ import_react9.default.createElement(
1844
- "button",
1845
- {
1846
- onClick: onResetResize,
1847
- title: "Reset to default size",
1848
- className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-blue-500 dark:text-white/40 dark:hover:text-blue-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
1849
- },
1850
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.RotateCcw, { className: "w-3.5 h-3.5" })
1851
- ), onMaximize && /* @__PURE__ */ import_react9.default.createElement(
1852
- "button",
1853
- {
1854
- onClick: onMaximize,
1855
- title: isMaximized ? "Minimize" : "Maximize",
1856
- className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-blue-500 dark:text-white/40 dark:hover:text-blue-400 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
1857
- },
1858
- isMaximized ? /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Maximize2, { className: "w-3.5 h-3.5" })
1859
- ), showClose && onClose && /* @__PURE__ */ import_react9.default.createElement(
1860
- "button",
1861
- {
1862
- onClick: onClose,
1863
- title: "Close chat",
1864
- className: "w-7 h-7 rounded-md flex items-center justify-center text-slate-400 hover:text-slate-600 dark:text-white/40 dark:hover:text-white/70 hover:bg-white dark:hover:bg-white/10 transition-all cursor-pointer"
1865
- },
1866
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.X, { className: "w-4 h-4" })
1867
- )))
1868
- ),
1869
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex-1 overflow-y-auto px-4 py-4 space-y-5 scrollbar-thin scrollbar-thumb-slate-200 dark:scrollbar-thumb-white/10 scrollbar-track-transparent min-h-0 bg-slate-50 dark:bg-transparent" }, !mounted || isEmpty ? (
1870
- /* Welcome state */
1871
- /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12" }, /* @__PURE__ */ import_react9.default.createElement(
1872
- "div",
1873
- {
1874
- className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
1875
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1876
- },
1877
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-8 h-8 text-white" })
1878
- ), /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed" }, ui.welcomeMessage), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2" }, "Ask a question to get started")), /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex flex-wrap gap-2 justify-center mt-2" }, CHAT_SUGGESTIONS.map(
1879
- (suggestion) => /* @__PURE__ */ import_react9.default.createElement(
1880
- "button",
2634
+ style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style),
2635
+ children: [
2636
+ onResizeStart && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2637
+ "div",
1881
2638
  {
1882
- key: suggestion,
1883
- onClick: () => {
1884
- var _a2;
1885
- setInput(suggestion);
1886
- (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1887
- },
1888
- className: "px-3 py-1.5 rounded-full text-xs border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/50 hover:text-slate-900 dark:hover:text-white/80 hover:border-slate-300 dark:hover:border-white/20 hover:bg-slate-100 dark:hover:bg-white/5 transition-all"
1889
- },
1890
- suggestion
1891
- )
1892
- )))
1893
- ) : messages.map((msg, index) => /* @__PURE__ */ import_react9.default.createElement(
1894
- MessageBubble,
1895
- {
1896
- key: msg.id,
1897
- message: msg,
1898
- sources: msg.sources,
1899
- isStreaming: isLoading && index === messages.length - 1 && msg.role === "assistant",
1900
- primaryColor: ui.primaryColor,
1901
- accentColor: ui.accentColor,
1902
- onAddToCart,
1903
- viewportSize
1904
- }
1905
- )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ import_react9.default.createElement(
1906
- MessageBubble,
1907
- {
1908
- message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
1909
- isStreaming: true,
1910
- primaryColor: ui.primaryColor,
1911
- accentColor: ui.accentColor,
1912
- onAddToCart,
1913
- viewportSize
1914
- }
1915
- ), error && /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 text-rose-500 dark:text-rose-400 text-sm bg-rose-50 dark:bg-rose-400/10 border border-rose-200 dark:border-rose-400/20 rounded-xl px-4 py-3" }, /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ import_react9.default.createElement("span", null, error)), /* @__PURE__ */ import_react9.default.createElement("div", { ref: bottomRef })),
1916
- /* @__PURE__ */ import_react9.default.createElement("div", { className: `px-4 pb-4 pt-2 border-t border-slate-200 dark:border-white/10 ${isGlass ? "bg-transparent" : "bg-white dark:bg-[#0f0f1a]"}` }, (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ import_react9.default.createElement("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300" }, suggestions.map((suggestion) => /* @__PURE__ */ import_react9.default.createElement(
1917
- "button",
1918
- {
1919
- key: suggestion,
1920
- onClick: () => {
1921
- var _a2;
1922
- setInput(suggestion);
1923
- setSuggestions([]);
1924
- (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
1925
- },
1926
- className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium bg-white dark:bg-white/5 border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/60 hover:text-slate-900 dark:hover:text-white/90 hover:border-slate-300 dark:hover:border-white/20 transition-all shadow-sm cursor-pointer group"
1927
- },
1928
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3 text-amber-400" }),
1929
- suggestion
1930
- )), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-1.5 px-3 py-1.5 rounded-xl text-[11px] font-medium text-slate-400 dark:text-white/30 animate-pulse" }, /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ import_react9.default.createElement("div", { className: `flex items-end gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 p-2 focus-within:border-slate-300 dark:focus-within:border-white/20 transition-all ${ui.borderRadius === "full" ? "rounded-3xl" : "rounded-2xl"}` }, /* @__PURE__ */ import_react9.default.createElement(
1931
- "textarea",
1932
- {
1933
- ref: inputRef,
1934
- value: input,
1935
- onChange: (e) => {
1936
- const val = e.target.value;
1937
- setInput(val);
1938
- if (val.trim().length < 3) {
1939
- setSuggestions([]);
2639
+ onMouseDown: onResizeStart,
2640
+ className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
2641
+ title: "Drag to resize",
2642
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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]" })
1940
2643
  }
1941
- },
1942
- onKeyDown: handleKeyDown,
1943
- placeholder: ui.placeholder,
1944
- rows: 1,
1945
- disabled: isLoading,
1946
- className: "flex-1 bg-transparent text-slate-900 dark:text-white/90 placeholder-slate-400 dark:placeholder-white/30 text-sm resize-none outline-none py-1.5 px-2 max-h-32 leading-relaxed disabled:opacity-50",
1947
- style: { scrollbarWidth: "none" }
1948
- }
1949
- ), ui.enableVoiceInput !== false && /* @__PURE__ */ import_react9.default.createElement(
1950
- "button",
1951
- {
1952
- type: "button",
1953
- onClick: toggleListening,
1954
- disabled: isLoading,
1955
- className: `flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all hover:scale-105 active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:active:scale-100 ${isListening ? "bg-rose-500 text-white animate-pulse shadow-md shadow-rose-500/20" : "text-slate-400 dark:text-white/40 hover:bg-slate-200 dark:hover:bg-white/10"}`,
1956
- title: isListening ? "Stop listening" : "Start voice input"
1957
- },
1958
- isListening ? /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.Mic, { className: "w-4 h-4" })
1959
- ), /* @__PURE__ */ import_react9.default.createElement(
1960
- "button",
1961
- {
1962
- onClick: sendMessage,
1963
- disabled: !input.trim() || isLoading,
1964
- className: "flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all disabled:opacity-30 disabled:cursor-not-allowed hover:scale-105 active:scale-95 shadow-md",
1965
- style: {
1966
- background: input.trim() && !isLoading ? `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` : "rgba(148, 163, 184, 0.2)"
1967
- // slate-400/20 for light mode disabled
1968
- }
1969
- },
1970
- /* @__PURE__ */ import_react9.default.createElement(import_lucide_react5.ArrowUp, { className: "w-4 h-4 text-white" })
1971
- )), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-center text-[10px] text-slate-400 dark:text-white/20 mt-2" }, "Press Enter to send \xB7 Shift+Enter for new line"))
2644
+ ),
2645
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2646
+ "div",
2647
+ {
2648
+ className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
2649
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` },
2650
+ children: [
2651
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-3", children: [
2652
+ ui.logoUrl ? (
2653
+ // eslint-disable-next-line @next/next/no-img-element
2654
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
2655
+ ) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2656
+ "div",
2657
+ {
2658
+ className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
2659
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2660
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Bot, { className: "w-5 h-5 text-white" })
2661
+ }
2662
+ ),
2663
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2664
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight", children: ui.title }),
2665
+ ui.poweredBy && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight", children: `Powered by ${ui.poweredBy}` })
2666
+ ] })
2667
+ ] }),
2668
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2669
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2", children: [
2670
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }),
2671
+ "Online"
2672
+ ] }),
2673
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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", children: [
2674
+ mounted && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2675
+ "button",
2676
+ {
2677
+ onClick: clearHistory,
2678
+ title: "Clear conversation",
2679
+ 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",
2680
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Trash2, { className: "w-3.5 h-3.5" })
2681
+ }
2682
+ ),
2683
+ isResized && onResetResize && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2684
+ "button",
2685
+ {
2686
+ onClick: onResetResize,
2687
+ title: "Reset to default size",
2688
+ 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",
2689
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.RotateCcw, { className: "w-3.5 h-3.5" })
2690
+ }
2691
+ ),
2692
+ onMaximize && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2693
+ "button",
2694
+ {
2695
+ onClick: onMaximize,
2696
+ title: isMaximized ? "Minimize" : "Maximize",
2697
+ 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",
2698
+ children: isMaximized ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Maximize2, { className: "w-3.5 h-3.5" })
2699
+ }
2700
+ ),
2701
+ showClose && onClose && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2702
+ "button",
2703
+ {
2704
+ onClick: onClose,
2705
+ title: "Close chat",
2706
+ 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",
2707
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.X, { className: "w-4 h-4" })
2708
+ }
2709
+ )
2710
+ ] })
2711
+ ] })
2712
+ ]
2713
+ }
2714
+ ),
2715
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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", children: [
2716
+ !mounted || isEmpty ? (
2717
+ /* Welcome state */
2718
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12", children: [
2719
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2720
+ "div",
2721
+ {
2722
+ className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
2723
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2724
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Sparkles, { className: "w-8 h-8 text-white" })
2725
+ }
2726
+ ),
2727
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed", children: ui.welcomeMessage }),
2729
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2", children: "Ask a question to get started" })
2730
+ ] }),
2731
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-wrap gap-2 justify-center mt-2", children: CHAT_SUGGESTIONS.map(
2732
+ (suggestion) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2733
+ "button",
2734
+ {
2735
+ onClick: () => {
2736
+ var _a2;
2737
+ setInput(suggestion);
2738
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
2739
+ },
2740
+ className: "px-3 py-1.5 rounded-full text-xs border border-slate-200 dark:border-white/10 text-slate-600 dark:text-white/50 hover:text-slate-900 dark:hover:text-white/80 hover:border-slate-300 dark:hover:border-white/20 hover:bg-slate-100 dark:hover:bg-white/5 transition-all",
2741
+ children: suggestion
2742
+ },
2743
+ suggestion
2744
+ )
2745
+ ) })
2746
+ ] })
2747
+ ) : messages.map((msg, index) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2748
+ MessageBubble,
2749
+ {
2750
+ message: msg,
2751
+ sources: msg.sources,
2752
+ isStreaming: isLoading && index === messages.length - 1 && msg.role === "assistant",
2753
+ primaryColor: ui.primaryColor,
2754
+ accentColor: ui.accentColor,
2755
+ onAddToCart,
2756
+ viewportSize
2757
+ },
2758
+ msg.id
2759
+ )),
2760
+ isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2761
+ MessageBubble,
2762
+ {
2763
+ message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
2764
+ isStreaming: true,
2765
+ primaryColor: ui.primaryColor,
2766
+ accentColor: ui.accentColor,
2767
+ onAddToCart,
2768
+ viewportSize
2769
+ }
2770
+ ),
2771
+ error && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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", children: [
2772
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }),
2773
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: error })
2774
+ ] }),
2775
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: bottomRef })
2776
+ ] }),
2777
+ isUploadModalOpen && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm rounded-inherit", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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", children: [
2778
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2779
+ "button",
2780
+ {
2781
+ onClick: () => setIsUploadModalOpen(false),
2782
+ 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",
2783
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.X, { className: "w-5 h-5" })
2784
+ }
2785
+ ),
2786
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2787
+ DocumentUpload,
2788
+ {
2789
+ namespace: projectId,
2790
+ onUploadComplete: () => setIsUploadModalOpen(false)
2791
+ }
2792
+ )
2793
+ ] }) }),
2794
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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]"}`, children: [
2795
+ (suggestions.length > 0 || isSuggesting) && !isLoading && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mb-3 flex flex-wrap gap-2 animate-in fade-in slide-in-from-bottom-2 duration-300", children: [
2796
+ suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2797
+ "button",
2798
+ {
2799
+ onClick: () => {
2800
+ var _a2;
2801
+ setInput(suggestion);
2802
+ setSuggestions([]);
2803
+ (_a2 = inputRef.current) == null ? void 0 : _a2.focus();
2804
+ },
2805
+ 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",
2806
+ children: [
2807
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Sparkles, { className: "w-3 h-3 text-amber-400" }),
2808
+ suggestion
2809
+ ]
2810
+ },
2811
+ suggestion
2812
+ )),
2813
+ isSuggesting && suggestions.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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", children: [
2814
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Sparkles, { className: "w-3 h-3" }),
2815
+ "Thinking..."
2816
+ ] })
2817
+ ] }),
2818
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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"}`, children: [
2819
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2820
+ "textarea",
2821
+ {
2822
+ ref: inputRef,
2823
+ value: input,
2824
+ onChange: (e) => {
2825
+ const val = e.target.value;
2826
+ setInput(val);
2827
+ if (val.trim().length < 3) {
2828
+ setSuggestions([]);
2829
+ }
2830
+ },
2831
+ onKeyDown: handleKeyDown,
2832
+ placeholder: ui.placeholder,
2833
+ rows: 1,
2834
+ disabled: isLoading,
2835
+ 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",
2836
+ style: { scrollbarWidth: "none" }
2837
+ }
2838
+ ),
2839
+ ui.enableVoiceInput !== false && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2840
+ "button",
2841
+ {
2842
+ type: "button",
2843
+ onClick: toggleListening,
2844
+ disabled: isLoading,
2845
+ 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"}`,
2846
+ title: isListening ? "Stop listening" : "Start voice input",
2847
+ children: isListening ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Mic, { className: "w-4 h-4" })
2848
+ }
2849
+ ),
2850
+ ui.allowUpload !== false && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2851
+ "button",
2852
+ {
2853
+ type: "button",
2854
+ onClick: () => setIsUploadModalOpen(true),
2855
+ 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",
2856
+ title: "Upload Document",
2857
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.Paperclip, { className: "w-4 h-4" })
2858
+ }
2859
+ ),
2860
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2861
+ "button",
2862
+ {
2863
+ onClick: sendMessage,
2864
+ disabled: !input.trim() || isLoading,
2865
+ className: "flex-shrink-0 w-9 h-9 rounded-xl flex items-center justify-center transition-all disabled:opacity-30 disabled:cursor-not-allowed hover:scale-105 active:scale-95 shadow-md",
2866
+ style: {
2867
+ background: input.trim() && !isLoading ? `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` : "rgba(148, 163, 184, 0.2)"
2868
+ // slate-400/20 for light mode disabled
2869
+ },
2870
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react7.ArrowUp, { className: "w-4 h-4 text-white" })
2871
+ }
2872
+ )
2873
+ ] }),
2874
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-center text-[10px] text-slate-400 dark:text-white/20 mt-2", children: "Press Enter to send \xB7 Shift+Enter for new line" })
2875
+ ] })
2876
+ ]
2877
+ }
1972
2878
  );
1973
2879
  }
1974
2880
 
1975
2881
  // src/components/ChatWidget.tsx
2882
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1976
2883
  var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
1977
2884
  function ChatWidget({ position = "bottom-right", onAddToCart }) {
1978
2885
  const { ui } = useConfig();
1979
- const [isOpen, setIsOpen] = (0, import_react10.useState)(false);
1980
- const [hasUnread, setHasUnread] = (0, import_react10.useState)(false);
1981
- const [dimensions, setDimensions] = (0, import_react10.useState)(DEFAULT_DIMENSIONS);
1982
- const [isResizing, setIsResizing] = (0, import_react10.useState)(false);
1983
- const [isMaximized, setIsMaximized] = (0, import_react10.useState)(false);
1984
- const [prevDimensions, setPrevDimensions] = (0, import_react10.useState)(DEFAULT_DIMENSIONS);
2886
+ const [isOpen, setIsOpen] = (0, import_react12.useState)(false);
2887
+ const [hasUnread, setHasUnread] = (0, import_react12.useState)(false);
2888
+ const [dimensions, setDimensions] = (0, import_react12.useState)(DEFAULT_DIMENSIONS);
2889
+ const [isResizing, setIsResizing] = (0, import_react12.useState)(false);
2890
+ const [isMaximized, setIsMaximized] = (0, import_react12.useState)(false);
2891
+ const [prevDimensions, setPrevDimensions] = (0, import_react12.useState)(DEFAULT_DIMENSIONS);
1985
2892
  if (ui.showWidget === false) return null;
1986
2893
  const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
1987
2894
  const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
1988
2895
  const handleOpen = () => {
1989
- console.log("ChatWidget: Opening...");
1990
2896
  setIsOpen(true);
1991
2897
  setHasUnread(false);
1992
2898
  };
@@ -2031,186 +2937,67 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2031
2937
  }
2032
2938
  };
2033
2939
  const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
2034
- return /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(
2035
- "div",
2036
- {
2037
- 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"}`,
2038
- style: {
2039
- width: `${dimensions.width}px`,
2040
- height: `${dimensions.height}px`,
2041
- maxHeight: "calc(100vh - 6rem)"
2042
- }
2043
- },
2044
- /* @__PURE__ */ import_react10.default.createElement(
2045
- ChatWindow,
2046
- {
2047
- className: "h-full relative z-10",
2048
- showClose: true,
2049
- onClose: () => setIsOpen(false),
2050
- onResizeStart: ui.allowResize !== false ? handleResizeStart : void 0,
2051
- onResetResize: ui.allowResize !== false ? handleResetResize : void 0,
2052
- isResized,
2053
- onMaximize: ui.allowResize !== false ? handleMaximize : void 0,
2054
- isMaximized,
2055
- onAddToCart
2056
- }
2057
- ),
2058
- /* @__PURE__ */ import_react10.default.createElement(
2940
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2941
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2059
2942
  "div",
2060
2943
  {
2061
- className: `absolute -bottom-1.5 w-4 h-4 rotate-45 border-r border-b border-slate-200 dark:border-white/10 z-0 ${ui.visualStyle === "solid" ? "bg-white dark:bg-[#0f0f1a]" : "bg-white/90 dark:bg-[#0f0f1a]/90 backdrop-blur-xl"} ${position === "bottom-left" ? "left-5" : "right-5"}`
2062
- }
2063
- )
2064
- ), /* @__PURE__ */ import_react10.default.createElement(
2065
- "button",
2066
- {
2067
- onClick: isOpen ? () => setIsOpen(false) : handleOpen,
2068
- className: `fixed z-[9999] w-14 h-14 shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} ${positionClass}`,
2069
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2070
- "aria-label": "Open chat"
2071
- },
2072
- /* @__PURE__ */ import_react10.default.createElement(
2073
- "span",
2074
- {
2075
- className: "absolute inset-0 rounded-full animate-ping opacity-20",
2076
- style: { background: ui.primaryColor }
2944
+ 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"}`,
2945
+ style: {
2946
+ width: `${dimensions.width}px`,
2947
+ height: `${dimensions.height}px`,
2948
+ maxHeight: "calc(100vh - 6rem)"
2949
+ },
2950
+ children: [
2951
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2952
+ ChatWindow,
2953
+ {
2954
+ className: "h-full relative z-10",
2955
+ showClose: true,
2956
+ onClose: () => setIsOpen(false),
2957
+ onResizeStart: ui.allowResize !== false ? handleResizeStart : void 0,
2958
+ onResetResize: ui.allowResize !== false ? handleResetResize : void 0,
2959
+ isResized,
2960
+ onMaximize: ui.allowResize !== false ? handleMaximize : void 0,
2961
+ isMaximized,
2962
+ onAddToCart
2963
+ }
2964
+ ),
2965
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2966
+ "div",
2967
+ {
2968
+ 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"}`
2969
+ }
2970
+ )
2971
+ ]
2077
2972
  }
2078
2973
  ),
2079
- /* @__PURE__ */ import_react10.default.createElement(
2080
- "span",
2081
- {
2082
- className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
2083
- },
2084
- isOpen ? /* @__PURE__ */ import_react10.default.createElement(import_lucide_react6.X, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ import_react10.default.createElement(import_lucide_react6.MessageSquare, { className: "w-6 h-6 text-white" })
2085
- ),
2086
- hasUnread && !isOpen && /* @__PURE__ */ import_react10.default.createElement("span", { className: "absolute top-0 right-0 w-4 h-4 bg-rose-500 rounded-full border-2 border-white flex items-center justify-center text-[9px] font-bold text-white" }, "1")
2087
- ));
2088
- }
2089
-
2090
- // src/components/DocumentUpload.tsx
2091
- var import_react11 = __toESM(require("react"));
2092
- var import_lucide_react7 = require("lucide-react");
2093
- function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
2094
- const { ui } = useConfig();
2095
- const [fileStates, setFileStates] = (0, import_react11.useState)([]);
2096
- const [isDragging, setIsDragging] = (0, import_react11.useState)(false);
2097
- const fileInputRef = (0, import_react11.useRef)(null);
2098
- const addFiles = (files) => {
2099
- const newStates = files.map((file) => ({ file, status: "idle" }));
2100
- setFileStates((prev) => [...prev, ...newStates]);
2101
- };
2102
- const removeFile = (index) => {
2103
- setFileStates((prev) => prev.filter((_, i) => i !== index));
2104
- };
2105
- const onDragOver = (e) => {
2106
- e.preventDefault();
2107
- setIsDragging(true);
2108
- };
2109
- const onDragLeave = () => {
2110
- setIsDragging(false);
2111
- };
2112
- const onDrop = (e) => {
2113
- e.preventDefault();
2114
- setIsDragging(false);
2115
- if (e.dataTransfer.files) {
2116
- addFiles(Array.from(e.dataTransfer.files));
2117
- }
2118
- };
2119
- const onFileSelect = (e) => {
2120
- if (e.target.files) {
2121
- addFiles(Array.from(e.target.files));
2122
- }
2123
- };
2124
- const uploadFiles = async () => {
2125
- const idleFiles = fileStates.filter((s) => s.status === "idle");
2126
- if (idleFiles.length === 0) return;
2127
- setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
2128
- const formData = new FormData();
2129
- idleFiles.forEach((s) => formData.append("files", s.file));
2130
- if (namespace) formData.append("namespace", namespace);
2131
- try {
2132
- const response = await fetch("/api/upload", {
2133
- method: "POST",
2134
- body: formData
2135
- });
2136
- const result = await response.json();
2137
- if (!response.ok) throw new Error(result.error || "Upload failed");
2138
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
2139
- if (onUploadComplete) onUploadComplete(result);
2140
- } catch (err) {
2141
- const message = err instanceof Error ? err.message : "Upload failed";
2142
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
2143
- }
2144
- };
2145
- const isUploading = fileStates.some((s) => s.status === "uploading");
2146
- const hasIdle = fileStates.some((s) => s.status === "idle");
2147
- return /* @__PURE__ */ import_react11.default.createElement(
2148
- "div",
2149
- {
2150
- className: `p-6 border-2 border-dashed transition-all duration-300 rounded-2xl ${isDragging ? "border-emerald-500 bg-emerald-50/50 dark:bg-emerald-500/5" : "border-slate-200 dark:border-white/10 bg-white dark:bg-white/5"} ${className}`,
2151
- onDragOver,
2152
- onDragLeave,
2153
- onDrop
2154
- },
2155
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ import_react11.default.createElement(
2156
- "div",
2157
- {
2158
- className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
2159
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
2160
- },
2161
- /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
2162
- ), /* @__PURE__ */ import_react11.default.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-slate-500 dark:text-white/40 text-sm mb-6 max-w-xs" }, "Drag and drop PDF, DOCX, TXT, or JSON files to train your AI on your own data."), /* @__PURE__ */ import_react11.default.createElement(
2974
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
2163
2975
  "button",
2164
2976
  {
2165
- onClick: () => {
2166
- var _a;
2167
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
2168
- },
2169
- disabled: isUploading,
2170
- className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
2171
- style: {
2172
- backgroundColor: `${ui.primaryColor}15`,
2173
- color: ui.primaryColor,
2174
- border: `1px solid ${ui.primaryColor}30`
2175
- }
2176
- },
2177
- "Select Files"
2178
- ), /* @__PURE__ */ import_react11.default.createElement(
2179
- "input",
2180
- {
2181
- ref: fileInputRef,
2182
- type: "file",
2183
- multiple: true,
2184
- onChange: onFileSelect,
2185
- className: "hidden",
2186
- accept: ".pdf,.docx,.txt,.md,.json,.csv"
2977
+ onClick: isOpen ? () => setIsOpen(false) : handleOpen,
2978
+ className: `fixed z-[9999] w-14 h-14 shadow-2xl flex items-center justify-center transition-all duration-300 cursor-pointer hover:scale-110 active:scale-95 ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"} ${positionClass}`,
2979
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2980
+ "aria-label": "Open chat",
2981
+ children: [
2982
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2983
+ "span",
2984
+ {
2985
+ className: "absolute inset-0 rounded-full animate-ping opacity-20",
2986
+ style: { background: ui.primaryColor }
2987
+ }
2988
+ ),
2989
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2990
+ "span",
2991
+ {
2992
+ className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
2993
+ children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react8.X, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react8.MessageSquare, { className: "w-6 h-6 text-white" })
2994
+ }
2995
+ ),
2996
+ hasUnread && !isOpen && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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", children: "1" })
2997
+ ]
2187
2998
  }
2188
- )),
2189
- fileStates.length > 0 && /* @__PURE__ */ import_react11.default.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ import_react11.default.createElement(
2190
- "div",
2191
- {
2192
- key: i,
2193
- className: "flex items-center justify-between p-3 rounded-xl bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/10"
2194
- },
2195
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ import_react11.default.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ import_react11.default.createElement("div", { className: "truncate" }, /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ import_react11.default.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
2196
- /* @__PURE__ */ import_react11.default.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ import_react11.default.createElement("div", { title: state.error }, /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ import_react11.default.createElement(
2197
- "button",
2198
- {
2199
- onClick: () => removeFile(i),
2200
- className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
2201
- },
2202
- /* @__PURE__ */ import_react11.default.createElement(import_lucide_react7.X, { className: "w-3.5 h-3.5 text-slate-400" })
2203
- ))
2204
- )), hasIdle && !isUploading && /* @__PURE__ */ import_react11.default.createElement(
2205
- "button",
2206
- {
2207
- onClick: uploadFiles,
2208
- className: "w-full mt-4 py-2.5 rounded-xl text-sm font-semibold text-white shadow-lg transition-all hover:scale-[1.02] active:scale-[0.98]",
2209
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
2210
- },
2211
- "Start Upload"
2212
- ))
2213
- );
2999
+ )
3000
+ ] });
2214
3001
  }
2215
3002
  // Annotate the CommonJS export names for ESM import in node:
2216
3003
  0 && (module.exports = {
@@ -2219,7 +3006,9 @@ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
2219
3006
  ConfigProvider,
2220
3007
  DocumentUpload,
2221
3008
  MessageBubble,
3009
+ ObservabilityPanel,
2222
3010
  SourceCard,
3011
+ addSynonyms,
2223
3012
  useConfig,
2224
3013
  useRagChat
2225
3014
  });