@retrivora-ai/rag-engine 1.8.1 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/{ILLMProvider-BfRgI1Xh.d.mts → ILLMProvider-BOJFz3Na.d.mts} +47 -1
  2. package/dist/{ILLMProvider-BfRgI1Xh.d.ts → ILLMProvider-BOJFz3Na.d.ts} +47 -1
  3. package/dist/{MultiTablePostgresProvider-YY7LPNJK.mjs → MultiTablePostgresProvider-ZLGSKTJR.mjs} +1 -1
  4. package/dist/chunk-ICKRMZQK.mjs +76 -0
  5. package/dist/{chunk-BFYLQYQU.mjs → chunk-LZVVLSDN.mjs} +192 -100
  6. package/dist/{chunk-R3RGUMHE.mjs → chunk-OZFBG4BA.mjs} +121 -48
  7. package/dist/handlers/index.d.mts +2 -2
  8. package/dist/handlers/index.d.ts +2 -2
  9. package/dist/handlers/index.js +368 -147
  10. package/dist/handlers/index.mjs +4 -1
  11. package/dist/{index-BV0z5mb6.d.mts → index-BwpcaziY.d.ts} +4 -2
  12. package/dist/{index-1Z4GuYBi.d.ts → index-D3V9Et2M.d.mts} +4 -2
  13. package/dist/index.d.mts +23 -3
  14. package/dist/index.d.ts +23 -3
  15. package/dist/index.js +1143 -790
  16. package/dist/index.mjs +1065 -770
  17. package/dist/server.d.mts +15 -25
  18. package/dist/server.d.ts +15 -25
  19. package/dist/server.js +366 -147
  20. package/dist/server.mjs +3 -2
  21. package/package.json +4 -2
  22. package/src/app/api/upload/route.ts +4 -0
  23. package/src/app/constants.tsx +2 -2
  24. package/src/app/page.tsx +12 -321
  25. package/src/components/AmbientBackground.tsx +29 -0
  26. package/src/components/ArchitectureCard.tsx +17 -0
  27. package/src/components/ArchitectureCardsSection.tsx +15 -0
  28. package/src/components/ChatWindow.tsx +32 -0
  29. package/src/components/CodeViewer.tsx +51 -0
  30. package/src/components/ConfigProvider.tsx +1 -0
  31. package/src/components/DocViewer.tsx +37 -0
  32. package/src/components/DocumentUpload.tsx +44 -1
  33. package/src/components/Documentation.tsx +58 -0
  34. package/src/components/DynamicChart.tsx +27 -2
  35. package/src/components/Hero.tsx +59 -0
  36. package/src/components/HourglassLoader.tsx +87 -0
  37. package/src/components/Lifecycle.tsx +37 -0
  38. package/src/components/MarkdownComponents.tsx +140 -0
  39. package/src/components/MessageBubble.tsx +88 -1010
  40. package/src/components/Navbar.tsx +55 -0
  41. package/src/components/ObservabilityPanel.tsx +374 -0
  42. package/src/components/ProductCard.tsx +3 -1
  43. package/src/components/UIDispatcher.tsx +344 -0
  44. package/src/components/VisualizationRenderer.tsx +48 -26
  45. package/src/core/Pipeline.ts +186 -76
  46. package/src/handlers/index.ts +72 -12
  47. package/src/hooks/useRagChat.ts +19 -9
  48. package/src/index.ts +9 -1
  49. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  50. package/src/types/chat.ts +2 -0
  51. package/src/types/index.ts +52 -0
  52. package/src/types/props.ts +9 -1
  53. package/src/utils/ProductExtractor.ts +347 -0
  54. package/src/utils/UITransformer.ts +4 -53
  55. package/src/utils/synonyms.ts +78 -0
package/dist/index.mjs CHANGED
@@ -1,3 +1,7 @@
1
+ import {
2
+ addSynonyms,
3
+ resolveMetadataValue
4
+ } from "./chunk-ICKRMZQK.mjs";
1
5
  import {
2
6
  mergeDefined
3
7
  } from "./chunk-YLTMFW4M.mjs";
@@ -8,15 +12,15 @@ import {
8
12
  } from "./chunk-X4TOT24V.mjs";
9
13
 
10
14
  // src/components/ChatWidget.tsx
11
- import React10, { useState as useState5 } from "react";
12
- import { MessageSquare, X as X2 } from "lucide-react";
15
+ import React15, { useState as useState6 } from "react";
16
+ import { MessageSquare, X as X3 } from "lucide-react";
13
17
 
14
18
  // src/components/ChatWindow.tsx
