floating-copilot-widget 1.1.0 → 1.3.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.
@@ -8,6 +8,452 @@
8
8
  --shadow-xl: 0 20px 48px rgba(0, 0, 0, 0.18);
9
9
  }
10
10
 
11
+ /* Wrapper for positioning */
12
+ .copilot-wrapper {
13
+ position: fixed;
14
+ z-index: var(--z-index, 9999);
15
+ }
16
+
17
+ /* Floating Chat Icon - Modern Bot Design */
18
+ .copilot-chat-icon {
19
+ width: 64px;
20
+ height: 64px;
21
+ border-radius: 50%;
22
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
23
+ border: none;
24
+ cursor: pointer;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ box-shadow: 0 8px 24px rgba(102, 126, 234, 0.4);
29
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
30
+ backdrop-filter: blur(10px);
31
+ position: relative;
32
+ overflow: hidden;
33
+ padding: 0;
34
+ z-index: 999;
35
+ }
36
+
37
+ .copilot-chat-icon::before {
38
+ content: '';
39
+ position: absolute;
40
+ top: 0;
41
+ left: 0;
42
+ right: 0;
43
+ bottom: 0;
44
+ background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.2), transparent);
45
+ pointer-events: none;
46
+ border-radius: 50%;
47
+ }
48
+
49
+ .copilot-chat-icon svg {
50
+ width: 100%;
51
+ height: 100%;
52
+ position: relative;
53
+ z-index: 1;
54
+ filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
55
+ }
56
+
57
+ .copilot-chat-icon:hover {
58
+ transform: scale(1.12);
59
+ box-shadow: 0 12px 32px rgba(102, 126, 234, 0.5);
60
+ }
61
+
62
+ .copilot-chat-icon:active {
63
+ transform: scale(0.94);
64
+ }
65
+
66
+ .floating-copilot {
67
+ display: flex;
68
+ flex-direction: column;
69
+ border-radius: 16px;
70
+ box-shadow: var(--shadow-xl);
71
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
72
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
73
+ sans-serif;
74
+ background-color: var(--bg-color, #ffffff);
75
+ color: var(--text-color, #1f2937);
76
+ border: 1px solid var(--border-color, #e5e7eb);
77
+ overflow: hidden;
78
+ transition: var(--transition-smooth);
79
+ backdrop-filter: blur(10px);
80
+ }
81
+
82
+ .floating-copilot:hover {
83
+ box-shadow: var(--shadow-xl);
84
+ }
85
+
86
+ .floating-copilot.maximized {
87
+ border-radius: 0;
88
+ box-shadow: none;
89
+ }
90
+
91
+ /* Header */
92
+ .copilot-header {
93
+ display: flex;
94
+ justify-content: space-between;
95
+ align-items: center;
96
+ padding: 16px 20px;
97
+ background: linear-gradient(135deg, var(--primary-color, #6366f1) 0%,
98
+ rgba(var(--primary-color, 99, 102, 241), 0.8) 100%);
99
+ color: white;
100
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
101
+ user-select: none;
102
+ cursor: grab;
103
+ transition: var(--transition-smooth);
104
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
105
+ }
106
+
107
+ .copilot-header:hover {
108
+ background: linear-gradient(135deg, var(--primary-color, #6366f1) 0%,
109
+ rgba(var(--primary-color, 99, 102, 241), 0.9) 100%);
110
+ }
111
+
112
+ .copilot-header:active {
113
+ cursor: grabbing;
114
+ }
115
+
116
+ .copilot-title {
117
+ margin: 0;
118
+ font-size: 16px;
119
+ font-weight: 600;
120
+ flex: 1;
121
+ letter-spacing: 0.3px;
122
+ }
123
+
124
+ .copilot-buttons {
125
+ display: flex;
126
+ gap: 8px;
127
+ align-items: center;
128
+ margin-left: auto;
129
+ padding-left: 12px;
130
+ }
131
+
132
+ .copilot-btn {
133
+ background: rgba(255, 255, 255, 0.25) !important;
134
+ border: 1px solid rgba(255, 255, 255, 0.4) !important;
135
+ color: white !important;
136
+ width: 36px;
137
+ height: 36px;
138
+ border-radius: 8px;
139
+ cursor: pointer;
140
+ font-size: 18px;
141
+ display: flex !important;
142
+ align-items: center !important;
143
+ justify-content: center !important;
144
+ transition: all 0.2s ease !important;
145
+ backdrop-filter: blur(10px);
146
+ padding: 0 !important;
147
+ flex-shrink: 0;
148
+ position: relative;
149
+ z-index: 10;
150
+ visibility: visible !important;
151
+ opacity: 1 !important;
152
+ }
153
+
154
+ .copilot-btn:hover {
155
+ background: rgba(255, 255, 255, 0.35) !important;
156
+ border-color: rgba(255, 255, 255, 0.5) !important;
157
+ transform: translateY(-2px);
158
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
159
+ }
160
+
161
+ .copilot-btn:active {
162
+ transform: translateY(0);
163
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1) !important;
164
+ }
165
+
166
+ .copilot-minimize-btn,
167
+ .copilot-maximize-btn,
168
+ .copilot-close-btn {
169
+ display: flex !important;
170
+ visibility: visible !important;
171
+ }
172
+
173
+ /* Messages Container */
174
+ .copilot-messages {
175
+ flex: 1;
176
+ overflow-y: auto;
177
+ padding: 16px;
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: 12px;
181
+ background: linear-gradient(to bottom, var(--bg-color, #ffffff) 0%,
182
+ rgba(var(--primary-color, 99, 102, 241), 0.02) 100%);
183
+ }
184
+
185
+ .copilot-empty-state {
186
+ display: flex;
187
+ align-items: center;
188
+ justify-content: center;
189
+ height: 100%;
190
+ color: #9ca3af;
191
+ text-align: center;
192
+ font-size: 14px;
193
+ flex-direction: column;
194
+ gap: 12px;
195
+ }
196
+
197
+ .copilot-empty-icon {
198
+ width: 80px;
199
+ height: 80px;
200
+ opacity: 0.7;
201
+ display: flex;
202
+ align-items: center;
203
+ justify-content: center;
204
+ }
205
+
206
+ /* Message Styles */
207
+ .copilot-message {
208
+ display: flex;
209
+ flex-direction: column;
210
+ gap: 4px;
211
+ animation: slideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
212
+ }
213
+
214
+ @keyframes slideIn {
215
+ from {
216
+ opacity: 0;
217
+ transform: translateY(12px) scale(0.95);
218
+ }
219
+ to {
220
+ opacity: 1;
221
+ transform: translateY(0) scale(1);
222
+ }
223
+ }
224
+
225
+ .copilot-message-user {
226
+ align-items: flex-end;
227
+ }
228
+
229
+ .copilot-message-bot {
230
+ align-items: flex-start;
231
+ }
232
+
233
+ .copilot-message-content {
234
+ padding: 12px 16px;
235
+ border-radius: 12px;
236
+ max-width: 85%;
237
+ word-wrap: break-word;
238
+ line-height: 1.5;
239
+ font-size: 14px;
240
+ font-weight: 500;
241
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
242
+ }
243
+
244
+ .copilot-message-user .copilot-message-content {
245
+ background: linear-gradient(135deg, var(--primary-color, #6366f1) 0%,
246
+ rgba(var(--primary-color, 99, 102, 241), 0.9) 100%);
247
+ color: white;
248
+ border-radius: 16px 4px 16px 16px;
249
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
250
+ }
251
+
252
+ .copilot-message-bot .copilot-message-content {
253
+ background: #f3f4f6;
254
+ color: var(--text-color, #1f2937);
255
+ border-radius: 4px 16px 16px 16px;
256
+ border: 1px solid #e5e7eb;
257
+ }
258
+
259
+ .copilot-message-time {
260
+ font-size: 11px;
261
+ color: #9ca3af;
262
+ padding: 0 4px;
263
+ }
264
+
265
+ /* Input Area */
266
+ .copilot-input-area {
267
+ display: flex;
268
+ gap: 10px;
269
+ padding: 14px 16px;
270
+ border-top: 1px solid var(--border-color, #e5e7eb);
271
+ background: linear-gradient(to top, rgba(var(--primary-color, 99, 102, 241), 0.02),
272
+ var(--bg-color, #ffffff));
273
+ backdrop-filter: blur(10px);
274
+ }
275
+
276
+ .copilot-input {
277
+ flex: 1;
278
+ padding: 11px 14px;
279
+ border: 1.5px solid var(--border-color, #e5e7eb);
280
+ border-radius: 10px;
281
+ font-size: 14px;
282
+ font-family: inherit;
283
+ color: var(--text-color, #1f2937);
284
+ background: white;
285
+ transition: var(--transition-smooth);
286
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
287
+ }
288
+
289
+ .copilot-input:focus {
290
+ outline: none;
291
+ border-color: var(--primary-color, #6366f1);
292
+ background: white;
293
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.1), 0 4px 12px rgba(99, 102, 241, 0.2);
294
+ transform: translateY(-1px);
295
+ }
296
+
297
+ .copilot-input::placeholder {
298
+ color: #d1d5db;
299
+ }
300
+
301
+ .copilot-send-btn {
302
+ padding: 11px 16px;
303
+ background: linear-gradient(135deg, var(--primary-color, #6366f1) 0%,
304
+ rgba(var(--primary-color, 99, 102, 241), 0.8) 100%);
305
+ color: white;
306
+ border: none;
307
+ border-radius: 10px;
308
+ cursor: pointer;
309
+ font-weight: 600;
310
+ font-size: 16px;
311
+ transition: var(--transition-smooth);
312
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
313
+ letter-spacing: 0.3px;
314
+ min-width: 50px;
315
+ flex-shrink: 0;
316
+ }
317
+
318
+ .copilot-send-btn:hover {
319
+ background: linear-gradient(135deg, var(--primary-color, #6366f1) 0%,
320
+ rgba(var(--primary-color, 99, 102, 241), 0.7) 100%);
321
+ transform: translateY(-2px);
322
+ box-shadow: 0 6px 16px rgba(99, 102, 241, 0.4);
323
+ }
324
+
325
+ .copilot-send-btn:active {
326
+ transform: translateY(0);
327
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2);
328
+ }
329
+
330
+ .copilot-send-btn:disabled {
331
+ opacity: 0.6;
332
+ cursor: not-allowed;
333
+ transform: none;
334
+ }
335
+
336
+ /* Resize Handle */
337
+ .copilot-resize-handle {
338
+ position: absolute;
339
+ bottom: 0;
340
+ right: 0;
341
+ width: 24px;
342
+ height: 24px;
343
+ cursor: nwse-resize;
344
+ background: linear-gradient(135deg, transparent 50%, var(--primary-color, #6366f1) 50%);
345
+ border-radius: 0 0 16px 0;
346
+ opacity: 0.6;
347
+ transition: var(--transition-smooth);
348
+ }
349
+
350
+ .copilot-resize-handle:hover {
351
+ opacity: 1;
352
+ background: linear-gradient(135deg, transparent 50%, #4f46e5 50%);
353
+ box-shadow: -2px -2px 8px rgba(0, 0, 0, 0.1);
354
+ }
355
+
356
+ /* Scrollbar Styling */
357
+ .copilot-messages::-webkit-scrollbar {
358
+ width: 6px;
359
+ }
360
+
361
+ .copilot-messages::-webkit-scrollbar-track {
362
+ background: rgba(0, 0, 0, 0.03);
363
+ border-radius: 10px;
364
+ }
365
+
366
+ .copilot-messages::-webkit-scrollbar-thumb {
367
+ background: rgba(99, 102, 241, 0.3);
368
+ border-radius: 10px;
369
+ transition: var(--transition-smooth);
370
+ }
371
+
372
+ .copilot-messages::-webkit-scrollbar-thumb:hover {
373
+ background: rgba(99, 102, 241, 0.5);
374
+ }
375
+
376
+ /* Responsive Design */
377
+ @media (max-width: 480px) {
378
+ .floating-copilot {
379
+ width: 100vw !important;
380
+ height: 100vh !important;
381
+ border-radius: 0;
382
+ position: fixed !important;
383
+ top: 0 !important;
384
+ left: 0 !important;
385
+ right: 0 !important;
386
+ bottom: 0 !important;
387
+ }
388
+
389
+ .copilot-message-content {
390
+ max-width: 90%;
391
+ font-size: 15px;
392
+ }
393
+
394
+ .copilot-header {
395
+ padding: 14px 16px;
396
+ }
397
+
398
+ .copilot-input-area {
399
+ padding: 12px 14px;
400
+ }
401
+
402
+ .copilot-resize-handle {
403
+ display: none;
404
+ }
405
+
406
+ .copilot-messages {
407
+ padding: 12px;
408
+ }
409
+
410
+ .copilot-chat-icon {
411
+ width: 56px;
412
+ height: 56px;
413
+ font-size: 24px;
414
+ }
415
+ }
416
+
417
+ /* Dark mode support */
418
+ @media (prefers-color-scheme: dark) {
419
+ .floating-copilot {
420
+ background-color: #1f2937;
421
+ border-color: #374151;
422
+ }
423
+
424
+ .copilot-header {
425
+ border-bottom-color: rgba(0, 0, 0, 0.2);
426
+ }
427
+
428
+ .copilot-messages {
429
+ background: linear-gradient(to bottom, #1f2937 0%, rgba(99, 102, 241, 0.05) 100%);
430
+ }
431
+
432
+ .copilot-input {
433
+ background: #111827;
434
+ color: #f3f4f6;
435
+ border-color: #374151;
436
+ }
437
+
438
+ .copilot-input:focus {
439
+ background: #1f2937;
440
+ }
441
+
442
+ .copilot-input::placeholder {
443
+ color: #6b7280;
444
+ }
445
+
446
+ .copilot-message-bot .copilot-message-content {
447
+ background: #374151;
448
+ color: #f3f4f6;
449
+ border-color: #4b5563;
450
+ }
451
+
452
+ .copilot-empty-state {
453
+ color: #6b7280;
454
+ }
455
+ }
456
+
11
457
  .floating-copilot {
12
458
  display: flex;
13
459
  flex-direction: column;
@@ -1,12 +1,30 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
2
  import { defaultConfig } from './types';
3
3
  import './FloatingCopilot.css';
4
+ // Bot Avatar SVG Component
5
+ const BotAvatar = ({ primaryColor = '#667eea' }) => {
6
+ const secondaryColor = '#764ba2';
7
+ return (React.createElement("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: { display: 'block' } },
8
+ React.createElement("defs", null,
9
+ React.createElement("linearGradient", { id: "botGradient", x1: "0%", y1: "0%", x2: "100%", y2: "100%" },
10
+ React.createElement("stop", { offset: "0%", stopColor: primaryColor }),
11
+ React.createElement("stop", { offset: "100%", stopColor: secondaryColor }))),
12
+ React.createElement("rect", { x: "12", y: "16", width: "40", height: "36", rx: "8", fill: "url(#botGradient)" }),
13
+ React.createElement("circle", { cx: "18", cy: "18", r: "5", fill: "url(#botGradient)" }),
14
+ React.createElement("circle", { cx: "46", cy: "18", r: "5", fill: "url(#botGradient)" }),
15
+ React.createElement("circle", { cx: "24", cy: "26", r: "4", fill: "white" }),
16
+ React.createElement("circle", { cx: "40", cy: "26", r: "4", fill: "white" }),
17
+ React.createElement("circle", { cx: "24", cy: "27", r: "2", fill: "#764ba2" }),
18
+ React.createElement("circle", { cx: "40", cy: "27", r: "2", fill: "#764ba2" }),
19
+ React.createElement("path", { d: "M 26 38 Q 32 42 38 38", stroke: "white", strokeWidth: "1.5", fill: "none", strokeLinecap: "round" }),
20
+ React.createElement("circle", { cx: "32", cy: "32", r: "33", fill: "none", stroke: "url(#botGradient)", strokeWidth: "0.5", opacity: "0.3" })));
21
+ };
4
22
  export const FloatingCopilot = ({ config = {} }) => {
5
- var _a, _b;
23
+ var _a, _b, _c, _d;
6
24
  const finalConfig = Object.assign(Object.assign({}, defaultConfig), config);
7
25
  const [messages, setMessages] = useState([]);
8
26
  const [inputValue, setInputValue] = useState('');
9
- const [isMinimized, setIsMinimized] = useState(finalConfig.minimized || false);
27
+ const [isOpen, setIsOpen] = useState(!finalConfig.minimized);
10
28
  const [isMaximized, setIsMaximized] = useState(finalConfig.maximized || false);
11
29
  const [position, setPosition] = useState({
12
30
  x: ((_a = finalConfig.position) === null || _a === void 0 ? void 0 : _a.includes('left')) ? 20 : 20,
@@ -122,11 +140,13 @@ export const FloatingCopilot = ({ config = {} }) => {
122
140
  setMessages((prev) => [...prev, botMessage]);
123
141
  }, 500);
124
142
  };
125
- // Handle minimize
126
- const handleMinimize = () => {
143
+ // Handle toggle open/close
144
+ const handleToggleOpen = () => {
127
145
  var _a;
128
- setIsMinimized(!isMinimized);
129
- (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
146
+ setIsOpen(!isOpen);
147
+ if (!isOpen) {
148
+ (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
149
+ }
130
150
  };
131
151
  // Handle maximize
132
152
  const handleMaximize = () => {
@@ -137,6 +157,7 @@ export const FloatingCopilot = ({ config = {} }) => {
137
157
  // Handle close
138
158
  const handleClose = () => {
139
159
  var _a;
160
+ setIsOpen(false);
140
161
  (_a = finalConfig.onClose) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
141
162
  };
142
163
  const getPositionStyle = () => {
@@ -179,30 +200,38 @@ export const FloatingCopilot = ({ config = {} }) => {
179
200
  '--border-color': (_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.borderColor,
180
201
  });
181
202
  };
182
- return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''} ${isMinimized ? 'minimized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
183
- finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable ? 'grab' : 'default' } },
203
+ // Closed state - show only chat icon
204
+ if (!isOpen) {
205
+ return (React.createElement("div", { style: Object.assign(Object.assign({}, getPositionStyle()), getThemeStyle()), className: "copilot-wrapper" },
206
+ React.createElement("button", { className: "copilot-chat-icon", onClick: handleToggleOpen, title: "Open chat", "aria-label": "Open chat" },
207
+ React.createElement(BotAvatar, { primaryColor: ((_c = finalConfig.theme) === null || _c === void 0 ? void 0 : _c.primaryColor) || '#667eea' }))));
208
+ }
209
+ // Open state - show full chat
210
+ return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
211
+ finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable && !isMaximized ? 'grab' : 'default' } },
184
212
  React.createElement("h3", { className: "copilot-title" }, finalConfig.headerContent || finalConfig.title),
185
213
  React.createElement("div", { className: "copilot-buttons" },
186
- finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleMinimize, title: "Minimize" }, isMinimized ? '▲' : '▼')),
187
- finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: "Maximize" }, isMaximized ? '' : '')),
188
- finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close" }, "\u2715"))))),
189
- !isMinimized && (React.createElement(React.Fragment, null,
190
- React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
191
- React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
192
- messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
193
- React.createElement("div", { className: "copilot-message-content" }, msg.text),
194
- React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
195
- hour: '2-digit',
196
- minute: '2-digit',
197
- }))))),
198
- React.createElement("div", { ref: messagesEndRef })))),
199
- React.createElement("div", { className: "copilot-input-area" },
200
- React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
201
- if (e.key === 'Enter') {
202
- handleSendMessage();
203
- }
204
- } }),
205
- React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage }, "Send")),
206
- finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))))));
214
+ finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleToggleOpen, title: "Minimize", "aria-label": "Minimize" }, "\u2212")),
215
+ finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: isMaximized ? 'Restore' : 'Maximize', "aria-label": isMaximized ? 'Restore' : 'Maximize' }, isMaximized ? '' : '')),
216
+ finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close", "aria-label": "Close" }, "\u2715"))))),
217
+ React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
218
+ React.createElement("div", { className: "copilot-empty-icon" },
219
+ React.createElement(BotAvatar, { primaryColor: ((_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.primaryColor) || '#667eea' })),
220
+ React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
221
+ messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
222
+ React.createElement("div", { className: "copilot-message-content" }, msg.text),
223
+ React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
224
+ hour: '2-digit',
225
+ minute: '2-digit',
226
+ }))))),
227
+ React.createElement("div", { ref: messagesEndRef })))),
228
+ React.createElement("div", { className: "copilot-input-area" },
229
+ React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
230
+ if (e.key === 'Enter') {
231
+ handleSendMessage();
232
+ }
233
+ } }),
234
+ React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage, "aria-label": "Send message" }, "\u23CE")),
235
+ finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))));
207
236
  };
