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