15
- import React9, { useState as useState4, useRef as useRef3, useEffect as useEffect4, useCallback as useCallback2 } from "react";
19
+ import React14, { useState as useState5, useRef as useRef4, useEffect as useEffect4, useCallback as useCallback2 } from "react";
16
20
  import {
17
21
  Bot as Bot2,
18
22
  Trash2,
19
- X,
23
+ X as X2,
20
24
  Sparkles,
21
25
  ArrowUp,
22
26
  TriangleAlert,
@@ -24,47 +28,256 @@ import {
24
28
  Maximize2,
25
29
  Minimize2,
26
30
  Mic,
27
- MicOff
31
+ MicOff,
32
+ Paperclip
28
33
  } from "lucide-react";
29
34
 
35
+ // src/components/DocumentUpload.tsx
36
+ import React2, { useState, useRef } from "react";
37
+ import { Upload, File, X, CheckCircle, AlertCircle, Loader2 } from "lucide-react";
38
+
39
+ // src/components/ConfigProvider.tsx
40
+ import React, { createContext, useContext } from "react";
41
+
42
+ // src/config/uiConstants.ts
43
+ var DEFAULT_CONFIG = {
44
+ projectId: "default",
45
+ ui: {
46
+ title: "AI Assistant",
47
+ subtitle: "Powered by RAG",
48
+ primaryColor: "#6366f1",
49
+ accentColor: "#8b5cf6",
50
+ logoUrl: "",
51
+ placeholder: "Ask me anything\u2026",
52
+ showSources: true,
53
+ welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
54
+ showWidget: true,
55
+ poweredBy: "RAG",
56
+ visualStyle: "glass",
57
+ borderRadius: "xl",
58
+ allowUpload: true,
59
+ allowResize: true,
60
+ enableVoiceInput: true
61
+ }
62
+ };
63
+ var BORDER_RADIUS_MAP = {
64
+ none: "rounded-none",
65
+ sm: "rounded-sm",
66
+ md: "rounded-md",
67
+ lg: "rounded-lg",
68
+ xl: "rounded-xl",
69
+ full: "rounded-3xl"
70
+ };
71
+ var CHAT_SUGGESTIONS = [
72
+ "What can you help me with?",
73
+ "Summarise the key topics",
74
+ "Show me an example"
75
+ ];
76
+
77
+ // src/components/ConfigProvider.tsx
78
+ var ConfigContext = createContext(DEFAULT_CONFIG);
79
+ function ConfigProvider({
80
+ config,
81
+ children
82
+ }) {
83
+ const merged = {
84
+ projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
85
+ ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui),
86
+ embedding: config == null ? void 0 : config.embedding
87
+ };
88
+ return /* @__PURE__ */ React.createElement(ConfigContext.Provider, { value: merged }, children);
89
+ }
90
+ function useConfig() {
91
+ return useContext(ConfigContext);
92
+ }
93
+
94
+ // src/components/DocumentUpload.tsx
95
+ function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
96
+ var _a;
97
+ const { ui, embedding } = useConfig();
98
+ const [fileStates, setFileStates] = useState([]);
99
+ const [isDragging, setIsDragging] = useState(false);
100
+ const fileInputRef = useRef(null);
101
+ const MULTI_DIMENSION_MODELS = {
102
+ "text-embedding-3-small": [512, 1536],
103
+ "text-embedding-3-large": [256, 1024, 3072],
104
+ "nomic-embed-text": [64, 128, 256, 512, 768]
105
+ };
106
+ const COMMON_DIMENSIONS = [64, 128, 256, 384, 512, 768, 1024, 1536, 3072];
107
+ const currentModel = (embedding == null ? void 0 : embedding.model) || "unknown";
108
+ const defaultDimension = (embedding == null ? void 0 : embedding.dimensions) || 1536;
109
+ let availableDimensions = (_a = Object.entries(MULTI_DIMENSION_MODELS).find(([k]) => currentModel.includes(k))) == null ? void 0 : _a[1];
110
+ if (!availableDimensions) {
111
+ availableDimensions = COMMON_DIMENSIONS.includes(defaultDimension) ? COMMON_DIMENSIONS : [...COMMON_DIMENSIONS, defaultDimension].sort((a, b) => a - b);
112
+ }
113
+ const isChangeable = true;
114
+ const [dimension, setDimension] = useState(defaultDimension);
115
+ const addFiles = (files) => {
116
+ const newStates = files.map((file) => ({ file, status: "idle" }));
117
+ setFileStates((prev) => [...prev, ...newStates]);
118
+ };
119
+ const removeFile = (index) => {
120
+ setFileStates((prev) => prev.filter((_, i) => i !== index));
121
+ };
122
+ const onDragOver = (e) => {
123
+ e.preventDefault();
124
+ setIsDragging(true);
125
+ };
126
+ const onDragLeave = () => {
127
+ setIsDragging(false);
128
+ };
129
+ const onDrop = (e) => {
130
+ e.preventDefault();
131
+ setIsDragging(false);
132
+ if (e.dataTransfer.files) {
133
+ addFiles(Array.from(e.dataTransfer.files));
134
+ }
135
+ };
136
+ const onFileSelect = (e) => {
137
+ if (e.target.files) {
138
+ addFiles(Array.from(e.target.files));
139
+ }
140
+ };
141
+ const uploadFiles = async () => {
142
+ const idleFiles = fileStates.filter((s) => s.status === "idle");
143
+ if (idleFiles.length === 0) return;
144
+ setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
145
+ const formData = new FormData();
146
+ idleFiles.forEach((s) => formData.append("files", s.file));
147
+ if (namespace) formData.append("namespace", namespace);
148
+ formData.append("dimension", dimension.toString());
149
+ try {
150
+ const response = await fetch("/api/upload", {
151
+ method: "POST",
152
+ body: formData
153
+ });
154
+ const result = await response.json();
155
+ if (!response.ok) throw new Error(result.error || "Upload failed");
156
+ setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
157
+ if (onUploadComplete) onUploadComplete(result);
158
+ } catch (err) {
159
+ const message = err instanceof Error ? err.message : "Upload failed";
160
+ setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
161
+ }
162
+ };
163
+ const isUploading = fileStates.some((s) => s.status === "uploading");
164
+ const hasIdle = fileStates.some((s) => s.status === "idle");
165
+ return /* @__PURE__ */ React2.createElement(
166
+ "div",
167
+ {
168
+ 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}`,
169
+ onDragOver,
170
+ onDragLeave,
171
+ onDrop
172
+ },
173
+ /* @__PURE__ */ React2.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ React2.createElement(
174
+ "div",
175
+ {
176
+ className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
177
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
178
+ },
179
+ /* @__PURE__ */ React2.createElement(Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
180
+ ), /* @__PURE__ */ React2.createElement("h3", { className: "text-slate-900 dark:text-white font-semibold mb-1" }, "Upload Documents"), /* @__PURE__ */ React2.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__ */ React2.createElement(
181
+ "button",
182
+ {
183
+ onClick: () => {
184
+ var _a2;
185
+ return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
186
+ },
187
+ disabled: isUploading,
188
+ className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
189
+ style: {
190
+ backgroundColor: `${ui.primaryColor}15`,
191
+ color: ui.primaryColor,
192
+ border: `1px solid ${ui.primaryColor}30`
193
+ }
194
+ },
195
+ "Select Files"
196
+ ), /* @__PURE__ */ React2.createElement(
197
+ "input",
198
+ {
199
+ ref: fileInputRef,
200
+ type: "file",
201
+ multiple: true,
202
+ onChange: onFileSelect,
203
+ className: "hidden",
204
+ accept: ".pdf,.docx,.txt,.md,.json,.csv"
205
+ }
206
+ ), /* @__PURE__ */ React2.createElement("div", { className: "mt-6 flex flex-col items-center gap-2 text-sm" }, /* @__PURE__ */ React2.createElement("label", { className: "text-slate-600 dark:text-white/60 font-medium" }, "Embedding Dimension"), isChangeable ? /* @__PURE__ */ React2.createElement(
207
+ "select",
208
+ {
209
+ value: dimension,
210
+ onChange: (e) => setDimension(Number(e.target.value)),
211
+ disabled: isUploading,
212
+ 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"
213
+ },
214
+ availableDimensions.map((dim) => /* @__PURE__ */ React2.createElement("option", { key: dim, value: dim, className: "bg-white dark:bg-slate-900" }, dim))
215
+ ) : /* @__PURE__ */ React2.createElement("div", { className: "px-3 py-1.5 rounded-lg bg-slate-100 dark:bg-white/10 border border-slate-200 dark:border-white/10 text-slate-500 dark:text-white/50 cursor-not-allowed text-xs" }, dimension, " (Fixed for ", currentModel, ")"))),
216
+ fileStates.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ React2.createElement(
217
+ "div",
218
+ {
219
+ key: i,
220
+ 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"
221
+ },
222
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3 overflow-hidden" }, /* @__PURE__ */ React2.createElement("div", { className: "w-8 h-8 rounded-lg bg-white dark:bg-white/10 flex items-center justify-center shadow-sm" }, /* @__PURE__ */ React2.createElement(File, { className: "w-4 h-4 text-slate-400" })), /* @__PURE__ */ React2.createElement("div", { className: "truncate" }, /* @__PURE__ */ React2.createElement("p", { className: "text-xs font-medium text-slate-700 dark:text-white/80 truncate" }, state.file.name), /* @__PURE__ */ React2.createElement("p", { className: "text-[10px] text-slate-400" }, (state.file.size / 1024).toFixed(1), " KB"))),
223
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, state.status === "uploading" && /* @__PURE__ */ React2.createElement(Loader2, { className: "w-4 h-4 text-slate-400 animate-spin" }), state.status === "success" && /* @__PURE__ */ React2.createElement(CheckCircle, { className: "w-4 h-4 text-emerald-500" }), state.status === "error" && /* @__PURE__ */ React2.createElement("div", { title: state.error }, /* @__PURE__ */ React2.createElement(AlertCircle, { className: "w-4 h-4 text-rose-500" })), state.status !== "uploading" && /* @__PURE__ */ React2.createElement(
224
+ "button",
225
+ {
226
+ onClick: () => removeFile(i),
227
+ className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
228
+ },
229
+ /* @__PURE__ */ React2.createElement(X, { className: "w-3.5 h-3.5 text-slate-400" })
230
+ ))
231
+ )), hasIdle && !isUploading && /* @__PURE__ */ React2.createElement(
232
+ "button",
233
+ {
234
+ onClick: uploadFiles,
235
+ 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]",
236
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
237
+ },
238
+ "Start Upload"
239
+ ))
240
+ );
241
+ }
242
+
30
243
  // src/components/MessageBubble.tsx
31
- import React6 from "react";
32
- import { Bot, User, ChevronDown as ChevronDown2, ChevronRight as ChevronRight2 } from "lucide-react";
244
+ import React12 from "react";
245
+ import { Bot, User, ChevronDown as ChevronDown3, ChevronRight as ChevronRight3, Activity } from "lucide-react";
33
246
  import ReactMarkdown from "react-markdown";
34
247
 
35
248
  // src/components/SourceCard.tsx
36
- import React from "react";
249
+ import React3 from "react";
37
250
  import { ChevronDown, ChevronUp } from "lucide-react";
38
251
  function SourceCard({ source, index }) {
39
252
  var _a, _b;
40
- const [expanded, setExpanded] = React.useState(false);
253
+ const [expanded, setExpanded] = React3.useState(false);
41
254
  const scorePercent = Math.round(source.score * 100);
42
255
  const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
43
256
  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
257
  const preview = source.content.slice(0, 120);
45
258
  const hasMore = source.content.length > 120;
46
- return /* @__PURE__ */ React.createElement(
259
+ return /* @__PURE__ */ React3.createElement(
47
260
  "div",
48
261
  {
49
262
  className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
50
263
  onClick: () => setExpanded((e) => !e)
51
264
  },
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"))
265
+ /* @__PURE__ */ React3.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React3.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__ */ React3.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__ */ React3.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
266
+ /* @__PURE__ */ React3.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
267
+ hasMore && /* @__PURE__ */ React3.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(ChevronDown, { className: "w-3 h-3" }), " Show more"))
55
268
  );
56
269
  }
57
270
 
58
271
  // src/components/ProductCarousel.tsx
59
- import React3, { useRef, useState, useEffect } from "react";
272
+ import React5, { useRef as useRef2, useState as useState2, useEffect } from "react";
60
273
  import { ChevronLeft, ChevronRight } from "lucide-react";
61
274
 
62
275
  // src/components/ProductCard.tsx
63
- import React2 from "react";
276
+ import React4 from "react";
64
277
  import Image from "next/image";
65
278
  import { ShoppingCart, ExternalLink } from "lucide-react";
66
279
  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(
280
+ return /* @__PURE__ */ React4.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__ */ React4.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__ */ React4.createElement(
68
281
  Image,
69
282
  {
70
283
  src: product.image,
@@ -73,16 +286,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
73
286
  unoptimized: true,
74
287
  className: "object-cover group-hover:scale-105 transition-transform duration-500"
75
288
  }
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(
289
+ ) : /* @__PURE__ */ React4.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ React4.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__ */ React4.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ React4.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ React4.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ React4.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : String(product.price).match(/^[\d.]/) ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ React4.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ React4.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ React4.createElement(
77
290
  "button",
78
291
  {
79
292
  onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
80
293
  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
294
  style: { background: primaryColor }
82
295
  },
83
- /* @__PURE__ */ React2.createElement(ShoppingCart, { className: "w-3 h-3" }),
296
+ /* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-3 h-3" }),
84
297
  "Add to Cart"
85
- ), product.link && /* @__PURE__ */ React2.createElement(
298
+ ), product.link && /* @__PURE__ */ React4.createElement(
86
299
  "a",
87
300
  {
88
301
  href: product.link,
@@ -90,14 +303,14 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
90
303
  rel: "noopener noreferrer",
91
304
  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
305
  },
93
- /* @__PURE__ */ React2.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
306
+ /* @__PURE__ */ React4.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
94
307
  ))));
95
308
  }
96
309
 
97
310
  // src/components/ProductCarousel.tsx
98
311
  function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
99
- const scrollRef = useRef(null);
100
- const [canScroll, setCanScroll] = useState(false);
312
+ const scrollRef = useRef2(null);
313
+ const [canScroll, setCanScroll] = useState2(false);
101
314
  useEffect(() => {
102
315
  const checkScroll = () => {
103
316
  if (scrollRef.current) {
@@ -122,9 +335,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
122
335
  };
123
336
  if (!products || products.length === 0) return null;
124
337
  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 }));
338
+ return /* @__PURE__ */ React5.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React5.createElement(ProductCard, { product: products[0], primaryColor, onAddToCart }));
126
339
  }
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(
340
+ return /* @__PURE__ */ React5.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.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__ */ React5.createElement(
128
341
  "button",
129
342
  {
130
343
  onClick: (e) => {
@@ -134,8 +347,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
134
347
  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
348
  "aria-label": "Scroll left"
136
349
  },
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(
350
+ /* @__PURE__ */ React5.createElement(ChevronLeft, { className: "w-5 h-5" })
351
+ )), /* @__PURE__ */ React5.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__ */ React5.createElement(
139
352
  "button",
140
353
  {
141
354
  onClick: (e) => {
@@ -145,27 +358,50 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
145
358
  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
359
  "aria-label": "Scroll right"
147
360
  },
148
- /* @__PURE__ */ React3.createElement(ChevronRight, { className: "w-5 h-5" })
149
- ))), /* @__PURE__ */ React3.createElement(
361
+ /* @__PURE__ */ React5.createElement(ChevronRight, { className: "w-5 h-5" })
362
+ ))), /* @__PURE__ */ React5.createElement(
150
363
  "div",
151
364
  {
152
365
  ref: scrollRef,
153
366
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
154
367
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
155
368
  },
156
- products.map((product) => /* @__PURE__ */ React3.createElement(
369
+ products.map((product) => /* @__PURE__ */ React5.createElement(
157
370
  "div",
158
371
  {
159
372
  key: product.id,
160
373
  className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
161
374
  },
162
- /* @__PURE__ */ React3.createElement(ProductCard, { product, primaryColor, onAddToCart })
375
+ /* @__PURE__ */ React5.createElement(ProductCard, { product, primaryColor, onAddToCart })
163
376
  ))
164
377
  ));
165
378
  }
166
379
 
380
+ // src/components/VisualizationRenderer.tsx
381
+ import React7 from "react";
382
+ import {
383
+ PieChart as PieChart2,
384
+ Pie as Pie2,
385
+ Cell as Cell2,
386
+ BarChart as BarChart2,
387
+ Bar as Bar2,
388
+ LineChart as LineChart2,
389
+ Line as Line2,
390
+ XAxis as XAxis2,
391
+ YAxis as YAxis2,
392
+ CartesianGrid as CartesianGrid2,
393
+ Tooltip as Tooltip2,
394
+ Legend as Legend2,
395
+ ResponsiveContainer as ResponsiveContainer2,
396
+ RadarChart,
397
+ Radar,
398
+ PolarGrid,
399
+ PolarAngleAxis,
400
+ PolarRadiusAxis
401
+ } from "recharts";
402
+
167
403
  // src/components/DynamicChart.tsx
168
- import React4 from "react";
404
+ import React6 from "react";
169
405
  import {
170
406
  BarChart,
171
407
  Bar,
@@ -181,18 +417,19 @@ import {
181
417
  Legend,
182
418
  ResponsiveContainer
183
419
  } from "recharts";
184
- var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
420
+ var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
185
421
  function DynamicChart({
186
422
  config,
187
423
  primaryColor = "#6366f1",
188
424
  accentColor = "#8b5cf6",
189
- viewportSize = "large"
425
+ viewportSize = "large",
426
+ isStreaming = false
190
427
  }) {
191
428
  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(() => {
429
+ const colors = config.colors || [primaryColor, accentColor, ...CHART_COLORS];
430
+ const containerRef = React6.useRef(null);
431
+ const [containerWidth, setContainerWidth] = React6.useState(0);
432
+ React6.useEffect(() => {
196
433
  const element = containerRef.current;
197
434
  if (!element || typeof ResizeObserver === "undefined") return;
198
435
  const updateWidth = (width) => {
@@ -218,7 +455,7 @@ function DynamicChart({
218
455
  return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
219
456
  };
220
457
  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(() => {
458
+ const sanitizedData = React6.useMemo(() => {
222
459
  if (!data) return [];
223
460
  return data.map((item) => {
224
461
  const newItem = {};
@@ -250,8 +487,19 @@ function DynamicChart({
250
487
  return newItem;
251
488
  });
252
489
  }, [data]);
490
+ if (isStreaming) {
491
+ const bars = type === "pie" ? 6 : 5;
492
+ return /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, type === "pie" ? /* @__PURE__ */ React6.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }) : /* @__PURE__ */ React6.createElement("div", { className: "flex items-end gap-2 h-32 px-2" }, Array.from({ length: bars }).map((_, i) => /* @__PURE__ */ React6.createElement(
493
+ "div",
494
+ {
495
+ key: i,
496
+ className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10",
497
+ style: { height: `${40 + i % 3 * 25}%` }
498
+ }
499
+ ))));
500
+ }
253
501
  if (!data || data.length === 0) {
254
- return /* @__PURE__ */ React4.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
502
+ return /* @__PURE__ */ React6.createElement("div", { className: "p-4 text-sm text-slate-500" }, "No data available for chart.");
255
503
  }
256
504
  const firstItem = sanitizedData[0];
257
505
  const allKeys = Object.keys(firstItem);
@@ -276,13 +524,13 @@ function DynamicChart({
276
524
  const renderTooltip = (row, label) => {
277
525
  var _a, _b;
278
526
  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]) => {
527
+ return /* @__PURE__ */ React6.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__ */ React6.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
528
  var _a2;
281
529
  if (value === null || value === void 0 || key === finalXKey) return null;
282
530
  if (typeof value === "object") return null;
283
531
  const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
284
532
  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)));
533
+ return /* @__PURE__ */ React6.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React6.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ React6.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, String(value)));
286
534
  }));
287
535
  };
288
536
  const getTooltipRow = (payload) => {
@@ -294,7 +542,7 @@ function DynamicChart({
294
542
  return row;
295
543
  };
296
544
  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(
545
+ return /* @__PURE__ */ React6.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React6.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(PieChart, null, /* @__PURE__ */ React6.createElement(
298
546
  Pie,
299
547
  {
300
548
  data: sanitizedData,
@@ -306,10 +554,10 @@ function DynamicChart({
306
554
  label: false,
307
555
  labelLine: false
308
556
  },
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" })))));
557
+ sanitizedData.map((_entry, index) => /* @__PURE__ */ React6.createElement(Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
558
+ ), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" })))));
311
559
  }
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(
560
+ return /* @__PURE__ */ React6.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React6.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React6.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React6.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React6.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React6.createElement(
313
561
  Bar,
314
562
  {
315
563
  key,
@@ -318,7 +566,7 @@ function DynamicChart({
318
566
  radius: [4, 4, 0, 0],
319
567
  barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
320
568
  }
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(
569
+ ))) : /* @__PURE__ */ React6.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React6.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React6.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React6.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React6.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React6.createElement(
322
570
  Line,
323
571
  {
324
572
  key,
@@ -333,45 +581,6 @@ function DynamicChart({
333
581
  }
334
582
 
335
583
  // 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
- ];
375
584
  function getColor(index) {
376
585
  return CHART_COLORS[index % CHART_COLORS.length];
377
586
  }
@@ -381,60 +590,71 @@ function CustomTooltip({
381
590
  label
382
591
  }) {
383
592
  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(
593
+ return /* @__PURE__ */ React7.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__ */ React7.createElement("p", { className: "mb-2 font-semibold text-slate-800 dark:text-white/90" }, label), payload.map((entry, i) => /* @__PURE__ */ React7.createElement("div", { key: i, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React7.createElement("span", { className: "flex items-center gap-1.5" }, entry.color && /* @__PURE__ */ React7.createElement(
385
594
  "span",
386
595
  {
387
596
  className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
388
597
  style: { background: entry.color }
389
598
  }
390
- ), entry.name), /* @__PURE__ */ React5.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
599
+ ), entry.name), /* @__PURE__ */ React7.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
391
600
  }
392
601
  function VisualizationRenderer({
393
602
  data,
394
603
  className = "",
395
604
  primaryColor,
396
- onAddToCart
605
+ onAddToCart,
606
+ isStreaming
397
607
  }) {
398
608
  var _a;
399
609
  if (!data) {
400
- return /* @__PURE__ */ React5.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
610
+ return /* @__PURE__ */ React7.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
401
611
  }
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));
612
+ return /* @__PURE__ */ React7.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ React7.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React7.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__ */ React7.createElement("p", { className: "text-xs text-slate-500 dark:text-slate-400" }, data.description)), renderVisualization(data.type, data.data, onAddToCart, primaryColor, isStreaming));
403
613
  }
404
- function renderVisualization(type, data, onAddToCart, primaryColor) {
614
+ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
405
615
  try {
406
616
  switch (type) {
407
617
  case "pie_chart":
408
- return /* @__PURE__ */ React5.createElement(PieChartView, { data });
618
+ return /* @__PURE__ */ React7.createElement(PieChartView, { data, isStreaming });
409
619
  case "bar_chart":
410
- return /* @__PURE__ */ React5.createElement(BarChartView, { data, primaryColor });
620
+ return /* @__PURE__ */ React7.createElement(BarChartView, { data, primaryColor, isStreaming });
411
621
  case "line_chart":
412
- return /* @__PURE__ */ React5.createElement(LineChartView, { data, primaryColor });
622
+ return /* @__PURE__ */ React7.createElement(LineChartView, { data, primaryColor, isStreaming });
413
623
  case "radar_chart":
414
- return /* @__PURE__ */ React5.createElement(RadarChartView, { data });
624
+ return /* @__PURE__ */ React7.createElement(RadarChartView, { data, isStreaming });
415
625
  case "table":
416
- return /* @__PURE__ */ React5.createElement(TableView, { data });
626
+ return /* @__PURE__ */ React7.createElement(TableView, { data, isStreaming });
417
627
  case "product_carousel":
418
628
  case "carousel":
419
- return /* @__PURE__ */ React5.createElement(CarouselView, { data, onAddToCart, primaryColor });
629
+ return /* @__PURE__ */ React7.createElement(CarouselView, { data, onAddToCart, primaryColor });
420
630
  case "text":
421
631
  default:
422
- return /* @__PURE__ */ React5.createElement(TextView, { data });
632
+ return /* @__PURE__ */ React7.createElement(TextView, { data });
423
633
  }
424
634
  } catch (error) {
425
635
  console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
426
- return /* @__PURE__ */ React5.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
636
+ return /* @__PURE__ */ React7.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
637
+ }
638
+ }
639
+ function ChartSkeleton({ type }) {
640
+ if (type === "pie") {
641
+ return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse" }, /* @__PURE__ */ React7.createElement("div", { className: "mx-auto w-40 h-40 rounded-full bg-slate-100 dark:bg-white/10" }));
427
642
  }
643
+ if (type === "table") {
644
+ return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse space-y-2" }, /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }), /* @__PURE__ */ React7.createElement("div", { className: "h-8 bg-slate-100 dark:bg-white/10 rounded" }));
645
+ }
646
+ return /* @__PURE__ */ React7.createElement("div", { className: "w-full mt-4 mb-2 animate-pulse flex items-end gap-2 h-32 px-2" }, Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ React7.createElement("div", { key: i, className: "flex-1 rounded-t-md bg-slate-100 dark:bg-white/10", style: { height: `${40 + i % 3 * 25}%` } })));
428
647
  }
429
- function PieChartView({ data }) {
648
+ function PieChartView({ data, isStreaming }) {
649
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
430
650
  if (!Array.isArray(data) || data.length === 0) {
431
- return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
651
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
432
652
  }
433
653
  const chartData = data.map((item) => ({
434
654
  name: item.label,
435
655
  value: item.value
436
656
  }));
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(
657
+ return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 280 }, /* @__PURE__ */ React7.createElement(PieChart2, null, /* @__PURE__ */ React7.createElement(
438
658
  Pie2,
439
659
  {
440
660
  data: chartData,
@@ -446,12 +666,13 @@ function PieChartView({ data }) {
446
666
  label: false,
447
667
  labelLine: false
448
668
  },
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" }))));
669
+ chartData.map((_entry, index) => /* @__PURE__ */ React7.createElement(Cell2, { key: `cell-${index}`, fill: getColor(index) }))
670
+ ), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 }, verticalAlign: "bottom" }))));
451
671
  }
452
- function BarChartView({ data, primaryColor }) {
672
+ function BarChartView({ data, primaryColor, isStreaming }) {
673
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
453
674
  if (!Array.isArray(data) || data.length === 0) {
454
- return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
675
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
455
676
  }
456
677
  const chartData = data.map((item) => __spreadValues(__spreadValues({
457
678
  category: item.category,
@@ -459,7 +680,7 @@ function BarChartView({ data, primaryColor }) {
459
680
  }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
460
681
  const barColor = primaryColor != null ? primaryColor : getColor(0);
461
682
  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(
683
+ return /* @__PURE__ */ React7.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__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React7.createElement(BarChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(
463
684
  XAxis2,
464
685
  {
465
686
  dataKey: "category",
@@ -467,11 +688,12 @@ function BarChartView({ data, primaryColor }) {
467
688
  tickLine: false,
468
689
  axisLine: { stroke: "#cbd5e1" }
469
690
  }
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] }))));
691
+ ), /* @__PURE__ */ React7.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null), cursor: { fill: "#f1f5f9" } }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }), hasStock ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "inStockCount", name: "In Stock", fill: getColor(1), radius: [4, 4, 0, 0] }), /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "outOfStockCount", name: "Out of Stock", fill: getColor(3), radius: [4, 4, 0, 0] })) : /* @__PURE__ */ React7.createElement(Bar2, { dataKey: "value", name: "Value", fill: barColor, radius: [4, 4, 0, 0] }))));
471
692
  }
472
- function LineChartView({ data, primaryColor }) {
693
+ function LineChartView({ data, primaryColor, isStreaming }) {
694
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
473
695
  if (!Array.isArray(data) || data.length === 0) {
474
- return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
696
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
475
697
  }
476
698
  const chartData = data.map((point) => {
477
699
  var _a;
@@ -481,7 +703,7 @@ function LineChartView({ data, primaryColor }) {
481
703
  };
482
704
  });
483
705
  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(
706
+ return /* @__PURE__ */ React7.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__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 260 }, /* @__PURE__ */ React7.createElement(LineChart2, { data: chartData, margin: { top: 10, right: 10, left: -20, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(
485
707
  XAxis2,
486
708
  {
487
709
  dataKey: "label",
@@ -489,7 +711,7 @@ function LineChartView({ data, primaryColor }) {
489
711
  tickLine: false,
490
712
  axisLine: { stroke: "#cbd5e1" }
491
713
  }
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(
714
+ ), /* @__PURE__ */ React7.createElement(YAxis2, { tick: { fontSize: 11, fill: "#64748b" }, tickLine: false, axisLine: false }), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(
493
715
  Line2,
494
716
  {
495
717
  type: "monotone",
@@ -502,14 +724,15 @@ function LineChartView({ data, primaryColor }) {
502
724
  }
503
725
  ))));
504
726
  }
505
- function RadarChartView({ data }) {
727
+ function RadarChartView({ data, isStreaming }) {
728
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
506
729
  if (!Array.isArray(data) || data.length === 0) {
507
- return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
730
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
508
731
  }
509
732
  const productNames = Array.from(
510
733
  new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
511
734
  );
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(
735
+ return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: 300 }, /* @__PURE__ */ React7.createElement(RadarChart, { cx: "50%", cy: "50%", outerRadius: "80%", data }, /* @__PURE__ */ React7.createElement(PolarGrid, { stroke: "#e2e8f0" }), /* @__PURE__ */ React7.createElement(PolarAngleAxis, { dataKey: "attribute", tick: { fontSize: 11, fill: "#64748b" } }), /* @__PURE__ */ React7.createElement(PolarRadiusAxis, { tick: { fontSize: 10, fill: "#64748b" } }), productNames.map((productName, index) => /* @__PURE__ */ React7.createElement(
513
736
  Radar,
514
737
  {
515
738
  key: productName,
@@ -519,26 +742,27 @@ function RadarChartView({ data }) {
519
742
  fill: getColor(index),
520
743
  fillOpacity: 0.4
521
744
  }
522
- )), /* @__PURE__ */ React5.createElement(Tooltip2, { content: /* @__PURE__ */ React5.createElement(CustomTooltip, null) }), /* @__PURE__ */ React5.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }))));
745
+ )), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }))));
523
746
  }
524
- function TableView({ data }) {
747
+ function TableView({ data, isStreaming }) {
748
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "table" });
525
749
  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");
750
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
527
751
  }
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(
752
+ return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden" }, /* @__PURE__ */ React7.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React7.createElement("table", { className: "w-full text-sm border-collapse" }, /* @__PURE__ */ React7.createElement("thead", { className: "bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10" }, /* @__PURE__ */ React7.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ React7.createElement(
529
753
  "th",
530
754
  {
531
755
  key: idx,
532
756
  className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
533
757
  },
534
758
  col
535
- )))), /* @__PURE__ */ React5.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React5.createElement(
759
+ )))), /* @__PURE__ */ React7.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React7.createElement(
536
760
  "tr",
537
761
  {
538
762
  key: rowIdx,
539
763
  className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
540
764
  },
541
- row.map((cell, cellIdx) => /* @__PURE__ */ React5.createElement(
765
+ row.map((cell, cellIdx) => /* @__PURE__ */ React7.createElement(
542
766
  "td",
543
767
  {
544
768
  key: cellIdx,
@@ -546,7 +770,7 @@ function TableView({ data }) {
546
770
  },
547
771
  String(cell)
548
772
  ))
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" : "")));
773
+ ))))), /* @__PURE__ */ React7.createElement("div", { className: "px-4 py-2 bg-slate-50 dark:bg-white/5 border-t border-slate-100 dark:border-white/5" }, /* @__PURE__ */ React7.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30" }, data.rows.length, " row", data.rows.length !== 1 ? "s" : "")));
550
774
  }
551
775
  function CarouselView({
552
776
  data,
@@ -554,7 +778,7 @@ function CarouselView({
554
778
  primaryColor
555
779
  }) {
556
780
  if (!Array.isArray(data)) {
557
- return /* @__PURE__ */ React5.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
781
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
558
782
  }
559
783
  const products = data.map((product) => ({
560
784
  id: product.id,
@@ -566,7 +790,7 @@ function CarouselView({
566
790
  description: product.description,
567
791
  inStock: product.inStock
568
792
  }));
569
- return /* @__PURE__ */ React5.createElement("div", { className: "w-full" }, /* @__PURE__ */ React5.createElement(
793
+ return /* @__PURE__ */ React7.createElement("div", { className: "w-full" }, /* @__PURE__ */ React7.createElement(
570
794
  ProductCarousel,
571
795
  {
572
796
  products,
@@ -577,10 +801,272 @@ function CarouselView({
577
801
  }
578
802
  function TextView({ data }) {
579
803
  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));
804
+ return /* @__PURE__ */ React7.createElement("div", { className: "bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-white/10 p-4 shadow-sm" }, /* @__PURE__ */ React7.createElement("p", { className: "text-sm text-slate-700 dark:text-slate-300 whitespace-pre-wrap leading-relaxed" }, content));
581
805
  }
582
806
 
583
- // src/components/MessageBubble.tsx
807
+ // src/components/ObservabilityPanel.tsx
808
+ import React8 from "react";
809
+ import {
810
+ Clock,
811
+ Cpu,
812
+ Zap,
813
+ AlertTriangle,
814
+ FileText,
815
+ Layers,
816
+ ChevronDown as ChevronDown2,
817
+ ChevronRight as ChevronRight2,
818
+ CheckCircle as CheckCircle2,
819
+ AlertCircle as AlertCircle2
820
+ } from "lucide-react";
821
+ function fmtMs(ms) {
822
+ return ms >= 1e3 ? `${(ms / 1e3).toFixed(2)}s` : `${Math.round(ms)}ms`;
823
+ }
824
+ function fmtTokens(n) {
825
+ return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
826
+ }
827
+ function ScoreBadge({ score }) {
828
+ const pct = Math.round(score * 100);
829
+ 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";
830
+ return /* @__PURE__ */ React8.createElement("span", { className: `inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-semibold border ${color}` }, pct <= 20 ? /* @__PURE__ */ React8.createElement(CheckCircle2, { className: "w-3 h-3" }) : /* @__PURE__ */ React8.createElement(AlertCircle2, { className: "w-3 h-3" }), pct, "% hallucination risk");
831
+ }
832
+ function RetrievalTab({ trace }) {
833
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/40" }, trace.chunks.length, " chunk", trace.chunks.length !== 1 ? "s" : "", " retrieved", trace.rewrittenQuery && /* @__PURE__ */ React8.createElement("span", { className: "ml-2 italic" }, "(query rewritten: ", /* @__PURE__ */ React8.createElement("em", { className: "text-indigo-500" }, trace.rewrittenQuery), ")")), /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 max-h-56 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => /* @__PURE__ */ React8.createElement(
834
+ "div",
835
+ {
836
+ key: String(chunk.id),
837
+ className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5"
838
+ },
839
+ /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, "[", i + 1, "] ", String(chunk.id).slice(0, 20)), /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, "score: ", chunk.score.toFixed(3))),
840
+ /* @__PURE__ */ React8.createElement("div", { className: "h-1 rounded-full bg-slate-200 dark:bg-white/10 mb-2 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
841
+ "div",
842
+ {
843
+ className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
844
+ style: { width: `${Math.min(100, chunk.score * 100)}%` }
845
+ }
846
+ )),
847
+ /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 line-clamp-2 leading-relaxed" }, chunk.content)
848
+ ))));
849
+ }
850
+ function LatencyTab({ trace }) {
851
+ const { latency } = trace;
852
+ const bars = [
853
+ { label: "Embed", ms: latency.embedMs, color: "#6366f1" },
854
+ { label: "Retrieve", ms: latency.retrieveMs, color: "#10b981" },
855
+ ...latency.rerankMs != null ? [{ label: "Rerank", ms: latency.rerankMs, color: "#f59e0b" }] : [],
856
+ { label: "Generate", ms: latency.generateMs, color: "#8b5cf6" }
857
+ ];
858
+ const maxMs = Math.max(...bars.map((b) => b.ms), 1);
859
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-slate-500 dark:text-white/40" }, "Pipeline stages"), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-semibold text-slate-700 dark:text-white/70" }, "Total: ", fmtMs(latency.totalMs))), /* @__PURE__ */ React8.createElement("div", { className: "space-y-2" }, bars.map((bar) => /* @__PURE__ */ React8.createElement("div", { key: bar.label, className: "space-y-0.5" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-slate-600 dark:text-white/60" }, bar.label), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, fmtMs(bar.ms))), /* @__PURE__ */ React8.createElement("div", { className: "h-2 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
860
+ "div",
861
+ {
862
+ className: "h-full rounded-full transition-all duration-700",
863
+ style: {
864
+ width: `${bar.ms / maxMs * 100}%`,
865
+ background: bar.color,
866
+ opacity: 0.85
867
+ }
868
+ }
869
+ ))))));
870
+ }
871
+ function TokensTab({ trace }) {
872
+ const { tokens } = trace;
873
+ if (!tokens) {
874
+ return /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Token data not available.");
875
+ }
876
+ const stats = [
877
+ { label: "Prompt Tokens", value: fmtTokens(tokens.promptTokens), color: "text-indigo-600 dark:text-indigo-400" },
878
+ { label: "Completion Tokens", value: fmtTokens(tokens.completionTokens), color: "text-emerald-600 dark:text-emerald-400" },
879
+ { label: "Total Tokens", value: fmtTokens(tokens.totalTokens), color: "text-slate-700 dark:text-white/80" }
880
+ ];
881
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "grid grid-cols-3 gap-2" }, stats.map((s) => /* @__PURE__ */ React8.createElement("div", { key: s.label, className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5 text-center" }, /* @__PURE__ */ React8.createElement("p", { className: `text-lg font-bold ${s.color}` }, s.value), /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 mt-0.5" }, s.label)))), tokens.estimatedCostUsd != null && /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between rounded-lg border border-emerald-100 dark:border-emerald-500/20 bg-emerald-50 dark:bg-emerald-500/10 px-3 py-2" }, /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] text-emerald-700 dark:text-emerald-400 font-medium" }, "Estimated Cost"), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-bold text-emerald-700 dark:text-emerald-400" }, "$", tokens.estimatedCostUsd < 1e-4 ? tokens.estimatedCostUsd.toExponential(2) : tokens.estimatedCostUsd.toFixed(5), " USD")), /* @__PURE__ */ React8.createElement("div", { className: "h-2 rounded-full overflow-hidden flex" }, /* @__PURE__ */ React8.createElement(
882
+ "div",
883
+ {
884
+ className: "h-full bg-indigo-500 dark:bg-indigo-400",
885
+ style: { width: `${tokens.promptTokens / tokens.totalTokens * 100}%` },
886
+ title: "Prompt tokens"
887
+ }
888
+ ), /* @__PURE__ */ React8.createElement(
889
+ "div",
890
+ {
891
+ className: "h-full bg-emerald-500 dark:bg-emerald-400",
892
+ style: { width: `${tokens.completionTokens / tokens.totalTokens * 100}%` },
893
+ title: "Completion tokens"
894
+ }
895
+ )), /* @__PURE__ */ React8.createElement("div", { className: "flex gap-3 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-indigo-500" }), " Prompt"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " Completion")));
896
+ }
897
+ function HallucinationTab({ trace }) {
898
+ if (trace.hallucinationScore == null) {
899
+ return /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-400 dark:text-white/30 italic" }, "Hallucination scoring not available for this response.");
900
+ }
901
+ const score = trace.hallucinationScore;
902
+ const pct = Math.round(score * 100);
903
+ const barColor = pct <= 20 ? "#10b981" : pct <= 50 ? "#f59e0b" : "#ef4444";
904
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React8.createElement(ScoreBadge, { score }), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-mono text-slate-500 dark:text-white/40" }, pct, "/100")), /* @__PURE__ */ React8.createElement("div", { className: "h-3 rounded-full bg-slate-100 dark:bg-white/8 overflow-hidden" }, /* @__PURE__ */ React8.createElement(
905
+ "div",
906
+ {
907
+ className: "h-full rounded-full transition-all duration-700",
908
+ style: { width: `${pct}%`, background: barColor }
909
+ }
910
+ )), trace.hallucinationReason && /* @__PURE__ */ React8.createElement("div", { className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 p-2.5" }, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/40 font-medium mb-0.5" }, "AI self-assessment"), /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-700 dark:text-white/70 leading-relaxed" }, trace.hallucinationReason)), /* @__PURE__ */ React8.createElement("div", { className: "flex gap-2 text-[10px] text-slate-400 dark:text-white/30" }, /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-emerald-500" }), " 0\u201320%: Grounded"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-amber-500" }), " 21\u201350%: Uncertain"), /* @__PURE__ */ React8.createElement("span", { className: "flex items-center gap-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-block w-2 h-2 rounded-full bg-red-500" }), " 51\u2013100%: Risk")));
911
+ }
912
+ function PromptTab({ trace }) {
913
+ const [showSystem, setShowSystem] = React8.useState(false);
914
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement(
915
+ "button",
916
+ {
917
+ onClick: () => setShowSystem((s) => !s),
918
+ 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"
919
+ },
920
+ showSystem ? /* @__PURE__ */ React8.createElement(ChevronDown2, { className: "w-3 h-3" }) : /* @__PURE__ */ React8.createElement(ChevronRight2, { className: "w-3 h-3" }),
921
+ "System Prompt"
922
+ ), showSystem && /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-600 dark:text-white/60 bg-slate-900 dark:bg-black/30 rounded-lg p-3 overflow-auto max-h-40 whitespace-pre-wrap leading-relaxed" }, trace.systemPrompt || "(empty)")), /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] font-semibold text-slate-600 dark:text-white/60 mb-1.5" }, "User Prompt"), /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-600 dark:text-white/60 bg-slate-900 dark:bg-black/30 rounded-lg p-3 overflow-auto max-h-40 whitespace-pre-wrap leading-relaxed" }, trace.userPrompt)));
923
+ }
924
+ function ChunksTab({ trace }) {
925
+ const [expanded, setExpanded] = React8.useState(/* @__PURE__ */ new Set());
926
+ const toggle = (i) => setExpanded((prev) => {
927
+ const next = new Set(prev);
928
+ if (next.has(i)) {
929
+ next.delete(i);
930
+ } else {
931
+ next.add(i);
932
+ }
933
+ return next;
934
+ });
935
+ return /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 max-h-64 overflow-y-auto pr-1 custom-scrollbar" }, trace.chunks.map((chunk, i) => {
936
+ const isOpen = expanded.has(i);
937
+ return /* @__PURE__ */ React8.createElement(
938
+ "div",
939
+ {
940
+ key: String(chunk.id),
941
+ className: "rounded-lg border border-slate-100 dark:border-white/8 bg-slate-50 dark:bg-white/3 overflow-hidden"
942
+ },
943
+ /* @__PURE__ */ React8.createElement(
944
+ "button",
945
+ {
946
+ onClick: () => toggle(i),
947
+ 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"
948
+ },
949
+ /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2" }, isOpen ? /* @__PURE__ */ React8.createElement(ChevronDown2, { className: "w-3 h-3 text-slate-400" }) : /* @__PURE__ */ React8.createElement(ChevronRight2, { className: "w-3 h-3 text-slate-400" }), /* @__PURE__ */ React8.createElement("span", { className: "text-[11px] font-medium text-slate-700 dark:text-white/70" }, "Chunk ", i + 1), /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-mono text-slate-400 dark:text-white/30" }, String(chunk.id).slice(0, 16))),
950
+ /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] font-semibold text-indigo-600 dark:text-indigo-400" }, chunk.score.toFixed(3))
951
+ ),
952
+ isOpen && /* @__PURE__ */ React8.createElement("div", { className: "px-3 pb-3 space-y-2 border-t border-slate-100 dark:border-white/5 pt-2" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Content"), /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 leading-relaxed whitespace-pre-wrap" }, chunk.content)), Object.keys(chunk.metadata).length > 0 && /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("p", { className: "text-[10px] text-slate-400 dark:text-white/30 font-semibold uppercase tracking-wider mb-1" }, "Metadata"), /* @__PURE__ */ React8.createElement("pre", { className: "text-[10px] font-mono text-slate-500 dark:text-white/40 bg-slate-900 dark:bg-black/30 rounded-md p-2 overflow-auto max-h-24" }, JSON.stringify(chunk.metadata, null, 2))))
953
+ );
954
+ }));
955
+ }
956
+ var TABS = [
957
+ { id: "retrieval", label: "Retrieval", icon: /* @__PURE__ */ React8.createElement(Layers, { className: "w-3 h-3" }) },
958
+ { id: "latency", label: "Latency", icon: /* @__PURE__ */ React8.createElement(Clock, { className: "w-3 h-3" }) },
959
+ { id: "tokens", label: "Tokens", icon: /* @__PURE__ */ React8.createElement(Cpu, { className: "w-3 h-3" }) },
960
+ { id: "hallucination", label: "Grounding", icon: /* @__PURE__ */ React8.createElement(AlertTriangle, { className: "w-3 h-3" }) },
961
+ { id: "prompt", label: "Prompt", icon: /* @__PURE__ */ React8.createElement(FileText, { className: "w-3 h-3" }) },
962
+ { id: "chunks", label: "Chunks", icon: /* @__PURE__ */ React8.createElement(Zap, { className: "w-3 h-3" }) }
963
+ ];
964
+ function ObservabilityPanel({ trace, primaryColor = "#6366f1" }) {
965
+ const [activeTab, setActiveTab] = React8.useState("retrieval");
966
+ return /* @__PURE__ */ React8.createElement("div", { className: "mt-2 w-full rounded-xl border border-slate-200 dark:border-white/8 bg-white dark:bg-slate-900/60 shadow-sm overflow-hidden text-[12px]" }, /* @__PURE__ */ React8.createElement(
967
+ "div",
968
+ {
969
+ className: "flex items-center gap-2 px-3 py-2 border-b border-slate-100 dark:border-white/8",
970
+ style: { background: `${primaryColor}08` }
971
+ },
972
+ /* @__PURE__ */ React8.createElement(
973
+ "span",
974
+ {
975
+ className: "inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider",
976
+ style: { color: primaryColor }
977
+ },
978
+ /* @__PURE__ */ React8.createElement(Zap, { className: "w-3 h-3" }),
979
+ "Observability Trace"
980
+ ),
981
+ /* @__PURE__ */ React8.createElement("span", { className: "text-[10px] text-slate-400 dark:text-white/30 ml-auto font-mono" }, trace.requestId)
982
+ ), /* @__PURE__ */ React8.createElement("div", { className: "flex border-b border-slate-100 dark:border-white/8 overflow-x-auto" }, TABS.map((tab) => /* @__PURE__ */ React8.createElement(
983
+ "button",
984
+ {
985
+ key: tab.id,
986
+ onClick: () => setActiveTab(tab.id),
987
+ 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"}`,
988
+ style: activeTab === tab.id ? { borderBottomColor: primaryColor, color: primaryColor } : {}
989
+ },
990
+ tab.icon,
991
+ tab.label
992
+ ))), /* @__PURE__ */ React8.createElement("div", { className: "p-3" }, activeTab === "retrieval" && /* @__PURE__ */ React8.createElement(RetrievalTab, { trace }), activeTab === "latency" && /* @__PURE__ */ React8.createElement(LatencyTab, { trace }), activeTab === "tokens" && /* @__PURE__ */ React8.createElement(TokensTab, { trace }), activeTab === "hallucination" && /* @__PURE__ */ React8.createElement(HallucinationTab, { trace }), activeTab === "prompt" && /* @__PURE__ */ React8.createElement(PromptTab, { trace }), activeTab === "chunks" && /* @__PURE__ */ React8.createElement(ChunksTab, { trace })));
993
+ }
994
+
995
+ // src/components/HourglassLoader.tsx
996
+ import React9 from "react";
997
+ var HourglassLoader = ({
998
+ size = 16,
999
+ primaryColor = "#10b981",
1000
+ glow = false
1001
+ }) => {
1002
+ return /* @__PURE__ */ React9.createElement(
1003
+ "svg",
1004
+ {
1005
+ xmlns: "http://www.w3.org/2000/svg",
1006
+ viewBox: "0 0 100 100",
1007
+ preserveAspectRatio: "xMidYMid",
1008
+ style: {
1009
+ width: size,
1010
+ height: size,
1011
+ background: "transparent",
1012
+ filter: glow ? `drop-shadow(0 0 4px ${primaryColor})` : "none"
1013
+ }
1014
+ },
1015
+ /* @__PURE__ */ React9.createElement("g", null, /* @__PURE__ */ React9.createElement(
1016
+ "animateTransform",
1017
+ {
1018
+ attributeName: "transform",
1019
+ type: "rotate",
1020
+ calcMode: "spline",
1021
+ values: "0 50 50; 0 50 50; 180 50 50",
1022
+ keyTimes: "0; 0.8; 1",
1023
+ keySplines: "0 0 1 1; 0.4 0 0.2 1",
1024
+ dur: "2s",
1025
+ repeatCount: "indefinite"
1026
+ }
1027
+ ), /* @__PURE__ */ React9.createElement(
1028
+ "path",
1029
+ {
1030
+ 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",
1031
+ fill: "none",
1032
+ stroke: primaryColor,
1033
+ strokeWidth: "5",
1034
+ strokeLinejoin: "round",
1035
+ strokeLinecap: "round"
1036
+ }
1037
+ ), /* @__PURE__ */ React9.createElement("path", { fill: primaryColor }, /* @__PURE__ */ React9.createElement(
1038
+ "animate",
1039
+ {
1040
+ attributeName: "d",
1041
+ 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",
1042
+ keyTimes: "0; 0.8; 1",
1043
+ dur: "2s",
1044
+ repeatCount: "indefinite"
1045
+ }
1046
+ )), /* @__PURE__ */ React9.createElement("path", { fill: primaryColor }, /* @__PURE__ */ React9.createElement(
1047
+ "animate",
1048
+ {
1049
+ attributeName: "d",
1050
+ 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",
1051
+ keyTimes: "0; 0.8; 1",
1052
+ dur: "2s",
1053
+ repeatCount: "indefinite"
1054
+ }
1055
+ )), /* @__PURE__ */ React9.createElement("line", { x1: "50", y1: "50", x2: "50", y2: "80", stroke: primaryColor, strokeWidth: "3" }, /* @__PURE__ */ React9.createElement(
1056
+ "animate",
1057
+ {
1058
+ attributeName: "opacity",
1059
+ values: "0; 1; 1; 0; 0",
1060
+ keyTimes: "0; 0.1; 0.7; 0.8; 1",
1061
+ dur: "2s",
1062
+ repeatCount: "indefinite"
1063
+ }
1064
+ )))
1065
+ );
1066
+ };
1067
+ var HourglassLoader_default = HourglassLoader;
1068
+
1069
+ // src/utils/ProductExtractor.ts
584
1070
  function sanitizeJson(raw) {
585
1071
  let s = raw.trim();
586
1072
  s = s.replace(
@@ -597,36 +1083,34 @@ function sanitizeJson(raw) {
597
1083
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
598
1084
  s = s.replace(/,\s*([}\]])/g, "$1");
599
1085
  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
- }
1086
+ const stack = [];
1087
+ let inString = false;
1088
+ let escape = false;
1089
+ for (const ch of s) {
1090
+ if (escape) {
1091
+ escape = false;
1092
+ continue;
1093
+ }
1094
+ if (ch === "\\" && inString) {
1095
+ escape = true;
1096
+ continue;
625
1097
  }
626
- if (inString) s += '"';
627
- for (let i = stack.length - 1; i >= 0; i--) {
628
- s += stack[i] === "{" ? "}" : "]";
1098
+ if (ch === '"') {
1099
+ inString = !inString;
1100
+ continue;
629
1101
  }
1102
+ if (inString) continue;
1103
+ if (ch === "{" || ch === "[") {
1104
+ stack.push(ch);
1105
+ } else if (ch === "}") {
1106
+ if (stack[stack.length - 1] === "{") stack.pop();
1107
+ } else if (ch === "]") {
1108
+ if (stack[stack.length - 1] === "[") stack.pop();
1109
+ }
1110
+ }
1111
+ if (inString) s += '"';
1112
+ for (let i = stack.length - 1; i >= 0; i--) {
1113
+ s += stack[i] === "{" ? "}" : "]";
630
1114
  }
631
1115
  const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
632
1116
  if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
@@ -727,10 +1211,6 @@ function extractStructuredPayload(raw) {
727
1211
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
728
1212
  };
729
1213
  }
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
1214
  function resolveImage(data) {
735
1215
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
736
1216
  for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
@@ -740,92 +1220,140 @@ function resolveImage(data) {
740
1220
  }
741
1221
  return void 0;
742
1222
  }
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());
1223
+ function isLikelyUiOnlyMessage(text) {
1224
+ const trimmed = text.trim();
1225
+ 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
1226
  }
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
- )
1227
+ function extractProductsFromSources(sources, isUser) {
1228
+ if (isUser || !sources) return [];
1229
+ return sources.filter((s) => {
1230
+ var _a;
1231
+ const m = (_a = s.metadata) != null ? _a : {};
1232
+ const keys = Object.keys(m).map((k) => k.toLowerCase());
1233
+ const hasProductKey = keys.some(
1234
+ (k) => ["price", "image", "img", "thumbnail", "images", "brand", "product", "sku", "category", "model", "cost"].includes(k)
774
1235
  );
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;
1236
+ const hasPricePattern = /\$\s*\d+/.test(s.content);
1237
+ return hasProductKey || hasPricePattern || m.type === "product";
1238
+ }).map((s) => {
1239
+ var _a, _b;
1240
+ const m = (_a = s.metadata) != null ? _a : {};
1241
+ const name = resolveMetadataValue(m, "name");
1242
+ const brand = resolveMetadataValue(m, "brand");
1243
+ const price = resolveMetadataValue(m, "price");
1244
+ const description = resolveMetadataValue(m, "description");
1245
+ console.log("\u{1F680} ~ extractProductsFromSources ~ description:", s);
1246
+ return {
1247
+ id: s.id,
1248
+ name: (_b = name != null ? name : s.content.split("\n")[0]) != null ? _b : "Unknown Product",
1249
+ brand,
1250
+ price,
1251
+ image: resolveImage(m),
1252
+ link: resolveMetadataValue(m, "link"),
1253
+ description: description ? description : ""
1254
+ //description ?? s.content,
782
1255
  };
783
- if (Array.isArray(config.columns) && config.columns.length) {
784
- return config.columns.map((label) => ({
785
- label: formatColumnLabel(label),
786
- accessor: findAccessor(label)
787
- }));
1256
+ });
1257
+ }
1258
+ function extractProductsFromContent(content, payloadText, payloadCandidates, productsFromSources) {
1259
+ var _a, _b;
1260
+ const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
1261
+ const products = [];
1262
+ let cleanContent = payloadText;
1263
+ for (const candidate of payloadCandidates) {
1264
+ try {
1265
+ const data = JSON.parse(sanitizeJson(candidate));
1266
+ const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
1267
+ if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
1268
+ products.push(
1269
+ ...itemSet.map((item) => {
1270
+ var _a2;
1271
+ return __spreadProps(__spreadValues({}, item), {
1272
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1273
+ });
1274
+ })
1275
+ );
1276
+ }
1277
+ } catch (e) {
788
1278
  }
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
- }));
1279
+ }
1280
+ if (cleanContent.includes('"type": "products"') || cleanContent.includes('"type":"products"')) {
1281
+ for (const match of Array.from(cleanContent.matchAll(jsonRegex))) {
1282
+ try {
1283
+ const data = JSON.parse(sanitizeJson(match[1]));
1284
+ if (data.type === "products" && Array.isArray(data.items)) {
1285
+ products.push(
1286
+ ...data.items.map((item) => {
1287
+ var _a2;
1288
+ return __spreadProps(__spreadValues({}, item), {
1289
+ image: (_a2 = item.image) != null ? _a2 : resolveImage(item)
1290
+ });
1291
+ })
1292
+ );
1293
+ cleanContent = cleanContent.replace(match[0], "");
1294
+ }
1295
+ } catch (e) {
1296
+ }
795
1297
  }
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
1298
  }
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" : "")));
1299
+ const bulletRegex = /^[ \t]*(?:[•*-]|\d+\.)\s+(?:\*\*)?([^\n\-\$–—\(]+?)(?:\*\*)?(?:\s*\(?Price\s*[:\-–—]?\s*)?(?:\s*[:\-–—]\s*|\s+)\$?([\d,.]+)(?:\s*USD)?/gim;
1300
+ const matches = Array.from(cleanContent.matchAll(bulletRegex));
1301
+ if (matches.length >= 2) {
1302
+ const normalize = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
1303
+ for (const match of matches) {
1304
+ let name = ((_a = match[1]) == null ? void 0 : _a.trim()) || "";
1305
+ name = name.replace(/\s*\(?Price\s*$/i, "").replace(/[:\-–—]\s*$/g, "").trim();
1306
+ name = name.replace(/^\*\*|\*\*$/g, "").replace(/^\*|\*$/g, "").trim();
1307
+ let price = ((_b = match[2]) == null ? void 0 : _b.trim()) || "";
1308
+ price = price.replace(/[,\s]+$/, "").trim();
1309
+ if (name && price) {
1310
+ const newProduct = {
1311
+ id: `text-prod-${name}-${price}`,
1312
+ name,
1313
+ price: `$${price}`
1314
+ };
1315
+ const normName = normalize(name);
1316
+ const sourceMatch = productsFromSources.find((s) => {
1317
+ const sn = normalize(s.name);
1318
+ return sn.includes(normName) || normName.includes(sn);
1319
+ });
1320
+ if (sourceMatch) {
1321
+ newProduct.image = sourceMatch.image;
1322
+ newProduct.brand = sourceMatch.brand;
1323
+ newProduct.link = sourceMatch.link;
1324
+ newProduct.description = sourceMatch.description;
1325
+ }
1326
+ products.push(newProduct);
1327
+ cleanContent = cleanContent.replace(match[0], `
1328
+ **${name}** \u2014 $${price}`);
1329
+ }
1330
+ }
1331
+ cleanContent = cleanContent.replace(/\n{3,}/g, "\n\n").trim();
1332
+ }
1333
+ return { productsFromContent: products, cleanContent: cleanContent.trim() };
828
1334
  }
1335
+ function deduplicateProducts(productsFromSources, productsFromContent, messageContent) {
1336
+ if (productsFromContent.length > 0) return productsFromContent;
1337
+ const seen = /* @__PURE__ */ new Set();
1338
+ const contentLower = messageContent.toLowerCase();
1339
+ return productsFromSources.filter((p) => {
1340
+ var _a;
1341
+ const id = String((_a = p.id) != null ? _a : p.name);
1342
+ if (seen.has(id)) return false;
1343
+ const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
1344
+ if (mentioned) {
1345
+ seen.add(id);
1346
+ return true;
1347
+ }
1348
+ return false;
1349
+ });
1350
+ }
1351
+
1352
+ // src/components/MarkdownComponents.tsx
1353
+ import React11 from "react";
1354
+
1355
+ // src/components/UIDispatcher.tsx
1356
+ import React10 from "react";
829
1357
  function normalizeTabularRows(rows, columns) {
830
1358
  if (!Array.isArray(rows)) return [];
831
1359
  const SYNONYMS = {
@@ -893,8 +1421,7 @@ function resolveStructuredRows(config) {
893
1421
  }
894
1422
  function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
895
1423
  var _a;
896
- const result = React6.useMemo(() => {
897
- if (isStreaming) return { loading: true };
1424
+ const result = React10.useMemo(() => {
898
1425
  try {
899
1426
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
900
1427
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
@@ -923,10 +1450,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
923
1450
  } 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
1451
  config2.view = "chart";
925
1452
  config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
926
- } else if (viewStr.includes("table")) {
1453
+ } else if (viewStr.includes("table") || config2.type === "table") {
927
1454
  config2.view = "table";
928
1455
  } else {
929
- config2.view = "table";
1456
+ config2.view = "none";
930
1457
  }
931
1458
  }
932
1459
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
@@ -934,22 +1461,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
934
1461
  });
935
1462
  return { config: normalizedConfig };
936
1463
  } catch (err) {
1464
+ if (isStreaming) return { loading: true };
937
1465
  return { error: String(err) };
938
1466
  }
939
1467
  }, [rawContent, isStreaming]);
940
1468
  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..."));
1469
+ return /* @__PURE__ */ React10.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__ */ React10.createElement("div", { className: "w-5 h-5 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React10.createElement("p", { className: "text-xs text-slate-500 font-medium italic" }, "Preparing view..."));
942
1470
  }
943
1471
  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));