208
237
  export default FloatingCopilot;
package/dist/index.esm.js CHANGED
@@ -23,12 +23,30 @@ const defaultConfig = {
23
23
  },
24
24
  };
25
25
 
26
+ // Bot Avatar SVG Component
27
+ const BotAvatar = ({ primaryColor = '#667eea' }) => {
28
+ const secondaryColor = '#764ba2';
29
+ return (React.createElement("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: { display: 'block' } },
30
+ React.createElement("defs", null,
31
+ React.createElement("linearGradient", { id: "botGradient", x1: "0%", y1: "0%", x2: "100%", y2: "100%" },
32
+ React.createElement("stop", { offset: "0%", stopColor: primaryColor }),
33
+ React.createElement("stop", { offset: "100%", stopColor: secondaryColor }))),
34
+ React.createElement("rect", { x: "12", y: "16", width: "40", height: "36", rx: "8", fill: "url(#botGradient)" }),
35
+ React.createElement("circle", { cx: "18", cy: "18", r: "5", fill: "url(#botGradient)" }),
36
+ React.createElement("circle", { cx: "46", cy: "18", r: "5", fill: "url(#botGradient)" }),
37
+ React.createElement("circle", { cx: "24", cy: "26", r: "4", fill: "white" }),
38
+ React.createElement("circle", { cx: "40", cy: "26", r: "4", fill: "white" }),
39
+ React.createElement("circle", { cx: "24", cy: "27", r: "2", fill: "#764ba2" }),
40
+ React.createElement("circle", { cx: "40", cy: "27", r: "2", fill: "#764ba2" }),
41
+ React.createElement("path", { d: "M 26 38 Q 32 42 38 38", stroke: "white", strokeWidth: "1.5", fill: "none", strokeLinecap: "round" }),
42
+ React.createElement("circle", { cx: "32", cy: "32", r: "33", fill: "none", stroke: "url(#botGradient)", strokeWidth: "0.5", opacity: "0.3" })));
43
+ };
26
44
  const FloatingCopilot = ({ config = {} }) => {
27
- var _a, _b;
45
+ var _a, _b, _c, _d;
28
46
  const finalConfig = Object.assign(Object.assign({}, defaultConfig), config);
29
47
  const [messages, setMessages] = useState([]);
30
48
  const [inputValue, setInputValue] = useState('');
31
- const [isMinimized, setIsMinimized] = useState(finalConfig.minimized || false);
49
+ const [isOpen, setIsOpen] = useState(!finalConfig.minimized);
32
50
  const [isMaximized, setIsMaximized] = useState(finalConfig.maximized || false);
33
51
  const [position, setPosition] = useState({
34
52
  x: ((_a = finalConfig.position) === null || _a === void 0 ? void 0 : _a.includes('left')) ? 20 : 20,
@@ -144,11 +162,13 @@ const FloatingCopilot = ({ config = {} }) => {
144
162
  setMessages((prev) => [...prev, botMessage]);
145
163
  }, 500);
146
164
  };
147
- // Handle minimize
148
- const handleMinimize = () => {
165
+ // Handle toggle open/close
166
+ const handleToggleOpen = () => {
149
167
  var _a;
150
- setIsMinimized(!isMinimized);
151
- (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
168
+ setIsOpen(!isOpen);
169
+ if (!isOpen) {
170
+ (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
171
+ }
152
172
  };
153
173
  // Handle maximize
154
174
  const handleMaximize = () => {
@@ -159,6 +179,7 @@ const FloatingCopilot = ({ config = {} }) => {
159
179
  // Handle close
160
180
  const handleClose = () => {
161
181
  var _a;
182
+ setIsOpen(false);
162
183
  (_a = finalConfig.onClose) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
163
184
  };
164
185
  const getPositionStyle = () => {
@@ -201,31 +222,39 @@ const FloatingCopilot = ({ config = {} }) => {
201
222
  '--border-color': (_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.borderColor,
202
223
  });
203
224
  };
204
- return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''} ${isMinimized ? 'minimized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
205
- finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable ? 'grab' : 'default' } },
225
+ // Closed state - show only chat icon
226
+ if (!isOpen) {
227
+ return (React.createElement("div", { style: Object.assign(Object.assign({}, getPositionStyle()), getThemeStyle()), className: "copilot-wrapper" },
228
+ React.createElement("button", { className: "copilot-chat-icon", onClick: handleToggleOpen, title: "Open chat", "aria-label": "Open chat" },
229
+ React.createElement(BotAvatar, { primaryColor: ((_c = finalConfig.theme) === null || _c === void 0 ? void 0 : _c.primaryColor) || '#667eea' }))));
230
+ }
231
+ // Open state - show full chat
232
+ return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
233
+ finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable && !isMaximized ? 'grab' : 'default' } },
206
234
  React.createElement("h3", { className: "copilot-title" }, finalConfig.headerContent || finalConfig.title),
207
235
  React.createElement("div", { className: "copilot-buttons" },
208
- finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleMinimize, title: "Minimize" }, isMinimized ? '▲' : '▼')),
209
- finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: "Maximize" }, isMaximized ? '' : '')),
210
- finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close" }, "\u2715"))))),
211
- !isMinimized && (React.createElement(React.Fragment, null,
212
- React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
213
- React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
214
- messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
215
- React.createElement("div", { className: "copilot-message-content" }, msg.text),
216
- React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
217
- hour: '2-digit',
218
- minute: '2-digit',
219
- }))))),
220
- React.createElement("div", { ref: messagesEndRef })))),
221
- React.createElement("div", { className: "copilot-input-area" },
222
- React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
223
- if (e.key === 'Enter') {
224
- handleSendMessage();
225
- }
226
- } }),
227
- React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage }, "Send")),
228
- finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))))));
236
+ finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleToggleOpen, title: "Minimize", "aria-label": "Minimize" }, "\u2212")),
237
+ finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: isMaximized ? 'Restore' : 'Maximize', "aria-label": isMaximized ? 'Restore' : 'Maximize' }, isMaximized ? '' : '')),
238
+ finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close", "aria-label": "Close" }, "\u2715"))))),
239
+ React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
240
+ React.createElement("div", { className: "copilot-empty-icon" },
241
+ React.createElement(BotAvatar, { primaryColor: ((_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.primaryColor) || '#667eea' })),
242
+ React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
243
+ messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
244
+ React.createElement("div", { className: "copilot-message-content" }, msg.text),
245
+ React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
246
+ hour: '2-digit',
247
+ minute: '2-digit',
248
+ }))))),
249
+ React.createElement("div", { ref: messagesEndRef })))),
250
+ React.createElement("div", { className: "copilot-input-area" },
251
+ React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
252
+ if (e.key === 'Enter') {
253
+ handleSendMessage();
254
+ }
255
+ } }),
256
+ React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage, "aria-label": "Send message" }, "\u23CE")),
257
+ finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))));
229
258
  };
