@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.
- package/dist/DocumentChunker-Dh9TvmGG.d.mts +45 -0
- package/dist/DocumentChunker-Dh9TvmGG.d.ts +45 -0
- package/dist/{index-DPsQodME.d.mts → ILLMProvider-BOJFz3Na.d.mts} +104 -1
- package/dist/{index-DPsQodME.d.ts → ILLMProvider-BOJFz3Na.d.ts} +104 -1
- package/dist/MultiTablePostgresProvider-ZLGSKTJR.mjs +8 -0
- package/dist/chunk-ICKRMZQK.mjs +76 -0
- package/dist/{chunk-PV3MFHWU.mjs → chunk-LZVVLSDN.mjs} +977 -516
- package/dist/chunk-OZFBG4BA.mjs +291 -0
- package/dist/handlers/index.d.mts +2 -2
- package/dist/handlers/index.d.ts +2 -2
- package/dist/handlers/index.js +1269 -656
- package/dist/handlers/index.mjs +4 -1
- package/dist/{index-Bb2yEopi.d.mts → index-BwpcaziY.d.ts} +10 -2
- package/dist/{index-CkbTzj9J.d.ts → index-D3V9Et2M.d.mts} +10 -2
- package/dist/index.d.mts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +1354 -826
- package/dist/index.mjs +1284 -795
- package/dist/server.d.mts +47 -27
- package/dist/server.d.ts +47 -27
- package/dist/server.js +1417 -829
- package/dist/server.mjs +164 -176
- package/package.json +6 -2
- package/src/app/api/upload/route.ts +4 -0
- package/src/app/constants.tsx +2 -2
- package/src/app/page.tsx +12 -322
- package/src/components/AmbientBackground.tsx +29 -0
- package/src/components/ArchitectureCard.tsx +17 -0
- package/src/components/ArchitectureCardsSection.tsx +15 -0
- package/src/components/ChatWindow.tsx +32 -0
- package/src/components/CodeViewer.tsx +51 -0
- package/src/components/ConfigProvider.tsx +1 -0
- package/src/components/DocViewer.tsx +37 -0
- package/src/components/DocumentUpload.tsx +44 -1
- package/src/components/Documentation.tsx +58 -0
- package/src/components/DynamicChart.tsx +27 -2
- package/src/components/Hero.tsx +59 -0
- package/src/components/HourglassLoader.tsx +87 -0
- package/src/components/Lifecycle.tsx +37 -0
- package/src/components/MarkdownComponents.tsx +140 -0
- package/src/components/MessageBubble.tsx +124 -904
- package/src/components/Navbar.tsx +55 -0
- package/src/components/ObservabilityPanel.tsx +374 -0
- package/src/components/ProductCard.tsx +5 -3
- package/src/components/UIDispatcher.tsx +344 -0
- package/src/components/VisualizationRenderer.tsx +372 -250
- package/src/config/RagConfig.ts +5 -0
- package/src/config/serverConfig.ts +3 -1
- package/src/core/Pipeline.ts +240 -271
- package/src/core/ProviderRegistry.ts +2 -2
- package/src/core/VectorPlugin.ts +9 -0
- package/src/handlers/index.ts +91 -15
- package/src/hooks/useRagChat.ts +21 -11
- package/src/index.ts +9 -1
- package/src/llm/LLMFactory.ts +54 -2
- package/src/llm/providers/AnthropicProvider.ts +12 -8
- package/src/llm/providers/GeminiProvider.ts +188 -143
- package/src/llm/providers/OllamaProvider.ts +7 -3
- package/src/llm/providers/OpenAIProvider.ts +12 -8
- package/src/providers/vectordb/MultiTablePostgresProvider.ts +150 -64
- package/src/types/chat.ts +8 -0
- package/src/types/index.ts +132 -0
- package/src/types/props.ts +9 -1
- package/src/utils/ProductExtractor.ts +347 -0
- package/src/utils/SchemaMapper.ts +129 -0
- package/src/utils/UITransformer.ts +470 -209
- package/src/utils/synonyms.ts +78 -0
- package/dist/DocumentChunker-C1GEEosY.d.ts +0 -93
- package/dist/DocumentChunker-CFEiRopR.d.mts +0 -93
- package/dist/PostgreSQLProvider-BMOETDZA.mjs +0 -8
- 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
|
|
12
|
-
import { MessageSquare, X as
|
|
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
|
|
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
|
|
32
|
-
import { Bot, User, ChevronDown as
|
|
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
|
|
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] =
|
|
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__ */
|
|
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__ */
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
hasMore && /* @__PURE__ */
|
|
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
|
|
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
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
296
|
+
/* @__PURE__ */ React4.createElement(ShoppingCart, { className: "w-3 h-3" }),
|
|
85
297
|
"Add to Cart"
|
|
86
|
-
), product.link && /* @__PURE__ */
|
|
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__ */
|
|
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 =
|
|
101
|
-
const [canScroll, setCanScroll] =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
139
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
150
|
-
))), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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
|
|
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, ...
|
|
194
|
-
const containerRef =
|
|
195
|
-
const [containerWidth, setContainerWidth] =
|
|
196
|
-
|
|
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 =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
311
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
-
|
|
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__ */
|
|
610
|
+
return /* @__PURE__ */ React7.createElement("div", { className: `p-4 text-gray-500 ${className}` }, "No data available for visualization");
|
|
344
611
|
}
|
|
345
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
|
365
|
-
if (
|
|
366
|
-
return /* @__PURE__ */
|
|
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
|
-
|
|
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
|
-
|
|
372
|
-
|
|
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__ */
|
|
375
|
-
|
|
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
|
|
392
|
-
if (
|
|
393
|
-
|
|
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
|
-
|
|
396
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
686
|
+
dataKey: "category",
|
|
687
|
+
tick: { fontSize: 11, fill: "#64748b" },
|
|
688
|
+
tickLine: false,
|
|
689
|
+
axisLine: { stroke: "#cbd5e1" }
|
|
403
690
|
}
|
|
404
|
-
)),
|
|
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
|
|
413
|
-
if (
|
|
414
|
-
|
|
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
|
-
|
|
417
|
-
|
|
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
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
|
427
|
-
if (
|
|
428
|
-
|
|
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
|
-
|
|
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-
|
|
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__ */
|
|
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-
|
|
763
|
+
className: "hover:bg-slate-50/60 dark:hover:bg-white/5 transition-colors"
|
|
442
764
|
},
|
|
443
|
-
row.map((cell, cellIdx) => /* @__PURE__ */
|
|
765
|
+
row.map((cell, cellIdx) => /* @__PURE__ */ React7.createElement(
|
|
444
766
|
"td",
|
|
445
767
|
{
|
|
446
768
|
key: cellIdx,
|
|
447
|
-
className: "px-4 py-
|
|
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
|
|
775
|
+
function CarouselView({
|
|
776
|
+
data,
|
|
777
|
+
onAddToCart,
|
|
778
|
+
primaryColor
|
|
779
|
+
}) {
|
|
454
780
|
if (!Array.isArray(data)) {
|
|
455
|
-
return /* @__PURE__ */
|
|
781
|
+
return /* @__PURE__ */ React7.createElement("div", { className: "text-xs text-red-500" }, "Invalid carousel data");
|
|
456
782
|
}
|
|
457
|
-
|
|
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:
|
|
461
|
-
className: "
|
|
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
|
-
|
|
464
|
-
|
|
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
|
-
|
|
467
|
-
|
|
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__ */
|
|
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
|
|
483
|
-
const
|
|
484
|
-
|
|
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
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
"
|
|
493
|
-
|
|
494
|
-
"
|
|
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
|
|
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/
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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 (
|
|
552
|
-
|
|
553
|
-
|
|
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
|
-
|
|
1148
|
+
let start = raw.indexOf("{");
|
|
590
1149
|
if (start === -1) return null;
|
|
591
|
-
let
|
|
592
|
-
let
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (
|
|
613
|
-
|
|
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
|
|
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
|
|
661
|
-
|
|
662
|
-
|
|
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
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
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
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
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
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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
|
-
|
|
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 =
|
|
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 = "
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
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] =
|
|
878
|
-
const
|
|
879
|
-
|
|
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
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
return
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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 } =
|
|
1683
|
+
const { productsFromContent, cleanContent } = React12.useMemo(() => {
|
|
908
1684
|
if (isUser) {
|
|
909
1685
|
return { productsFromContent: [], cleanContent: message.content };
|
|
910
1686
|
}
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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 =
|
|
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 =
|
|
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__ */
|
|
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__ */
|
|
1133
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1150
|
-
|
|
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 &&
|
|
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
|
|
1788
|
+
className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1"
|
|
1162
1789
|
},
|
|
1163
|
-
showSources ? /* @__PURE__ */
|
|
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
|
-
),
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
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
|
|
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
|
|
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] =
|
|
1247
|
-
|
|
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] =
|
|
1288
|
-
const [error, setError] =
|
|
1289
|
-
const lastInputRef =
|
|
1290
|
-
const messagesRef =
|
|
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
|
|
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
|
|
1970
|
+
uiTransformation: uiTransformation != null ? uiTransformation : void 0,
|
|
1971
|
+
trace,
|
|
1386
1972
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1387
1973
|
};
|
|
1388
|
-
|
|
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] =
|
|
1439
|
-
const [mounted, setMounted] =
|
|
1440
|
-
const [viewportSize, setViewportSize] =
|
|
1441
|
-
const windowRef =
|
|
1442
|
-
const bottomRef =
|
|
1443
|
-
const inputRef =
|
|
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] =
|
|
1448
|
-
const [isSuggesting, setIsSuggesting] =
|
|
1449
|
-
const [isListening, setIsListening] =
|
|
1450
|
-
const
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1601
|
-
) : /* @__PURE__ */
|
|
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__ */
|
|
1608
|
-
), /* @__PURE__ */
|
|
1609
|
-
/* @__PURE__ */
|
|
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__ */
|
|
1617
|
-
), isResized && onResetResize && /* @__PURE__ */
|
|
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__ */
|
|
1625
|
-
), onMaximize && /* @__PURE__ */
|
|
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__ */
|
|
1633
|
-
), showClose && onClose && /* @__PURE__ */
|
|
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__ */
|
|
2230
|
+
/* @__PURE__ */ React14.createElement(X2, { className: "w-4 h-4" })
|
|
1641
2231
|
)))
|
|
1642
2232
|
),
|
|
1643
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1652
|
-
), /* @__PURE__ */
|
|
1653
|
-
(suggestion) => /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
1690
|
-
/* @__PURE__ */
|
|
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__ */
|
|
2306
|
+
/* @__PURE__ */ React14.createElement(Sparkles, { className: "w-3 h-3 text-amber-400" }),
|
|
1703
2307
|
suggestion
|
|
1704
|
-
)), isSuggesting && suggestions.length === 0 && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
1733
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
1745
|
-
)), /* @__PURE__ */
|
|
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] =
|
|
1754
|
-
const [hasUnread, setHasUnread] =
|
|
1755
|
-
const [dimensions, setDimensions] =
|
|
1756
|
-
const [isResizing, setIsResizing] =
|
|
1757
|
-
const [isMaximized, setIsMaximized] =
|
|
1758
|
-
const [prevDimensions, setPrevDimensions] =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
};
|