@retrivora-ai/rag-engine 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
  2. package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
  3. package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
  4. package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
  5. package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
  6. package/dist/chunk-ICKRMZQK.mjs +76 -0
  7. package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
  8. package/dist/chunk-OZFBG4BA.mjs +291 -0
  9. package/dist/handlers/index.d.mts +2 -2
  10. package/dist/handlers/index.d.ts +2 -2
  11. package/dist/handlers/index.js +1269 -656
  12. package/dist/handlers/index.mjs +4 -1
  13. package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
  14. package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
  15. package/dist/index.d.mts +24 -4
  16. package/dist/index.d.ts +24 -4
  17. package/dist/index.js +1354 -826
  18. package/dist/index.mjs +1284 -795
  19. package/dist/server.d.mts +47 -27
  20. package/dist/server.d.ts +47 -27
  21. package/dist/server.js +1417 -829
  22. package/dist/server.mjs +164 -176
  23. package/package.json +6 -2
  24. package/src/app/api/upload/route.ts +4 -0
  25. package/src/app/constants.tsx +2 -2
  26. package/src/app/page.tsx +12 -322
  27. package/src/components/AmbientBackground.tsx +29 -0
  28. package/src/components/ArchitectureCard.tsx +17 -0
  29. package/src/components/ArchitectureCardsSection.tsx +15 -0
  30. package/src/components/ChatWindow.tsx +32 -0
  31. package/src/components/CodeViewer.tsx +51 -0
  32. package/src/components/ConfigProvider.tsx +1 -0
  33. package/src/components/DocViewer.tsx +37 -0
  34. package/src/components/DocumentUpload.tsx +44 -1
  35. package/src/components/Documentation.tsx +58 -0
  36. package/src/components/DynamicChart.tsx +27 -2
  37. package/src/components/Hero.tsx +59 -0
  38. package/src/components/HourglassLoader.tsx +87 -0
  39. package/src/components/Lifecycle.tsx +37 -0
  40. package/src/components/MarkdownComponents.tsx +140 -0
  41. package/src/components/MessageBubble.tsx +124 -904
  42. package/src/components/Navbar.tsx +55 -0
  43. package/src/components/ObservabilityPanel.tsx +374 -0
  44. package/src/components/ProductCard.tsx +5 -3
  45. package/src/components/UIDispatcher.tsx +344 -0
  46. package/src/components/VisualizationRenderer.tsx +372 -250
  47. package/src/config/RagConfig.ts +5 -0
  48. package/src/config/serverConfig.ts +3 -1
  49. package/src/core/Pipeline.ts +240 -271
  50. package/src/core/ProviderRegistry.ts +2 -2
  51. package/src/core/VectorPlugin.ts +9 -0
  52. package/src/handlers/index.ts +91 -15
  53. package/src/hooks/useRagChat.ts +21 -11
  54. package/src/index.ts +9 -1
  55. package/src/llm/LLMFactory.ts +54 -2
  56. package/src/llm/providers/AnthropicProvider.ts +12 -8
  57. package/src/llm/providers/GeminiProvider.ts +188 -143
  58. package/src/llm/providers/OllamaProvider.ts +7 -3
  59. package/src/llm/providers/OpenAIProvider.ts +12 -8
  60. package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
  61. package/src/types/chat.ts +8 -0
  62. package/src/types/index.ts +132 -0
  63. package/src/types/props.ts +9 -1
  64. package/src/utils/ProductExtractor.ts +347 -0
  65. package/src/utils/SchemaMapper.ts +129 -0
  66. package/src/utils/UITransformer.ts +470 -209
  67. package/src/utils/synonyms.ts +78 -0
  68. package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
  69. package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
  70. package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
  71. package/dist/chunk-FLOSGE6A.mjs +0 -202
package/dist/index.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,48 +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
- import remarkGfm from "remark-gfm";
35
247
 
36
248
  // src/components/SourceCard.tsx
37
- import React2 from "react";
249
+ import React3 from "react";
38
250
  import { ChevronDown, ChevronUp } from "lucide-react";
39
251
  function SourceCard({ source, index }) {
40
252
  var _a, _b;
41
- const [expanded, setExpanded] = React2.useState(false);
253
+ const [expanded, setExpanded] = React3.useState(false);
42
254
  const scorePercent = Math.round(source.score * 100);
43
255
  const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
44
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";
45
257
  const preview = source.content.slice(0, 120);
46
258
  const hasMore = source.content.length > 120;
47
- return /* @__PURE__ */ React2.createElement(
259
+ return /* @__PURE__ */ React3.createElement(
48
260
  "div",
49
261
  {
50
262
  className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
51
263
  onClick: () => setExpanded((e) => !e)
52
264
  },
53
- /* @__PURE__ */ React2.createElement("div", { className: "flex items-start justify-between gap-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React2.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__ */ React2.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__ */ React2.createElement("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums` }, scorePercent, "%")),
54
- /* @__PURE__ */ React2.createElement("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed" }, expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}`),
55
- hasMore && /* @__PURE__ */ React2.createElement("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5" }, expanded ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-3 h-3" }), " Show less") : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.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"))
56
268
  );
57
269
  }
58
270
 
59
271
  // src/components/ProductCarousel.tsx
60
- import React4, { useRef, useState, useEffect } from "react";
272
+ import React5, { useRef as useRef2, useState as useState2, useEffect } from "react";
61
273
  import { ChevronLeft, ChevronRight } from "lucide-react";
62
274
 
63
275
  // src/components/ProductCard.tsx
64
- import React3 from "react";
276
+ import React4 from "react";
65
277
  import Image from "next/image";
66
278
  import { ShoppingCart, ExternalLink } from "lucide-react";
67
279
  function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
68
- return /* @__PURE__ */ React3.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__ */ React3.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__ */ React3.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(
69
281
  Image,
70
282
  {
71
283
  src: product.image,
@@ -74,16 +286,16 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
74
286
  unoptimized: true,
75
287
  className: "object-cover group-hover:scale-105 transition-transform duration-500"
76
288
  }
77
- ) : /* @__PURE__ */ React3.createElement("div", { className: "text-slate-400 dark:text-white/20" }, /* @__PURE__ */ React3.createElement(ShoppingCart, { className: "w-12 h-12" })), product.brand && /* @__PURE__ */ React3.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__ */ React3.createElement("div", { className: "p-4 flex flex-col gap-2" }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-start gap-2" }, /* @__PURE__ */ React3.createElement("h3", { className: "text-sm font-semibold text-slate-800 dark:text-white/90 line-clamp-1" }, product.name), product.price && /* @__PURE__ */ React3.createElement("span", { className: "text-sm font-bold", style: { color: primaryColor } }, typeof product.price === "number" ? `$${product.price}` : product.price)), product.description && /* @__PURE__ */ React3.createElement("p", { className: "text-[11px] text-slate-500 dark:text-white/50 line-clamp-2 leading-relaxed" }, product.description), /* @__PURE__ */ React3.createElement("div", { className: "mt-2 flex gap-2" }, /* @__PURE__ */ React3.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(
78
290
  "button",
79
291
  {
80
292
  onClick: () => onAddToCart == null ? void 0 : onAddToCart(product),
81
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",
82
294
  style: { background: primaryColor }
83
295
  },
84
- /* @__PURE__ */ React3.createElement(ShoppingCart, { className: "w-3 h-3" }),
296
+ /* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-3 h-3" }),
85
297
  "Add to Cart"
86
- ), product.link && /* @__PURE__ */ React3.createElement(
298
+ ), product.link && /* @__PURE__ */ React4.createElement(
87
299
  "a",
88
300
  {
89
301
  href: product.link,
@@ -91,14 +303,14 @@ function ProductCard({ product, primaryColor = "#6366f1", onAddToCart }) {
91
303
  rel: "noopener noreferrer",
92
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"
93
305
  },
94
- /* @__PURE__ */ React3.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
306
+ /* @__PURE__ */ React4.createElement(ExternalLink, { className: "w-3.5 h-3.5" })
95
307
  ))));
96
308
  }
97
309
 
98
310
  // src/components/ProductCarousel.tsx
99
311
  function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
100
- const scrollRef = useRef(null);
101
- const [canScroll, setCanScroll] = useState(false);
312
+ const scrollRef = useRef2(null);
313
+ const [canScroll, setCanScroll] = useState2(false);
102
314
  useEffect(() => {
103
315
  const checkScroll = () => {
104
316
  if (scrollRef.current) {
@@ -123,9 +335,9 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
123
335
  };
124
336
  if (!products || products.length === 0) return null;
125
337
  if (products.length === 1) {
126
- return /* @__PURE__ */ React4.createElement("div", { className: "my-4 w-[min(88%,18rem)] min-w-0 max-w-full animate-fade-in-up" }, /* @__PURE__ */ React4.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 }));
127
339
  }
128
- return /* @__PURE__ */ React4.createElement("div", { className: "relative w-full my-4 group/carousel animate-fade-in-up" }, canScroll && /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.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__ */ React4.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(
129
341
  "button",
130
342
  {
131
343
  onClick: (e) => {
@@ -135,8 +347,8 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
135
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",
136
348
  "aria-label": "Scroll left"
137
349
  },
138
- /* @__PURE__ */ React4.createElement(ChevronLeft, { className: "w-5 h-5" })
139
- )), /* @__PURE__ */ React4.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__ */ React4.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(
140
352
  "button",
141
353
  {
142
354
  onClick: (e) => {
@@ -146,27 +358,50 @@ function ProductCarousel({ products, primaryColor = "#6366f1", onAddToCart }) {
146
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",
147
359
  "aria-label": "Scroll right"
148
360
  },
149
- /* @__PURE__ */ React4.createElement(ChevronRight, { className: "w-5 h-5" })
150
- ))), /* @__PURE__ */ React4.createElement(
361
+ /* @__PURE__ */ React5.createElement(ChevronRight, { className: "w-5 h-5" })
362
+ ))), /* @__PURE__ */ React5.createElement(
151
363
  "div",
152
364
  {
153
365
  ref: scrollRef,
154
366
  className: "flex gap-4 overflow-x-auto pb-4 px-1 scrollbar-hide snap-x snap-mandatory",
155
367
  style: { scrollbarWidth: "none", msOverflowStyle: "none" }
156
368
  },
157
- products.map((product) => /* @__PURE__ */ React4.createElement(
369
+ products.map((product) => /* @__PURE__ */ React5.createElement(
158
370
  "div",
159
371
  {
160
372
  key: product.id,
161
373
  className: "snap-start flex-shrink-0 w-[min(88%,18rem)] min-w-[11rem] max-w-full"
162
374
  },
163
- /* @__PURE__ */ React4.createElement(ProductCard, { product, primaryColor, onAddToCart })
375
+ /* @__PURE__ */ React5.createElement(ProductCard, { product, primaryColor, onAddToCart })
164
376
  ))
165
377
  ));
166
378
  }