230
259
 
231
260
  export { FloatingCopilot, FloatingCopilot as default, defaultConfig };
package/dist/index.js CHANGED
@@ -27,12 +27,30 @@ const defaultConfig = {
27
27
  },
28
28
  };
29
29
 
30
+ // Bot Avatar SVG Component
31
+ const BotAvatar = ({ primaryColor = '#667eea' }) => {
32
+ const secondaryColor = '#764ba2';
33
+ return (React.createElement("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", style: { display: 'block' } },
34
+ React.createElement("defs", null,
35
+ React.createElement("linearGradient", { id: "botGradient", x1: "0%", y1: "0%", x2: "100%", y2: "100%" },
36
+ React.createElement("stop", { offset: "0%", stopColor: primaryColor }),
37
+ React.createElement("stop", { offset: "100%", stopColor: secondaryColor }))),
38
+ React.createElement("rect", { x: "12", y: "16", width: "40", height: "36", rx: "8", fill: "url(#botGradient)" }),
39
+ React.createElement("circle", { cx: "18", cy: "18", r: "5", fill: "url(#botGradient)" }),
40
+ React.createElement("circle", { cx: "46", cy: "18", r: "5", fill: "url(#botGradient)" }),
41
+ React.createElement("circle", { cx: "24", cy: "26", r: "4", fill: "white" }),
42
+ React.createElement("circle", { cx: "40", cy: "26", r: "4", fill: "white" }),
43
+ React.createElement("circle", { cx: "24", cy: "27", r: "2", fill: "#764ba2" }),
44
+ React.createElement("circle", { cx: "40", cy: "27", r: "2", fill: "#764ba2" }),
45
+ React.createElement("path", { d: "M 26 38 Q 32 42 38 38", stroke: "white", strokeWidth: "1.5", fill: "none", strokeLinecap: "round" }),
46
+ React.createElement("circle", { cx: "32", cy: "32", r: "33", fill: "none", stroke: "url(#botGradient)", strokeWidth: "0.5", opacity: "0.3" })));
47
+ };
30
48
  const FloatingCopilot = ({ config = {} }) => {
31
- var _a, _b;
49
+ var _a, _b, _c, _d;
32
50
  const finalConfig = Object.assign(Object.assign({}, defaultConfig), config);
33
51
  const [messages, setMessages] = React.useState([]);
34
52
  const [inputValue, setInputValue] = React.useState('');
35
- const [isMinimized, setIsMinimized] = React.useState(finalConfig.minimized || false);
53
+ const [isOpen, setIsOpen] = React.useState(!finalConfig.minimized);
36
54
  const [isMaximized, setIsMaximized] = React.useState(finalConfig.maximized || false);
37
55
  const [position, setPosition] = React.useState({
38
56
  x: ((_a = finalConfig.position) === null || _a === void 0 ? void 0 : _a.includes('left')) ? 20 : 20,
@@ -148,11 +166,13 @@ const FloatingCopilot = ({ config = {} }) => {
148
166
  setMessages((prev) => [...prev, botMessage]);
149
167
  }, 500);
150
168
  };
151
- // Handle minimize
152
- const handleMinimize = () => {
169
+ // Handle toggle open/close
170
+ const handleToggleOpen = () => {
153
171
  var _a;
154
- setIsMinimized(!isMinimized);
155
- (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
172
+ setIsOpen(!isOpen);
173
+ if (!isOpen) {
174
+ (_a = finalConfig.onMinimize) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
175
+ }
156
176
  };
157
177
  // Handle maximize
158
178
  const handleMaximize = () => {
@@ -163,6 +183,7 @@ const FloatingCopilot = ({ config = {} }) => {
163
183
  // Handle close
164
184
  const handleClose = () => {
165
185
  var _a;
186
+ setIsOpen(false);
166
187
  (_a = finalConfig.onClose) === null || _a === void 0 ? void 0 : _a.call(finalConfig);
167
188
  };
168
189
  const getPositionStyle = () => {
@@ -205,31 +226,39 @@ const FloatingCopilot = ({ config = {} }) => {
205
226
  '--border-color': (_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.borderColor,
206
227
  });
207
228
  };
208
- return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''} ${isMinimized ? 'minimized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
209
- finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable ? 'grab' : 'default' } },
229
+ // Closed state - show only chat icon
230
+ if (!isOpen) {
231
+ return (React.createElement("div", { style: Object.assign(Object.assign({}, getPositionStyle()), getThemeStyle()), className: "copilot-wrapper" },
232
+ React.createElement("button", { className: "copilot-chat-icon", onClick: handleToggleOpen, title: "Open chat", "aria-label": "Open chat" },
233
+ React.createElement(BotAvatar, { primaryColor: ((_c = finalConfig.theme) === null || _c === void 0 ? void 0 : _c.primaryColor) || '#667eea' }))));
234
+ }
235
+ // Open state - show full chat
236
+ return (React.createElement("div", { ref: containerRef, className: `floating-copilot ${isMaximized ? 'maximized' : ''}`, style: Object.assign(Object.assign(Object.assign({}, getPositionStyle()), { width: isMaximized ? '100%' : size.width, height: isMaximized ? '100%' : size.height }), getThemeStyle()) },
237
+ finalConfig.showHeader && (React.createElement("div", { className: "copilot-header", onMouseDown: handleMouseDownDrag, style: { cursor: finalConfig.draggable && !isMaximized ? 'grab' : 'default' } },
210
238
  React.createElement("h3", { className: "copilot-title" }, finalConfig.headerContent || finalConfig.title),
211
239
  React.createElement("div", { className: "copilot-buttons" },
212
- finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleMinimize, title: "Minimize" }, isMinimized ? '▲' : '▼')),
213
- finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: "Maximize" }, isMaximized ? '' : '')),
214
- finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close" }, "\u2715"))))),
215
- !isMinimized && (React.createElement(React.Fragment, null,
216
- React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
217
- React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
218
- messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
219
- React.createElement("div", { className: "copilot-message-content" }, msg.text),
220
- React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
221
- hour: '2-digit',
222
- minute: '2-digit',
223
- }))))),
224
- React.createElement("div", { ref: messagesEndRef })))),
225
- React.createElement("div", { className: "copilot-input-area" },
226
- React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
227
- if (e.key === 'Enter') {
228
- handleSendMessage();
229
- }
230
- } }),
231
- React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage }, "Send")),
232
- finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))))));
240
+ finalConfig.showMinimizeBtn && (React.createElement("button", { className: "copilot-btn copilot-minimize-btn", onClick: handleToggleOpen, title: "Minimize", "aria-label": "Minimize" }, "\u2212")),
241
+ finalConfig.showMaximizeBtn && (React.createElement("button", { className: "copilot-btn copilot-maximize-btn", onClick: handleMaximize, title: isMaximized ? 'Restore' : 'Maximize', "aria-label": isMaximized ? 'Restore' : 'Maximize' }, isMaximized ? '' : '')),
242
+ finalConfig.showCloseBtn && (React.createElement("button", { className: "copilot-btn copilot-close-btn", onClick: handleClose, title: "Close", "aria-label": "Close" }, "\u2715"))))),
243
+ React.createElement("div", { className: "copilot-messages" }, messages.length === 0 ? (React.createElement("div", { className: "copilot-empty-state" },
244
+ React.createElement("div", { className: "copilot-empty-icon" },
245
+ React.createElement(BotAvatar, { primaryColor: ((_d = finalConfig.theme) === null || _d === void 0 ? void 0 : _d.primaryColor) || '#667eea' })),
246
+ React.createElement("p", null, "Start a conversation..."))) : (React.createElement(React.Fragment, null,
247
+ messages.map((msg) => (React.createElement("div", { key: msg.id, className: `copilot-message copilot-message-${msg.sender}` },
248
+ React.createElement("div", { className: "copilot-message-content" }, msg.text),
249
+ React.createElement("span", { className: "copilot-message-time" }, msg.timestamp.toLocaleTimeString([], {
250
+ hour: '2-digit',
251
+ minute: '2-digit',
252
+ }))))),
253
+ React.createElement("div", { ref: messagesEndRef })))),
254
+ React.createElement("div", { className: "copilot-input-area" },
255
+ React.createElement("input", { type: "text", className: "copilot-input", placeholder: finalConfig.placeholder, value: inputValue, onChange: (e) => setInputValue(e.target.value), onKeyPress: (e) => {
256
+ if (e.key === 'Enter') {
257
+ handleSendMessage();
258
+ }
259
+ } }),
260
+ React.createElement("button", { className: "copilot-send-btn", onClick: handleSendMessage, "aria-label": "Send message" }, "\u23CE")),
261
+ finalConfig.resizable && !isMaximized && (React.createElement("div", { className: "copilot-resize-handle", onMouseDown: handleMouseDownResize, title: "Drag to resize" }))));
233
262
  };
234
263
 
235
264
  exports.FloatingCopilot = FloatingCopilot;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floating-copilot-widget",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A highly configurable floating chat widget plugin for React websites. Draggable, resizable, themeable, and production-ready.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",