1472
+ return /* @__PURE__ */ React10.createElement("pre", { className: "p-4 my-2 bg-slate-900 text-slate-100 rounded-lg text-[10px] overflow-auto max-h-40" }, /* @__PURE__ */ React10.createElement("code", null, rawContent));
945
1473
  }
946
1474
  const { config } = result;
947
1475
  const isCompact = viewportSize === "compact";
948
1476
  const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
949
1477
  const hasDescription = typeof config.description === "string" && config.description.trim().length > 0;
1478
+ console.log("\u{1F680} ~ UIDispatcher ~ description:", config.description);
1479
+ console.log("\u{1F680} ~ UIDispatcher ~ hasDescription:", hasDescription);
950
1480
  switch (config.view) {
951
1481
  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(
1482
+ return /* @__PURE__ */ React10.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__ */ React10.createElement("h4", { className: `${isCompact ? "text-[11px] mb-3" : "text-xs mb-4"} font-semibold text-slate-500 px-2` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `px-2 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(
953
1483
  DynamicChart,
954
1484
  {
955
1485
  config: {
@@ -961,9 +1491,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
961
1491
  },
962
1492
  primaryColor,
963
1493
  accentColor,
964
- viewportSize
1494
+ viewportSize,
1495
+ isStreaming
965
1496
  }
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(
1497
+ ), hasInsights && /* @__PURE__ */ React10.createElement("div", { className: "mt-4 flex flex-wrap gap-2 px-2" }, (_a = config.insights) == null ? void 0 : _a.map((insight, i) => /* @__PURE__ */ React10.createElement(
967
1498
  "span",
968
1499
  {
969
1500
  key: `insight-${i}`,
@@ -972,26 +1503,151 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
972
1503
  String(insight)
973
1504
  ))));
974
1505
  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) => {
1506
+ return /* @__PURE__ */ React10.createElement("div", { className: "my-4" }, config.title && /* @__PURE__ */ React10.createElement("h4", { className: `${isCompact ? "mb-1.5 text-[11px]" : "mb-2 text-xs"} font-semibold text-slate-500` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(ProductCarousel, { products: config.data.map((item) => {
976
1507
  var _a2;
977
1508
  return __spreadProps(__spreadValues({}, item), {
978
1509
  image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
979
1510
  });
980
1511
  }), primaryColor, onAddToCart }));
981
1512
  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 }));
1513
+ return /* @__PURE__ */ React10.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__ */ React10.createElement("h4", { className: `${isCompact ? "text-[11px]" : "text-xs"} font-semibold text-slate-500 mb-2` }, config.title), hasDescription && /* @__PURE__ */ React10.createElement("p", { className: `mb-3 ${isCompact ? "text-xs" : "text-sm"} text-slate-500 dark:text-white/60` }, config.description), /* @__PURE__ */ React10.createElement(
1514
+ VisualizationRenderer,
1515
+ {
1516
+ data: {
1517
+ type: "table",
1518
+ title: config.title || "",
1519
+ description: config.description,
1520
+ data: {
1521
+ columns: config.columns || Object.keys(config.data[0] || {}),
1522
+ rows: config.data.map((row) => Object.values(row))
1523
+ }
1524
+ },
1525
+ primaryColor,
1526
+ isStreaming
1527
+ }
1528
+ ));
991
1529
  default:
992
1530
  return null;
993
1531
  }
994
1532
  }
1533
+
1534
+ // src/components/MarkdownComponents.tsx
1535
+ function createMarkdownComponents({
1536
+ primaryColor,
1537
+ accentColor,
1538
+ isStreaming,
1539
+ onAddToCart,
1540
+ viewportSize
1541
+ }) {
1542
+ return {
1543
+ table: (_a) => {
1544
+ var props = __objRest(_a, []);
1545
+ if (isStreaming) {
1546
+ return /* @__PURE__ */ React11.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__ */ React11.createElement("div", { className: "w-4 h-4 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin" }), /* @__PURE__ */ React11.createElement("p", { className: "text-xs text-slate-500 font-medium italic animate-pulse" }, "Generating data table..."));
1547
+ }
1548
+ return /* @__PURE__ */ React11.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__ */ React11.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React11.createElement(
1549
+ "table",
1550
+ __spreadValues({
1551
+ className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
1552
+ }, props)
1553
+ )));
1554
+ },
1555
+ thead: (_b) => {
1556
+ var props = __objRest(_b, []);
1557
+ return /* @__PURE__ */ React11.createElement(
1558
+ "thead",
1559
+ __spreadValues({
1560
+ className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
1561
+ }, props)
1562
+ );
1563
+ },
1564
+ tbody: (_c) => {
1565
+ var props = __objRest(_c, []);
1566
+ return /* @__PURE__ */ React11.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1567
+ },
1568
+ tr: (_d) => {
1569
+ var props = __objRest(_d, []);
1570
+ return /* @__PURE__ */ React11.createElement(
1571
+ "tr",
1572
+ __spreadValues({
1573
+ className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
1574
+ }, props)
1575
+ );
1576
+ },
1577
+ th: (_e) => {
1578
+ var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
1579
+ return /* @__PURE__ */ React11.createElement(
1580
+ "th",
1581
+ __spreadValues({
1582
+ 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"
1583
+ }, props),
1584
+ normaliseChild(children)
1585
+ );
1586
+ },
1587
+ td: (_g) => {
1588
+ var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
1589
+ return /* @__PURE__ */ React11.createElement(
1590
+ "td",
1591
+ __spreadValues({
1592
+ className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
1593
+ }, props),
1594
+ normaliseChild(children)
1595
+ );
1596
+ },
1597
+ code(_i) {
1598
+ var _j = _i, {
1599
+ inline,
1600
+ className,
1601
+ children
1602
+ } = _j, props = __objRest(_j, [
1603
+ "inline",
1604
+ "className",
1605
+ "children"
1606
+ ]);
1607
+ var _a;
1608
+ const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
1609
+ if (!inline && (lang === "ui" || lang === "chart" || lang === "json")) {
1610
+ const content = String(children != null ? children : "").trim();
1611
+ return /* @__PURE__ */ React11.createElement(
1612
+ UIDispatcher,
1613
+ {
1614
+ rawContent: content,
1615
+ primaryColor,
1616
+ accentColor,
1617
+ isStreaming,
1618
+ onAddToCart,
1619
+ viewportSize
1620
+ }
1621
+ );
1622
+ }
1623
+ if (!inline) {
1624
+ const content = String(children != null ? children : "").trim();
1625
+ if (content.startsWith("{") && content.endsWith("}")) {
1626
+ return /* @__PURE__ */ React11.createElement(
1627
+ UIDispatcher,
1628
+ {
1629
+ rawContent: content,
1630
+ primaryColor,
1631
+ accentColor,
1632
+ isStreaming,
1633
+ onAddToCart,
1634
+ viewportSize
1635
+ }
1636
+ );
1637
+ }
1638
+ }
1639
+ return /* @__PURE__ */ React11.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
1640
+ }
1641
+ };
1642
+ }
1643
+ function normaliseChild(children) {
1644
+ if (typeof children === "object" && children !== null && !Array.isArray(children) && !React11.isValidElement(children)) {
1645
+ return JSON.stringify(children);
1646
+ }
1647
+ return children;
1648
+ }
1649
+
1650
+ // src/components/MessageBubble.tsx
995
1651
  function MessageBubble({
996
1652
  message,
997
1653
  sources,
@@ -1004,12 +1660,13 @@ function MessageBubble({
1004
1660
  const isUser = message.role === "user";
1005
1661
  const isCompact = viewportSize === "compact";
1006
1662
  const isMedium = viewportSize === "medium";
1007
- const [showSources, setShowSources] = React6.useState(false);
1008
- const structuredContent = React6.useMemo(
1663
+ const [showSources, setShowSources] = React12.useState(false);
1664
+ const [showTrace, setShowTrace] = React12.useState(false);
1665
+ const structuredContent = React12.useMemo(
1009
1666
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
1010
1667
  [isUser, message.content]
1011
1668
  );
1012
- const hasRichUI = React6.useMemo(() => {
1669
+ const hasRichUI = React12.useMemo(() => {
1013
1670
  if (message.uiTransformation) {
1014
1671
  const type = message.uiTransformation.type;
1015
1672
  if (type && !["text", "table"].includes(type)) return true;
@@ -1020,131 +1677,28 @@ function MessageBubble({
1020
1677
  return false;
1021
1678
  }, [message.uiTransformation, structuredContent.payload]);
1022
1679
  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
- });
1680
+ const productsFromSources = React12.useMemo(() => {
1681
+ return extractProductsFromSources(sources, isUser);
1050
1682
  }, [sources, isUser]);
1051
- const { productsFromContent, cleanContent } = React6.useMemo(() => {
1052
- var _a, _b;
1683
+ const { productsFromContent, cleanContent } = React12.useMemo(() => {
1053
1684
  if (isUser) {
1054
1685
  return { productsFromContent: [], cleanContent: message.content };
1055
1686
  }
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() };
1687
+ const payloadCandidates = [
1688
+ structuredContent.payload,
1689
+ ...Array.from(structuredContent.text.matchAll(/```(?:json|ui)?\s*([\s\S]*?)\s*```/g)).map((m) => m[1])
1690
+ ].filter(Boolean);
1691
+ return extractProductsFromContent(
1692
+ message.content,
1693
+ structuredContent.text,
1694
+ payloadCandidates,
1695
+ productsFromSources
1696
+ );
1130
1697
  }, [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
- });
1698
+ const allProducts = React12.useMemo(() => {
1699
+ return deduplicateProducts(productsFromSources, productsFromContent, message.content);
1146
1700
  }, [productsFromSources, productsFromContent, message.content]);
1147
- const processedMarkdown = React6.useMemo(() => {
1701
+ const processedMarkdown = React12.useMemo(() => {
1148
1702
  let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
1149
1703
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
1150
1704
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
@@ -1165,156 +1719,26 @@ ${match.trim()}
1165
1719
  if (hasStructuredUiBlock) {
1166
1720
  raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
1167
1721
  }
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
1722
  return raw;
1180
1723
  }, [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
- }),
1724
+ const markdownComponents = React12.useMemo(
1725
+ () => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
1302
1726
  [primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
1303
1727
  );
1304
- return /* @__PURE__ */ React6.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React6.createElement(
1728
+ return /* @__PURE__ */ React12.createElement("div", { className: `flex ${isCompact ? "gap-2" : "gap-3"} ${isUser ? "flex-row-reverse" : "flex-row"} items-start` }, /* @__PURE__ */ React12.createElement(
1305
1729
  "div",
1306
1730
  {
1307
1731
  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
1732
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1309
1733
  },
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(
1734
+ isUser ? /* @__PURE__ */ React12.createElement(User, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"} text-white` }) : /* @__PURE__ */ React12.createElement(Bot, { className: `${isCompact ? "w-3.5 h-3.5" : "w-4 h-4"}` })
1735
+ ), /* @__PURE__ */ React12.createElement("div", { className: `flex flex-col gap-1 min-w-0 ${isCompact ? "max-w-[94%]" : isMedium ? "max-w-[92%]" : "max-w-[90%]"} ${isUser ? "items-end" : "items-start"}` }, /* @__PURE__ */ React12.createElement(
1312
1736
  "div",
1313
1737
  {
1314
1738
  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
1739
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1316
1740
  },
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(
1741
+ isStreaming && !message.content ? /* @__PURE__ */ React12.createElement("span", { className: "flex items-center gap-1 py-1" }, /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }), /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }), /* @__PURE__ */ React12.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })) : /* @__PURE__ */ React12.createElement("div", { className: `prose ${isCompact ? "prose-xs" : "prose-sm"} max-w-none break-words ${isUser ? "prose-invert" : "dark:prose-invert"}` }, !isUser && structuredContent.payload && /* @__PURE__ */ React12.createElement(
1318
1742
  UIDispatcher,
1319
1743
  {
1320
1744
  rawContent: structuredContent.payload,
@@ -1324,7 +1748,15 @@ ${match.trim()}
1324
1748
  onAddToCart,
1325
1749
  viewportSize
1326
1750
  }
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" }))
1751
+ ), !shouldRenderStructuredOnly && /* @__PURE__ */ React12.createElement(ReactMarkdown, { components: markdownComponents }, processedMarkdown), isStreaming && /* @__PURE__ */ React12.createElement("span", { className: "inline-flex items-center gap-1.5 ml-2 text-emerald-500 animate-pulse align-middle text-xs font-medium" }, /* @__PURE__ */ React12.createElement(
1752
+ HourglassLoader_default,
1753
+ {
1754
+ size: 18,
1755
+ primaryColor,
1756
+ accentColor,
1757
+ glow: false
1758
+ }
1759
+ ), "Thinking..."))
1328
1760
  ), (() => {
1329
1761
  var _a, _b;
1330
1762
  if (isUser || structuredContent.payload || !message.uiTransformation) return null;
@@ -1332,95 +1764,50 @@ ${match.trim()}
1332
1764
  const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
1333
1765
  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
1766
  if (!shouldShow) return null;
1335
- return /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ React6.createElement(
1767
+ return /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ React12.createElement(
1336
1768
  VisualizationRenderer,
1337
1769
  {
1338
1770
  data: ui,
1339
1771
  primaryColor,
1340
1772
  onAddToCart,
1341
- className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4"
1773
+ className: "rounded-3xl border border-slate-200/60 dark:border-white/10 bg-white/90 dark:bg-slate-900/80 p-4",
1774
+ isStreaming
1342
1775
  }
1343
1776
  ));
1344
- })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React6.createElement(
1777
+ })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React12.createElement(
1345
1778
  ProductCarousel,
1346
1779
  {
1347
1780
  products: allProducts,
1348
1781
  primaryColor,
1349
1782
  onAddToCart
1350
1783
  }
1351
- )), !isUser && sources && sources.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "w-full" }, /* @__PURE__ */ React6.createElement(
1784
+ )), !isUser && /* @__PURE__ */ React12.createElement("div", { className: "flex items-center gap-4 mt-1 w-full flex-wrap" }, sources && sources.length > 0 && /* @__PURE__ */ React12.createElement(
1352
1785
  "button",
1353
1786
  {
1354
1787
  onClick: () => setShowSources((s) => !s),
1355
- className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1 mt-1"
1788
+ className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1"
1356
1789
  },
1357
- showSources ? /* @__PURE__ */ React6.createElement(ChevronDown2, { className: "w-3 h-3" }) : /* @__PURE__ */ React6.createElement(ChevronRight2, { className: "w-3 h-3" }),
1790
+ showSources ? /* @__PURE__ */ React12.createElement(ChevronDown3, { className: "w-3 h-3" }) : /* @__PURE__ */ React12.createElement(ChevronRight3, { className: "w-3 h-3" }),
1358
1791
  sources.length,
1359
1792
  " source",
1360
1793
  sources.length !== 1 ? "s" : "",
1361
1794
  " 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
- ];
1402
-
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);
1795
+ ), message.trace && /* @__PURE__ */ React12.createElement(
1796
+ "button",
1797
+ {
1798
+ onClick: () => setShowTrace((t) => !t),
1799
+ className: "text-[11px] text-emerald-500 hover:text-emerald-400 transition-colors flex items-center gap-1"
1800
+ },
1801
+ /* @__PURE__ */ React12.createElement(Activity, { className: "w-3 h-3" }),
1802
+ showTrace ? "Hide Trace" : "Trace"
1803
+ )), /* @__PURE__ */ React12.createElement("div", { className: "w-full flex flex-col gap-2 min-w-0" }, showSources && sources && sources.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "mt-1 flex flex-col gap-2" }, sources.map((src, i) => /* @__PURE__ */ React12.createElement(SourceCard, { key: src.id, source: src, index: i }))), showTrace && message.trace && /* @__PURE__ */ React12.createElement(ObservabilityPanel, { trace: message.trace, primaryColor }))));
1417
1804
  }