167
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
+
168
403
  // src/components/DynamicChart.tsx
169
- import React5 from "react";
404
+ import React6 from "react";
170
405
  import {
171
406
  BarChart,
172
407
  Bar,
@@ -182,18 +417,19 @@ import {
182
417
  Legend,
183
418
  ResponsiveContainer
184
419
  } from "recharts";
185
- var DEFAULT_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
420
+ var CHART_COLORS = ["#6366f1", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899"];
186
421
  function DynamicChart({
187
422
  config,
188
423
  primaryColor = "#6366f1",
189
424
  accentColor = "#8b5cf6",
190
- viewportSize = "large"
425
+ viewportSize = "large",
426
+ isStreaming = false
191
427
  }) {
192
428
  const { type, data } = config;
193
- const colors = config.colors || [primaryColor, accentColor, ...DEFAULT_COLORS];
194
- const containerRef = React5.useRef(null);
195
- const [containerWidth, setContainerWidth] = React5.useState(0);
196
- React5.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(() => {
197
433
  const element = containerRef.current;
198
434
  if (!element || typeof ResizeObserver === "undefined") return;
199
435
  const updateWidth = (width) => {
@@ -219,7 +455,7 @@ function DynamicChart({
219
455
  return text.length > maxLength ? `${text.slice(0, maxLength)}\u2026` : text;
220
456
  };
221
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 };
222
- const sanitizedData = React5.useMemo(() => {
458
+ const sanitizedData = React6.useMemo(() => {
223
459
  if (!data) return [];
224
460
  return data.map((item) => {
225
461
  const newItem = {};
@@ -251,8 +487,19 @@ function DynamicChart({
251
487
  return newItem;
252
488
  });
253
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
+ }
254
501
  if (!data || data.length === 0) {
255
- return /* @__PURE__ */ React5.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.");
256
503
  }
257
504
  const firstItem = sanitizedData[0];
258
505
  const allKeys = Object.keys(firstItem);
@@ -277,13 +524,13 @@ function DynamicChart({
277
524
  const renderTooltip = (row, label) => {
278
525
  var _a, _b;
279
526
  if (!row) return null;
280
- 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" }, /* @__PURE__ */ React5.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]) => {
281
528
  var _a2;
282
529
  if (value === null || value === void 0 || key === finalXKey) return null;
283
530
  if (typeof value === "object") return null;
284
531
  const forbiddenKeys = ["fill", "color", "payload", "percent", "stroke", "inStock"];
285
532
  if (forbiddenKeys.includes(key)) return null;
286
- return /* @__PURE__ */ React5.createElement("div", { key, className: "flex items-center justify-between gap-3 text-slate-600 dark:text-white/70" }, /* @__PURE__ */ React5.createElement("span", null, (_a2 = tooltipLabelMap[key]) != null ? _a2 : key), /* @__PURE__ */ React5.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)));
287
534
  }));
288
535
  };
289
536
  const getTooltipRow = (payload) => {
@@ -295,7 +542,7 @@ function DynamicChart({
295
542
  return row;
296
543
  };
297
544
  if (type === "pie") {
298
- return /* @__PURE__ */ React5.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React5.createElement("div", { className: "mx-auto w-full max-w-full aspect-square" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(PieChart, null, /* @__PURE__ */ React5.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(
299
546
  Pie,
300
547
  {
301
548
  data: sanitizedData,
@@ -307,10 +554,10 @@ function DynamicChart({
307
554
  label: false,
308
555
  labelLine: false
309
556
  },
310
- sanitizedData.map((_entry, index) => /* @__PURE__ */ React5.createElement(Cell, { key: `cell-${index}`, fill: colors[index % colors.length] }))
311
- ), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && !isCompact && /* @__PURE__ */ React5.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" })))));
312
559
  }
313
- return /* @__PURE__ */ React5.createElement("div", { ref: containerRef, className: "w-full min-w-0 mt-4 mb-2 select-none overflow-hidden" }, /* @__PURE__ */ React5.createElement("div", { className: "w-full aspect-[4/3]" }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, type === "bar" ? /* @__PURE__ */ React5.createElement(BarChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React5.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React5.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0), cursor: { fill: "#f1f5f9" } }), showLegend && /* @__PURE__ */ React5.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React5.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(
314
561
  Bar,
315
562
  {
316
563
  key,
@@ -319,7 +566,7 @@ function DynamicChart({
319
566
  radius: [4, 4, 0, 0],
320
567
  barSize: isTiny ? Math.max(10, 28 / data.length) : isCompact ? Math.max(14, 42 / data.length) : Math.max(20, 60 / data.length)
321
568
  }
322
- ))) : /* @__PURE__ */ React5.createElement(LineChart, { data: sanitizedData, margin: chartMargin }, /* @__PURE__ */ React5.createElement(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#e2e8f0" }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: finalXKey, tick: axisTick, tickFormatter: truncateAxisLabel, tickLine: false, axisLine: { stroke: "#cbd5e1" }, interval: 0, minTickGap: isTiny ? 2 : isCompact ? 6 : 12 }), /* @__PURE__ */ React5.createElement(YAxis, { tick: axisTick, tickLine: false, axisLine: false }), /* @__PURE__ */ React5.createElement(Tooltip, { content: (props) => renderTooltip(getTooltipRow(props.payload), typeof props.label === "string" ? props.label : void 0) }), showLegend && /* @__PURE__ */ React5.createElement(Legend, { wrapperStyle: { fontSize: isCompact ? 10 : 12 } }), finalDataKeys.map((key, index) => /* @__PURE__ */ React5.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(
323
570
  Line,
324
571
  {
325
572
  key,
@@ -334,178 +581,492 @@ function DynamicChart({
334
581
  }
335
582
 
336
583
  // src/components/VisualizationRenderer.tsx
337
- import Image2 from "next/image";
584
+ function getColor(index) {
585
+ return CHART_COLORS[index % CHART_COLORS.length];
586
+ }
587
+ function CustomTooltip({
588
+ active,
589
+ payload,
590
+ label
591
+ }) {
592
+ if (!active || !payload || payload.length === 0) return null;
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(
594
+ "span",
595
+ {
596
+ className: "inline-block w-2 h-2 rounded-full flex-shrink-0",
597
+ style: { background: entry.color }
598
+ }
599
+ ), entry.name), /* @__PURE__ */ React7.createElement("span", { className: "font-medium text-slate-900 dark:text-white/90" }, entry.value))));
600
+ }
338
601
  function VisualizationRenderer({
339
602
  data,
340
- className = ""
603
+ className = "",
604
+ primaryColor,
605
+ onAddToCart,
606
+ isStreaming
341
607
  }) {
608
+ var _a;
342
609
  if (!data) {
343
- return /* @__PURE__ */ React.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");
344
611
  }
345
- return /* @__PURE__ */ React.createElement("div", { className: `space-y-4 ${className}` }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, data.title), data.description && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400" }, data.description)), renderVisualization(data.type, data.data));
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));
346
613
  }
347
- function renderVisualization(type, data) {
348
- switch (type) {
349
- case "pie_chart":
350
- return renderPieChart(data);
351
- case "bar_chart":
352
- return renderBarChart(data);
353
- case "line_chart":
354
- return renderLineChart(data);
355
- case "table":
356
- return renderTable(data);
357
- case "product_carousel":
358
- return renderProductCarousel(data);
359
- case "text":
360
- default:
361
- return renderText(data);
614
+ function renderVisualization(type, data, onAddToCart, primaryColor, isStreaming) {
615
+ try {
616
+ switch (type) {
617
+ case "pie_chart":
618
+ return /* @__PURE__ */ React7.createElement(PieChartView, { data, isStreaming });
619
+ case "bar_chart":
620
+ return /* @__PURE__ */ React7.createElement(BarChartView, { data, primaryColor, isStreaming });
621
+ case "line_chart":
622
+ return /* @__PURE__ */ React7.createElement(LineChartView, { data, primaryColor, isStreaming });
623
+ case "radar_chart":
624
+ return /* @__PURE__ */ React7.createElement(RadarChartView, { data, isStreaming });
625
+ case "table":
626
+ return /* @__PURE__ */ React7.createElement(TableView, { data, isStreaming });
627
+ case "product_carousel":
628
+ case "carousel":
629
+ return /* @__PURE__ */ React7.createElement(CarouselView, { data, onAddToCart, primaryColor });
630
+ case "text":
631
+ default:
632
+ return /* @__PURE__ */ React7.createElement(TextView, { data });
633
+ }
634
+ } catch (error) {
635
+ console.warn("[VisualizationRenderer] Rendering failed, falling back to text:", error);
636
+ return /* @__PURE__ */ React7.createElement(TextView, { data: { content: "Unable to render the requested visualization." } });
362
637
  }
363
638
  }
364
- function renderPieChart(data) {
365
- if (!Array.isArray(data)) {
366
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid pie chart data");
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" }));
367
642
  }
368
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow" }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-wrap gap-2" }, data.map((item, idx) => /* @__PURE__ */ React.createElement(
369
- "div",
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}%` } })));
647
+ }
648
+ function PieChartView({ data, isStreaming }) {
649
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
650
+ if (!Array.isArray(data) || data.length === 0) {
651
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid pie chart data");
652
+ }
653
+ const chartData = data.map((item) => ({
654
+ name: item.label,
655
+ value: item.value
656
+ }));
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(
658
+ Pie2,
370
659
  {
371
- key: idx,
372
- className: "flex items-center gap-2 p-2 bg-gray-50 dark:bg-gray-700 rounded"
660
+ data: chartData,
661
+ dataKey: "value",
662
+ nameKey: "name",
663
+ cx: "50%",
664
+ cy: "50%",
665
+ outerRadius: "68%",
666
+ label: false,
667
+ labelLine: false
373
668
  },
374
- /* @__PURE__ */ React.createElement(
375
- "div",
376
- {
377
- className: "w-3 h-3 rounded-full",
378
- style: { backgroundColor: generateColor(idx) }
379
- }
380
- ),
381
- /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.label, ": ", item.value),
382
- item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
383
- "span",
384
- {
385
- className: `text-xs px-2 py-1 rounded ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
386
- },
387
- item.inStock ? "In Stock" : "Out of Stock"
388
- )
389
- ))));
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" }))));
390
671
  }
391
- function renderBarChart(data) {
392
- if (!Array.isArray(data)) {
393
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid bar chart data");
672
+ function BarChartView({ data, primaryColor, isStreaming }) {
673
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
674
+ if (!Array.isArray(data) || data.length === 0) {
675
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid bar chart data");
394
676
  }
395
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-3" }, data.map((item, idx) => /* @__PURE__ */ React.createElement("div", { key: idx, className: "space-y-1" }, /* @__PURE__ */ React.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React.createElement("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300" }, item.category), /* @__PURE__ */ React.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, item.value)), /* @__PURE__ */ React.createElement("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2" }, /* @__PURE__ */ React.createElement(
396
- "div",
677
+ const chartData = data.map((item) => __spreadValues(__spreadValues({
678
+ category: item.category,
679
+ value: item.value
680
+ }, item.inStockCount !== void 0 && { inStockCount: item.inStockCount }), item.outOfStockCount !== void 0 && { outOfStockCount: item.outOfStockCount }));
681
+ const barColor = primaryColor != null ? primaryColor : getColor(0);
682
+ const hasStock = chartData.some((d) => "inStockCount" in d);
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(
684
+ XAxis2,
397
685
  {
398
- className: "h-full rounded-full transition-all",
399
- style: {
400
- width: `${item.value / Math.max(...data.map((d) => d.value)) * 100}%`,
401
- backgroundColor: generateColor(idx)
402
- }
686
+ dataKey: "category",
687
+ tick: { fontSize: 11, fill: "#64748b" },
688
+ tickLine: false,
689
+ axisLine: { stroke: "#cbd5e1" }
403
690
  }
404
- )), item.inStock !== void 0 && /* @__PURE__ */ React.createElement(
405
- "span",
406
- {
407
- className: `text-xs px-2 py-1 rounded inline-block ${item.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
408
- },
409
- item.inStock ? "In Stock" : "Out of Stock"
410
- )))));
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] }))));
411
692
  }
412
- function renderLineChart(data) {
413
- if (!Array.isArray(data)) {
414
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid line chart data");
693
+ function LineChartView({ data, primaryColor, isStreaming }) {
694
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "bar" });
695
+ if (!Array.isArray(data) || data.length === 0) {
696
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid line chart data");
415
697
  }
416
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, data.map((point, idx) => /* @__PURE__ */ React.createElement(
417
- "div",
698
+ const chartData = data.map((point) => {
699
+ var _a;
700
+ return {
701
+ label: String((_a = point.label) != null ? _a : point.timestamp),
702
+ value: Number(point.value)
703
+ };
704
+ });
705
+ const lineColor = primaryColor != null ? primaryColor : getColor(0);
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(
707
+ XAxis2,
418
708
  {
419
- key: idx,
420
- className: "flex justify-between items-center p-2 bg-gray-50 dark:bg-gray-700 rounded"
421
- },
422
- /* @__PURE__ */ React.createElement("span", { className: "text-sm text-gray-600 dark:text-gray-400" }, point.label || point.timestamp),
423
- /* @__PURE__ */ React.createElement("span", { className: "text-sm font-semibold text-gray-900 dark:text-white" }, typeof point.value === "number" ? point.value.toFixed(2) : point.value)
709
+ dataKey: "label",
710
+ tick: { fontSize: 11, fill: "#64748b" },
711
+ tickLine: false,
712
+ axisLine: { stroke: "#cbd5e1" }
713
+ }
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(
715
+ Line2,
716
+ {
717
+ type: "monotone",
718
+ dataKey: "value",
719
+ name: "Value",
720
+ stroke: lineColor,
721
+ strokeWidth: 2.5,
722
+ dot: { r: 3.5, strokeWidth: 2, fill: lineColor },
723
+ activeDot: { r: 5 }
724
+ }
424
725
  ))));
425
726
  }
426
- function renderTable(data) {
427
- if (!data.columns || !data.rows) {
428
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid table data");
727
+ function RadarChartView({ data, isStreaming }) {
728
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "pie" });
729
+ if (!Array.isArray(data) || data.length === 0) {
730
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid radar chart data");
429
731
  }
430
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 rounded-lg shadow overflow-x-auto" }, /* @__PURE__ */ React.createElement("table", { className: "w-full text-sm" }, /* @__PURE__ */ React.createElement("thead", { className: "bg-gray-100 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-600" }, /* @__PURE__ */ React.createElement("tr", null, data.columns.map((col, idx) => /* @__PURE__ */ React.createElement(
732
+ const productNames = Array.from(
733
+ new Set(data.flatMap((item) => Object.keys(item).filter((key) => key !== "attribute")))
734
+ );
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(
736
+ Radar,
737
+ {
738
+ key: productName,
739
+ name: productName,
740
+ dataKey: productName,
741
+ stroke: getColor(index),
742
+ fill: getColor(index),
743
+ fillOpacity: 0.4
744
+ }
745
+ )), /* @__PURE__ */ React7.createElement(Tooltip2, { content: /* @__PURE__ */ React7.createElement(CustomTooltip, null) }), /* @__PURE__ */ React7.createElement(Legend2, { wrapperStyle: { fontSize: 12 } }))));
746
+ }
747
+ function TableView({ data, isStreaming }) {
748
+ if (isStreaming) return /* @__PURE__ */ React7.createElement(ChartSkeleton, { type: "table" });
749
+ if (!(data == null ? void 0 : data.columns) || !(data == null ? void 0 : data.rows)) {
750
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid table data");
751
+ }
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(
431
753
  "th",
432
754
  {
433
755
  key: idx,
434
- className: "px-4 py-2 text-left font-semibold text-gray-900 dark:text-white"
756
+ className: "px-4 py-3 text-left text-xs font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap uppercase tracking-wider"
435
757
  },
436
758
  col
437
- )))), /* @__PURE__ */ React.createElement("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React.createElement(
759
+ )))), /* @__PURE__ */ React7.createElement("tbody", { className: "divide-y divide-slate-100 dark:divide-white/5" }, data.rows.map((row, rowIdx) => /* @__PURE__ */ React7.createElement(
438
760
  "tr",
439
761
  {
440
762
  key: rowIdx,
441
- className: "hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
763
+ className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
442
764
  },
443
- row.map((cell, cellIdx) => /* @__PURE__ */ React.createElement(
765
+ row.map((cell, cellIdx) => /* @__PURE__ */ React7.createElement(
444
766
  "td",
445
767
  {
446
768
  key: cellIdx,
447
- className: "px-4 py-2 text-gray-700 dark:text-gray-300"
769
+ className: "px-4 py-3 text-slate-600 dark:text-white/70 text-sm"
448
770
  },
449
771
  String(cell)
450
772
  ))
451
- )))));
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" : "")));
452
774
  }
453
- function renderProductCarousel(data) {
775
+ function CarouselView({
776
+ data,
777
+ onAddToCart,
778
+ primaryColor
779
+ }) {
454
780
  if (!Array.isArray(data)) {
455
- return /* @__PURE__ */ React.createElement("div", { className: "text-red-500" }, "Invalid carousel data");
781
+ return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
456
782
  }
457
- return /* @__PURE__ */ React.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React.createElement("div", { className: "flex gap-4 pb-4" }, data.map((product) => /* @__PURE__ */ React.createElement(
783
+ const products = data.map((product) => ({
784
+ id: product.id,
785
+ name: product.name,
786
+ brand: product.brand,
787
+ price: product.price,
788
+ image: product.image,
789
+ link: typeof product.link === "string" ? product.link : void 0,
790
+ description: product.description,
791
+ inStock: product.inStock
792
+ }));
793
+ return /* @__PURE__ */ React7.createElement("div", { className: "w-full" }, /* @__PURE__ */ React7.createElement(
794
+ ProductCarousel,
795
+ {
796
+ products,
797
+ primaryColor: primaryColor != null ? primaryColor : "#6366f1",
798
+ onAddToCart: (p) => onAddToCart == null ? void 0 : onAddToCart(p)
799
+ }
800
+ ));
801
+ }
802
+ function TextView({ data }) {
803
+ const content = (data == null ? void 0 : data.content) || String(data != null ? data : "");
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));
805
+ }
806
+
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(
458
834
  "div",
459
835
  {
460
- key: product.id,
461
- className: "flex-shrink-0 w-64 bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow"
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"
462
838
  },
463
- product.image && /* @__PURE__ */ React.createElement("div", { className: "relative w-full h-40 bg-gray-200" }, /* @__PURE__ */ React.createElement(
464
- Image2,
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",
465
842
  {
466
- src: product.image,
467
- alt: product.name,
468
- fill: true,
469
- className: "object-cover",
470
- sizes: "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
843
+ className: "h-full rounded-full bg-indigo-500 dark:bg-indigo-400 transition-all",
844
+ style: { width: `${Math.min(100, chunk.score * 100)}%` }
471
845
  }
472
846
  )),
473
- /* @__PURE__ */ React.createElement("div", { className: "p-4 space-y-2" }, /* @__PURE__ */ React.createElement("h4", { className: "font-semibold text-gray-900 dark:text-white line-clamp-2" }, product.name), product.brand && /* @__PURE__ */ React.createElement("p", { className: "text-xs text-gray-500 dark:text-gray-400" }, "Brand: ", product.brand), product.price && /* @__PURE__ */ React.createElement("p", { className: "text-lg font-bold text-gray-900 dark:text-white" }, "$", typeof product.price === "number" ? product.price.toFixed(2) : product.price), product.description && /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-600 dark:text-gray-400 line-clamp-2" }, product.description), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2 pt-2" }, /* @__PURE__ */ React.createElement(
474
- "span",
475
- {
476
- className: `text-xs px-2 py-1 rounded flex-1 text-center ${product.inStock ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}`
477
- },
478
- product.inStock ? "In Stock" : "Out of Stock"
479
- )))
847
+ /* @__PURE__ */ React8.createElement("p", { className: "text-[11px] text-slate-600 dark:text-white/60 line-clamp-2 leading-relaxed" }, chunk.content)
480
848
  ))));
481
849
  }
482
- function renderText(data) {
483
- const content = (data == null ? void 0 : data.content) || String(data);
484
- return /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 p-4 rounded-lg shadow prose dark:prose-invert max-w-none" }, /* @__PURE__ */ React.createElement("p", { className: "text-gray-700 dark:text-gray-300 whitespace-pre-wrap" }, content));
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
+ ))))));
485
870
  }
486
- function generateColor(index) {
487
- const colors = [
488
- "#3B82F6",
489
- // blue
490
- "#10B981",
491
- // green
492
- "#F59E0B",
493
- // amber
494
- "#EF4444",
495
- // red
496
- "#8B5CF6",
497
- // purple
498
- "#EC4899",
499
- // pink
500
- "#06B6D4",
501
- // cyan
502
- "#F97316"
503
- // orange
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" }
504
880
  ];
505
- return colors[index % colors.length];
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 })));
506
993
  }
507
994
 
508
- // src/components/MessageBubble.tsx
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
509
1070
  function sanitizeJson(raw) {
510
1071
  let s = raw.trim();
511
1072
  s = s.replace(
@@ -522,37 +1083,35 @@ function sanitizeJson(raw) {
522
1083
  s = s.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, '"$1"');
523
1084
  s = s.replace(/,\s*([}\]])/g, "$1");
524
1085
  s = s.replace(/([{,])\s*""\s*(?=[,}])/g, "$1");
525
- {
526
- const stack = [];
527
- let inString = false;
528
- let escape = false;
529
- for (const ch of s) {
530
- if (escape) {
531
- escape = false;
532
- continue;
533
- }
534
- if (ch === "\\" && inString) {
535
- escape = true;
536
- continue;
537
- }
538
- if (ch === '"') {
539
- inString = !inString;
540
- continue;
541
- }
542
- if (inString) continue;
543
- if (ch === "{" || ch === "[") {
544
- stack.push(ch);
545
- } else if (ch === "}") {
546
- if (stack[stack.length - 1] === "{") stack.pop();
547
- } else if (ch === "]") {
548
- if (stack[stack.length - 1] === "[") stack.pop();
549
- }
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;
550
1097
  }
551
- if (inString) s += '"';
552
- for (let i = stack.length - 1; i >= 0; i--) {
553
- s += stack[i] === "{" ? "}" : "]";
1098
+ if (ch === '"') {
1099
+ inString = !inString;
1100
+ continue;
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();
554
1109
  }
555
1110
  }
1111
+ if (inString) s += '"';
1112
+ for (let i = stack.length - 1; i >= 0; i--) {
1113
+ s += stack[i] === "{" ? "}" : "]";
1114
+ }
556
1115
  const lastClose = Math.max(s.lastIndexOf("}"), s.lastIndexOf("]"));
557
1116
  if (lastClose !== -1 && lastClose < s.length - 1 && /\S/.test(s.slice(lastClose + 1))) {
558
1117
  s = s.substring(0, lastClose + 1);
@@ -586,41 +1145,51 @@ function stripStructuredUiLabels(raw) {
586
1145
  );
587
1146
  }
588
1147
  function extractBareJsonObject(raw) {
589
- const start = raw.indexOf("{");
1148
+ let start = raw.indexOf("{");
590
1149
  if (start === -1) return null;
591
- let depth = 0;
592
- let inString = false;
593
- let escape = false;
594
- for (let i = start; i < raw.length; i += 1) {
595
- const ch = raw[i];
596
- if (escape) {
597
- escape = false;
598
- continue;
599
- }
600
- if (ch === "\\" && inString) {
601
- escape = true;
602
- continue;
603
- }
604
- if (ch === '"') {
605
- inString = !inString;
606
- continue;
607
- }
608
- if (inString) continue;
609
- if (ch === "{") depth += 1;
610
- if (ch === "}") {
611
- depth -= 1;
612
- if (depth === 0) {
613
- return raw.slice(start, i + 1);
1150
+ let bestJson = null;
1151
+ let maxLen = 0;
1152
+ while (start !== -1) {
1153
+ let depth = 0;
1154
+ let inString = false;
1155
+ let escape = false;
1156
+ for (let i = start; i < raw.length; i += 1) {
1157
+ const ch = raw[i];
1158
+ if (escape) {
1159
+ escape = false;
1160
+ continue;
1161
+ }
1162
+ if (ch === "\\" && inString) {
1163
+ escape = true;
1164
+ continue;
1165
+ }
1166
+ if (ch === '"') {
1167
+ inString = !inString;
1168
+ continue;
1169
+ }
1170
+ if (inString) continue;
1171
+ if (ch === "{") depth += 1;
1172
+ else if (ch === "}") {
1173
+ depth -= 1;
1174
+ if (depth === 0) {
1175
+ const candidate = raw.slice(start, i + 1);
1176
+ if (candidate.length > maxLen) {
1177
+ maxLen = candidate.length;
1178
+ bestJson = candidate;
1179
+ }
1180
+ break;
1181
+ }
614
1182
  }
615
1183
  }
1184
+ start = raw.indexOf("{", start + 1);
616
1185
  }
617
- return null;
1186
+ return bestJson;
618
1187
  }
619
1188
  function extractStructuredPayload(raw) {
620
1189
  var _a, _b;
621
1190
  let working = raw;
622
1191
  const fenceRegex = /```(?:[a-z]+)?\s*([\s\S]*?)```/gi;
623
- for (const match of raw.matchAll(fenceRegex)) {
1192
+ for (const match of Array.from(raw.matchAll(fenceRegex))) {
624
1193
  const body = (_b = (_a = match[1]) == null ? void 0 : _a.trim()) != null ? _b : "";
625
1194
  if (!looksLikeStructuredPayload(body)) continue;
626
1195
  working = working.replace(match[0], "");
@@ -642,98 +1211,158 @@ function extractStructuredPayload(raw) {
642
1211
  text: stripStructuredUiLabels(raw).replace(/\n{3,}/g, "\n\n").trim()
643
1212
  };
644
1213
  }
645
- function isLikelyUiOnlyMessage(text) {
646
- const trimmed = text.trim();
647
- return trimmed.length === 0 || /^(chart data|table data|visualization|visual representation|product catalog|product recommendations|catalog summary)\s*:?\s*$/i.test(trimmed);
648
- }
649
1214
  function resolveImage(data) {
650
1215
  const isPlaceholder = (val) => typeof val === "string" && (val === "..." || val === "" || val.toLowerCase() === "null" || val.toLowerCase() === "undefined");
651
- for (const key of ["image", "img", "thumbnail"]) {
1216
+ for (const key of ["image", "img", "thumbnail", "images", "product_image", "Image", "Thumbnail"]) {
652
1217
  const v = data[key];
653
1218
  if (typeof v === "string" && v && !isPlaceholder(v)) return v;
654
- }
655
- if (Array.isArray(data.images) && typeof data.images[0] === "string" && !isPlaceholder(data.images[0])) {
656
- return data.images[0];
1219
+ if (Array.isArray(v) && typeof v[0] === "string" && v[0] && !isPlaceholder(v[0])) return v[0];
657
1220
  }
658
1221
  return void 0;
659
1222
  }
660
- function normaliseChild(children) {
661
- if (typeof children === "object" && children !== null && !Array.isArray(children) && !React6.isValidElement(children)) {
662
- return JSON.stringify(children);
663
- }
664
- return children;
665
- }
666
- function normaliseFieldName(value) {
667
- return value.toLowerCase().replace(/[^a-z0-9]/g, "");
668
- }
669
- function formatColumnLabel(value) {
670
- 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);
671
1226
  }
672
- function DataTable({ config, viewportSize = "large" }) {
673
- const isCompact = viewportSize === "compact";
674
- const isMedium = viewportSize === "medium";
675
- const columns = React6.useMemo(() => {
676
- const rowKeys = Array.from(
677
- new Set(
678
- config.data.flatMap((row) => Object.keys(row))
679
- )
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)
680
1235
  );
681
- const findAccessor = (label) => {
682
- const exact = rowKeys.find((key) => key === label);
683
- if (exact) return exact;
684
- const normalisedLabel = normaliseFieldName(label);
685
- const normalisedMatch = rowKeys.find((key) => normaliseFieldName(key) === normalisedLabel);
686
- if (normalisedMatch) return normalisedMatch;
687
- 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,
688
1255
  };
689
- if (Array.isArray(config.columns) && config.columns.length) {
690
- return config.columns.map((label) => ({
691
- label: formatColumnLabel(label),
692
- accessor: findAccessor(label)
693
- }));
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) {
694
1278
  }
695
- if (Array.isArray(config.dataKeys) && config.dataKeys.length) {
696
- const keys = config.xAxisKey && !config.dataKeys.includes(config.xAxisKey) ? [config.xAxisKey, ...config.dataKeys] : config.dataKeys;
697
- return keys.map((key) => ({
698
- label: formatColumnLabel(key),
699
- accessor: findAccessor(key)
700
- }));
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
+ }
701
1297
  }
702
- return rowKeys.map((key) => ({ label: formatColumnLabel(key), accessor: key }));
703
- }, [config]);
704
- if (!config.data.length || !columns.length) {
705
- return /* @__PURE__ */ React6.createElement("p", { className: "text-xs text-slate-400 italic my-2" }, "No data to display.");
706
1298
  }
707
- const formatCell = (val) => {
708
- if (val === null || val === void 0) return "\u2014";
709
- if (typeof val === "boolean") return val ? "\u2713" : "\u2717";
710
- return String(val);
711
- };
712
- 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(
713
- "th",
714
- {
715
- key: column.label,
716
- className: `${isCompact ? "px-2.5 py-2" : "px-4 py-3"} font-semibold text-slate-700 dark:text-white/90 whitespace-nowrap`
717
- },
718
- column.label
719
- )))), /* @__PURE__ */ React6.createElement("tbody", null, config.data.map((row, ri) => /* @__PURE__ */ React6.createElement(
720
- "tr",
721
- {
722
- key: ri,
723
- 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"
724
- },
725
- columns.map((column) => /* @__PURE__ */ React6.createElement(
726
- "td",
727
- {
728
- key: column.label,
729
- 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"}`
730
- },
731
- formatCell(row[column.accessor])
732
- ))
733
- ))))), /* @__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() };
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
+ });
734
1350
  }
1351
+
1352
+ // src/components/MarkdownComponents.tsx
1353
+ import React11 from "react";
1354
+
1355
+ // src/components/UIDispatcher.tsx
1356
+ import React10 from "react";
735
1357
  function normalizeTabularRows(rows, columns) {
736
1358
  if (!Array.isArray(rows)) return [];
1359
+ const SYNONYMS = {
1360
+ name: ["product", "item", "title", "label", "heading", "product name", "item name"],
1361
+ price: ["cost", "amount", "msrp", "price", "rate", "value", "price_usd"],
1362
+ brand: ["manufacturer", "vendor", "make", "company", "brand_name", "supplier"],
1363
+ image: ["imageUrl", "thumbnail", "img", "url", "photo", "picture", "media"],
1364
+ stock: ["inventory", "quantity", "count", "availability", "stock_level", "in stock", "status"]
1365
+ };
737
1366
  return rows.map((row) => {
738
1367
  if (Array.isArray(row)) {
739
1368
  const keys = Array.isArray(columns) && columns.length > 0 ? columns : row.map((_, index) => `column_${index + 1}`);
@@ -743,7 +1372,36 @@ function normalizeTabularRows(rows, columns) {
743
1372
  }, {});
744
1373
  }
745
1374
  if (row && typeof row === "object") {
746
- return row;
1375
+ const result = __spreadValues({}, row);
1376
+ if (Array.isArray(columns)) {
1377
+ columns.forEach((col) => {
1378
+ if (result[col] !== void 0) return;
1379
+ const rowKeys = Object.keys(row);
1380
+ const colLower = col.toLowerCase().replace(/_/g, " ");
1381
+ const foundKey = rowKeys.find((rk) => {
1382
+ const rkLower = rk.toLowerCase().replace(/_/g, " ");
1383
+ return rkLower === colLower || rkLower.includes(colLower) || colLower.includes(rkLower);
1384
+ });
1385
+ if (foundKey) {
1386
+ result[col] = row[foundKey];
1387
+ return;
1388
+ }
1389
+ for (const [target, aliases] of Object.entries(SYNONYMS)) {
1390
+ const isMatch = target === colLower || aliases.some((a) => colLower.includes(a) || a.includes(colLower));
1391
+ if (isMatch) {
1392
+ const mappedKey = rowKeys.find((rk) => {
1393
+ const rkL = rk.toLowerCase();
1394
+ return rkL === target || aliases.some((a) => rkL.includes(a) || a.includes(rkL));
1395
+ });
1396
+ if (mappedKey) {
1397
+ result[col] = row[mappedKey];
1398
+ break;
1399
+ }
1400
+ }
1401
+ }
1402
+ });
1403
+ }
1404
+ return result;
747
1405
  }
748
1406
  return { value: row };
749
1407
  }).filter((row) => Object.keys(row).length > 0);
@@ -763,8 +1421,7 @@ function resolveStructuredRows(config) {
763
1421
  }
764
1422
  function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAddToCart, viewportSize = "large" }) {
765
1423
  var _a;
766
- const result = React6.useMemo(() => {
767
- if (isStreaming) return { loading: true };
1424
+ const result = React10.useMemo(() => {
768
1425
  try {
769
1426
  const clean = rawContent.replace(/^```[a-z]*\s*/i, "").replace(/```\s*$/, "").trim();
770
1427
  const parsed = JSON.parse(sanitizeJson(extractLikelyJsonBlock(clean)));
@@ -793,10 +1450,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
793
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)) {
794
1451
  config2.view = "chart";
795
1452
  config2.chartType = config2.chartType || viewStr.replace("_chart", "") || config2.type || "bar";
796
- } else if (viewStr.includes("table")) {
1453
+ } else if (viewStr.includes("table") || config2.type === "table") {
797
1454
  config2.view = "table";
798
1455
  } else {
799
- config2.view = "table";
1456
+ config2.view = "none";
800
1457
  }
801
1458
  }
802
1459
  const normalizedConfig = __spreadProps(__spreadValues({}, config2), {
@@ -804,22 +1461,25 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
804
1461
  });
805
1462
  return { config: normalizedConfig };
806
1463
  } catch (err) {
1464
+ if (isStreaming) return { loading: true };
807
1465
  return { error: String(err) };
808
1466
  }
809
1467
  }, [rawContent, isStreaming]);
810
1468
  if ("loading" in result) {
811
- 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..."));
812
1470
  }
813
1471
  if ("error" in result) {
814
- 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));
815
1473
  }
816
1474
  const { config } = result;
817
1475
  const isCompact = viewportSize === "compact";
818
1476
  const hasInsights = Array.isArray(config.insights) && config.insights.length > 0;
819
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);
820
1480
  switch (config.view) {
821
1481
  case "chart":
822
- 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(
823
1483
  DynamicChart,
824
1484
  {
825
1485
  config: {
@@ -831,9 +1491,10 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
831
1491
  },
832
1492
  primaryColor,
833
1493
  accentColor,
834
- viewportSize
1494
+ viewportSize,
1495
+ isStreaming
835
1496
  }
836
- ), 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(
837
1498
  "span",
838
1499
  {
839
1500
  key: `insight-${i}`,
@@ -842,26 +1503,151 @@ function UIDispatcher({ rawContent, primaryColor, accentColor, isStreaming, onAd
842
1503
  String(insight)
843
1504
  ))));
844
1505
  case "carousel":
845
- 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) => {
846
1507
  var _a2;
847
1508
  return __spreadProps(__spreadValues({}, item), {
848
1509
  image: (_a2 = item.image) != null ? _a2 : typeof resolveImage === "function" ? resolveImage(item) : void 0
849
1510
  });
850
1511
  }), primaryColor, onAddToCart }));
851
1512
  case "table":
852
- 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: {
853
- type: "table",
854
- title: config.title,
855
- description: config.description,
856
- columns: config.columns,
857
- dataKeys: config.dataKeys,
858
- xAxisKey: config.xAxisKey,
859
- data: config.data
860
- }, 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
+ ));
861
1529
  default:
862
1530
  return null;
863
1531
  }
864
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
865
1651
  function MessageBubble({
866
1652
  message,
867
1653
  sources,
@@ -874,99 +1660,45 @@ function MessageBubble({
874
1660
  const isUser = message.role === "user";
875
1661
  const isCompact = viewportSize === "compact";
876
1662
  const isMedium = viewportSize === "medium";
877
- const [showSources, setShowSources] = React6.useState(false);
878
- const hasStructuredProductBlock = React6.useMemo(
879
- () => /```(?:ui|json|chart)\s*[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products|table)"?/i.test(message.content) || /\{[\s\S]*?"?(?:view|type|data|products)"?\s*:\s*"?(?:carousel|products)"?[\s\S]*\}/i.test(message.content) && looksLikeStructuredPayload(message.content),
880
- [message.content]
881
- );
882
- const structuredContent = React6.useMemo(
1663
+ const [showSources, setShowSources] = React12.useState(false);
1664
+ const [showTrace, setShowTrace] = React12.useState(false);
1665
+ const structuredContent = React12.useMemo(
883
1666
  () => isUser ? { payload: null, text: message.content } : extractStructuredPayload(message.content),
884
1667
  [isUser, message.content]
885
1668
  );
886
- const shouldRenderStructuredOnly = !isUser && Boolean(structuredContent.payload) && isLikelyUiOnlyMessage(structuredContent.text);
887
- const productsFromSources = React6.useMemo(() => {
888
- if (isUser || !sources) return [];
889
- return sources.filter((s) => {
890
- var _a;
891
- const m = (_a = s.metadata) != null ? _a : {};
892
- return m.price || m.image || m.img || m.thumbnail || m.images || m.brand || m.type === "product";
893
- }).map((s) => {
894
- var _a, _b, _c, _d;
895
- const m = (_a = s.metadata) != null ? _a : {};
896
- return {
897
- id: s.id,
898
- name: (_d = (_c = (_b = m.name) != null ? _b : m.title) != null ? _c : s.content.split("\n")[0]) != null ? _d : "Unknown Product",
899
- brand: m.brand,
900
- price: m.price,
901
- image: resolveImage(m),
902
- link: m.link,
903
- description: s.content
904
- };
905
- });
1669
+ const hasRichUI = React12.useMemo(() => {
1670
+ if (message.uiTransformation) {
1671
+ const type = message.uiTransformation.type;
1672
+ if (type && !["text", "table"].includes(type)) return true;
1673
+ }
1674
+ if (structuredContent.payload) {
1675
+ return /"?(?:view|type|chartType)"?\s*:\s*"?(?:chart|carousel|pie|bar|line|product_carousel)"?/i.test(structuredContent.payload);
1676
+ }
1677
+ return false;
1678
+ }, [message.uiTransformation, structuredContent.payload]);
1679
+ const shouldRenderStructuredOnly = !isUser && hasRichUI && isLikelyUiOnlyMessage(structuredContent.text);
1680
+ const productsFromSources = React12.useMemo(() => {
1681
+ return extractProductsFromSources(sources, isUser);
906
1682
  }, [sources, isUser]);
907
- const { productsFromContent, cleanContent } = React6.useMemo(() => {
1683
+ const { productsFromContent, cleanContent } = React12.useMemo(() => {
908
1684
  if (isUser) {
909
1685
  return { productsFromContent: [], cleanContent: message.content };
910
1686
  }
911
- const jsonRegex = /```(?:json|ui)?\s*([\s\S]*?)\s*```/g;
912
- const products = [];
913
- let content = structuredContent.text;
914
- const payloadCandidates = [structuredContent.payload, ...content.matchAll(jsonRegex).map((match) => match[1])].filter(Boolean);
915
- for (const candidate of payloadCandidates) {
916
- try {
917
- const data = JSON.parse(sanitizeJson(candidate));
918
- const itemSet = Array.isArray(data.data) ? data.data : Array.isArray(data.items) ? data.items : Array.isArray(data.products) ? data.products : null;
919
- if ((data.type === "products" || data.view === "carousel" || data.view === "table" || Array.isArray(data.products)) && itemSet) {
920
- products.push(
921
- ...itemSet.map((item) => {
922
- var _a;
923
- return __spreadProps(__spreadValues({}, item), {
924
- image: (_a = item.image) != null ? _a : resolveImage(item)
925
- });
926
- })
927
- );
928
- }
929
- } catch (e) {
930
- }
931
- }
932
- if (content.includes('"type": "products"') || content.includes('"type":"products"')) {
933
- for (const match of content.matchAll(jsonRegex)) {
934
- try {
935
- const data = JSON.parse(sanitizeJson(match[1]));
936
- if (data.type === "products" && Array.isArray(data.items)) {
937
- products.push(
938
- ...data.items.map((item) => {
939
- var _a;
940
- return __spreadProps(__spreadValues({}, item), {
941
- image: (_a = item.image) != null ? _a : resolveImage(item)
942
- });
943
- })
944
- );
945
- content = content.replace(match[0], "");
946
- }
947
- } catch (e) {
948
- }
949
- }
950
- }
951
- return { productsFromContent: products, cleanContent: content.trim() };
952
- }, [message.content, isUser, structuredContent]);
953
- const allProducts = React6.useMemo(() => {
954
- if (productsFromContent.length > 0) return productsFromContent;
955
- const seen = /* @__PURE__ */ new Set();
956
- const contentLower = message.content.toLowerCase();
957
- return productsFromSources.filter((p) => {
958
- var _a;
959
- const id = String((_a = p.id) != null ? _a : p.name);
960
- if (seen.has(id)) return false;
961
- const mentioned = contentLower.includes(p.name.toLowerCase()) || (p.brand ? contentLower.includes(p.brand.toLowerCase()) : false);
962
- if (mentioned) {
963
- seen.add(id);
964
- return true;
965
- }
966
- return false;
967
- });
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
+ );
1697
+ }, [message.content, isUser, structuredContent, productsFromSources]);
1698
+ const allProducts = React12.useMemo(() => {
1699
+ return deduplicateProducts(productsFromSources, productsFromContent, message.content);
968
1700
  }, [productsFromSources, productsFromContent, message.content]);
969
- const processedMarkdown = React6.useMemo(() => {
1701
+ const processedMarkdown = React12.useMemo(() => {
970
1702
  let raw = structuredContent.payload ? structuredContent.text : cleanContent || message.content;
971
1703
  const hasStructuredUiBlock = Boolean(structuredContent.payload) || /```(?:ui|json|chart)\s*[\s\S]*?"(?:view|chartType|type|data|items|rows|products|results)"\s*:/i.test(raw);
972
1704
  raw = raw.replace(/([^\n])\n\|/g, "$1\n\n|").replace(/(\|[^\n]*\|)\n\n+(?=\|)/g, "$1\n");
@@ -987,156 +1719,26 @@ ${match.trim()}
987
1719
  if (hasStructuredUiBlock) {
988
1720
  raw = stripStructuredUiLabels(stripMarkdownTables(raw)).replace(/\n{3,}/g, "\n\n").trim();
989
1721
  }
990
- if (isStreaming) {
991
- raw = raw.replace(/((?:^|\n)\|.*)+/g, (match) => {
992
- return `
993
-
994
- \`\`\`table-loading
995
- ${match.trim()}
996
- \`\`\`
997
-
998
- `;
999
- });
1000
- }
1001
1722
  return raw;
1002
1723
  }, [cleanContent, message.content, isStreaming, structuredContent]);
1003
- const markdownComponents = React6.useMemo(
1004
- () => ({
1005
- // Wrap in not-prose so Tailwind Typography resets don't clobber our styles.
1006
- // prose applies display:block and padding:0 to table/th/td — not-prose opts out.
1007
- table: (_a) => {
1008
- var props = __objRest(_a, []);
1009
- if (isStreaming) {
1010
- 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..."));
1011
- }
1012
- 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(
1013
- "table",
1014
- __spreadValues({
1015
- className: "!table w-full text-left border-collapse min-w-[400px] text-sm"
1016
- }, props)
1017
- )));
1018
- },
1019
- thead: (_b) => {
1020
- var props = __objRest(_b, []);
1021
- return /* @__PURE__ */ React6.createElement(
1022
- "thead",
1023
- __spreadValues({
1024
- className: "!table-header-group bg-slate-50 dark:bg-white/5 border-b border-slate-200 dark:border-white/10"
1025
- }, props)
1026
- );
1027
- },
1028
- tbody: (_c) => {
1029
- var props = __objRest(_c, []);
1030
- return /* @__PURE__ */ React6.createElement("tbody", __spreadValues({ className: "!table-row-group divide-y divide-slate-100 dark:divide-white/5" }, props));
1031
- },
1032
- tr: (_d) => {
1033
- var props = __objRest(_d, []);
1034
- return /* @__PURE__ */ React6.createElement(
1035
- "tr",
1036
- __spreadValues({
1037
- className: "!table-row hover:bg-slate-50/70 dark:hover:bg-white/5 transition-colors"
1038
- }, props)
1039
- );
1040
- },
1041
- th: (_e) => {
1042
- var _f = _e, { children } = _f, props = __objRest(_f, ["children"]);
1043
- return /* @__PURE__ */ React6.createElement(
1044
- "th",
1045
- __spreadValues({
1046
- 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"
1047
- }, props),
1048
- normaliseChild(children)
1049
- );
1050
- },
1051
- td: (_g) => {
1052
- var _h = _g, { children } = _h, props = __objRest(_h, ["children"]);
1053
- return /* @__PURE__ */ React6.createElement(
1054
- "td",
1055
- __spreadValues({
1056
- className: "!table-cell px-4 py-3 text-slate-600 dark:text-white/70 whitespace-nowrap text-sm"
1057
- }, props),
1058
- normaliseChild(children)
1059
- );
1060
- },
1061
- code(_i) {
1062
- var _j = _i, {
1063
- inline,
1064
- className,
1065
- children
1066
- } = _j, props = __objRest(_j, [
1067
- "inline",
1068
- "className",
1069
- "children"
1070
- ]);
1071
- var _a;
1072
- const lang = (_a = /language-(\w+)/.exec(className != null ? className : "")) == null ? void 0 : _a[1];
1073
- if (!inline && (lang === "ui" || lang === "chart")) {
1074
- return /* @__PURE__ */ React6.createElement(
1075
- UIDispatcher,
1076
- {
1077
- rawContent: String(children != null ? children : "").trim(),
1078
- primaryColor,
1079
- accentColor,
1080
- isStreaming,
1081
- onAddToCart,
1082
- viewportSize
1083
- }
1084
- );
1085
- }
1086
- if (!inline && lang === "json") {
1087
- const content = String(children != null ? children : "").trim();
1088
- if (looksLikeStructuredPayload(content)) {
1089
- return /* @__PURE__ */ React6.createElement(
1090
- UIDispatcher,
1091
- {
1092
- rawContent: content,
1093
- primaryColor,
1094
- accentColor,
1095
- isStreaming,
1096
- onAddToCart,
1097
- viewportSize
1098
- }
1099
- );
1100
- }
1101
- }
1102
- if (!inline) {
1103
- const content = String(children != null ? children : "").trim();
1104
- if (looksLikeStructuredPayload(content)) {
1105
- return /* @__PURE__ */ React6.createElement(
1106
- UIDispatcher,
1107
- {
1108
- rawContent: content,
1109
- primaryColor,
1110
- accentColor,
1111
- isStreaming,
1112
- onAddToCart,
1113
- viewportSize
1114
- }
1115
- );
1116
- }
1117
- }
1118
- if (!inline && lang === "table-loading") {
1119
- 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..."));
1120
- }
1121
- return /* @__PURE__ */ React6.createElement("code", __spreadValues({ className }, props), typeof children === "string" || typeof children === "number" ? children : JSON.stringify(children));
1122
- }
1123
- }),
1724
+ const markdownComponents = React12.useMemo(
1725
+ () => createMarkdownComponents({ primaryColor, accentColor, isStreaming, onAddToCart, viewportSize }),
1124
1726
  [primaryColor, accentColor, isStreaming, onAddToCart, viewportSize]
1125
1727
  );
1126
- 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(
1127
1729
  "div",
1128
1730
  {
1129
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"}`,
1130
1732
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1131
1733
  },
1132
- 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"}` })
1133
- ), /* @__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(
1134
1736
  "div",
1135
1737
  {
1136
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"}`,
1137
1739
  style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {}
1138
1740
  },
1139
- 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(
1140
1742
  UIDispatcher,
1141
1743
  {
1142
1744
  rawContent: structuredContent.payload,
@@ -1146,87 +1748,66 @@ ${match.trim()}
1146
1748
  onAddToCart,
1147
1749
  viewportSize
1148
1750
  }
1149
- ), !shouldRenderStructuredOnly && /* @__PURE__ */ React6.createElement(ReactMarkdown, { remarkPlugins: [remarkGfm], components: markdownComponents }, processedMarkdown), !isUser && !structuredContent.payload && !!message.uiTransformation && /* @__PURE__ */ React6.createElement("div", { className: "mt-4 border-t border-slate-100 dark:border-white/5 pt-4" }, /* @__PURE__ */ React6.createElement(VisualizationRenderer, { data: message.uiTransformation })), 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" }))
1150
- ), !isUser && !hasStructuredProductBlock && allProducts.length > 0 && /* @__PURE__ */ React6.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React6.createElement(
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..."))
1760
+ ), (() => {
1761
+ var _a, _b;
1762
+ if (isUser || structuredContent.payload || !message.uiTransformation) return null;
1763
+ const ui = message.uiTransformation;
1764
+ const textContent = (_b = (_a = ui.data) == null ? void 0 : _a.content) != null ? _b : "";
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());
1766
+ if (!shouldShow) return null;
1767
+ return /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-3" }, /* @__PURE__ */ React12.createElement(
1768
+ VisualizationRenderer,
1769
+ {
1770
+ data: ui,
1771
+ primaryColor,
1772
+ onAddToCart,
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
1775
+ }
1776
+ ));
1777
+ })(), !isUser && !hasRichUI && allProducts.length > 0 && /* @__PURE__ */ React12.createElement("div", { className: "w-full mt-1" }, /* @__PURE__ */ React12.createElement(
1151
1778
  ProductCarousel,
1152
1779
  {
1153
1780
  products: allProducts,
1154
1781
  primaryColor,
1155
1782
  onAddToCart
1156
1783
  }
1157
- )), !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(
1158
1785
  "button",
1159
1786
  {
1160
1787
  onClick: () => setShowSources((s) => !s),
1161
- 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"
1162
1789
  },
1163
- 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" }),
1164
1791
  sources.length,
1165
1792
  " source",
1166
1793
  sources.length !== 1 ? "s" : "",
1167
1794
  " used"
1168
- ), 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 }))))));
1169
- }
1170
-
1171
- // src/components/ConfigProvider.tsx
1172
- import React7, { createContext, useContext } from "react";
1173
-
1174
- // src/config/uiConstants.ts
1175
- var DEFAULT_CONFIG = {
1176
- projectId: "default",
1177
- ui: {
1178
- title: "AI Assistant",
1179
- subtitle: "Powered by RAG",
1180
- primaryColor: "#6366f1",
1181
- accentColor: "#8b5cf6",
1182
- logoUrl: "",
1183
- placeholder: "Ask me anything\u2026",
1184
- showSources: true,
1185
- welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
1186
- showWidget: true,
1187
- poweredBy: "RAG",
1188
- visualStyle: "glass",
1189
- borderRadius: "xl",
1190
- allowUpload: true,
1191
- allowResize: true,
1192
- enableVoiceInput: true
1193
- }
1194
- };
1195
- var BORDER_RADIUS_MAP = {
1196
- none: "rounded-none",
1197
- sm: "rounded-sm",
1198
- md: "rounded-md",
1199
- lg: "rounded-lg",
1200
- xl: "rounded-xl",
1201
- full: "rounded-3xl"
1202
- };
1203
- var CHAT_SUGGESTIONS = [
1204
- "What can you help me with?",
1205
- "Summarise the key topics",
1206
- "Show me an example"
1207
- ];
1208
-
1209
- // src/components/ConfigProvider.tsx
1210
- var ConfigContext = createContext(DEFAULT_CONFIG);
1211
- function ConfigProvider({
1212
- config,
1213
- children
1214
- }) {
1215
- const merged = {
1216
- projectId: (config == null ? void 0 : config.projectId) || DEFAULT_CONFIG.projectId,
1217
- ui: mergeDefined(DEFAULT_CONFIG.ui, config == null ? void 0 : config.ui)
1218
- };
1219
- return /* @__PURE__ */ React7.createElement(ConfigContext.Provider, { value: merged }, children);
1220
- }
1221
- function useConfig() {
1222
- 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 }))));
1223
1804
  }
