tigerbase-chatbot 1.0.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.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # 🐯 TigerBase Chatbot
2
+
3
+ A React chatbot component for TigerBase SaaS platform.
4
+
5
+ ## 📦 Installation
6
+
7
+ ```bash
8
+ npm install solomdev-chatbot
9
+ ```
10
+
11
+ ## 🚀 Quick Start
12
+
13
+ ```tsx
14
+ import { tigerbaseConfig, ChatbotPopup } from "solomdev-chatbot";
15
+
16
+ // Initialize (once in your app)
17
+ await tigerbaseConfig({
18
+ apiKey: "your-api-key",
19
+ apiSecret: "your-api-secret",
20
+ mcpUrl: "https://api.tigerbase.cloud/mcp",
21
+ aiKey: "your-gemini-api-key",
22
+ defaultLang: "ar",
23
+ });
24
+
25
+ // Use the component
26
+ function App() {
27
+ return <ChatbotPopup />;
28
+ }
29
+ ```
30
+
31
+ ## 🎨 Custom Styling
32
+
33
+ ```tsx
34
+ await tigerbaseConfig({
35
+ // ... keys
36
+ style: {
37
+ theme: "dark", // 'light' | 'dark'
38
+ primaryColor: "#8b5cf6", // Purple accent
39
+ backgroundColor: "#1f2937",
40
+ borderRadius: "xl", // 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'
41
+ buttonPosition: "bottom-right",
42
+ fontSize: "md",
43
+ },
44
+ });
45
+ ```
46
+
47
+ ## ⚙️ Configuration Options
48
+
49
+ | Option | Type | Default | Description |
50
+ | ----------------- | -------------- | -------- | ------------------------ |
51
+ | `apiKey` | `string` | required | Your API key |
52
+ | `apiSecret` | `string` | required | Your API secret |
53
+ | `mcpUrl` | `string` | required | MCP server URL |
54
+ | `aiKey` | `string` | required | Google GenAI key |
55
+ | `defaultLang` | `'ar' \| 'en'` | `'ar'` | Default language |
56
+ | `autoDeleteChats` | `number` | - | Auto-delete after X mins |
57
+ | `customPrompt` | `string` | - | Custom AI prompt |
58
+ | `style` | `ChatbotStyle` | - | Custom styling |
59
+
60
+ ## 🎨 Style Options
61
+
62
+ | Option | Values | Default |
63
+ | ----------------- | -------------------------------------------------- | ---------------- |
64
+ | `theme` | `'light' \| 'dark'` | `'light'` |
65
+ | `primaryColor` | Any hex color | `'#3b82f6'` |
66
+ | `backgroundColor` | Any hex color | `'#ffffff'` |
67
+ | `borderRadius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | `'lg'` |
68
+ | `buttonPosition` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` |
69
+
70
+ ## 🔧 Requirements
71
+
72
+ - React 18+
73
+ - Google GenAI API key
74
+ - TigerBase subscription
75
+
76
+ ## 📄 License
77
+
78
+ MIT © SolomDev00
@@ -0,0 +1,2 @@
1
+ declare const ChatbotPopup: () => import("react/jsx-runtime").JSX.Element;
2
+ export default ChatbotPopup;
@@ -0,0 +1,401 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ import { useState, useEffect, useRef } from "react";
4
+ import { GoogleGenerativeAI, SchemaType } from "@google/generative-ai";
5
+ import { toast } from "react-toastify";
6
+ import { X, Maximize2, Minimize2, Send, RefreshCw, ShoppingCart, Trash2, Bot, User, Sparkles, } from "lucide-react";
7
+ import { Button } from "./components/ui/button";
8
+ import { Input } from "./components/ui/input";
9
+ import { Card, CardContent, CardHeader } from "./components/ui/card";
10
+ import { Badge } from "./components/ui/badge";
11
+ import { ScrollArea } from "./components/ui/scroll-area";
12
+ import { getConfig } from "./config";
13
+ const ChatbotPopup = () => {
14
+ const cfg = getConfig();
15
+ const MCP_URL = cfg.secretKey || "";
16
+ const genAIKey = cfg.aiKey;
17
+ const defaultLang = cfg.defaultLang || "ar";
18
+ if (!genAIKey) {
19
+ toast.error(defaultLang === "ar"
20
+ ? "مفتاح Google GenAI غير موجود"
21
+ : "Google GenAI key not set");
22
+ throw new Error("Google GenAI key not set");
23
+ }
24
+ const genAI = new GoogleGenerativeAI(genAIKey);
25
+ const systemInstruction = `You are a helpful assistant for product management. Always respond in ${defaultLang === "ar" ? "Arabic" : "English"}. ${cfg.customPrompt || ""}
26
+ When user asks for product information with an ID, use the get-product-info tool and pass the ID in the arguments.
27
+ When user asks to list products, use the list-products tool.
28
+ When user asks to add a product, use the add-product tool with name, price, and description.`;
29
+ const model = genAI.getGenerativeModel({
30
+ model: "gemini-2.5-flash",
31
+ systemInstruction,
32
+ });
33
+ const [isOpen, setIsOpen] = useState(false);
34
+ const [isExpanded, setIsExpanded] = useState(false);
35
+ const [messages, setMessages] = useState([
36
+ {
37
+ role: "model",
38
+ content: defaultLang === "ar"
39
+ ? "أهلاً وسهلاً! أنا هنا لمساعدتك في إدارة المنتجات. كيف يمكنني مساعدتك؟"
40
+ : "Hello! I'm here to help with product management. How can I assist you?",
41
+ timestamp: new Date().toLocaleTimeString(defaultLang === "ar" ? "ar-EG" : "en-US", {
42
+ hour: "2-digit",
43
+ minute: "2-digit",
44
+ hour12: true,
45
+ }),
46
+ },
47
+ ]);
48
+ const [input, setInput] = useState("");
49
+ const [loading, setLoading] = useState(false);
50
+ const messagesEndRef = useRef(null);
51
+ // Auto delete chats
52
+ useEffect(() => {
53
+ if (cfg.autoDeleteChats) {
54
+ const timer = setTimeout(() => {
55
+ setMessages([
56
+ {
57
+ role: "model",
58
+ content: defaultLang === "ar"
59
+ ? "أهلاً وسهلاً! أنا هنا لمساعدتك في إدارة المنتجات. كيف يمكنني مساعدتك؟"
60
+ : "Hello! I'm here to help with product management. How can I assist you?",
61
+ timestamp: new Date().toLocaleTimeString(defaultLang === "ar" ? "ar-EG" : "en-US", {
62
+ hour: "2-digit",
63
+ minute: "2-digit",
64
+ hour12: true,
65
+ }),
66
+ },
67
+ ]);
68
+ toast.info(defaultLang === "ar"
69
+ ? "تم مسح المحادثة تلقائيًا"
70
+ : "Chat cleared automatically");
71
+ }, cfg.autoDeleteChats * 60 * 1000);
72
+ return () => clearTimeout(timer);
73
+ }
74
+ }, [messages, cfg.autoDeleteChats, defaultLang]);
75
+ const scrollToBottom = () => {
76
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
77
+ };
78
+ useEffect(() => {
79
+ scrollToBottom();
80
+ }, [messages]);
81
+ const formatTimestamp = () => {
82
+ return new Date().toLocaleTimeString(defaultLang === "ar" ? "ar-EG" : "en-US", {
83
+ hour: "2-digit",
84
+ minute: "2-digit",
85
+ hour12: true,
86
+ });
87
+ };
88
+ const handleMcpToolCall = async (toolName, args, retries = 2) => {
89
+ if (!cfg.validatedTools.includes(toolName)) {
90
+ throw new Error(defaultLang === "ar"
91
+ ? `الأداة ${toolName} غير متاحة لهذا السيريال`
92
+ : `Tool ${toolName} not available for this serial`);
93
+ }
94
+ for (let i = 0; i <= retries; i++) {
95
+ try {
96
+ const methodName = "tools/call";
97
+ const response = await fetch(MCP_URL, {
98
+ method: "POST",
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ Accept: "application/json, text/event-stream",
102
+ },
103
+ body: JSON.stringify({
104
+ jsonrpc: "2.0",
105
+ method: methodName,
106
+ params: {
107
+ name: toolName,
108
+ arguments: args,
109
+ _meta: { progressToken: 0 },
110
+ },
111
+ id: Date.now(),
112
+ }),
113
+ });
114
+ if (!response.ok) {
115
+ throw new Error(`HTTP error! status: ${response.status}`);
116
+ }
117
+ const data = await response.json();
118
+ if (data.error) {
119
+ throw new Error(`فشل استدعاء ${toolName}: ${data.error.message}`);
120
+ }
121
+ if (data.result &&
122
+ data.result.content &&
123
+ data.result.content[0]?.text) {
124
+ return JSON.parse(data.result.content[0].text);
125
+ }
126
+ return (data.result ||
127
+ (defaultLang === "ar"
128
+ ? "تم تنفيذ الأمر بنجاح"
129
+ : "Command executed successfully"));
130
+ }
131
+ catch (e) {
132
+ if (i < retries) {
133
+ await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
134
+ continue;
135
+ }
136
+ throw new Error(defaultLang === "ar"
137
+ ? `فشل استدعاء ${toolName} بعد ${retries + 1} محاولة: ${e.message}`
138
+ : `Failed to call ${toolName} after ${retries + 1} attempts: ${e.message}`);
139
+ }
140
+ }
141
+ };
142
+ const handleSend = async (retryCount = 3) => {
143
+ if (!input.trim())
144
+ return;
145
+ const userMessage = {
146
+ role: "user",
147
+ content: input,
148
+ timestamp: formatTimestamp(),
149
+ };
150
+ setMessages((prev) => [...prev, userMessage]);
151
+ const userInput = input;
152
+ setInput("");
153
+ setLoading(true);
154
+ try {
155
+ const conversationHistory = messages
156
+ .map((m) => ({
157
+ role: m.role,
158
+ parts: [
159
+ {
160
+ text: typeof m.content === "string"
161
+ ? m.content
162
+ : JSON.stringify(m.content),
163
+ },
164
+ ],
165
+ }))
166
+ .concat({
167
+ role: "user",
168
+ parts: [{ text: userInput }],
169
+ });
170
+ const result = await model.generateContent({
171
+ contents: conversationHistory,
172
+ generationConfig: {
173
+ maxOutputTokens: 1024,
174
+ temperature: 0.7,
175
+ },
176
+ tools: [
177
+ {
178
+ functionDeclarations: [
179
+ {
180
+ name: "mcp_tool",
181
+ description: "Call MCP server tools for product management",
182
+ parameters: {
183
+ type: SchemaType.OBJECT,
184
+ properties: {
185
+ tool: {
186
+ type: SchemaType.STRING,
187
+ format: "enum",
188
+ enum: cfg.validatedTools,
189
+ description: "The MCP tool to call",
190
+ },
191
+ arguments: {
192
+ type: SchemaType.OBJECT,
193
+ properties: {
194
+ id: {
195
+ type: SchemaType.STRING,
196
+ description: "Product ID for get-product-info tool",
197
+ },
198
+ name: {
199
+ type: SchemaType.STRING,
200
+ description: "Product name for add-product tool",
201
+ },
202
+ price: {
203
+ type: SchemaType.NUMBER,
204
+ description: "Product price for add-product tool",
205
+ },
206
+ description: {
207
+ type: SchemaType.STRING,
208
+ description: "Product description for add-product tool",
209
+ },
210
+ },
211
+ description: "Arguments to pass to the selected tool",
212
+ },
213
+ },
214
+ required: ["tool"],
215
+ },
216
+ },
217
+ ],
218
+ },
219
+ ],
220
+ });
221
+ let assistantReply = defaultLang === "ar"
222
+ ? "عذرًا، لم أفهم الطلب."
223
+ : "Sorry, I didn't understand the request.";
224
+ const response = await result.response;
225
+ const candidate = response.candidates?.[0];
226
+ if (candidate?.content?.parts) {
227
+ const parts = candidate.content.parts;
228
+ const functionCallPart = parts.find((part) => part.functionCall);
229
+ if (functionCallPart?.functionCall) {
230
+ const { name, args } = functionCallPart.functionCall;
231
+ if (name === "mcp_tool") {
232
+ try {
233
+ const functionArgs = args;
234
+ const toolResult = await handleMcpToolCall(functionArgs.tool || functionArgs.name || "list-products", functionArgs.arguments || functionArgs.params || {});
235
+ assistantReply = {
236
+ products: Array.isArray(toolResult) ? toolResult : [toolResult],
237
+ };
238
+ toast.success(defaultLang === "ar"
239
+ ? "تم جلب البيانات بنجاح!"
240
+ : "Data fetched successfully!");
241
+ }
242
+ catch (e) {
243
+ assistantReply =
244
+ defaultLang === "ar"
245
+ ? `عذرًا، حدث خطأ أثناء تنفيذ الأمر: ${e.message}`
246
+ : `Sorry, an error occurred while executing the command: ${e.message}`;
247
+ toast.error(e.message);
248
+ }
249
+ }
250
+ }
251
+ else {
252
+ const textParts = parts
253
+ .filter((part) => typeof part.text === "string")
254
+ .map((part) => part.text)
255
+ .join("\n");
256
+ assistantReply =
257
+ textParts ||
258
+ (defaultLang === "ar" ? "لم أتمكن من الرد." : "Couldn't respond.");
259
+ }
260
+ }
261
+ setMessages((prev) => [
262
+ ...prev,
263
+ {
264
+ role: "model",
265
+ content: assistantReply,
266
+ timestamp: formatTimestamp(),
267
+ },
268
+ ]);
269
+ }
270
+ catch (error) {
271
+ const errorMessage = error instanceof Error ? error.message : "";
272
+ const isOverloaded = errorMessage.includes("overloaded") || errorMessage.includes("503");
273
+ const waitTime = isOverloaded ? 5000 : 1000;
274
+ if (retryCount > 0) {
275
+ toast.warn(defaultLang === "ar"
276
+ ? `فشل الطلب، جاري إعادة المحاولة... (${retryCount} محاولة متبقية)`
277
+ : `Request failed, retrying... (${retryCount} attempts left)`);
278
+ setTimeout(() => handleSend(retryCount - 1), waitTime);
279
+ return;
280
+ }
281
+ const errorMsg = errorMessage ||
282
+ (defaultLang === "ar" ? "حدث خطأ غير متوقع" : "Unexpected error");
283
+ toast.error(`${defaultLang === "ar" ? "خطأ" : "Error"}: ${errorMsg}`);
284
+ setMessages((prev) => [
285
+ ...prev,
286
+ {
287
+ role: "model",
288
+ content: `${defaultLang === "ar" ? "عذرًا، حدث خطأ" : "Sorry, an error occurred"}: ${errorMsg}`,
289
+ timestamp: formatTimestamp(),
290
+ },
291
+ ]);
292
+ }
293
+ finally {
294
+ setLoading(false);
295
+ }
296
+ };
297
+ const handleKeyPress = (e) => {
298
+ if (e.key === "Enter" && !e.shiftKey) {
299
+ e.preventDefault();
300
+ handleSend();
301
+ }
302
+ };
303
+ const refreshProducts = async () => {
304
+ setLoading(true);
305
+ try {
306
+ const mcpResult = await handleMcpToolCall("list-products", {});
307
+ setMessages((prev) => [
308
+ ...prev,
309
+ {
310
+ role: "model",
311
+ content: {
312
+ products: Array.isArray(mcpResult) ? mcpResult : [mcpResult],
313
+ },
314
+ timestamp: formatTimestamp(),
315
+ },
316
+ ]);
317
+ toast.success(defaultLang === "ar"
318
+ ? "تم تحديث قائمة المنتجات!"
319
+ : "Product list updated!");
320
+ }
321
+ catch (e) {
322
+ toast.error(defaultLang === "ar"
323
+ ? `فشل تحديث المنتجات: ${e.message}`
324
+ : `Failed to update products: ${e.message}`);
325
+ }
326
+ finally {
327
+ setLoading(false);
328
+ }
329
+ };
330
+ const clearChat = () => {
331
+ setMessages([
332
+ {
333
+ role: "model",
334
+ content: defaultLang === "ar"
335
+ ? "أهلاً وسهلاً! أنا هنا لمساعدتك في إدارة المنتجات. كيف يمكنني مساعدتك؟"
336
+ : "Hello! I'm here to help with product management. How can I assist you?",
337
+ timestamp: formatTimestamp(),
338
+ },
339
+ ]);
340
+ setInput("");
341
+ toast.info(defaultLang === "ar" ? "تم مسح المحادثة" : "Chat cleared");
342
+ };
343
+ const showCart = () => {
344
+ const cart = JSON.parse(localStorage.getItem("cart") || "[]");
345
+ if (cart.length === 0) {
346
+ toast.warn(defaultLang === "ar" ? "السلة فاضية!" : "Cart is empty!");
347
+ setMessages((prev) => [
348
+ ...prev,
349
+ {
350
+ role: "model",
351
+ content: defaultLang === "ar"
352
+ ? "السلة فاضية حالياً! يمكنك إضافة المنتجات من قائمة المنتجات."
353
+ : "The cart is empty! You can add products from the product list.",
354
+ timestamp: formatTimestamp(),
355
+ },
356
+ ]);
357
+ return;
358
+ }
359
+ setMessages((prev) => [
360
+ ...prev,
361
+ {
362
+ role: "model",
363
+ content: { products: cart },
364
+ timestamp: formatTimestamp(),
365
+ },
366
+ ]);
367
+ toast.info(defaultLang === "ar"
368
+ ? `عرض السلة (${cart.length} منتج)`
369
+ : `Showing cart (${cart.length} items)`);
370
+ };
371
+ const ProductCard = ({ product }) => (_jsx(Card, { className: "mb-3 border border-gray-200 shadow-sm hover:shadow-md transition-shadow", children: _jsx(CardContent, { className: "p-4", children: _jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h4", { className: "font-semibold text-gray-800 text-sm", children: product.name }), _jsx("p", { className: "text-xs text-gray-600 mt-1 line-clamp-2", children: product.description }), _jsxs("div", { className: "flex items-center gap-2 mt-2", children: [_jsxs(Badge, { variant: "secondary", className: "text-xs", children: [product.price, " ", defaultLang === "ar" ? "جنيه" : "EGP"] }), _jsxs(Badge, { variant: "outline", className: "text-xs", children: ["ID: ", product.id] })] })] }), _jsx(Button, { size: "sm", variant: "outline", className: "text-xs h-8 px-3", onClick: () => {
372
+ const cart = JSON.parse(localStorage.getItem("cart") || "[]");
373
+ cart.push(product);
374
+ localStorage.setItem("cart", JSON.stringify(cart));
375
+ toast.success(defaultLang === "ar"
376
+ ? "تم إضافة المنتج للسلة!"
377
+ : "Product added to cart!");
378
+ }, children: defaultLang === "ar" ? "إضافة للسلة" : "Add to Cart" })] }) }) }));
379
+ const MessageBubble = ({ message }) => {
380
+ const isUser = message.role === "user";
381
+ return (_jsx("div", { className: `flex mb-4 ${isUser ? "justify-end" : "justify-start"}`, children: _jsxs("div", { className: `flex max-w-[85%] ${isUser ? "flex-row-reverse" : "flex-row"}`, children: [_jsx("div", { className: `w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${isUser ? "bg-blue-500 ml-3" : "bg-gray-200 mr-3"}`, children: isUser ? (_jsx(User, { className: "w-4 h-4 text-white" })) : (_jsx(Bot, { className: "w-4 h-4 text-gray-600" })) }), _jsxs("div", { className: `p-3 rounded-2xl ${isUser
382
+ ? "bg-blue-500 text-white rounded-br-md"
383
+ : "bg-gray-100 text-gray-800 rounded-bl-md"}`, children: [typeof message.content === "string" ? (_jsx("p", { className: "text-sm leading-relaxed", children: message.content })) : message.content.products ? (_jsxs("div", { children: [_jsx("p", { className: "text-sm mb-3 font-medium", children: defaultLang === "ar"
384
+ ? "المنتجات المتاحة:"
385
+ : "Available products:" }), _jsx("div", { className: "space-y-2", children: message.content.products.map((product, index) => (_jsx(ProductCard, { product: product }, product.id || index))) })] })) : null, _jsx("p", { className: `text-xs mt-2 ${isUser ? "text-blue-100" : "text-gray-500"}`, children: message.timestamp })] })] }) }));
386
+ };
387
+ if (!isOpen) {
388
+ return (_jsx("div", { className: "fixed bottom-6 right-6 z-50", children: _jsx(Button, { onClick: () => setIsOpen(true), className: "w-16 h-16 rounded-full shadow-xl bg-black hover:bg-gray-800 transition-all duration-300 hover:scale-105 flex items-center justify-center group cursor-pointer", children: _jsxs("div", { className: "flex items-center", children: [_jsx(Sparkles, { className: "text-white" }), _jsx("span", { className: "text-white font-medium text-sm opacity-0 group-hover:opacity-100 transition-opacity absolute -left-20 bg-black px-3 py-1 rounded-full whitespace-nowrap", children: defaultLang === "ar" ? "اسأل الذكاء الاصطناعي" : "Ask AI" })] }) }) }));
389
+ }
390
+ const chatClasses = isExpanded
391
+ ? "fixed inset-0 z-50 animate-in slide-in-from-right-4 duration-300"
392
+ : "fixed bottom-0 right-0 w-96 h-screen z-50 animate-in slide-in-right-bottom-4 slide-in-from-right-4 duration-300";
393
+ return (_jsx("div", { className: chatClasses, children: _jsxs(Card, { className: "h-full shadow-2xl border-0 overflow-hidden bg-white flex flex-col", children: [_jsx(CardHeader, { className: "bg-white border-b p-4 flex-shrink-0", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center space-x-3 rtl:space-x-reverse", children: [_jsx("div", { className: "w-8 h-8 rounded-full bg-red-500 flex items-center justify-center text-white font-bold text-sm", children: "tb" }), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold text-gray-800 text-sm", children: defaultLang === "ar" ? "اسأل Tigerbase" : "Ask Tigerbase" }), _jsx("p", { className: "text-xs text-gray-500", children: defaultLang === "ar"
394
+ ? "متاح الآن للمساعدة"
395
+ : "Available now to assist" })] })] }), _jsxs("div", { className: "flex items-center space-x-2 rtl:space-x-reverse", children: [_jsx(Button, { variant: "ghost", size: "icon", onClick: () => setIsExpanded(!isExpanded), className: "text-gray-500 hover:bg-gray-100 w-8 h-8", children: isExpanded ? (_jsx(Minimize2, { className: "w-4 h-4" })) : (_jsx(Maximize2, { className: "w-4 h-4" })) }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setIsOpen(false), className: "text-gray-500 hover:bg-gray-100 w-8 h-8", children: _jsx(X, { className: "w-4 h-4" }) })] })] }) }), _jsxs(CardContent, { className: "flex-1 p-0 flex flex-col overflow-hidden", children: [_jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(ScrollArea, { className: "h-full", children: _jsxs("div", { className: "p-4", children: [messages.length === 1 && (_jsxs("div", { className: "text-center py-12", children: [_jsx("div", { className: "w-16 h-16 mx-auto mb-4 bg-gradient-to-br from-purple-500 to-blue-600 rounded-full flex items-center justify-center", children: _jsx(Sparkles, { className: "w-8 h-8 text-white" }) }), _jsx("h3", { className: "text-lg font-semibold text-gray-800 mb-2", children: defaultLang === "ar"
396
+ ? "اسأل أي شيء عن Tigerbase"
397
+ : "Ask anything about Tigerbase" }), _jsx("p", { className: "text-sm text-gray-500 max-w-xs mx-auto", children: defaultLang === "ar"
398
+ ? "يستخدم Tigerbase أحدث البيانات في التوثيق للإجابة على أسئلتك."
399
+ : "Tigerbase uses the latest data in the documentation to answer your questions." })] })), messages.map((message, index) => (_jsx(MessageBubble, { message: message }, index))), loading && (_jsx("div", { className: "flex justify-start mb-4", children: _jsxs("div", { className: "flex", children: [_jsx("div", { className: "w-8 h-8 rounded-full bg-gray-200 mr-3 flex items-center justify-center", children: _jsx(Bot, { className: "w-4 h-4 text-gray-600" }) }), _jsx("div", { className: "bg-gray-100 p-3 rounded-2xl rounded-bl-md", children: _jsxs("div", { className: "flex space-x-1", children: [_jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce" }), _jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "0.1s" } }), _jsx("div", { className: "w-2 h-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "0.2s" } })] }) })] }) })), _jsx("div", { ref: messagesEndRef })] }) }) }), messages.length > 1 && (_jsx("div", { className: "border-t bg-gray-50 p-3 flex-shrink-0", children: _jsxs("div", { className: "flex space-x-2 rtl:space-x-reverse mb-0 flex-wrap gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: refreshProducts, className: "text-xs h-8 px-3 flex items-center gap-1", disabled: loading, children: [_jsx(RefreshCw, { className: "w-3 h-3" }), defaultLang === "ar" ? "المنتجات" : "Products"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: showCart, className: "text-xs h-8 px-3 flex items-center gap-1", children: [_jsx(ShoppingCart, { className: "w-3 h-3" }), defaultLang === "ar" ? "السلة" : "Cart"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: clearChat, className: "text-xs h-8 px-3 flex items-center gap-1", children: [_jsx(Trash2, { className: "w-3 h-3" }), defaultLang === "ar" ? "مسح" : "Clear"] })] }) })), _jsxs("div", { className: "border-t bg-white p-4 flex-shrink-0", children: [_jsxs("div", { className: "flex space-x-2 rtl:space-x-reverse items-center", children: [_jsx(Input, { value: input, onChange: (e) => setInput(e.target.value), onKeyPress: handleKeyPress, placeholder: defaultLang === "ar" ? "اسأل أي شيء" : "Ask anything", className: "flex-1 border-0 bg-gray-100 focus:bg-white transition-colors text-right placeholder:text-gray-500 text-sm", disabled: loading }), _jsx(Button, { onClick: () => handleSend(1), disabled: loading || !input.trim(), size: "icon", className: "bg-gray-300 hover:bg-gray-400 text-gray-600 w-8 h-8 rounded-full shrink-0", children: _jsx(Send, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "mt-3 text-center", children: _jsxs("p", { className: "text-xs text-gray-500", children: [defaultLang === "ar" ? "مدعوم بواسطة" : "Powered by", " ", _jsx("span", { className: "text-red-500 font-semibold", children: "Tigerbase" })] }) })] })] })] }) }));
400
+ };
401
+ export default ChatbotPopup;
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const badgeVariants: (props?: ({
4
+ variant?: "default" | "outline" | "destructive" | "secondary" | null | undefined;
5
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
6
+ export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
7
+ }
8
+ declare function Badge({ className, variant, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
9
+ export { Badge, badgeVariants };
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
+ import { cn } from "../../lib/utils";
4
+ const badgeVariants = cva("inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
5
+ variants: {
6
+ variant: {
7
+ default: "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
8
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
9
+ destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
10
+ outline: "text-foreground",
11
+ },
12
+ },
13
+ defaultVariants: {
14
+ variant: "default",
15
+ },
16
+ });
17
+ function Badge({ className, variant, ...props }) {
18
+ return (_jsx("div", { className: cn(badgeVariants({ variant }), className), ...props }));
19
+ }
20
+ export { Badge, badgeVariants };
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ import { type VariantProps } from "class-variance-authority";
3
+ declare const buttonVariants: (props?: ({
4
+ variant?: "link" | "default" | "outline" | "destructive" | "secondary" | "ghost" | null | undefined;
5
+ size?: "default" | "icon" | "sm" | "lg" | null | undefined;
6
+ } & import("class-variance-authority/dist/types").ClassProp) | undefined) => string;
7
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
8
+ asChild?: boolean;
9
+ }
10
+ declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
11
+ export { Button, buttonVariants };
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { cva } from "class-variance-authority";
5
+ import { cn } from "../../lib/utils";
6
+ const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", {
7
+ variants: {
8
+ variant: {
9
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
10
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
11
+ outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
12
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
13
+ ghost: "hover:bg-accent hover:text-accent-foreground",
14
+ link: "text-primary underline-offset-4 hover:underline",
15
+ },
16
+ size: {
17
+ default: "h-9 px-4 py-2",
18
+ sm: "h-8 rounded-md px-3 text-xs",
19
+ lg: "h-10 rounded-md px-8",
20
+ icon: "h-9 w-9",
21
+ },
22
+ },
23
+ defaultVariants: {
24
+ variant: "default",
25
+ size: "default",
26
+ },
27
+ });
28
+ const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
29
+ const Comp = asChild ? Slot : "button";
30
+ return (_jsx(Comp, { className: cn(buttonVariants({ variant, size, className })), ref: ref, ...props }));
31
+ });
32
+ Button.displayName = "Button";
33
+ export { Button, buttonVariants };
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
3
+ declare const CardHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
4
+ declare const CardTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
5
+ declare const CardDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
6
+ declare const CardContent: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
7
+ declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
8
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, };
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+ const Card = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("rounded-xl border bg-card text-card-foreground shadow", className), ...props })));
5
+ Card.displayName = "Card";
6
+ const CardHeader = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })));
7
+ CardHeader.displayName = "CardHeader";
8
+ const CardTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("font-semibold leading-none tracking-tight", className), ...props })));
9
+ CardTitle.displayName = "CardTitle";
10
+ const CardDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("text-sm text-muted-foreground", className), ...props })));
11
+ CardDescription.displayName = "CardDescription";
12
+ const CardContent = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("p-6 pt-0", className), ...props })));
13
+ CardContent.displayName = "CardContent";
14
+ const CardFooter = React.forwardRef(({ className, ...props }, ref) => (_jsx("div", { ref: ref, className: cn("flex items-center p-6 pt-0", className), ...props })));
15
+ CardFooter.displayName = "CardFooter";
16
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, };
@@ -0,0 +1,19 @@
1
+ import * as React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ declare const Dialog: React.FC<DialogPrimitive.DialogProps>;
4
+ declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
5
+ declare const DialogPortal: React.FC<DialogPrimitive.DialogPortalProps>;
6
+ declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
7
+ declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
8
+ declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
9
+ declare const DialogHeader: {
10
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
11
+ displayName: string;
12
+ };
13
+ declare const DialogFooter: {
14
+ ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
15
+ displayName: string;
16
+ };
17
+ declare const DialogTitle: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogTitleProps & React.RefAttributes<HTMLHeadingElement>, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
18
+ declare const DialogDescription: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogDescriptionProps & React.RefAttributes<HTMLParagraphElement>, "ref"> & React.RefAttributes<HTMLParagraphElement>>;
19
+ export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+ import { X } from "lucide-react";
5
+ import { cn } from "../../lib/utils";
6
+ const Dialog = DialogPrimitive.Root;
7
+ const DialogTrigger = DialogPrimitive.Trigger;
8
+ const DialogPortal = DialogPrimitive.Portal;
9
+ const DialogClose = DialogPrimitive.Close;
10
+ const DialogOverlay = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Overlay, { ref: ref, className: cn("fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", className), ...props })));
11
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
12
+ const DialogContent = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Content, { ref: ref, className: cn("fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", className), ...props, children: [children, _jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground", children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { className: "sr-only", children: "Close" })] })] })] })));
13
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
14
+ const DialogHeader = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col space-y-1.5 text-center sm:text-left", className), ...props }));
15
+ DialogHeader.displayName = "DialogHeader";
16
+ const DialogFooter = ({ className, ...props }) => (_jsx("div", { className: cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className), ...props }));
17
+ DialogFooter.displayName = "DialogFooter";
18
+ const DialogTitle = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Title, { ref: ref, className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props })));
19
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
20
+ const DialogDescription = React.forwardRef(({ className, ...props }, ref) => (_jsx(DialogPrimitive.Description, { ref: ref, className: cn("text-sm text-muted-foreground", className), ...props })));
21
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
22
+ export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, };
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+ declare const Input: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
3
+ export { Input };
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../../lib/utils";
4
+ const Input = React.forwardRef(({ className, type, ...props }, ref) => {
5
+ return (_jsx("input", { type: type, className: cn("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", className), ref: ref, ...props }));
6
+ });
7
+ Input.displayName = "Input";
8
+ export { Input };
@@ -0,0 +1,5 @@
1
+ import * as React from "react";
2
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
3
+ declare const ScrollArea: React.ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
4
+ declare const ScrollBar: React.ForwardRefExoticComponent<Omit<ScrollAreaPrimitive.ScrollAreaScrollbarProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
5
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4
+ import { cn } from "../../lib/utils";
5
+ const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => (_jsxs(ScrollAreaPrimitive.Root, { ref: ref, className: cn("relative overflow-hidden", className), ...props, children: [_jsx(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children: children }), _jsx(ScrollBar, {}), _jsx(ScrollAreaPrimitive.Corner, {})] })));
6
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
7
+ const ScrollBar = React.forwardRef(({ className, orientation = "vertical", ...props }, ref) => (_jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, { ref: ref, orientation: orientation, className: cn("flex touch-none select-none transition-colors", orientation === "vertical" &&
8
+ "h-full w-2.5 border-l border-l-transparent p-[1px]", orientation === "horizontal" &&
9
+ "h-2.5 flex-col border-t border-t-transparent p-[1px]", className), ...props, children: _jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" }) })));
10
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
11
+ export { ScrollArea, ScrollBar };
@@ -0,0 +1,54 @@
1
+ export interface ChatbotStyle {
2
+ theme?: "light" | "dark";
3
+ primaryColor?: string;
4
+ backgroundColor?: string;
5
+ userMessageBg?: string;
6
+ botMessageBg?: string;
7
+ textColor?: string;
8
+ headerBg?: string;
9
+ borderRadius?: "none" | "sm" | "md" | "lg" | "xl" | "full";
10
+ buttonSize?: "sm" | "md" | "lg";
11
+ buttonPosition?: "bottom-right" | "bottom-left";
12
+ fontFamily?: string;
13
+ fontSize?: "sm" | "md" | "lg";
14
+ }
15
+ export interface TigerbaseConfig {
16
+ apiKey: string;
17
+ apiSecret: string;
18
+ mcpUrl: string;
19
+ aiKey: string;
20
+ secretKey?: string;
21
+ serialNum?: string;
22
+ defaultLang?: "ar" | "en";
23
+ autoDeleteChats?: number;
24
+ customPrompt?: string;
25
+ style?: ChatbotStyle;
26
+ }
27
+ export declare const tigerbaseConfig: (options: TigerbaseConfig) => Promise<void>;
28
+ export declare const getConfig: () => {
29
+ mcpUrl: string;
30
+ apiKey: string;
31
+ validatedTools: string[];
32
+ style: {
33
+ theme?: "light" | "dark";
34
+ primaryColor?: string;
35
+ backgroundColor?: string;
36
+ userMessageBg?: string;
37
+ botMessageBg?: string;
38
+ textColor?: string;
39
+ headerBg?: string;
40
+ borderRadius?: "none" | "sm" | "md" | "lg" | "xl" | "full";
41
+ buttonSize?: "sm" | "md" | "lg";
42
+ buttonPosition?: "bottom-right" | "bottom-left";
43
+ fontFamily?: string;
44
+ fontSize?: "sm" | "md" | "lg";
45
+ };
46
+ apiSecret: string;
47
+ aiKey: string;
48
+ secretKey?: string;
49
+ serialNum?: string;
50
+ defaultLang?: "ar" | "en";
51
+ autoDeleteChats?: number;
52
+ customPrompt?: string;
53
+ };
54
+ export declare const getCssVariables: (style: ChatbotStyle) => Record<string, string>;
package/dist/config.js ADDED
@@ -0,0 +1,102 @@
1
+ // Default styles
2
+ const defaultStyle = {
3
+ theme: "light",
4
+ primaryColor: "#3b82f6",
5
+ backgroundColor: "#ffffff",
6
+ userMessageBg: "#3b82f6",
7
+ botMessageBg: "#f3f4f6",
8
+ textColor: "#1f2937",
9
+ headerBg: "#ffffff",
10
+ borderRadius: "lg",
11
+ buttonSize: "md",
12
+ buttonPosition: "bottom-right",
13
+ fontSize: "md",
14
+ };
15
+ // Dark theme defaults
16
+ const darkStyle = {
17
+ theme: "dark",
18
+ backgroundColor: "#1f2937",
19
+ userMessageBg: "#3b82f6",
20
+ botMessageBg: "#374151",
21
+ textColor: "#f9fafb",
22
+ headerBg: "#111827",
23
+ };
24
+ let config = null;
25
+ let validatedData = null;
26
+ export const tigerbaseConfig = async (options) => {
27
+ config = options;
28
+ // Normalize legacy config
29
+ const mcpUrl = options.mcpUrl || options.secretKey || "";
30
+ const apiKey = options.apiKey || options.serialNum || "";
31
+ if (!mcpUrl || !apiKey) {
32
+ throw new Error("MCP URL and API Key are required");
33
+ }
34
+ // Validate with backend
35
+ try {
36
+ const response = await fetch(mcpUrl, {
37
+ method: "POST",
38
+ headers: {
39
+ "Content-Type": "application/json",
40
+ "X-API-Key": apiKey,
41
+ "X-API-Secret": options.apiSecret || "",
42
+ },
43
+ body: JSON.stringify({
44
+ jsonrpc: "2.0",
45
+ method: "tools/list",
46
+ id: Date.now(),
47
+ }),
48
+ });
49
+ if (!response.ok) {
50
+ throw new Error(`Validation failed: ${response.status}`);
51
+ }
52
+ const data = await response.json();
53
+ if (data.error) {
54
+ throw new Error(data.error.message);
55
+ }
56
+ validatedData = {
57
+ tools: data.result?.tools?.map((t) => t.name) || [],
58
+ };
59
+ console.log("✅ Tigerbase configured:", validatedData.tools);
60
+ }
61
+ catch (error) {
62
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
63
+ throw new Error(`Tigerbase validation failed: ${errorMessage}`);
64
+ }
65
+ };
66
+ export const getConfig = () => {
67
+ if (!config || !validatedData) {
68
+ throw new Error("Tigerbase not configured. Call tigerbaseConfig() first.");
69
+ }
70
+ // Merge styles with defaults
71
+ const baseStyle = config.style?.theme === "dark"
72
+ ? { ...defaultStyle, ...darkStyle }
73
+ : defaultStyle;
74
+ const mergedStyle = { ...baseStyle, ...config.style };
75
+ return {
76
+ ...config,
77
+ mcpUrl: config.mcpUrl || config.secretKey || "",
78
+ apiKey: config.apiKey || config.serialNum || "",
79
+ validatedTools: validatedData.tools,
80
+ style: mergedStyle,
81
+ };
82
+ };
83
+ // CSS variable generator
84
+ export const getCssVariables = (style) => {
85
+ const radiusMap = {
86
+ none: "0",
87
+ sm: "0.25rem",
88
+ md: "0.5rem",
89
+ lg: "0.75rem",
90
+ xl: "1rem",
91
+ full: "9999px",
92
+ };
93
+ return {
94
+ "--tb-primary": style.primaryColor || defaultStyle.primaryColor,
95
+ "--tb-bg": style.backgroundColor || defaultStyle.backgroundColor,
96
+ "--tb-user-msg": style.userMessageBg || defaultStyle.userMessageBg,
97
+ "--tb-bot-msg": style.botMessageBg || defaultStyle.botMessageBg,
98
+ "--tb-text": style.textColor || defaultStyle.textColor,
99
+ "--tb-header": style.headerBg || defaultStyle.headerBg,
100
+ "--tb-radius": radiusMap[style.borderRadius || "lg"],
101
+ };
102
+ };
@@ -0,0 +1,2 @@
1
+ export { default as ChatbotPopup } from "./ChatbotPopup";
2
+ export { tigerbaseConfig } from "./config";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { default as ChatbotPopup } from "./ChatbotPopup";
2
+ export { tigerbaseConfig } from "./config";
@@ -0,0 +1,2 @@
1
+ import { type ClassValue } from "clsx";
2
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,5 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ export function cn(...inputs) {
4
+ return twMerge(clsx(inputs));
5
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "tigerbase-chatbot",
3
+ "version": "1.0.0",
4
+ "description": "TigerBase Chatbot Client - AI-powered chatbot for product management",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "dependencies": {
13
+ "@google/generative-ai": "^0.21.0",
14
+ "@radix-ui/react-dialog": "^1.1.15",
15
+ "@radix-ui/react-scroll-area": "^1.0.5",
16
+ "@radix-ui/react-slot": "^1.2.3",
17
+ "class-variance-authority": "^0.7.1",
18
+ "clsx": "^2.0.0",
19
+ "lucide-react": "^0.441.0",
20
+ "react-toastify": "^10.0.6",
21
+ "tailwind-merge": "^2.3.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.14.2",
25
+ "@types/react": "^18.3.3",
26
+ "@types/react-dom": "^18.3.0",
27
+ "typescript": "^5.5.4"
28
+ },
29
+ "peerDependencies": {
30
+ "react": "^18.2.0 || ^19.0.0",
31
+ "react-dom": "^18.2.0 || ^19.0.0"
32
+ },
33
+ "author": "SolomDev00",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/SolomDev00/Tigerbase.git"
38
+ },
39
+ "homepage": "https://tigerbase.cloud",
40
+ "files": [
41
+ "dist"
42
+ ],
43
+ "keywords": [
44
+ "tigerbase",
45
+ "chatbot",
46
+ "react",
47
+ "ai",
48
+ "gemini",
49
+ "product-management",
50
+ "mcp"
51
+ ],
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }