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