fyrebot-widget 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fyrebot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,363 @@
1
+ # 🤖 Fyrebot AI Chatbot Widget
2
+
3
+ Production-ready, standalone chatbot popup widget for any React application. Features beautiful dark theme, multi-tenant support via X-API-Key authentication, and complete TypeScript support.
4
+
5
+ **Made with ❤️ by [Fyrebot](https://fyrebot.com)**
6
+
7
+ ## ✨ Features
8
+
9
+ - 🎨 Beautiful dark theme UI with smooth animations
10
+ - 🔐 Secure X-API-Key authentication
11
+ - 🏢 Multi-tenant support (automatic tenant identification via API key)
12
+ - 💬 Session-based conversations with context retention
13
+ - 📚 Source citations with relevance scores
14
+ - ⚡ Real-time typing indicators
15
+ - 📱 Fully responsive design
16
+ - 🎯 TypeScript support with complete type definitions
17
+ - 🔄 Error handling with user-friendly messages
18
+ - ⚙️ Highly customizable (colors, position, messages, etc.)
19
+ - 📦 Zero external UI dependencies (self-contained)
20
+
21
+ ## 📦 Installation
22
+
23
+ ```bash
24
+ npm install fyrebot-widget
25
+ # or
26
+ yarn add fyrebot-widget
27
+ # or
28
+ pnpm add fyrebot-widget
29
+ ```
30
+
31
+ ## 🚀 Quick Start
32
+
33
+ ### Basic Usage
34
+
35
+ ```tsx
36
+ import { ChatbotWidget } from 'fyrebot-widget';
37
+
38
+ function App() {
39
+ return (
40
+ <div>
41
+ {/* Your app content */}
42
+
43
+ <ChatbotWidget
44
+ apiUrl="https://api.fyrebot.com/api"
45
+ apiKey="your-api-key-from-dashboard"
46
+ />
47
+ </div>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ### Get Your API Key
53
+
54
+ 1. Sign up at [fyrebot.com](https://fyrebot.com)
55
+ 2. Go to your dashboard
56
+ 3. Copy your API key
57
+ 4. Use it in the widget configuration
58
+
59
+ ### Advanced Configuration
60
+
61
+ ```tsx
62
+ import { ChatbotWidget } from 'fyrebot-widget';
63
+
64
+ function App() {
65
+ return (
66
+ <ChatbotWidget
67
+ // Required
68
+ apiUrl="https://api.clarvobot.com/api"
69
+ apiKey="your-api-key-from-dashboard"
70
+
71
+ // Customization
72
+ title="Support Assistant"
73
+ subtitle="We're here to help"
74
+ brandName="Your Company"
75
+ primaryColor="#6366f1"
76
+ welcomeMessage="Hello! How can I assist you today?"
77
+ placeholder="Ask me anything..."
78
+ position="bottom-right"
79
+
80
+ // Features
81
+ showTypingIndicator={true}
82
+ showTimestamps={true}
83
+ showSources={true}
84
+
85
+ // Sizing
86
+ maxHeight="600px"
87
+ maxWidth="400px"
88
+
89
+ // Callbacks
90
+ onOpen={() => console.log('Chat opened')}
91
+ onClose={() => console.log('Chat closed')}
92
+ onMessageSent={(message) => console.log('Sent:', message)}
93
+ onMessageReceived={(message) => console.log('Received:', message)}
94
+ onError={(error) => console.error('Error:', error)}
95
+ />
96
+ );
97
+ }
98
+ ```
99
+
100
+ ## 🎨 Customization Options
101
+
102
+ ### Configuration Props
103
+
104
+ | Prop | Type | Default | Description |
105
+ |------|------|---------|-------------|
106
+ | `apiUrl` | `string` | **Required** | Your API endpoint URL |
107
+ | `apiKey` | `string` | **Required** | X-API-Key for authentication |
108
+ | `title` | `string` | `'AI Assistant'` | Header title |
109
+ | `subtitle` | `string` | `'Ask me anything'` | Header subtitle |
110
+ | `brandName` | `string` | `undefined` | Brand name for welcome message |
111
+ | `primaryColor` | `string` | `'#6366f1'` | Primary theme color (hex) |
112
+ | `welcomeMessage` | `string` | `'Hello! How can I help you today?'` | Initial welcome message |
113
+ | `placeholder` | `string` | `'Type your message...'` | Input placeholder text |
114
+ | `position` | `'bottom-right' \| 'bottom-left' \| 'top-right' \| 'top-left'` | `'bottom-right'` | Chat button position |
115
+ | `showTypingIndicator` | `boolean` | `true` | Show typing animation |
116
+ | `showTimestamps` | `boolean` | `true` | Show message timestamps |
117
+ | `showSources` | `boolean` | `true` | Show source citations |
118
+ | `maxHeight` | `string` | `'600px'` | Maximum chat window height |
119
+ | `maxWidth` | `string` | `'400px'` | Maximum chat window width |
120
+ | `className` | `string` | `''` | Custom CSS class |
121
+
122
+ ### Callbacks
123
+
124
+ ```tsx
125
+ <ChatbotWidget
126
+ // ... other props
127
+ onOpen={() => {
128
+ // Called when chat window opens
129
+ console.log('Chat opened');
130
+ }}
131
+
132
+ onClose={() => {
133
+ // Called when chat window closes
134
+ console.log('Chat closed');
135
+ }}
136
+
137
+ onMessageSent={(message) => {
138
+ // Called when user sends a message
139
+ console.log('User said:', message);
140
+ }}
141
+
142
+ onMessageReceived={(message) => {
143
+ // Called when bot responds
144
+ console.log('Bot said:', message.content);
145
+ }}
146
+
147
+ onError={(error) => {
148
+ // Called when an error occurs
149
+ console.error('Error:', error.message);
150
+ }}
151
+ />
152
+ ```
153
+
154
+ ## 🔐 API Integration
155
+
156
+ ### Backend Requirements
157
+
158
+ Your backend API must support:
159
+
160
+ 1. **Authentication**: X-API-Key header
161
+ 2. **Endpoint**: `POST /chat`
162
+ 3. **Request Format**:
163
+ ```json
164
+ {
165
+ "query": "User's message",
166
+ "sessionId": "optional-session-id"
167
+ }
168
+ ```
169
+ 4. **Response Format**:
170
+ ```json
171
+ {
172
+ "success": true,
173
+ "data": {
174
+ "answer": "Bot's response",
175
+ "sessionId": "session-123",
176
+ "sources": [
177
+ {
178
+ "title": "Source document",
179
+ "score": 0.95,
180
+ "content": "Optional excerpt"
181
+ }
182
+ ]
183
+ }
184
+ }
185
+ ```
186
+
187
+ ### Multi-Tenant Support
188
+
189
+ The widget automatically includes the X-API-Key header in all requests. Your backend should:
190
+
191
+ 1. Validate the X-API-Key
192
+ 2. Identify the tenant from the key
193
+ 3. Return responses based on the tenant's data
194
+
195
+ Example backend logic:
196
+ ```javascript
197
+ // Backend (Node.js/Express example)
198
+ app.post('/chat', async (req, res) => {
199
+ const apiKey = req.headers['x-api-key'];
200
+
201
+ // Validate and get tenant
202
+ const tenant = await validateApiKey(apiKey);
203
+ if (!tenant) {
204
+ return res.status(401).json({ error: 'Invalid API key' });
205
+ }
206
+
207
+ // Process query with tenant's data
208
+ const result = await processQuery(
209
+ tenant.id,
210
+ req.body.query,
211
+ req.body.sessionId
212
+ );
213
+
214
+ res.json({ success: true, data: result });
215
+ });
216
+ ```
217
+
218
+ ## 🎯 TypeScript Support
219
+
220
+ Full TypeScript definitions included:
221
+
222
+ ```tsx
223
+ import { ChatbotWidget, ChatbotConfig, ChatMessage } from 'fyrebot-widget';
224
+
225
+ const config: ChatbotConfig = {
226
+ apiUrl: 'https://api.fyrebot.com/api',
227
+ apiKey: 'your-key',
228
+ title: 'Assistant',
229
+ onMessageReceived: (message: ChatMessage) => {
230
+ console.log(message.content);
231
+ },
232
+ };
233
+
234
+ function App() {
235
+ return <ChatbotWidget {...config} />;
236
+ }
237
+ ```
238
+
239
+ ## 📱 Responsive Design
240
+
241
+ The widget automatically adapts to:
242
+ - Desktop screens
243
+ - Tablets
244
+ - Mobile devices (full-screen on small screens)
245
+
246
+ ## 🎨 Styling
247
+
248
+ The widget uses scoped inline styles with no external CSS dependencies. You can customize:
249
+
250
+ 1. **Theme Colors**: Use `primaryColor` prop
251
+ 2. **Size**: Use `maxHeight` and `maxWidth` props
252
+ 3. **Position**: Use `position` prop
253
+ 4. **Custom Overrides**: Use `className` prop with CSS variables
254
+
255
+ Example custom styling:
256
+ ```css
257
+ .my-custom-chatbot {
258
+ /* Your custom styles */
259
+ }
260
+ ```
261
+
262
+ ```tsx
263
+ <ChatbotWidget
264
+ className="my-custom-chatbot"
265
+ primaryColor="#ff6b6b"
266
+ />
267
+ ```
268
+
269
+ ## 🔧 Advanced Usage
270
+
271
+ ### Programmatic Control
272
+
273
+ ```tsx
274
+ import { ChatbotWidget, ChatbotApiClient } from 'fyrebot-widget';
275
+ import { useRef } from 'react';
276
+
277
+ function App() {
278
+ const apiClientRef = useRef<ChatbotApiClient>();
279
+
280
+ const handleReset = () => {
281
+ // Reset the conversation
282
+ apiClientRef.current?.resetSession();
283
+ };
284
+
285
+ return (
286
+ <>
287
+ <button onClick={handleReset}>Reset Chat</button>
288
+ <ChatbotWidget
289
+ apiUrl="https://api.clarvobot.com/api"
290
+ apiKey="your-key"
291
+ />
292
+ </>
293
+ );
294
+ }
295
+ ```
296
+
297
+ ## 🐛 Error Handling
298
+
299
+ The widget handles common errors gracefully:
300
+
301
+ - ❌ Invalid API key → "Invalid API key. Please check your credentials."
302
+ - ❌ Rate limiting → "Too many requests. Please wait a moment."
303
+ - ❌ Network issues → "Unable to connect to the server."
304
+ - ❌ Server errors → "Server error. Please try again later."
305
+
306
+ All errors are displayed in a user-friendly format within the chat window.
307
+
308
+ ## 📦 Bundle Size
309
+
310
+ - **Minified**: ~45KB
311
+ - **Gzipped**: ~15KB
312
+ - **Dependencies**: React, Lucide Icons, Axios
313
+
314
+ ## 🔒 Security
315
+
316
+ - ✅ X-API-Key authentication
317
+ - ✅ HTTPS required for production
318
+ - ✅ XSS protection (React's built-in escaping)
319
+ - ✅ CORS handled by your backend
320
+ - ✅ No data stored in localStorage (optional session storage)
321
+
322
+ ## 🚀 Deployment
323
+
324
+ ### Option 1: Direct Copy (Easiest)
325
+
326
+ Copy the entire `src` folder to your React project:
327
+
328
+ ```bash
329
+ cp -r chatbot-widget/src ./src/components/chatbot
330
+ ```
331
+
332
+ Then import:
333
+ ```tsx
334
+ import { ChatbotWidget } from './components/chatbot';
335
+ ```
336
+
337
+ ### Option 2: NPM Package
338
+
339
+ Build and publish:
340
+ ```bash
341
+ npm run build
342
+ npm publish
343
+ ```
344
+
345
+ Then install in your projects:
346
+ ```bash
347
+ npm install fyrebot-widget
348
+ ```
349
+
350
+ ## 📄 License
351
+
352
+ MIT © [Fyrebot](https://fyrebot.com)
353
+
354
+ ## 🤝 Support
355
+
356
+ For issues or questions:
357
+ - 📧 Email: support@fyrebot.com
358
+ - 🌐 Website: [fyrebot.com](https://fyrebot.com)
359
+ - 📚 Docs: [docs.fyrebot.com](https://docs.fyrebot.com)
360
+
361
+ ---
362
+
363
+ **Made with ❤️ by Fyrebot** - Empowering businesses with AI-powered conversations
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Chatbot Popup Widget
3
+ * Production-ready, standalone chatbot component
4
+ */
5
+ import React from 'react';
6
+ import type { ChatbotConfig } from './types';
7
+ export declare const ChatbotWidget: React.FC<ChatbotConfig>;
8
+ export default ChatbotWidget;
9
+ //# sourceMappingURL=ChatbotWidget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatbotWidget.d.ts","sourceRoot":"","sources":["../src/ChatbotWidget.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAGxE,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,SAAS,CAAC;AAE1D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CA6nBjD,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,479 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Chatbot Popup Widget
4
+ * Production-ready, standalone chatbot component
5
+ */
6
+ import { useState, useRef, useEffect, useCallback } from 'react';
7
+ import { MessageCircle, X, Send, Loader2, Bot, User, Sparkles, RefreshCw } from 'lucide-react';
8
+ import { ChatbotApiClient } from './api';
9
+ export const ChatbotWidget = ({ apiUrl, apiKey, title = 'AI Assistant', subtitle = 'Ask me anything', brandName, primaryColor = '#6366f1', welcomeMessage = 'Hello! How can I help you today?', placeholder = 'Type your message...', position = 'bottom-right', showTypingIndicator = true, showTimestamps = true, showSources = true, maxHeight = '600px', maxWidth = '400px', className = '', onOpen, onClose, onMessageSent, onMessageReceived, onError, }) => {
10
+ const [isOpen, setIsOpen] = useState(false);
11
+ const [messages, setMessages] = useState([]);
12
+ const [input, setInput] = useState('');
13
+ const [isLoading, setIsLoading] = useState(false);
14
+ const [error, setError] = useState(null);
15
+ const scrollRef = useRef(null);
16
+ const apiClient = useRef(null);
17
+ // Initialize API client
18
+ useEffect(() => {
19
+ apiClient.current = new ChatbotApiClient(apiUrl, apiKey);
20
+ }, [apiUrl, apiKey]);
21
+ // Auto-scroll to bottom
22
+ useEffect(() => {
23
+ if (scrollRef.current) {
24
+ scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
25
+ }
26
+ }, [messages, isLoading]);
27
+ // Handle open/close
28
+ const handleToggle = useCallback(() => {
29
+ const newState = !isOpen;
30
+ setIsOpen(newState);
31
+ if (newState) {
32
+ onOpen?.();
33
+ setError(null);
34
+ }
35
+ else {
36
+ onClose?.();
37
+ }
38
+ }, [isOpen, onOpen, onClose]);
39
+ // Send message
40
+ const handleSend = useCallback(async () => {
41
+ if (!input.trim() || isLoading || !apiClient.current)
42
+ return;
43
+ const userMessage = {
44
+ id: `user-${Date.now()}`,
45
+ role: 'user',
46
+ content: input.trim(),
47
+ timestamp: new Date(),
48
+ };
49
+ setMessages((prev) => [...prev, userMessage]);
50
+ setInput('');
51
+ setIsLoading(true);
52
+ setError(null);
53
+ onMessageSent?.(userMessage.content);
54
+ try {
55
+ const response = await apiClient.current.sendMessage(userMessage.content);
56
+ const assistantMessage = {
57
+ id: `assistant-${Date.now()}`,
58
+ role: 'assistant',
59
+ content: response.answer,
60
+ timestamp: new Date(),
61
+ sources: response.sources,
62
+ };
63
+ setMessages((prev) => [...prev, assistantMessage]);
64
+ onMessageReceived?.(assistantMessage);
65
+ }
66
+ catch (err) {
67
+ const error = err;
68
+ setError(error.message);
69
+ onError?.(error);
70
+ }
71
+ finally {
72
+ setIsLoading(false);
73
+ }
74
+ }, [input, isLoading, onMessageSent, onMessageReceived, onError]);
75
+ // Handle Enter key
76
+ const handleKeyPress = useCallback((e) => {
77
+ if (e.key === 'Enter' && !e.shiftKey) {
78
+ e.preventDefault();
79
+ handleSend();
80
+ }
81
+ }, [handleSend]);
82
+ // Reset conversation
83
+ const handleReset = useCallback(() => {
84
+ setMessages([]);
85
+ setError(null);
86
+ apiClient.current?.resetSession();
87
+ }, []);
88
+ // Position styles
89
+ const positionStyles = {
90
+ 'bottom-right': { bottom: '20px', right: '20px' },
91
+ 'bottom-left': { bottom: '20px', left: '20px' },
92
+ 'top-right': { top: '20px', right: '20px' },
93
+ 'top-left': { top: '20px', left: '20px' },
94
+ };
95
+ return (_jsxs("div", { className: `chatbot-widget-container ${className}`, style: positionStyles[position], children: [_jsx("style", { children: `
96
+ .chatbot-widget-container {
97
+ position: fixed;
98
+ z-index: 9999;
99
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
100
+ }
101
+
102
+ .chatbot-button {
103
+ width: 60px;
104
+ height: 60px;
105
+ border-radius: 50%;
106
+ background: ${primaryColor};
107
+ border: none;
108
+ cursor: pointer;
109
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ transition: all 0.3s ease;
114
+ color: white;
115
+ }
116
+
117
+ .chatbot-button:hover {
118
+ transform: scale(1.1);
119
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);
120
+ }
121
+
122
+ .chatbot-window {
123
+ position: absolute;
124
+ bottom: 80px;
125
+ right: 0;
126
+ width: ${maxWidth};
127
+ max-width: calc(100vw - 40px);
128
+ height: ${maxHeight};
129
+ max-height: calc(100vh - 120px);
130
+ background: #1a1a1a;
131
+ border-radius: 16px;
132
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
133
+ display: flex;
134
+ flex-direction: column;
135
+ overflow: hidden;
136
+ animation: slideUp 0.3s ease;
137
+ }
138
+
139
+ @keyframes slideUp {
140
+ from {
141
+ opacity: 0;
142
+ transform: translateY(20px);
143
+ }
144
+ to {
145
+ opacity: 1;
146
+ transform: translateY(0);
147
+ }
148
+ }
149
+
150
+ .chatbot-header {
151
+ background: linear-gradient(135deg, ${primaryColor}, ${primaryColor}dd);
152
+ padding: 16px;
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: space-between;
156
+ color: white;
157
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
158
+ }
159
+
160
+ .chatbot-header-content {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: 12px;
164
+ }
165
+
166
+ .chatbot-icon {
167
+ width: 36px;
168
+ height: 36px;
169
+ border-radius: 50%;
170
+ background: rgba(255, 255, 255, 0.2);
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ }
175
+
176
+ .chatbot-title {
177
+ display: flex;
178
+ flex-direction: column;
179
+ }
180
+
181
+ .chatbot-title h3 {
182
+ margin: 0;
183
+ font-size: 16px;
184
+ font-weight: 600;
185
+ }
186
+
187
+ .chatbot-title p {
188
+ margin: 0;
189
+ font-size: 12px;
190
+ opacity: 0.9;
191
+ }
192
+
193
+ .chatbot-close {
194
+ background: none;
195
+ border: none;
196
+ color: white;
197
+ cursor: pointer;
198
+ padding: 8px;
199
+ border-radius: 8px;
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ transition: background 0.2s;
204
+ }
205
+
206
+ .chatbot-close:hover {
207
+ background: rgba(255, 255, 255, 0.1);
208
+ }
209
+
210
+ .chatbot-messages {
211
+ flex: 1;
212
+ overflow-y: auto;
213
+ padding: 16px;
214
+ background: #1a1a1a;
215
+ }
216
+
217
+ .chatbot-messages::-webkit-scrollbar {
218
+ width: 8px;
219
+ }
220
+
221
+ .chatbot-messages::-webkit-scrollbar-track {
222
+ background: #2a2a2a;
223
+ }
224
+
225
+ .chatbot-messages::-webkit-scrollbar-thumb {
226
+ background: #444;
227
+ border-radius: 4px;
228
+ }
229
+
230
+ .chatbot-welcome {
231
+ text-align: center;
232
+ padding: 40px 20px;
233
+ color: #888;
234
+ }
235
+
236
+ .chatbot-welcome-icon {
237
+ width: 64px;
238
+ height: 64px;
239
+ margin: 0 auto 16px;
240
+ border-radius: 50%;
241
+ background: rgba(99, 102, 241, 0.1);
242
+ display: flex;
243
+ align-items: center;
244
+ justify-content: center;
245
+ color: ${primaryColor};
246
+ }
247
+
248
+ .chatbot-message {
249
+ display: flex;
250
+ gap: 12px;
251
+ margin-bottom: 16px;
252
+ animation: fadeIn 0.3s ease;
253
+ }
254
+
255
+ @keyframes fadeIn {
256
+ from { opacity: 0; transform: translateY(10px); }
257
+ to { opacity: 1; transform: translateY(0); }
258
+ }
259
+
260
+ .chatbot-message.user {
261
+ flex-direction: row-reverse;
262
+ }
263
+
264
+ .chatbot-avatar {
265
+ width: 32px;
266
+ height: 32px;
267
+ border-radius: 50%;
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+ flex-shrink: 0;
272
+ }
273
+
274
+ .chatbot-avatar.bot {
275
+ background: ${primaryColor};
276
+ color: white;
277
+ }
278
+
279
+ .chatbot-avatar.user {
280
+ background: #333;
281
+ color: #aaa;
282
+ }
283
+
284
+ .chatbot-message-content {
285
+ flex: 1;
286
+ max-width: 75%;
287
+ }
288
+
289
+ .chatbot-message.user .chatbot-message-content {
290
+ display: flex;
291
+ flex-direction: column;
292
+ align-items: flex-end;
293
+ }
294
+
295
+ .chatbot-bubble {
296
+ padding: 12px 16px;
297
+ border-radius: 12px;
298
+ word-wrap: break-word;
299
+ line-height: 1.5;
300
+ }
301
+
302
+ .chatbot-bubble.bot {
303
+ background: #2a2a2a;
304
+ color: #e0e0e0;
305
+ border-bottom-left-radius: 4px;
306
+ }
307
+
308
+ .chatbot-bubble.user {
309
+ background: ${primaryColor};
310
+ color: white;
311
+ border-bottom-right-radius: 4px;
312
+ }
313
+
314
+ .chatbot-timestamp {
315
+ font-size: 11px;
316
+ color: #666;
317
+ margin-top: 4px;
318
+ }
319
+
320
+ .chatbot-sources {
321
+ margin-top: 8px;
322
+ display: flex;
323
+ flex-wrap: wrap;
324
+ gap: 6px;
325
+ }
326
+
327
+ .chatbot-source {
328
+ font-size: 11px;
329
+ padding: 4px 8px;
330
+ background: rgba(99, 102, 241, 0.1);
331
+ color: ${primaryColor};
332
+ border-radius: 4px;
333
+ border: 1px solid rgba(99, 102, 241, 0.2);
334
+ }
335
+
336
+ .chatbot-typing {
337
+ display: flex;
338
+ gap: 12px;
339
+ align-items: center;
340
+ animation: fadeIn 0.3s ease;
341
+ }
342
+
343
+ .chatbot-typing-dots {
344
+ display: flex;
345
+ gap: 4px;
346
+ padding: 12px 16px;
347
+ background: #2a2a2a;
348
+ border-radius: 12px;
349
+ }
350
+
351
+ .chatbot-typing-dot {
352
+ width: 8px;
353
+ height: 8px;
354
+ border-radius: 50%;
355
+ background: #666;
356
+ animation: typing 1.4s infinite;
357
+ }
358
+
359
+ .chatbot-typing-dot:nth-child(2) {
360
+ animation-delay: 0.2s;
361
+ }
362
+
363
+ .chatbot-typing-dot:nth-child(3) {
364
+ animation-delay: 0.4s;
365
+ }
366
+
367
+ @keyframes typing {
368
+ 0%, 60%, 100% {
369
+ transform: translateY(0);
370
+ opacity: 0.4;
371
+ }
372
+ 30% {
373
+ transform: translateY(-10px);
374
+ opacity: 1;
375
+ }
376
+ }
377
+
378
+ .chatbot-error {
379
+ background: rgba(239, 68, 68, 0.1);
380
+ border: 1px solid rgba(239, 68, 68, 0.3);
381
+ color: #ef4444;
382
+ padding: 12px;
383
+ border-radius: 8px;
384
+ margin: 0 16px 16px;
385
+ font-size: 13px;
386
+ display: flex;
387
+ align-items: center;
388
+ gap: 8px;
389
+ }
390
+
391
+ .chatbot-input-area {
392
+ padding: 16px;
393
+ background: #222;
394
+ border-top: 1px solid #333;
395
+ display: flex;
396
+ gap: 8px;
397
+ }
398
+
399
+ .chatbot-input-wrapper {
400
+ flex: 1;
401
+ display: flex;
402
+ align-items: center;
403
+ background: #2a2a2a;
404
+ border-radius: 12px;
405
+ padding: 0 12px;
406
+ border: 1px solid #333;
407
+ transition: border-color 0.2s;
408
+ }
409
+
410
+ .chatbot-input-wrapper:focus-within {
411
+ border-color: ${primaryColor};
412
+ }
413
+
414
+ .chatbot-input {
415
+ flex: 1;
416
+ background: none;
417
+ border: none;
418
+ outline: none;
419
+ padding: 12px 0;
420
+ color: #e0e0e0;
421
+ font-size: 14px;
422
+ }
423
+
424
+ .chatbot-input::placeholder {
425
+ color: #666;
426
+ }
427
+
428
+ .chatbot-button-icon {
429
+ background: ${primaryColor};
430
+ border: none;
431
+ color: white;
432
+ width: 40px;
433
+ height: 40px;
434
+ border-radius: 10px;
435
+ display: flex;
436
+ align-items: center;
437
+ justify-content: center;
438
+ cursor: pointer;
439
+ transition: all 0.2s;
440
+ }
441
+
442
+ .chatbot-button-icon:hover:not(:disabled) {
443
+ opacity: 0.9;
444
+ transform: scale(1.05);
445
+ }
446
+
447
+ .chatbot-button-icon:disabled {
448
+ opacity: 0.5;
449
+ cursor: not-allowed;
450
+ }
451
+
452
+ .chatbot-reset {
453
+ background: #2a2a2a;
454
+ border: 1px solid #333;
455
+ }
456
+
457
+ .chatbot-reset:hover:not(:disabled) {
458
+ background: #333;
459
+ }
460
+
461
+ @media (max-width: 480px) {
462
+ .chatbot-window {
463
+ width: calc(100vw - 40px);
464
+ height: calc(100vh - 120px);
465
+ }
466
+ }
467
+ ` }), !isOpen && (_jsx("button", { className: "chatbot-button", onClick: handleToggle, "aria-label": "Open chat", children: _jsx(MessageCircle, { size: 28 }) })), isOpen && (_jsxs("div", { className: "chatbot-window", children: [_jsxs("div", { className: "chatbot-header", children: [_jsxs("div", { className: "chatbot-header-content", children: [_jsx("div", { className: "chatbot-icon", children: _jsx(Sparkles, { size: 20 }) }), _jsxs("div", { className: "chatbot-title", children: [_jsx("h3", { children: title }), subtitle && _jsx("p", { children: subtitle })] })] }), _jsxs("div", { style: { display: 'flex', gap: '8px' }, children: [messages.length > 0 && (_jsx("button", { className: "chatbot-close", onClick: handleReset, "aria-label": "Reset conversation", title: "Start new conversation", children: _jsx(RefreshCw, { size: 18 }) })), _jsx("button", { className: "chatbot-close", onClick: handleToggle, "aria-label": "Close chat", children: _jsx(X, { size: 20 }) })] })] }), _jsxs("div", { className: "chatbot-messages", ref: scrollRef, children: [messages.length === 0 ? (_jsxs("div", { className: "chatbot-welcome", children: [_jsx("div", { className: "chatbot-welcome-icon", children: _jsx(Bot, { size: 32 }) }), _jsx("h4", { style: { margin: '0 0 8px 0', color: '#ccc' }, children: brandName ? `Welcome to ${brandName}!` : 'Welcome!' }), _jsx("p", { style: { margin: 0, fontSize: '14px' }, children: welcomeMessage })] })) : (messages.map((message) => (_jsxs("div", { className: `chatbot-message ${message.role}`, children: [_jsx("div", { className: `chatbot-avatar ${message.role === 'user' ? 'user' : 'bot'}`, children: message.role === 'user' ? _jsx(User, { size: 18 }) : _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-message-content", children: [_jsx("div", { className: `chatbot-bubble ${message.role === 'user' ? 'user' : 'bot'}`, children: message.content }), showSources && message.sources && message.sources.length > 0 && (_jsx("div", { className: "chatbot-sources", children: message.sources.map((source, idx) => (_jsxs("span", { className: "chatbot-source", children: [source.title, " (", Math.round(source.score * 100), "%)"] }, idx))) })), showTimestamps && (_jsx("div", { className: "chatbot-timestamp", children: new Date(message.timestamp).toLocaleTimeString([], {
468
+ hour: '2-digit',
469
+ minute: '2-digit',
470
+ }) }))] })] }, message.id)))), isLoading && showTypingIndicator && (_jsxs("div", { className: "chatbot-typing", children: [_jsx("div", { className: "chatbot-avatar bot", children: _jsx(Bot, { size: 18 }) }), _jsxs("div", { className: "chatbot-typing-dots", children: [_jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" }), _jsx("div", { className: "chatbot-typing-dot" })] })] }))] }), error && (_jsxs("div", { className: "chatbot-error", children: [_jsx("span", { children: "\u26A0\uFE0F" }), error] })), _jsxs("div", { className: "chatbot-input-area", children: [_jsx("div", { className: "chatbot-input-wrapper", children: _jsx("input", { type: "text", className: "chatbot-input", value: input, onChange: (e) => setInput(e.target.value), onKeyPress: handleKeyPress, placeholder: placeholder, disabled: isLoading }) }), _jsx("button", { className: "chatbot-button-icon", onClick: handleSend, disabled: !input.trim() || isLoading, "aria-label": "Send message", children: isLoading ? _jsx(Loader2, { size: 20, className: "spin" }) : _jsx(Send, { size: 20 }) })] })] })), _jsx("style", { children: `
471
+ .spin {
472
+ animation: spin 1s line <s }
473
+ @keyframes spin {
474
+ from { transform: rotate(0deg); }
475
+ to { transform: rotate(360deg); }
476
+ }
477
+ ` })] }));
478
+ };
479
+ export default ChatbotWidget;
package/dist/api.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * API Client for Chatbot Widget
3
+ * Handles all communication
4
+ with the backend using X-API-Key authentication
5
+ */
6
+ import type { ChatApiResponse } from './types';
7
+ export declare class ChatbotApiClient {
8
+ private client;
9
+ private apiKey;
10
+ private sessionId;
11
+ constructor(apiUrl: string, apiKey: string);
12
+ /**
13
+ * Send a chat message to the API
14
+ */
15
+ sendMessage(message: string): Promise<ChatApiResponse>;
16
+ /**
17
+ * Get current session ID
18
+ */
19
+ getSessionId(): string | null;
20
+ /**
21
+ * Reset session (start new conversation)
22
+ */
23
+ resetSession(): void;
24
+ /**
25
+ * Update API key
26
+ */
27
+ updateApiKey(apiKey: string): void;
28
+ /**
29
+ * Handle API errors with user-friendly messages
30
+ */
31
+ private handleError;
32
+ }
33
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,SAAS,CAAC;AAE5D,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAuB;gBAE5B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAqB1C;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IA6B5D;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,YAAY,IAAI,IAAI;IAIpB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKlC;;OAEG;IACH,OAAO,CAAC,WAAW;CA2BpB"}
package/dist/api.js ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * API Client for Chatbot Widget
3
+ * Handles all communication
4
+ with the backend using X-API-Key authentication
5
+ */
6
+ import axios from 'axios';
7
+ export class ChatbotApiClient {
8
+ constructor(apiUrl, apiKey) {
9
+ this.sessionId = null;
10
+ this.apiKey = apiKey;
11
+ this.client = axios.create({
12
+ baseURL: apiUrl,
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'X-API-Key': apiKey,
16
+ },
17
+ timeout: 30000, // 30 second timeout
18
+ });
19
+ // Response interceptor for error handling
20
+ this.client.interceptors.response.use((response) => response, (error) => {
21
+ return Promise.reject(this.handleError(error));
22
+ });
23
+ }
24
+ /**
25
+ * Send a chat message to the API
26
+ */
27
+ async sendMessage(message) {
28
+ try {
29
+ // Build request body - only include sessionId if it exists
30
+ const requestBody = {
31
+ query: message,
32
+ };
33
+ // Only add sessionId if we have one (don't send null or undefined)
34
+ if (this.sessionId) {
35
+ requestBody.sessionId = this.sessionId;
36
+ }
37
+ const response = await this.client.post('/chat', requestBody);
38
+ // Store session ID for conversation continuity
39
+ if (response.data.data?.sessionId) {
40
+ this.sessionId = response.data.data.sessionId;
41
+ }
42
+ if (!response.data.success || !response.data.data) {
43
+ throw new Error(response.data.error || 'Failed to get response');
44
+ }
45
+ return response.data.data;
46
+ }
47
+ catch (error) {
48
+ throw this.handleError(error);
49
+ }
50
+ }
51
+ /**
52
+ * Get current session ID
53
+ */
54
+ getSessionId() {
55
+ return this.sessionId;
56
+ }
57
+ /**
58
+ * Reset session (start new conversation)
59
+ */
60
+ resetSession() {
61
+ this.sessionId = null;
62
+ }
63
+ /**
64
+ * Update API key
65
+ */
66
+ updateApiKey(apiKey) {
67
+ this.apiKey = apiKey;
68
+ this.client.defaults.headers['X-API-Key'] = apiKey;
69
+ }
70
+ /**
71
+ * Handle API errors with user-friendly messages
72
+ */
73
+ handleError(error) {
74
+ if (axios.isAxiosError(error)) {
75
+ const axiosError = error;
76
+ if (axiosError.response) {
77
+ const status = axiosError.response.status;
78
+ const data = axiosError.response.data;
79
+ switch (status) {
80
+ case 401:
81
+ return new Error('Invalid API key. Please check your credentials.');
82
+ case 403:
83
+ return new Error('Access denied. You may have exceeded your usage limits.');
84
+ case 429:
85
+ return new Error('Too many requests. Please wait a moment and try again.');
86
+ case 500:
87
+ return new Error('Server error. Please try again later.');
88
+ default:
89
+ return new Error(data?.error || data?.message || 'An unexpected error occurred.');
90
+ }
91
+ }
92
+ else if (axiosError.request) {
93
+ return new Error('Unable to connect to the server. Please check your internet connection.');
94
+ }
95
+ }
96
+ return error instanceof Error ? error : new Error('An unexpected error occurred.');
97
+ }
98
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Chatbot Widget - Main Entry Point
3
+ * Export all public APIs
4
+ */
5
+ export { ChatbotWidget } from './ChatbotWidget';
6
+ export { ChatbotApiClient } from './api';
7
+ export type { ChatbotConfig, ChatMessage, ChatSession, ApiResponse, ChatApiResponse } from './types';
8
+ export { ChatbotWidget as default } from './ChatbotWidget';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AACzC,YAAY,EACV,aAAa,EACb,WAAW,EACX,WAAW,EACX,WAAW,EACX,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Chatbot Widget - Main Entry Point
3
+ * Export all public APIs
4
+ */
5
+ export { ChatbotWidget } from './ChatbotWidget';
6
+ export { ChatbotApiClient } from './api';
7
+ // Default export
8
+ export { ChatbotWidget as default } from './ChatbotWidget';
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Chatbot Widget Types
3
+ * Production-ready TypeScript definitions
4
+ */
5
+ export interface ChatMessage {
6
+ id: string;
7
+ role: 'user' | 'assistant';
8
+ content: string;
9
+ timestamp: Date;
10
+ sources?: Array<{
11
+ title: string;
12
+ score: number;
13
+ content?: string;
14
+ }>;
15
+ }
16
+ export interface ChatbotConfig {
17
+ /** API endpoint URL (e.g., 'https://api.example.com') */
18
+ apiUrl: string;
19
+ /** X-API-Key for authentication */
20
+ apiKey: string;
21
+ /** Title displayed in the chatbot header */
22
+ title?: string;
23
+ /** Subtitle or description */
24
+ subtitle?: string;
25
+ /** Brand name for personalization */
26
+ brandName?: string;
27
+ /** Primary color theme (hex color) */
28
+ primaryColor?: string;
29
+ /** Welcome message shown when chat is empty */
30
+ welcomeMessage?: string;
31
+ /** Placeholder text for input field */
32
+ placeholder?: string;
33
+ /** Position of the chat button */
34
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
35
+ /** Enable/disable typing indicators */
36
+ showTypingIndicator?: boolean;
37
+ /** Enable/disable timestamps */
38
+ showTimestamps?: boolean;
39
+ /** Enable/disable source citations */
40
+ showSources?: boolean;
41
+ /** Maximum height of the chat window */
42
+ maxHeight?: string;
43
+ /** Maximum width of the chat window */
44
+ maxWidth?: string;
45
+ /** Custom CSS class for styling */
46
+ className?: string;
47
+ /** Callback when chat is opened */
48
+ onOpen?: () => void;
49
+ /** Callback when chat is closed */
50
+ onClose?: () => void;
51
+ /** Callback when message is sent */
52
+ onMessageSent?: (message: string) => void;
53
+ /** Callback when response is received */
54
+ onMessageReceived?: (message: ChatMessage) => void;
55
+ /** Callback when error occurs */
56
+ onError?: (error: Error) => void;
57
+ }
58
+ export interface ChatSession {
59
+ sessionId: string;
60
+ messages: ChatMessage[];
61
+ createdAt: Date;
62
+ updatedAt: Date;
63
+ }
64
+ export interface ApiResponse<T = any> {
65
+ success: boolean;
66
+ data?: T;
67
+ error?: string;
68
+ message?: string;
69
+ }
70
+ export interface ChatApiResponse {
71
+ answer: string;
72
+ sessionId: string;
73
+ sources?: Array<{
74
+ title: string;
75
+ score: number;
76
+ content?: string;
77
+ }>;
78
+ }
79
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAC;IAEf,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IAEf,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;IAErE,uCAAuC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAE9B,gCAAgC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,sCAAsC;IACtC,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAErB,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C,yCAAyC;IACzC,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAEnD,iCAAiC;IACjC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Chatbot Widget Types
3
+ * Production-ready TypeScript definitions
4
+ */
5
+ export {};
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "fyrebot-widget",
3
+ "version": "1.0.0",
4
+ "description": "Production-ready AI chatbot popup widget by Fyrebot - Multi-tenant support with seamless React integration",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsc --watch",
24
+ "clean": "rm -rf dist",
25
+ "prepublishOnly": "npm run clean && npm run build",
26
+ "test": "echo \"No tests yet\" && exit 0"
27
+ },
28
+ "keywords": [
29
+ "fyrebot",
30
+ "chatbot",
31
+ "ai",
32
+ "widget",
33
+ "popup",
34
+ "react",
35
+ "typescript",
36
+ "multi-tenant",
37
+ "saas",
38
+ "customer-support",
39
+ "chat-widget",
40
+ "ai-assistant",
41
+ "embedded-chat",
42
+ "live-chat"
43
+ ],
44
+ "author": "Fyrebot <support@fyrebot.com>",
45
+ "license": "MIT",
46
+ "peerDependencies": {
47
+ "react": ">=16.8.0",
48
+ "react-dom": ">=16.8.0"
49
+ },
50
+ "dependencies": {
51
+ "axios": "^1.7.0",
52
+ "lucide-react": "^0.460.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/react": "^18.3.0",
56
+ "@types/react-dom": "^18.3.0",
57
+ "typescript": "^5.6.0"
58
+ },
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "https://github.com/fyrebot/chatbot-widget.git"
62
+ },
63
+ "bugs": {
64
+ "url": "https://github.com/fyrebot/chatbot-widget/issues"
65
+ },
66
+ "homepage": "https://fyrebot.com"
67
+ }