@retrivora-ai/rag-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.env.example +77 -0
  2. package/README.md +149 -0
  3. package/dist/DocumentChunker-BUrIrcPk.d.mts +43 -0
  4. package/dist/DocumentChunker-BUrIrcPk.d.ts +43 -0
  5. package/dist/RAGPipeline-BmkIv1HD.d.mts +298 -0
  6. package/dist/RAGPipeline-BmkIv1HD.d.ts +298 -0
  7. package/dist/chunk-NCG2JKXB.mjs +1254 -0
  8. package/dist/chunk-ZPXLQR5Q.mjs +67 -0
  9. package/dist/handlers/index.d.mts +68 -0
  10. package/dist/handlers/index.d.ts +68 -0
  11. package/dist/handlers/index.js +1319 -0
  12. package/dist/handlers/index.mjs +13 -0
  13. package/dist/index.d.mts +93 -0
  14. package/dist/index.d.ts +93 -0
  15. package/dist/index.js +612 -0
  16. package/dist/index.mjs +551 -0
  17. package/dist/server.d.mts +211 -0
  18. package/dist/server.d.ts +211 -0
  19. package/dist/server.js +1457 -0
  20. package/dist/server.mjs +148 -0
  21. package/package.json +90 -0
  22. package/src/app/api/chat/route.ts +4 -0
  23. package/src/app/api/health/route.ts +4 -0
  24. package/src/app/api/ingest/route.ts +26 -0
  25. package/src/app/favicon.ico +0 -0
  26. package/src/app/globals.css +163 -0
  27. package/src/app/layout.tsx +35 -0
  28. package/src/app/page.tsx +506 -0
  29. package/src/components/ChatWidget.tsx +91 -0
  30. package/src/components/ChatWindow.tsx +248 -0
  31. package/src/components/ConfigProvider.tsx +56 -0
  32. package/src/components/MessageBubble.tsx +99 -0
  33. package/src/components/SourceCard.tsx +66 -0
  34. package/src/components/ThemeProvider.tsx +8 -0
  35. package/src/components/ThemeToggle.tsx +29 -0
  36. package/src/config/RagConfig.ts +159 -0
  37. package/src/config/UniversalProfiles.ts +83 -0
  38. package/src/config/serverConfig.ts +142 -0
  39. package/src/handlers/index.ts +202 -0
  40. package/src/hooks/useHydrated.ts +13 -0
  41. package/src/hooks/useRagChat.ts +167 -0
  42. package/src/hooks/useStoredMessages.ts +53 -0
  43. package/src/index.ts +27 -0
  44. package/src/llm/ILLMProvider.ts +60 -0
  45. package/src/llm/LLMFactory.ts +54 -0
  46. package/src/llm/providers/AnthropicProvider.ts +87 -0
  47. package/src/llm/providers/OllamaProvider.ts +102 -0
  48. package/src/llm/providers/OpenAIProvider.ts +92 -0
  49. package/src/llm/providers/UniversalLLMAdapter.ts +154 -0
  50. package/src/rag/DocumentChunker.ts +105 -0
  51. package/src/rag/RAGPipeline.ts +196 -0
  52. package/src/server.ts +30 -0
  53. package/src/types/pdf-parse.d.ts +13 -0
  54. package/src/utils/DocumentParser.ts +75 -0
  55. package/src/utils/templateUtils.ts +78 -0
  56. package/src/vectordb/IVectorDB.ts +75 -0
  57. package/src/vectordb/VectorDBFactory.ts +41 -0
  58. package/src/vectordb/adapters/MongoDbAdapter.ts +175 -0
  59. package/src/vectordb/adapters/PgVectorAdapter.ts +159 -0
  60. package/src/vectordb/adapters/PineconeAdapter.ts +115 -0
  61. package/src/vectordb/adapters/UniversalVectorDBAdapter.ts +177 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,551 @@