1224
1805
 
1225
1806
  // src/hooks/useRagChat.ts
1226
- 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";
1227
1808
 
1228
1809
  // src/hooks/useStoredMessages.ts
1229
- import * as React8 from "react";
1810
+ import * as React13 from "react";
1230
1811
  function readStoredMessages(storageKey) {
1231
1812
  if (typeof window === "undefined") {
1232
1813
  return [];
@@ -1243,8 +1824,8 @@ function readStoredMessages(storageKey) {
1243
1824
  }
1244
1825
  }
1245
1826
  function useStoredMessages(storageKey, persist) {
1246
- const [messages, setMessages] = React8.useState(() => persist ? readStoredMessages(storageKey) : []);
1247
- React8.useEffect(() => {
1827
+ const [messages, setMessages] = React13.useState(() => persist ? readStoredMessages(storageKey) : []);
1828
+ React13.useEffect(() => {
1248
1829
  if (!persist || typeof window === "undefined") {
1249
1830
  return;
1250
1831
  }
@@ -1284,10 +1865,10 @@ function useRagChat(projectId, options = {}) {
1284
1865
  } = options;
1285
1866
  const storageKey = `rag_chat_${projectId}`;
1286
1867
  const [messages, setMessages] = useStoredMessages(storageKey, persist);
1287
- const [isLoading, setIsLoading] = useState3(false);
1288
- const [error, setError] = useState3(null);
1289
- const lastInputRef = useRef2("");
1290
- 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);
1291
1872
  useEffect3(() => {
1292
1873
  messagesRef.current = messages;
1293
1874
  }, [messages]);
@@ -1331,6 +1912,7 @@ function useRagChat(projectId, options = {}) {
1331
1912
  let assistantContent = "";
1332
1913
  let sources = [];
1333
1914
  let uiTransformation = null;
1915
+ let trace;
1334
1916
  let buffer = "";
1335
1917
  const assistantMessageId = `assistant_${Date.now()}`;
1336
1918
  setMessages((prev) => [
@@ -1351,12 +1933,14 @@ function useRagChat(projectId, options = {}) {
1351
1933
  const complete = buffer.slice(0, lastBoundary + 2);
1352
1934
  buffer = buffer.slice(lastBoundary + 2);
1353
1935
  for (const frame of parseSseChunk(complete)) {
1354
- if (frame.type === "text") {
1936
+ if (frame.type === "text" && frame.text) {
1355
1937
  assistantContent += frame.text;
1356
1938
  } else if (frame.type === "metadata") {
1357
1939
  sources = (_a = frame.sources) != null ? _a : [];
1358
1940
  } else if (frame.type === "ui_transformation") {
1359
1941
  uiTransformation = frame.data;
1942
+ } else if (frame.type === "observability") {
1943
+ trace = frame.data;
1360
1944
  } else if (frame.type === "error") {
1361
1945
  setError(frame.error || "Stream error");
1362
1946
  }
@@ -1373,19 +1957,24 @@ function useRagChat(projectId, options = {}) {
1373
1957
  }
1374
1958
  if (buffer.trim()) {
1375
1959
  for (const frame of parseSseChunk(buffer)) {
1376
- if (frame.type === "text") assistantContent += frame.text;
1960
+ if (frame.type === "text" && frame.text) assistantContent += frame.text;
1377
1961
  else if (frame.type === "metadata") sources = (_b = frame.sources) != null ? _b : [];
1962
+ else if (frame.type === "observability") trace = frame.data;
1378
1963
  }
1379
1964
  }
1380
- const finalAssistantMessage = {
1965
+ const finalMsg = {
1381
1966
  id: assistantMessageId,
1382
1967
  role: "assistant",
1383
1968
  content: assistantContent,
1384
1969
  sources: sources.length > 0 ? sources : void 0,
1385
- uiTransformation: uiTransformation || void 0,
1970
+ uiTransformation: uiTransformation != null ? uiTransformation : void 0,
1971
+ trace,
1386
1972
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
1387
1973
  };
1388
- 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);
1389
1978
  } catch (err) {
1390
1979
  const msg = err instanceof Error ? err.message : "Something went wrong. Please try again.";
1391
1980
  setError(msg);
@@ -1435,19 +2024,20 @@ function ChatWindow({
1435
2024
  }) {
1436
2025
  var _a;
1437
2026
  const { ui, projectId } = useConfig();
1438
- const [input, setInput] = useState4("");
1439
- const [mounted, setMounted] = useState4(false);
1440
- const [viewportSize, setViewportSize] = useState4("large");
1441
- const windowRef = useRef3(null);
1442
- const bottomRef = useRef3(null);
1443
- 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);
1444
2033
  const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
1445
2034
  namespace: projectId
1446
2035
  });
1447
- const [suggestions, setSuggestions] = useState4([]);
1448
- const [isSuggesting, setIsSuggesting] = useState4(false);
1449
- const [isListening, setIsListening] = useState4(false);
1450
- 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);
1451
2041
  useEffect4(() => {
1452
2042
  if (typeof window !== "undefined") {
1453
2043
  const win = window;
@@ -1573,84 +2163,84 @@ function ChatWindow({
1573
2163
  }, 800);
1574
2164
  return () => clearTimeout(timer);
1575
2165
  }, [input, projectId]);
1576
- return /* @__PURE__ */ React9.createElement(
2166
+ return /* @__PURE__ */ React14.createElement(
1577
2167
  "div",
1578
2168
  {
1579
2169
  ref: windowRef,
1580
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}`,
1581
2171
  style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style)
1582
2172
  },
1583
- onResizeStart && /* @__PURE__ */ React9.createElement(
2173
+ onResizeStart && /* @__PURE__ */ React14.createElement(
1584
2174
  "div",
1585
2175
  {
1586
2176
  onMouseDown: onResizeStart,
1587
2177
  className: "absolute top-0 left-0 w-6 h-6 cursor-nw-resize z-[100] group flex items-start justify-start p-1",
1588
2178
  title: "Drag to resize"
1589
2179
  },
1590
- /* @__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]" })
1591
2181
  ),
1592
- /* @__PURE__ */ React9.createElement(
2182
+ /* @__PURE__ */ React14.createElement(
1593
2183
  "div",
1594
2184
  {
1595
2185
  className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
1596
2186
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` }
1597
2187
  },
1598
- /* @__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 ? (
1599
2189
  // eslint-disable-next-line @next/next/no-img-element
1600
- /* @__PURE__ */ React9.createElement("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
1601
- ) : /* @__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(
1602
2192
  "div",
1603
2193
  {
1604
2194
  className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
1605
2195
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1606
2196
  },
1607
- /* @__PURE__ */ React9.createElement(Bot2, { className: "w-5 h-5 text-white" })
1608
- ), /* @__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}`))),
1609
- /* @__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(
1610
2200
  "button",
1611
2201
  {
1612
2202
  onClick: clearHistory,
1613
2203
  title: "Clear conversation",
1614
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"
1615
2205
  },
1616
- /* @__PURE__ */ React9.createElement(Trash2, { className: "w-3.5 h-3.5" })
1617
- ), isResized && onResetResize && /* @__PURE__ */ React9.createElement(
2206
+ /* @__PURE__ */ React14.createElement(Trash2, { className: "w-3.5 h-3.5" })
2207
+ ), isResized && onResetResize && /* @__PURE__ */ React14.createElement(
1618
2208
  "button",
1619
2209
  {
1620
2210
  onClick: onResetResize,
1621
2211
  title: "Reset to default size",
1622
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"
1623
2213
  },
1624
- /* @__PURE__ */ React9.createElement(RotateCcw, { className: "w-3.5 h-3.5" })
1625
- ), onMaximize && /* @__PURE__ */ React9.createElement(
2214
+ /* @__PURE__ */ React14.createElement(RotateCcw, { className: "w-3.5 h-3.5" })
2215
+ ), onMaximize && /* @__PURE__ */ React14.createElement(
1626
2216
  "button",
1627
2217
  {
1628
2218
  onClick: onMaximize,
1629
2219
  title: isMaximized ? "Minimize" : "Maximize",
1630
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"
1631
2221
  },
1632
- isMaximized ? /* @__PURE__ */ React9.createElement(Minimize2, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ React9.createElement(Maximize2, { className: "w-3.5 h-3.5" })
1633
- ), 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(
1634
2224
  "button",
1635
2225
  {
1636
2226
  onClick: onClose,
1637
2227
  title: "Close chat",
1638
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"
1639
2229
  },
1640
- /* @__PURE__ */ React9.createElement(X, { className: "w-4 h-4" })
2230
+ /* @__PURE__ */ React14.createElement(X2, { className: "w-4 h-4" })
1641
2231
  )))
1642
2232
  ),
1643
- /* @__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 ? (
1644
2234
  /* Welcome state */
1645
- /* @__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(
1646
2236
  "div",
1647
2237
  {
1648
2238
  className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
1649
2239
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1650
2240
  },
1651
- /* @__PURE__ */ React9.createElement(Sparkles, { className: "w-8 h-8 text-white" })
1652
- ), /* @__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(
1653
- (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(
1654
2244
  "button",
1655
2245
  {
1656
2246
  key: suggestion,
@@ -1664,7 +2254,7 @@ function ChatWindow({
1664
2254
  suggestion
1665
2255
  )
1666
2256
  )))
1667
- ) : messages.map((msg, index) => /* @__PURE__ */ React9.createElement(
2257
+ ) : messages.map((msg, index) => /* @__PURE__ */ React14.createElement(
1668
2258
  MessageBubble,
1669
2259
  {
1670
2260
  key: msg.id,
@@ -1676,7 +2266,7 @@ function ChatWindow({
1676
2266
  onAddToCart,
1677
2267
  viewportSize
1678
2268
  }
1679
- )), 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(
1680
2270
  MessageBubble,
1681
2271
  {
1682
2272
  message: { id: "loading", role: "assistant", content: "", createdAt: (/* @__PURE__ */ new Date()).toISOString() },
@@ -1686,8 +2276,22 @@ function ChatWindow({
1686
2276
  onAddToCart,
1687
2277
  viewportSize
1688
2278
  }
1689
- ), 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 })),
1690
- /* @__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(
1691
2295
  "button",
1692
2296
  {
1693
2297
  key: suggestion,
@@ -1699,9 +2303,9 @@ function ChatWindow({
1699
2303
  },
1700
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"
1701
2305
  },
1702
- /* @__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" }),
1703
2307
  suggestion
1704
- )), 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(
1705
2309
  "textarea",
1706
2310
  {
1707
2311
  ref: inputRef,
@@ -1720,7 +2324,7 @@ function ChatWindow({
1720
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",
1721
2325
  style: { scrollbarWidth: "none" }
1722
2326
  }
1723
- ), ui.enableVoiceInput !== false && /* @__PURE__ */ React9.createElement(
2327
+ ), ui.enableVoiceInput !== false && /* @__PURE__ */ React14.createElement(
1724
2328
  "button",
1725
2329
  {
1726
2330
  type: "button",
@@ -1729,8 +2333,17 @@ function ChatWindow({
1729
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"}`,
1730
2334
  title: isListening ? "Stop listening" : "Start voice input"
1731
2335
  },
1732
- isListening ? /* @__PURE__ */ React9.createElement(MicOff, { className: "w-4 h-4" }) : /* @__PURE__ */ React9.createElement(Mic, { className: "w-4 h-4" })
1733
- ), /* @__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(
1734
2347
  "button",
1735
2348
  {
1736
2349
  onClick: sendMessage,
@@ -1741,8 +2354,8 @@ function ChatWindow({
1741
2354
  // slate-400/20 for light mode disabled
1742
2355
  }
1743
2356
  },
1744
- /* @__PURE__ */ React9.createElement(ArrowUp, { className: "w-4 h-4 text-white" })
1745
- )), /* @__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"))
1746
2359
  );
1747
2360
  }
1748
2361
 
@@ -1750,12 +2363,12 @@ function ChatWindow({
1750
2363
  var DEFAULT_DIMENSIONS = { width: 380, height: 600 };
1751
2364
  function ChatWidget({ position = "bottom-right", onAddToCart }) {
1752
2365
  const { ui } = useConfig();
1753
- const [isOpen, setIsOpen] = useState5(false);
1754
- const [hasUnread, setHasUnread] = useState5(false);
1755
- const [dimensions, setDimensions] = useState5(DEFAULT_DIMENSIONS);
1756
- const [isResizing, setIsResizing] = useState5(false);
1757
- const [isMaximized, setIsMaximized] = useState5(false);
1758
- 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);
1759
2372
  if (ui.showWidget === false) return null;
1760
2373
  const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
1761
2374
  const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
@@ -1805,7 +2418,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
1805
2418
  }
1806
2419
  };
1807
2420
  const isResized = dimensions.width !== DEFAULT_DIMENSIONS.width || dimensions.height !== DEFAULT_DIMENSIONS.height;
1808
- return /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(
2421
+ return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
1809
2422
  "div",
1810
2423
  {
1811
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"}`,
@@ -1815,7 +2428,7 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
1815
2428
  maxHeight: "calc(100vh - 6rem)"
1816
2429
  }
1817
2430
  },
1818
- /* @__PURE__ */ React10.createElement(
2431
+ /* @__PURE__ */ React15.createElement(
1819
2432
  ChatWindow,
1820
2433
  {
1821
2434
  className: "h-full relative z-10",
@@ -1829,13 +2442,13 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
1829
2442
  onAddToCart
1830
2443
  }
1831
2444
  ),
1832
- /* @__PURE__ */ React10.createElement(
2445
+ /* @__PURE__ */ React15.createElement(
1833
2446
  "div",
1834
2447
  {
1835
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"}`
1836
2449
  }
1837
2450
  )
1838
- ), /* @__PURE__ */ React10.createElement(
2451
+ ), /* @__PURE__ */ React15.createElement(
1839
2452
  "button",
1840
2453
  {
1841
2454
  onClick: isOpen ? () => setIsOpen(false) : handleOpen,
@@ -1843,156 +2456,32 @@ function ChatWidget({ position = "bottom-right", onAddToCart }) {
1843
2456
  style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
1844
2457
  "aria-label": "Open chat"
1845
2458
  },
1846
- /* @__PURE__ */ React10.createElement(
2459
+ /* @__PURE__ */ React15.createElement(
1847
2460
  "span",
1848
2461
  {
1849
2462
  className: "absolute inset-0 rounded-full animate-ping opacity-20",
1850
2463
  style: { background: ui.primaryColor }
1851
2464
  }
1852
2465
  ),
1853
- /* @__PURE__ */ React10.createElement(
2466
+ /* @__PURE__ */ React15.createElement(
1854
2467
  "span",
1855
2468
  {
1856
2469
  className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`
1857
2470
  },
1858
- 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" })
1859
2472
  ),
1860
- 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")
1861
2474
  ));
1862
2475
  }
1863
-
1864
- // src/components/DocumentUpload.tsx
1865
- import React11, { useState as useState6, useRef as useRef4 } from "react";
1866
- import { Upload, File, X as X3, CheckCircle, AlertCircle, Loader2 } from "lucide-react";
1867
- function DocumentUpload({ namespace, onUploadComplete, className = "" }) {
1868
- const { ui } = useConfig();
1869
- const [fileStates, setFileStates] = useState6([]);
1870
- const [isDragging, setIsDragging] = useState6(false);
1871
- const fileInputRef = useRef4(null);
1872
- const addFiles = (files) => {
1873
- const newStates = files.map((file) => ({ file, status: "idle" }));
1874
- setFileStates((prev) => [...prev, ...newStates]);
1875
- };
1876
- const removeFile = (index) => {
1877
- setFileStates((prev) => prev.filter((_, i) => i !== index));
1878
- };
1879
- const onDragOver = (e) => {
1880
- e.preventDefault();
1881
- setIsDragging(true);
1882
- };
1883
- const onDragLeave = () => {
1884
- setIsDragging(false);
1885
- };
1886
- const onDrop = (e) => {
1887
- e.preventDefault();
1888
- setIsDragging(false);
1889
- if (e.dataTransfer.files) {
1890
- addFiles(Array.from(e.dataTransfer.files));
1891
- }
1892
- };
1893
- const onFileSelect = (e) => {
1894
- if (e.target.files) {
1895
- addFiles(Array.from(e.target.files));
1896
- }
1897
- };
1898
- const uploadFiles = async () => {
1899
- const idleFiles = fileStates.filter((s) => s.status === "idle");
1900
- if (idleFiles.length === 0) return;
1901
- setFileStates((prev) => prev.map((s) => s.status === "idle" ? __spreadProps(__spreadValues({}, s), { status: "uploading" }) : s));
1902
- const formData = new FormData();
1903
- idleFiles.forEach((s) => formData.append("files", s.file));
1904
- if (namespace) formData.append("namespace", namespace);
1905
- try {
1906
- const response = await fetch("/api/upload", {
1907
- method: "POST",
1908
- body: formData
1909
- });
1910
- const result = await response.json();
1911
- if (!response.ok) throw new Error(result.error || "Upload failed");
1912
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "success" }) : s));
1913
- if (onUploadComplete) onUploadComplete(result);
1914
- } catch (err) {
1915
- const message = err instanceof Error ? err.message : "Upload failed";
1916
- setFileStates((prev) => prev.map((s) => s.status === "uploading" ? __spreadProps(__spreadValues({}, s), { status: "error", error: message }) : s));
1917
- }
1918
- };
1919
- const isUploading = fileStates.some((s) => s.status === "uploading");
1920
- const hasIdle = fileStates.some((s) => s.status === "idle");
1921
- return /* @__PURE__ */ React11.createElement(
1922
- "div",
1923
- {
1924
- 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}`,
1925
- onDragOver,
1926
- onDragLeave,
1927
- onDrop
1928
- },
1929
- /* @__PURE__ */ React11.createElement("div", { className: "flex flex-col items-center justify-center text-center" }, /* @__PURE__ */ React11.createElement(
1930
- "div",
1931
- {
1932
- className: "w-12 h-12 rounded-full flex items-center justify-center mb-4 shadow-sm",
1933
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}20, ${ui.accentColor}20)` }
1934
- },
1935
- /* @__PURE__ */ React11.createElement(Upload, { className: "w-6 h-6", style: { color: ui.primaryColor } })
1936
- ), /* @__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(
1937
- "button",
1938
- {
1939
- onClick: () => {
1940
- var _a;
1941
- return (_a = fileInputRef.current) == null ? void 0 : _a.click();
1942
- },
1943
- disabled: isUploading,
1944
- className: "px-4 py-2 rounded-lg text-sm font-medium transition-all hover:scale-105 active:scale-95 disabled:opacity-50",
1945
- style: {
1946
- backgroundColor: `${ui.primaryColor}15`,
1947
- color: ui.primaryColor,
1948
- border: `1px solid ${ui.primaryColor}30`
1949
- }
1950
- },
1951
- "Select Files"
1952
- ), /* @__PURE__ */ React11.createElement(
1953
- "input",
1954
- {
1955
- ref: fileInputRef,
1956
- type: "file",
1957
- multiple: true,
1958
- onChange: onFileSelect,
1959
- className: "hidden",
1960
- accept: ".pdf,.docx,.txt,.md,.json,.csv"
1961
- }
1962
- )),
1963
- fileStates.length > 0 && /* @__PURE__ */ React11.createElement("div", { className: "mt-8 space-y-3" }, fileStates.map((state, i) => /* @__PURE__ */ React11.createElement(
1964
- "div",
1965
- {
1966
- key: i,
1967
- 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"
1968
- },
1969
- /* @__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"))),
1970
- /* @__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(
1971
- "button",
1972
- {
1973
- onClick: () => removeFile(i),
1974
- className: "p-1 rounded-md hover:bg-slate-200 dark:hover:bg-white/10 transition-colors"
1975
- },
1976
- /* @__PURE__ */ React11.createElement(X3, { className: "w-3.5 h-3.5 text-slate-400" })
1977
- ))
1978
- )), hasIdle && !isUploading && /* @__PURE__ */ React11.createElement(
1979
- "button",
1980
- {
1981
- onClick: uploadFiles,
1982
- 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]",
1983
- style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` }
1984
- },
1985
- "Start Upload"
1986
- ))
1987
- );
1988
- }
1989
2476
  export {
1990
2477
  ChatWidget,
1991
2478
  ChatWindow,
1992
2479
  ConfigProvider,
1993
2480
  DocumentUpload,
1994
2481
  MessageBubble,
2482
+ ObservabilityPanel,
1995
2483
  SourceCard,
2484
+ addSynonyms,
1996
2485
  useConfig,
1997
2486
  useRagChat
1998
2487
  };