1418
1805
 
1419
1806
  // src/hooks/useRagChat.ts
1420
- import { useState as useState3, useCallback, useRef as useRef2, useEffect as useEffect3 } from "react";
1807
+ import { useState as useState4, useCallback, useRef as useRef3, useEffect as useEffect3 } from "react";
1421
1808
 
1422
1809
  // src/hooks/useStoredMessages.ts
1423
- import * as React8 from "react";
1810
+ import * as React13 from "react";
1424
1811
  function readStoredMessages(storageKey) {
1425
1812
  if (typeof window === "undefined") {
1426
1813
  return [];
@@ -1437,8 +1824,8 @@ function readStoredMessages(storageKey) {
1437
1824
  }
1438
1825
  }
1439
1826
  function useStoredMessages(storageKey, persist) {
1440
- const [messages, setMessages] = React8.useState(() => persist ? readStoredMessages(storageKey) : []);
1441
- React8.useEffect(() => {
1827
+ const [messages, setMessages] = React13.useState(() => persist ? readStoredMessages(storageKey) : []);
1828
+ React13.useEffect(() => {
1442
1829
  if (!persist || typeof window === "undefined") {
1443
1830
  return;
1444
1831
  }
@@ -1478,10 +1865,10 @@ function useRagChat(projectId, options = {}) {
1478
1865
  } = options;
1479
1866
  const storageKey = `rag_chat_${projectId}`;
1480
1867
  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);
1868
+ const [isLoading, setIsLoading] = useState4(false);
1869
+ const [error, setError] = useState4(null);
1870
+ const lastInputRef = useRef3("");
1871
+ const messagesRef = useRef3(messages);
1485
1872
  useEffect3(() => {
1486
1873
  messagesRef.current = messages;
1487
1874
  }, [messages]);
@@ -1525,6 +1912,7 @@ function useRagChat(projectId, options = {}) {
1525
1912
  let assistantContent = "";
1526
1913
  let sources = [];
1527
1914
  let uiTransformation = null;
1915
+ let trace;
1528
1916
  let buffer = "";
1529
1917
  const assistantMessageId = `assistant_${Date.now()}`;
1530
1918
  setMessages((prev) => [
@@ -1551,6 +1939,8 @@ function useRagChat(projectId, options = {}) {
1551
1939
  sources = (_a = frame.sources) != null ? _a : [];
1552
1940
  } else if (frame.type === "ui_transformation") {
1553
1941
  uiTransformation = frame.data;
1942
+ } else if (frame.type === "observability") {
1943
+ trace = frame.data;
1554
1944
  } else if (frame.type === "error") {
1555
1945
  setError(frame.error || "Stream error");
1556
1946
  }
@@ -1569,17 +1959,22 @@ function useRagChat(projectId, options = {}) {
1569
1959
  for (const frame of parseSseChunk(buffer)) {
1570
1960
  if (frame.type === "text" && frame.text) assistantContent += frame.text;
1571
1961
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
1962
+ else if (frame.type === "observability") trace = frame.data;
1572
1963
  }
1573
1964
  }
1574
- const finalAssistantMessage = {
1965
+ const finalMsg = {
1575
1966
  id: assistantMessageId,
1576
1967
  role: "assistant",
1577
1968
  content: assistantContent,
1578
1969
  sources: sources.length > 0 ? sources : void 0,
1579
- uiTransformation: uiTransformation || void 0,
1970
+ uiTransformation: uiTransformation != null ? uiTransformation : void 0,
1971
+ trace,
1580
1972
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1581
1973
  };
1582
- onReply == null ? void 0 : onReply(finalAssistantMessage);
1974
+ setMessages(
1975
+ (prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMsg : msg)
1976
+ );
1977
+ onReply == null ? void 0 : onReply(finalMsg);
1583
1978
  } catch (err) {
1584
1979
  const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
1585
1980
  setError(msg);
@@ -1629,19 +2024,20 @@ function ChatWindow({
1629
2024
  }) {
1630
2025
  var _a;
1631
2026
  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);
2027
+ const [input, setInput] = useState5("");
2028
+ const [mounted, setMounted] = useState5(false);
2029
+ const [viewportSize, setViewportSize] = useState5("large");
2030
+ const windowRef = useRef4(null);
2031
+ const bottomRef = useRef4(null);
2032
+ const inputRef = useRef4(null);
1638
2033
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
1639
2034
  namespace: projectId
1640
2035
  });
1641
- const [suggestions, setSuggestions] = useState4([]);
1642
- const [isSuggesting, setIsSuggesting] = useState4(false);
1643
- const [isListening, setIsListening] = useState4(false);
1644
- const recognitionRef = useRef3(null);
2036
+ const [suggestions, setSuggestions] = useState5([]);
2037
+ const [isSuggesting, setIsSuggesting] = useState5(false);
2038
+ const [isListening, setIsListening] = useState5(false);
2039
+ const [isUploadModalOpen, setIsUploadModalOpen] = useState5(false);
2040
+ const recognitionRef = useRef4(null);
1645
2041
  useEffect4(() => {
1646
2042
  if (typeof window !== "undefined") {
1647
2043
  const win = window;
@@ -1767,84 +2163,84 @@ function ChatWindow({
1767
2163
  }, 800);
1768
2164
  return () => clearTimeout(timer);
1769
2165
  }, [input, projectId]);
1770
- return /* @__PURE__ */ React9.createElement(
2166
+ return /* @__PURE__ */ React14.createElement(
1771
2167
  "div",
1772
2168
  {
1773
2169
  ref: windowRef,
1774
2170
  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
2171
  style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
1776
2172
  },
1777
- onResizeStart && /* @__PURE__ */ React9.createElement(
2173
+ onResizeStart && /* @__PURE__ */ React14.createElement(
1778
2174
  "div",
1779
2175
  {
1780
2176
  onMouseDown: onResizeStart,
1781
2177
  className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
1782
2178
  title: "Drag to resize"
1783
2179
  },
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]" })
2180
+ /* @__PURE__ */ React14.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
2181
  ),
1786
- /* @__PURE__ */ React9.createElement(
2182
+ /* @__PURE__ */ React14.createElement(
1787
2183
  "div",
1788
2184
  {
1789
2185
  className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
1790
2186
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
1791
2187
  },
1792
- /* @__PURE__ */ React9.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
2188
+ /* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-3" }, ui.logoUrl ? (
1793
2189
  // 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(
2190
+ /* @__PURE__ */ React14.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
2191
+ ) : /* @__PURE__ */ React14.createElement(
1796
2192
  "div",
1797
2193
  {
1798
2194
  className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
1799
2195
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1800
2196
  },
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(
2197
+ /* @__PURE__ */ React14.createElement(Bot2, { className: "w-5 h-5 text-white" })
2198
+ ), /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight" }, ui.title), ui.poweredBy && /* @__PURE__ */ React14.createElement("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight" }, `Powered by ${ui.poweredBy}`))),
2199
+ /* @__PURE__ */ React14.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ React14.createElement("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400 mr-2" }, /* @__PURE__ */ React14.createElement("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }), "Online"), /* @__PURE__ */ React14.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__ */ React14.createElement(
1804
2200
  "button",
1805
2201
  {
1806
2202
  onClick: clearHistory,
1807
2203
  title: "Clear conversation",
1808
2204
  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
2205
  },
1810
- /* @__PURE__ */ React9.createElement(Trash2, { className: "w-3.5 h-3.5" })
1811
- ), isResized && onResetResize && /* @__PURE__ */ React9.createElement(
2206
+ /* @__PURE__ */ React14.createElement(Trash2, { className: "w-3.5 h-3.5" })
2207
+ ), isResized && onResetResize && /* @__PURE__ */ React14.createElement(
1812
2208
  "button",
1813
2209
  {
1814
2210
  onClick: onResetResize,
1815
2211
  title: "Reset to default size",
1816
2212
  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
2213
  },
1818
- /* @__PURE__ */ React9.createElement(RotateCcw, { className: "w-3.5 h-3.5" })
1819
- ), onMaximize && /* @__PURE__ */ React9.createElement(
2214
+ /* @__PURE__ */ React14.createElement(RotateCcw, { className: "w-3.5 h-3.5" })
2215
+ ), onMaximize && /* @__PURE__ */ React14.createElement(
1820
2216
  "button",
1821
2217
  {
1822
2218
  onClick: onMaximize,
1823
2219
  title: isMaximized ? "Minimize" : "Maximize",
1824
2220
  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
2221
  },
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(
2222
+ isMaximized ? /* @__PURE__ */ React14.createElement(Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ React14.createElement(Maximize2, { className: "w-3.5 h-3.5" })
2223
+ ), showClose && onClose && /* @__PURE__ */ React14.createElement(
1828
2224
  "button",
1829
2225
  {
1830
2226
  onClick: onClose,
1831
2227
  title: "Close chat",
1832
2228
  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
2229
  },
1834
- /* @__PURE__ */ React9.createElement(X, { className: "w-4 h-4" })
2230
+ /* @__PURE__ */ React14.createElement(X2, { className: "w-4 h-4" })
1835
2231
  )))
1836
2232
  ),
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 ? (
2233
+ /* @__PURE__ */ React14.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
2234
  /* 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(
2235
+ /* @__PURE__ */ React14.createElement("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12" }, /* @__PURE__ */ React14.createElement(
1840
2236
  "div",
1841
2237
  {
1842
2238
  className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
1843
2239
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1844
2240
  },
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(
2241
+ /* @__PURE__ */ React14.createElement(Sparkles, { className: "w-8 h-8 text-white" })
2242
+ ), /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed" }, ui.welcomeMessage), /* @__PURE__ */ React14.createElement("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2" }, "Ask a question to get started")), /* @__PURE__ */ React14.createElement("div", { className: "flex flex-wrap gap-2 justify-center mt-2" }, CHAT_SUGGESTIONS.map(
2243
+ (suggestion) => /* @__PURE__ */ React14.createElement(
1848
2244
  "button",
1849
2245
  {
1850
2246
  key: suggestion,
@@ -1858,7 +2254,7 @@ function ChatWindow({
1858
2254
  suggestion
1859
2255
  )
1860
2256
  )))
1861
- ) : messages.map((msg, index) => /* @__PURE__ */ React9.createElement(
2257
+ ) : messages.map((msg, index) => /* @__PURE__ */ React14.createElement(
1862
2258
  MessageBubble,
1863
2259
  {
1864
2260
  key: msg.id,
@@ -1870,7 +2266,7 @@ function ChatWindow({
1870
2266
  onAddToCart,
1871
2267
  viewportSize
1872
2268
  }
1873
- )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ React9.createElement(
2269
+ )), isLoading && ((_a = messages[messages.length - 1]) == null ? void 0 : _a.role) !== "assistant" && /* @__PURE__ */ React14.createElement(
1874
2270
  MessageBubble,
1875
2271
  {
1876
2272
  message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
@@ -1880,8 +2276,22 @@ function ChatWindow({
1880
2276
  onAddToCart,
1881
2277
  viewportSize
1882
2278
  }
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(
2279
+ ), error && /* @__PURE__ */ React14.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__ */ React14.createElement(TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }), /* @__PURE__ */ React14.createElement("span", null, error)), /* @__PURE__ */ React14.createElement("div", { ref: bottomRef })),
2280
+ isUploadModalOpen && /* @__PURE__ */ React14.createElement("div", { className: "absolute inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/50 backdrop-blur-sm rounded-inherit" }, /* @__PURE__ */ React14.createElement("div", { className: "relative w-full max-w-md bg-white dark:bg-[#0f0f1a] rounded-2xl shadow-2xl overflow-hidden animate-in zoom-in-95 duration-200" }, /* @__PURE__ */ React14.createElement(
2281
+ "button",
2282
+ {
2283
+ onClick: () => setIsUploadModalOpen(false),
2284
+ 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"
2285
+ },
2286
+ /* @__PURE__ */ React14.createElement(X2, { className: "w-5 h-5" })
2287
+ ), /* @__PURE__ */ React14.createElement(
2288
+ DocumentUpload,
2289
+ {
2290
+ namespace: projectId,
2291
+ onUploadComplete: () => setIsUploadModalOpen(false)
2292
+ }
2293
+ ))),
2294
+ /* @__PURE__ */ React14.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__ */ React14.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__ */ React14.createElement(
1885
2295
  "button",
1886
2296
  {
1887
2297
  key: suggestion,
@@ -1893,9 +2303,9 @@ function ChatWindow({
1893
2303
  },
1894
2304
  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
2305
  },
1896
- /* @__PURE__ */ React9.createElement(Sparkles, { className: "w-3 h-3 text-amber-400" }),
2306
+ /* @__PURE__ */ React14.createElement(Sparkles, { className: "w-3 h-3 text-amber-400" }),
1897
2307
  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(
2308
+ )), isSuggesting && suggestions.length === 0 && /* @__PURE__ */ React14.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__ */ React14.createElement(Sparkles, { className: "w-3 h-3" }), "Thinking...")), /* @__PURE__ */ React14.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__ */ React14.createElement(
1899
2309
  "textarea",
1900
2310
  {
1901
2311
  ref: inputRef,
@@ -1914,7 +2324,7 @@ function ChatWindow({
1914
2324
  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
2325
  style: { scrollbarWidth: "none" }
1916
2326
  }
1917
- ), ui.enableVoiceInput !== false && /* @__PURE__ */ React9.createElement(
2327
+ ), ui.enableVoiceInput !== false && /* @__PURE__ */ React14.createElement(
1918
2328
  "button",
1919
2329
  {
1920
2330
  type: "button",
@@ -1923,8 +2333,17 @@ function ChatWindow({
1923
2333
  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
2334
  title: isListening ? "Stop listening" : "Start voice input"
1925
2335
  },
1926
- isListening ? /* @__PURE__ */ React9.createElement(MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ React9.createElement(Mic, { className: "w-4 h-4" })
1927
- ), /* @__PURE__ */ React9.createElement(
2336
+ isListening ? /* @__PURE__ */ React14.createElement(MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ React14.createElement(Mic, { className: "w-4 h-4" })
2337
+ ), ui.allowUpload !== false && /* @__PURE__ */ React14.createElement(
2338
+ "button",
2339
+ {
2340
+ type: "button",
2341
+ onClick: () => setIsUploadModalOpen(true),
2342
+ 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",
2343
+ title: "Upload Document"
2344
+ },
2345
+ /* @__PURE__ */ React14.createElement(Paperclip, { className: "w-4 h-4" })
2346
+ ), /* @__PURE__ */ React14.createElement(
1928
2347
  "button",
1929
2348
  {
1930
2349
  onClick: sendMessage,
@@ -1935,8 +2354,8 @@ function ChatWindow({
1935
2354
  // slate-400/20 for light mode disabled
1936
2355
  }
1937
2356
  },
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"))
2357
+ /* @__PURE__ */ React14.createElement(ArrowUp, { className: "w-4 h-4 text-white" })
2358
+ )), /* @__PURE__ */ React14.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"))
1940
2359
  );
1941
2360
  }
1942
2361
 
@@ -1944,12 +2363,12 @@ function ChatWindow({
1944
2363
  var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
1945
2364
  function ChatWidget({ position = "bottom-right", onAddToCart }) {
1946
2365
  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);
2366
+ const [isOpen, setIsOpen] = useState6(false);
2367
+ const [hasUnread, setHasUnread] = useState6(false);
2368
+ const [dimensions, setDimensions] = useState6(DEFAULT_DIMENSIONS);
2369
+ const [isResizing, setIsResizing] = useState6(false);
2370
+ const [isMaximized, setIsMaximized] = useState6(false);
2371
+ const [prevDimensions, setPrevDimensions] = useState6(DEFAULT_DIMENSIONS);
1953
2372
  if (ui.showWidget === false) return null;
1954
2373
  const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
1955
2374
  const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
@@ -1999,7 +2418,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
1999
2418
  }
2000
2419
  };
2001
2420
  const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
2002
- return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
2421
+ return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
2003
2422
  "div",
2004
2423
  {
2005
2424
  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"}`,
@@ -2009,7 +2428,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2009
2428
  maxHeight: "calc(100vh - 6rem)"
2010
2429
  }
2011
2430
  },
2012
- /* @__PURE__ */ React10.createElement(
2431
+ /* @__PURE__ */ React15.createElement(
2013
2432
  ChatWindow,
2014
2433
  {
2015
2434
  className: "h-full relative z-10",
@@ -2023,13 +2442,13 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2023
2442
  onAddToCart
2024
2443
  }
2025
2444
  ),
2026
- /* @__PURE__ */ React10.createElement(
2445
+ /* @__PURE__ */ React15.createElement(
2027
2446
  "div",
2028
2447
  {
2029
2448
  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
2449
  }
2031
2450
  )
2032
- ), /* @__PURE__ */ React10.createElement(
2451
+ ), /* @__PURE__ */ React15.createElement(
2033
2452
  "button",
2034
2453
  {
2035
2454
  onClick: isOpen ? () => setIsOpen(false) : handleOpen,
@@ -2037,156 +2456,32 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
2037
2456
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
2038
2457
  "aria-label": "Open chat"
2039
2458
  },
2040
- /* @__PURE__ */ React10.createElement(
2459
+ /* @__PURE__ */ React15.createElement(
2041
2460
  "span",
2042
2461
  {
2043
2462
  className: "absolute inset-0 rounded-full animate-ping opacity-20",
2044
2463
  style: { background: ui.primaryColor }
2045
2464
  }
2046
2465
  ),
2047
- /* @__PURE__ */ React10.createElement(
2466
+ /* @__PURE__ */ React15.createElement(
2048
2467
  "span",
2049
2468
  {
2050
2469
  className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
2051
2470
  },
2052
- isOpen ? /* @__PURE__ */ React10.createElement(X2, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ React10.createElement(MessageSquare, { className: "w-6 h-6 text-white" })
2471
+ isOpen ? /* @__PURE__ */ React15.createElement(X3, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ React15.createElement(MessageSquare, { className: "w-6 h-6 text-white" })
2053
2472
  ),
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")
2473
+ hasUnread && !isOpen && /* @__PURE__ */ React15.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
2474
  ));
2056
2475
  }
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(
2131
- "button",
2132
- {
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"
2155
- }
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
- );
2182
- }
2183
2476
  export {
2184
2477
  ChatWidget,
2185
2478
  ChatWindow,
2186
2479
  ConfigProvider,
2187
2480
  DocumentUpload,
2188
2481
  MessageBubble,
2482
+ ObservabilityPanel,
2189
2483
  SourceCard,
2484
+ addSynonyms,
2190
2485
  useConfig,
2191
2486
  useRagChat
2192
2487
  };