1
+ import {
2
+ __spreadValues,
3
+ mergeDefined
4
+ } from "./chunk-ZPXLQR5Q.mjs";
5
+
6
+ // src/components/ChatWidget.tsx
7
+ import { useState as useState4 } from "react";
8
+ import { MessageSquare, X as X2 } from "lucide-react";
9
+
10
+ // src/components/ChatWindow.tsx
11
+ import { useState as useState3, useRef as useRef2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
12
+ import {
13
+ Bot as Bot2,
14
+ Trash2,
15
+ X,
16
+ Sparkles,
17
+ ArrowUp,
18
+ TriangleAlert
19
+ } from "lucide-react";
20
+
21
+ // src/components/MessageBubble.tsx
22
+ import React2 from "react";
23
+ import { Bot, User, ChevronDown as ChevronDown2, ChevronRight } from "lucide-react";
24
+ import ReactMarkdown from "react-markdown";
25
+ import remarkGfm from "remark-gfm";
26
+
27
+ // src/components/SourceCard.tsx
28
+ import React from "react";
29
+ import { ChevronDown, ChevronUp } from "lucide-react";
30
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
31
+ function SourceCard({ source, index }) {
32
+ var _a, _b;
33
+ const [expanded, setExpanded] = React.useState(false);
34
+ const scorePercent = Math.round(source.score * 100);
35
+ const scoreColor = source.score >= 0.8 ? "text-emerald-400" : source.score >= 0.6 ? "text-amber-400" : "text-rose-400";
36
+ 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";
37
+ const preview = source.content.slice(0, 120);
38
+ const hasMore = source.content.length > 120;
39
+ return /* @__PURE__ */ jsxs(
40
+ "div",
41
+ {
42
+ className: `rounded-xl border backdrop-blur-sm p-3 transition-all duration-200 hover:scale-[1.01] cursor-pointer ${scoreBg}`,
43
+ onClick: () => setExpanded((e) => !e),
44
+ children: [
45
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
46
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
47
+ /* @__PURE__ */ jsx("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 }),
48
+ /* @__PURE__ */ jsx("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 })
49
+ ] }),
50
+ /* @__PURE__ */ jsxs("span", { className: `flex-shrink-0 text-xs font-semibold ${scoreColor} tabular-nums`, children: [
51
+ scorePercent,
52
+ "%"
53
+ ] })
54
+ ] }),
55
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-xs text-slate-600 dark:text-white/70 leading-relaxed", children: expanded ? source.content : `${preview}${hasMore && !expanded ? "\u2026" : ""}` }),
56
+ hasMore && /* @__PURE__ */ jsx("button", { className: "mt-1 text-[10px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-0.5", children: expanded ? /* @__PURE__ */ jsxs(Fragment, { children: [
57
+ /* @__PURE__ */ jsx(ChevronUp, { className: "w-3 h-3" }),
58
+ " Show less"
59
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
60
+ /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3" }),
61
+ " Show more"
62
+ ] }) })
63
+ ]
64
+ }
65
+ );
66
+ }
67
+
68
+ // src/components/MessageBubble.tsx
69
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
70
+ function MessageBubble({
71
+ message,
72
+ sources,
73
+ isStreaming = false,
74
+ primaryColor = "#6366f1",
75
+ accentColor = "#8b5cf6"
76
+ }) {
77
+ const isUser = message.role === "user";
78
+ const [showSources, setShowSources] = React2.useState(false);
79
+ return /* @__PURE__ */ jsxs2("div", { className: `flex gap-3 ${isUser ? "flex-row-reverse" : "flex-row"} items-start`, children: [
80
+ /* @__PURE__ */ jsx2(
81
+ "div",
82
+ {
83
+ className: `flex-shrink-0 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"}`,
84
+ style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {},
85
+ children: isUser ? /* @__PURE__ */ jsx2(User, { className: "w-4 h-4 text-white" }) : /* @__PURE__ */ jsx2(Bot, { className: "w-4 h-4" })
86
+ }
87
+ ),
88
+ /* @__PURE__ */ jsxs2("div", { className: `flex flex-col gap-1 max-w-[75%] ${isUser ? "items-end" : "items-start"}`, children: [
89
+ /* @__PURE__ */ jsx2(
90
+ "div",
91
+ {
92
+ className: `relative px-4 py-3 rounded-2xl text-sm 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"}`,
93
+ style: isUser ? { background: `linear-gradient(135deg, ${primaryColor}, ${accentColor})` } : {},
94
+ children: isStreaming ? /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-1", children: [
95
+ /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:0ms]" }),
96
+ /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:150ms]" }),
97
+ /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-slate-400 dark:bg-white/60 animate-bounce [animation-delay:300ms]" })
98
+ ] }) : /* @__PURE__ */ jsx2("div", { className: `prose prose-sm max-w-none ${isUser ? "prose-invert" : "dark:prose-invert"}`, children: /* @__PURE__ */ jsx2(ReactMarkdown, { remarkPlugins: [remarkGfm], children: message.content }) })
99
+ }
100
+ ),
101
+ !isUser && sources && sources.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "w-full", children: [
102
+ /* @__PURE__ */ jsxs2(
103
+ "button",
104
+ {
105
+ onClick: () => setShowSources((s) => !s),
106
+ className: "text-[11px] text-indigo-400 hover:text-indigo-300 transition-colors flex items-center gap-1 mt-1",
107
+ children: [
108
+ showSources ? /* @__PURE__ */ jsx2(ChevronDown2, { className: "w-3 h-3" }) : /* @__PURE__ */ jsx2(ChevronRight, { className: "w-3 h-3" }),
109
+ sources.length,
110
+ " source",
111
+ sources.length !== 1 ? "s" : "",
112
+ " used"
113
+ ]
114
+ }
115
+ ),
116
+ showSources && /* @__PURE__ */ jsx2("div", { className: "mt-2 flex flex-col gap-2", children: sources.map((src, i) => /* @__PURE__ */ jsx2(SourceCard, { source: src, index: i }, src.id)) })
117
+ ] })
118
+ ] })
119
+ ] });
120
+ }
121
+
122
+ // src/components/ConfigProvider.tsx
123
+ import { createContext, useContext } from "react";
124
+ import { jsx as jsx3 } from "react/jsx-runtime";
125
+ var defaultConfig = {
126
+ projectId: "default",
127
+ ui: {
128
+ title: "AI Assistant",
129
+ subtitle: "Powered by RAG",
130
+ primaryColor: "#6366f1",
131
+ accentColor: "#8b5cf6",
132
+ logoUrl: "",
133
+ placeholder: "Ask me anything\u2026",
134
+ showSources: true,
135
+ welcomeMessage: "Hello! I'm your AI assistant. Ask me anything about your documents.",
136
+ showWidget: true,
137
+ poweredBy: "RAG",
138
+ visualStyle: "glass",
139
+ borderRadius: "xl"
140
+ }
141
+ };
142
+ var ConfigContext = createContext(defaultConfig);
143
+ function ConfigProvider({
144
+ config,
145
+ children
146
+ }) {
147
+ const merged = {
148
+ projectId: (config == null ? void 0 : config.projectId) || defaultConfig.projectId,
149
+ ui: mergeDefined(defaultConfig.ui, config == null ? void 0 : config.ui)
150
+ };
151
+ return /* @__PURE__ */ jsx3(ConfigContext.Provider, { value: merged, children });
152
+ }
153
+ function useConfig() {
154
+ return useContext(ConfigContext);
155
+ }
156
+
157
+ // src/hooks/useRagChat.ts
158
+ import { useState as useState2, useCallback, useRef, useEffect as useEffect2 } from "react";
159
+ import axios from "axios";
160
+
161
+ // src/hooks/useStoredMessages.ts
162
+ import * as React4 from "react";
163
+ function readStoredMessages(storageKey) {
164
+ if (typeof window === "undefined") {
165
+ return [];
166
+ }
167
+ const saved = window.localStorage.getItem(storageKey);
168
+ if (!saved) {
169
+ return [];
170
+ }
171
+ try {
172
+ const parsed = JSON.parse(saved);
173
+ return Array.isArray(parsed) ? parsed : [];
174
+ } catch (e) {
175
+ return [];
176
+ }
177
+ }
178
+ function useStoredMessages(storageKey, persist) {
179
+ const [messages, setMessages] = React4.useState(() => persist ? readStoredMessages(storageKey) : []);
180
+ React4.useEffect(() => {
181
+ if (!persist || typeof window === "undefined") {
182
+ return;
183
+ }
184
+ if (messages.length === 0) {
185
+ window.localStorage.removeItem(storageKey);
186
+ return;
187
+ }
188
+ window.localStorage.setItem(storageKey, JSON.stringify(messages));
189
+ }, [messages, persist, storageKey]);
190
+ return [messages, setMessages];
191
+ }
192
+
193
+ // src/hooks/useRagChat.ts
194
+ function useRagChat(projectId, options = {}) {
195
+ const {
196
+ apiUrl = "/api/chat",
197
+ namespace,
198
+ persist = true,
199
+ onReply,
200
+ onError
201
+ } = options;
202
+ const storageKey = `rag_chat_${projectId}`;
203
+ const [messages, setMessages] = useStoredMessages(storageKey, persist);
204
+ const [isLoading, setIsLoading] = useState2(false);
205
+ const [error, setError] = useState2(null);
206
+ const lastInputRef = useRef("");
207
+ const messagesRef = useRef(messages);
208
+ useEffect2(() => {
209
+ messagesRef.current = messages;
210
+ }, [messages]);
211
+ const sendMessage = useCallback(
212
+ async (text, options2) => {
213
+ var _a, _b, _c;
214
+ const trimmed = text.trim();
215
+ if (!trimmed || isLoading) return;
216
+ lastInputRef.current = trimmed;
217
+ if (!(options2 == null ? void 0 : options2.skipUserMessage)) {
218
+ const userMessage = {
219
+ id: `user_${Date.now()}`,
220
+ role: "user",
221
+ content: trimmed,
222
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
223
+ };
224
+ setMessages((prev) => [...prev, userMessage]);
225
+ }
226
+ setError(null);
227
+ setIsLoading(true);
228
+ try {
229
+ const history = messagesRef.current.map(({ role, content }) => ({ role, content }));
230
+ const { data } = await axios.post(apiUrl, {
231
+ message: trimmed,
232
+ history,
233
+ namespace: namespace != null ? namespace : projectId
234
+ });
235
+ const assistantMessage = {
236
+ id: `assistant_${Date.now()}`,
237
+ role: "assistant",
238
+ content: data.reply,
239
+ sources: data.sources,
240
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
241
+ };
242
+ setMessages((prev) => [...prev, assistantMessage]);
243
+ onReply == null ? void 0 : onReply(assistantMessage);
244
+ } catch (err) {
245
+ const msg = axios.isAxiosError(err) ? (_c = (_b = (_a = err.response) == null ? void 0 : _a.data) == null ? void 0 : _b.error) != null ? _c : err.message : "Something went wrong. Please try again.";
246
+ setError(msg);
247
+ onError == null ? void 0 : onError(msg);
248
+ } finally {
249
+ setIsLoading(false);
250
+ }
251
+ },
252
+ [apiUrl, isLoading, namespace, onError, onReply, projectId, setMessages]
253
+ );
254
+ const clear = useCallback(() => {
255
+ setMessages([]);
256
+ setError(null);
257
+ if (persist) localStorage.removeItem(storageKey);
258
+ }, [persist, setMessages, storageKey]);
259
+ const retry = useCallback(async () => {
260
+ if (lastInputRef.current) {
261
+ const latestMessage = messagesRef.current[messagesRef.current.length - 1];
262
+ await sendMessage(lastInputRef.current, {
263
+ skipUserMessage: (latestMessage == null ? void 0 : latestMessage.role) === "user" && latestMessage.content === lastInputRef.current
264
+ });
265
+ }
266
+ }, [sendMessage]);
267
+ return {
268
+ messages,
269
+ isLoading,
270
+ error,
271
+ send: sendMessage,
272
+ clear,
273
+ retry,
274
+ setMessages
275
+ };
276
+ }
277
+
278
+ // src/components/ChatWindow.tsx
279
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
280
+ function ChatWindow({ className = "", style, onClose, showClose = false }) {
281
+ const { ui, projectId } = useConfig();
282
+ const [input, setInput] = useState3("");
283
+ const [mounted, setMounted] = useState3(false);
284
+ const bottomRef = useRef2(null);
285
+ const inputRef = useRef2(null);
286
+ const { messages, send, clear, isLoading, error } = useRagChat(projectId, {
287
+ namespace: projectId
288
+ });
289
+ useEffect3(() => {
290
+ setMounted(true);
291
+ }, []);
292
+ useEffect3(() => {
293
+ var _a;
294
+ if (messages.length > 0 || isLoading) {
295
+ (_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
296
+ }
297
+ }, [messages, isLoading]);
298
+ const sendMessage = useCallback2(async () => {
299
+ const text = input.trim();
300
+ if (!text || isLoading) return;
301
+ setInput("");
302
+ await send(text);
303
+ window.setTimeout(() => {
304
+ var _a;
305
+ return (_a = inputRef.current) == null ? void 0 : _a.focus();
306
+ }, 50);
307
+ }, [input, isLoading, send]);
308
+ const handleKeyDown = (e) => {
309
+ if (e.key === "Enter" && !e.shiftKey) {
310
+ e.preventDefault();
311
+ sendMessage();
312
+ }
313
+ };
314
+ const clearHistory = () => {
315
+ clear();
316
+ };
317
+ const isEmpty = messages.length === 0;
318
+ const borderRadiusMap = {
319
+ none: "rounded-none",
320
+ sm: "rounded-sm",
321
+ md: "rounded-md",
322
+ lg: "rounded-lg",
323
+ xl: "rounded-xl",
324
+ full: "rounded-3xl"
325
+ };
326
+ const currentRadius = borderRadiusMap[ui.borderRadius || "xl"];
327
+ const isGlass = ui.visualStyle !== "solid";
328
+ return /* @__PURE__ */ jsxs3(
329
+ "div",
330
+ {
331
+ className: `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}`,
332
+ style: __spreadValues({ "--primary": ui.primaryColor, "--accent": ui.accentColor }, style),
333
+ children: [
334
+ /* @__PURE__ */ jsxs3(
335
+ "div",
336
+ {
337
+ className: "flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-white/10",
338
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}15, ${ui.accentColor}10)` },
339
+ children: [
340
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
341
+ ui.logoUrl ? (
342
+ // eslint-disable-next-line @next/next/no-img-element
343
+ /* @__PURE__ */ jsx4("img", { src: ui.logoUrl, alt: "logo", className: "w-8 h-8 rounded-lg object-cover" })
344
+ ) : /* @__PURE__ */ jsx4(
345
+ "div",
346
+ {
347
+ className: `w-9 h-9 flex items-center justify-center shadow-md ${ui.borderRadius === "full" ? "rounded-full" : "rounded-xl"}`,
348
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
349
+ children: /* @__PURE__ */ jsx4(Bot2, { className: "w-5 h-5 text-white" })
350
+ }
351
+ ),
352
+ /* @__PURE__ */ jsxs3("div", { children: [
353
+ /* @__PURE__ */ jsx4("h2", { className: "text-slate-900 dark:text-white font-semibold text-sm leading-tight", children: ui.title }),
354
+ ui.poweredBy && /* @__PURE__ */ jsx4("p", { className: "text-slate-500 dark:text-white/40 text-[11px] leading-tight", children: `Powered by ${ui.poweredBy}` })
355
+ ] })
356
+ ] }),
357
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
358
+ /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1 text-[10px] text-emerald-500 dark:text-emerald-400", children: [
359
+ /* @__PURE__ */ jsx4("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 dark:bg-emerald-400 animate-pulse" }),
360
+ "Online"
361
+ ] }),
362
+ mounted && messages.length > 0 && /* @__PURE__ */ jsx4(
363
+ "button",
364
+ {
365
+ onClick: clearHistory,
366
+ title: "Clear conversation",
367
+ className: "w-7 h-7 rounded-lg flex items-center justify-center text-slate-400 hover:text-slate-600 dark:text-white/40 dark:hover:text-white/70 hover:bg-slate-100 dark:hover:bg-white/10 transition-all cursor-pointer",
368
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "w-3.5 h-3.5" })
369
+ }
370
+ ),
371
+ showClose && onClose && /* @__PURE__ */ jsx4(
372
+ "button",
373
+ {
374
+ onClick: onClose,
375
+ title: "Close chat",
376
+ className: "w-7 h-7 rounded-lg flex items-center justify-center text-slate-400 hover:text-slate-600 dark:text-white/40 dark:hover:text-white/70 hover:bg-slate-100 dark:hover:bg-white/10 transition-all cursor-pointer",
377
+ children: /* @__PURE__ */ jsx4(X, { className: "w-4 h-4" })
378
+ }
379
+ )
380
+ ] })
381
+ ]
382
+ }
383
+ ),
384
+ /* @__PURE__ */ jsxs3("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: [
385
+ !mounted || isEmpty ? (
386
+ /* Welcome state */
387
+ /* @__PURE__ */ jsxs3("div", { className: "h-full flex flex-col items-center justify-center text-center gap-4 px-6 py-12", children: [
388
+ /* @__PURE__ */ jsx4(
389
+ "div",
390
+ {
391
+ className: `w-16 h-16 flex items-center justify-center shadow-lg ${ui.borderRadius === "full" ? "rounded-full" : "rounded-2xl"}`,
392
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
393
+ children: /* @__PURE__ */ jsx4(Sparkles, { className: "w-8 h-8 text-white" })
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsxs3("div", { children: [
397
+ /* @__PURE__ */ jsx4("p", { className: "text-slate-800 dark:text-white/80 font-medium leading-relaxed", children: ui.welcomeMessage }),
398
+ /* @__PURE__ */ jsx4("p", { className: "text-slate-500 dark:text-white/30 text-sm mt-2", children: "Ask a question to get started" })
399
+ ] }),
400
+ /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-2 justify-center mt-2", children: ["What can you help me with?", "Summarise the key topics", "Show me an example"].map(
401
+ (suggestion) => /* @__PURE__ */ jsx4(
402
+ "button",
403
+ {
404
+ onClick: () => {
405
+ var _a;
406
+ setInput(suggestion);
407
+ (_a = inputRef.current) == null ? void 0 : _a.focus();
408
+ },
409
+ 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",
410
+ children: suggestion
411
+ },
412
+ suggestion
413
+ )
414
+ ) })
415
+ ] })
416
+ ) : messages.map((msg) => /* @__PURE__ */ jsx4(
417
+ MessageBubble,
418
+ {
419
+ message: msg,
420
+ sources: msg.sources,
421
+ primaryColor: ui.primaryColor,
422
+ accentColor: ui.accentColor
423
+ },
424
+ msg.id
425
+ )),
426
+ isLoading && /* @__PURE__ */ jsx4(
427
+ MessageBubble,
428
+ {
429
+ message: { role: "assistant", content: "" },
430
+ isStreaming: true,
431
+ primaryColor: ui.primaryColor,
432
+ accentColor: ui.accentColor
433
+ }
434
+ ),
435
+ error && /* @__PURE__ */ jsxs3("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: [
436
+ /* @__PURE__ */ jsx4(TriangleAlert, { className: "w-4 h-4 flex-shrink-0" }),
437
+ /* @__PURE__ */ jsx4("span", { children: error })
438
+ ] }),
439
+ /* @__PURE__ */ jsx4("div", { ref: bottomRef })
440
+ ] }),
441
+ /* @__PURE__ */ jsxs3("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: [
442
+ /* @__PURE__ */ jsxs3("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: [
443
+ /* @__PURE__ */ jsx4(
444
+ "textarea",
445
+ {
446
+ ref: inputRef,
447
+ value: input,
448
+ onChange: (e) => setInput(e.target.value),
449
+ onKeyDown: handleKeyDown,
450
+ placeholder: ui.placeholder,
451
+ rows: 1,
452
+ disabled: isLoading,
453
+ 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",
454
+ style: { scrollbarWidth: "none" }
455
+ }
456
+ ),
457
+ /* @__PURE__ */ jsx4(
458
+ "button",
459
+ {
460
+ onClick: sendMessage,
461
+ disabled: !input.trim() || isLoading,
462
+ 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",
463
+ style: {
464
+ background: input.trim() && !isLoading ? `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` : "rgba(148, 163, 184, 0.2)"
465
+ // slate-400/20 for light mode disabled
466
+ },
467
+ children: /* @__PURE__ */ jsx4(ArrowUp, { className: "w-4 h-4 text-white" })
468
+ }
469
+ )
470
+ ] }),
471
+ /* @__PURE__ */ jsx4("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" })
472
+ ] })
473
+ ]
474
+ }
475
+ );
476
+ }
477
+
478
+ // src/components/ChatWidget.tsx
479
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
480
+ function ChatWidget({ position = "bottom-right" }) {
481
+ const { ui } = useConfig();
482
+ const [isOpen, setIsOpen] = useState4(false);
483
+ const [hasUnread, setHasUnread] = useState4(false);
484
+ if (ui.showWidget === false) return null;
485
+ const positionClass = position === "bottom-left" ? "bottom-6 left-6" : "bottom-6 right-6";
486
+ const windowPositionClass = position === "bottom-left" ? "bottom-20 left-6" : "bottom-20 right-6";
487
+ const handleOpen = () => {
488
+ setIsOpen(true);
489
+ setHasUnread(false);
490
+ };
491
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
492
+ /* @__PURE__ */ jsxs4(
493
+ "div",
494
+ {
495
+ className: `fixed z-[99] w-[380px] max-w-[calc(100vw-3rem)] transition-all duration-300 ease-in-out ${windowPositionClass} ${isOpen ? "opacity-100 translate-y-0 pointer-events-auto" : "opacity-0 translate-y-4 pointer-events-none"}`,
496
+ style: { height: "600px", maxHeight: "calc(100vh - 6rem)" },
497
+ children: [
498
+ /* @__PURE__ */ jsx5(
499
+ ChatWindow,
500
+ {
501
+ className: "h-full relative z-10",
502
+ showClose: true,
503
+ onClose: () => setIsOpen(false)
504
+ }
505
+ ),
506
+ /* @__PURE__ */ jsx5(
507
+ "div",
508
+ {
509
+ 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"}`
510
+ }
511
+ )
512
+ ]
513
+ }
514
+ ),
515
+ /* @__PURE__ */ jsxs4(
516
+ "button",
517
+ {
518
+ onClick: isOpen ? () => setIsOpen(false) : handleOpen,
519
+ className: `fixed z-[100] 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}`,
520
+ style: { background: `linear-gradient(135deg, ${ui.primaryColor}, ${ui.accentColor})` },
521
+ "aria-label": "Open chat",
522
+ children: [
523
+ /* @__PURE__ */ jsx5(
524
+ "span",
525
+ {
526
+ className: "absolute inset-0 rounded-full animate-ping opacity-20",
527
+ style: { background: ui.primaryColor }
528
+ }
529
+ ),
530
+ /* @__PURE__ */ jsx5(
531
+ "span",
532
+ {
533
+ className: `transition-transform duration-300 ${isOpen ? "rotate-90 scale-90" : "rotate-0 scale-100"}`,
534
+ children: isOpen ? /* @__PURE__ */ jsx5(X2, { className: "w-6 h-6 text-white" }) : /* @__PURE__ */ jsx5(MessageSquare, { className: "w-6 h-6 text-white" })
535
+ }
536
+ ),
537
+ hasUnread && !isOpen && /* @__PURE__ */ jsx5("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" })
538
+ ]
539
+ }
540
+ )
541
+ ] });
542
+ }
543
+ export {
544
+ ChatWidget,
545
+ ChatWindow,
546
+ ConfigProvider,
547
+ MessageBubble,
548
+ SourceCard,
549
+ useConfig,
550
+ useRagChat
551
+ };