@aspect-ops/exon-ui 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +341 -16
  2. package/dist/components/Accordion/Accordion.svelte +2 -2
  3. package/dist/components/Accordion/AccordionItem.svelte +2 -2
  4. package/dist/components/Chatbot/ChatMessage.svelte +143 -0
  5. package/dist/components/Chatbot/ChatMessage.svelte.d.ts +8 -0
  6. package/dist/components/Chatbot/Chatbot.svelte +640 -0
  7. package/dist/components/Chatbot/Chatbot.svelte.d.ts +22 -0
  8. package/dist/components/Chatbot/index.d.ts +3 -0
  9. package/dist/components/Chatbot/index.js +2 -0
  10. package/dist/components/Chatbot/types.d.ts +48 -0
  11. package/dist/components/Chatbot/types.js +2 -0
  12. package/dist/components/ContactForm/ContactForm.svelte +564 -0
  13. package/dist/components/ContactForm/ContactForm.svelte.d.ts +44 -0
  14. package/dist/components/ContactForm/index.d.ts +1 -0
  15. package/dist/components/ContactForm/index.js +1 -0
  16. package/dist/components/DoughnutChart/DoughnutChart.svelte +372 -0
  17. package/dist/components/DoughnutChart/DoughnutChart.svelte.d.ts +25 -0
  18. package/dist/components/DoughnutChart/index.d.ts +1 -0
  19. package/dist/components/DoughnutChart/index.js +1 -0
  20. package/dist/components/FAB/FAB.svelte +5 -1
  21. package/dist/components/FAB/FABGroup.svelte +10 -2
  22. package/dist/components/FileUpload/FileUpload.svelte +12 -12
  23. package/dist/components/Mermaid/Mermaid.svelte +206 -0
  24. package/dist/components/Mermaid/Mermaid.svelte.d.ts +28 -0
  25. package/dist/components/Mermaid/index.d.ts +1 -0
  26. package/dist/components/Mermaid/index.js +1 -0
  27. package/dist/components/Mermaid/mermaid.d.ts +21 -0
  28. package/dist/components/NumberInput/NumberInput.svelte +293 -0
  29. package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
  30. package/dist/components/NumberInput/index.d.ts +1 -0
  31. package/dist/components/NumberInput/index.js +1 -0
  32. package/dist/components/Pagination/Pagination.svelte +243 -0
  33. package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
  34. package/dist/components/Pagination/index.d.ts +1 -0
  35. package/dist/components/Pagination/index.js +1 -0
  36. package/dist/components/Popover/PopoverTrigger.svelte +1 -3
  37. package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
  38. package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
  39. package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
  40. package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
  41. package/dist/components/ToggleGroup/index.d.ts +3 -0
  42. package/dist/components/ToggleGroup/index.js +2 -0
  43. package/dist/components/ViewCounter/ViewCounter.svelte +157 -0
  44. package/dist/components/ViewCounter/ViewCounter.svelte.d.ts +17 -0
  45. package/dist/components/ViewCounter/index.d.ts +1 -0
  46. package/dist/components/ViewCounter/index.js +1 -0
  47. package/dist/index.d.ts +13 -1
  48. package/dist/index.js +12 -0
  49. package/dist/styles/tokens.css +2 -1
  50. package/dist/types/index.d.ts +1 -1
  51. package/dist/types/input.d.ts +35 -0
  52. package/package.json +2 -1
@@ -0,0 +1,640 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import ChatMessage from './ChatMessage.svelte';
4
+ import type { ChatMessage as ChatMessageType, LeadData } from './types.js';
5
+
6
+ interface Props {
7
+ initialMessages?: ChatMessageType[];
8
+ sessionId?: string;
9
+ placeholder?: string;
10
+ welcomeMessage?: string;
11
+ title?: string;
12
+ subtitle?: string;
13
+ defaultOpen?: boolean;
14
+ inactivityTimeout?: number;
15
+ showTypingIndicator?: boolean;
16
+ position?: 'bottom-right' | 'bottom-left';
17
+ class?: string;
18
+ onSendMessage?: (message: string, sessionId: string) => Promise<string | null>;
19
+ onEscalate?: (sessionId: string, messages: ChatMessageType[]) => Promise<void>;
20
+ onLeadCapture?: (data: LeadData, sessionId: string) => Promise<void>;
21
+ onOpen?: () => void;
22
+ onClose?: () => void;
23
+ }
24
+
25
+ let {
26
+ initialMessages = [],
27
+ sessionId: propSessionId,
28
+ placeholder = 'Type your message...',
29
+ welcomeMessage = 'Hello! How can I help you today?',
30
+ title = 'Chat Support',
31
+ subtitle = 'We typically reply within minutes',
32
+ defaultOpen = false,
33
+ inactivityTimeout = 40000,
34
+ showTypingIndicator = true,
35
+ position = 'bottom-right',
36
+ class: className = '',
37
+ onSendMessage,
38
+ onEscalate,
39
+ onLeadCapture,
40
+ onOpen,
41
+ onClose
42
+ }: Props = $props();
43
+
44
+ // State - intentionally capture initial values (not reactive to prop changes)
45
+ // eslint-disable-next-line svelte/valid-compile
46
+ let isOpen = $state(defaultOpen);
47
+ let isMinimized = $state(false);
48
+ // eslint-disable-next-line svelte/valid-compile
49
+ let messages = $state<ChatMessageType[]>([...initialMessages]);
50
+ let inputValue = $state('');
51
+ let isTyping = $state(false);
52
+ // eslint-disable-next-line svelte/valid-compile
53
+ let sessionId = $state(propSessionId || generateSessionId());
54
+ let messagesContainer: HTMLDivElement | undefined = $state();
55
+ let textareaRef: HTMLTextAreaElement | undefined = $state();
56
+ let inactivityTimer: ReturnType<typeof setTimeout> | null = null;
57
+
58
+ // Generate session ID
59
+ function generateSessionId(): string {
60
+ return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
61
+ }
62
+
63
+ // Create message helper
64
+ function createMessage(role: ChatMessageType['role'], content: string): ChatMessageType {
65
+ return {
66
+ id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
67
+ role,
68
+ content,
69
+ timestamp: new Date()
70
+ };
71
+ }
72
+
73
+ // Auto-resize textarea
74
+ function autoResize() {
75
+ if (textareaRef) {
76
+ textareaRef.style.height = 'auto';
77
+ textareaRef.style.height = Math.min(textareaRef.scrollHeight, 120) + 'px';
78
+ }
79
+ }
80
+
81
+ // Scroll to bottom
82
+ function scrollToBottom() {
83
+ if (messagesContainer) {
84
+ setTimeout(() => {
85
+ if (messagesContainer) {
86
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
87
+ }
88
+ }, 50);
89
+ }
90
+ }
91
+
92
+ // Handle send message
93
+ async function handleSend() {
94
+ const content = inputValue.trim();
95
+ if (!content) return;
96
+
97
+ // Add user message
98
+ const userMessage = createMessage('user', content);
99
+ messages = [...messages, userMessage];
100
+ inputValue = '';
101
+ if (textareaRef) {
102
+ textareaRef.style.height = 'auto';
103
+ }
104
+ scrollToBottom();
105
+
106
+ // Show typing indicator
107
+ if (showTypingIndicator && onSendMessage) {
108
+ isTyping = true;
109
+ }
110
+
111
+ // Call onSendMessage callback
112
+ if (onSendMessage) {
113
+ try {
114
+ const response = await onSendMessage(content, sessionId);
115
+ if (response) {
116
+ const assistantMessage = createMessage('assistant', response);
117
+ messages = [...messages, assistantMessage];
118
+ }
119
+ } catch (error) {
120
+ const errorMessage = createMessage(
121
+ 'system',
122
+ 'Sorry, something went wrong. Please try again.'
123
+ );
124
+ messages = [...messages, errorMessage];
125
+ } finally {
126
+ isTyping = false;
127
+ scrollToBottom();
128
+ }
129
+ }
130
+ }
131
+
132
+ // Handle key press
133
+ function handleKeyDown(event: KeyboardEvent) {
134
+ if (event.key === 'Enter' && !event.shiftKey) {
135
+ event.preventDefault();
136
+ handleSend();
137
+ }
138
+ }
139
+
140
+ // Handle escalation
141
+ async function handleEscalate() {
142
+ if (onEscalate) {
143
+ const systemMessage = createMessage('system', 'Connecting you to a human agent...');
144
+ messages = [...messages, systemMessage];
145
+ scrollToBottom();
146
+
147
+ try {
148
+ await onEscalate(sessionId, messages);
149
+ const successMessage = createMessage('system', 'A human agent will be with you shortly.');
150
+ messages = [...messages, successMessage];
151
+ } catch {
152
+ const errorMessage = createMessage(
153
+ 'system',
154
+ 'Unable to connect to an agent. Please try again later.'
155
+ );
156
+ messages = [...messages, errorMessage];
157
+ }
158
+ scrollToBottom();
159
+ }
160
+ }
161
+
162
+ // Open chat
163
+ function openChat() {
164
+ isOpen = true;
165
+ isMinimized = false;
166
+
167
+ // Add welcome message if no messages
168
+ if (messages.length === 0 && welcomeMessage) {
169
+ const welcome = createMessage('assistant', welcomeMessage);
170
+ messages = [welcome];
171
+ }
172
+
173
+ onOpen?.();
174
+ scrollToBottom();
175
+ }
176
+
177
+ // Close chat
178
+ function closeChat() {
179
+ isOpen = false;
180
+ isMinimized = false;
181
+ onClose?.();
182
+ }
183
+
184
+ // Minimize chat
185
+ function minimizeChat() {
186
+ isMinimized = true;
187
+ }
188
+
189
+ // Restore from minimized
190
+ function restoreChat() {
191
+ isMinimized = false;
192
+ scrollToBottom();
193
+ }
194
+
195
+ // Reset inactivity timer
196
+ function resetInactivityTimer() {
197
+ if (inactivityTimer) {
198
+ clearTimeout(inactivityTimer);
199
+ inactivityTimer = null;
200
+ }
201
+
202
+ if (inactivityTimeout > 0 && !isOpen) {
203
+ inactivityTimer = setTimeout(() => {
204
+ openChat();
205
+ }, inactivityTimeout);
206
+ }
207
+ }
208
+
209
+ // Setup inactivity detection
210
+ onMount(() => {
211
+ if (inactivityTimeout > 0 && !defaultOpen) {
212
+ resetInactivityTimer();
213
+
214
+ const events = ['mousemove', 'keydown', 'scroll', 'click'];
215
+ events.forEach((event) => {
216
+ document.addEventListener(event, resetInactivityTimer, { passive: true });
217
+ });
218
+
219
+ return () => {
220
+ events.forEach((event) => {
221
+ document.removeEventListener(event, resetInactivityTimer);
222
+ });
223
+ };
224
+ }
225
+ });
226
+
227
+ onDestroy(() => {
228
+ if (inactivityTimer) {
229
+ clearTimeout(inactivityTimer);
230
+ }
231
+ });
232
+
233
+ // Scroll on new messages
234
+ $effect(() => {
235
+ if (messages.length > 0) {
236
+ scrollToBottom();
237
+ }
238
+ });
239
+ </script>
240
+
241
+ <div
242
+ class="chatbot chatbot--{position} {className}"
243
+ class:chatbot--open={isOpen}
244
+ class:chatbot--minimized={isMinimized}
245
+ >
246
+ {#if isOpen}
247
+ <div class="chatbot__window">
248
+ <!-- Header -->
249
+ <div class="chatbot__header">
250
+ <div class="chatbot__header-info">
251
+ <div class="chatbot__title">{title}</div>
252
+ <div class="chatbot__subtitle">{subtitle}</div>
253
+ </div>
254
+ <div class="chatbot__header-actions">
255
+ <button
256
+ type="button"
257
+ class="chatbot__header-btn"
258
+ onclick={minimizeChat}
259
+ aria-label="Minimize chat"
260
+ >
261
+ <svg
262
+ xmlns="http://www.w3.org/2000/svg"
263
+ width="18"
264
+ height="18"
265
+ viewBox="0 0 24 24"
266
+ fill="none"
267
+ stroke="currentColor"
268
+ stroke-width="2"
269
+ stroke-linecap="round"
270
+ stroke-linejoin="round"
271
+ >
272
+ <path d="M5 12h14" />
273
+ </svg>
274
+ </button>
275
+ <button
276
+ type="button"
277
+ class="chatbot__header-btn"
278
+ onclick={closeChat}
279
+ aria-label="Close chat"
280
+ >
281
+ <svg
282
+ xmlns="http://www.w3.org/2000/svg"
283
+ width="18"
284
+ height="18"
285
+ viewBox="0 0 24 24"
286
+ fill="none"
287
+ stroke="currentColor"
288
+ stroke-width="2"
289
+ stroke-linecap="round"
290
+ stroke-linejoin="round"
291
+ >
292
+ <path d="M18 6L6 18" />
293
+ <path d="M6 6l12 12" />
294
+ </svg>
295
+ </button>
296
+ </div>
297
+ </div>
298
+
299
+ {#if !isMinimized}
300
+ <!-- Messages -->
301
+ <div class="chatbot__messages" bind:this={messagesContainer}>
302
+ {#each messages as message (message.id)}
303
+ <ChatMessage {message} />
304
+ {/each}
305
+
306
+ {#if isTyping}
307
+ <div class="chatbot__typing">
308
+ <div class="chatbot__typing-dot"></div>
309
+ <div class="chatbot__typing-dot"></div>
310
+ <div class="chatbot__typing-dot"></div>
311
+ </div>
312
+ {/if}
313
+ </div>
314
+
315
+ <!-- Quick Actions -->
316
+ {#if onEscalate}
317
+ <div class="chatbot__actions">
318
+ <button type="button" class="chatbot__action-btn" onclick={handleEscalate}>
319
+ Talk to human
320
+ </button>
321
+ </div>
322
+ {/if}
323
+
324
+ <!-- Input -->
325
+ <div class="chatbot__input-container">
326
+ <textarea
327
+ bind:this={textareaRef}
328
+ bind:value={inputValue}
329
+ {placeholder}
330
+ class="chatbot__input"
331
+ rows="1"
332
+ oninput={autoResize}
333
+ onkeydown={handleKeyDown}
334
+ ></textarea>
335
+ <button
336
+ type="button"
337
+ class="chatbot__send-btn"
338
+ onclick={handleSend}
339
+ disabled={!inputValue.trim()}
340
+ aria-label="Send message"
341
+ >
342
+ <svg
343
+ xmlns="http://www.w3.org/2000/svg"
344
+ width="20"
345
+ height="20"
346
+ viewBox="0 0 24 24"
347
+ fill="none"
348
+ stroke="currentColor"
349
+ stroke-width="2"
350
+ stroke-linecap="round"
351
+ stroke-linejoin="round"
352
+ >
353
+ <path d="M22 2L11 13" />
354
+ <path d="M22 2l-7 20-4-9-9-4 20-7z" />
355
+ </svg>
356
+ </button>
357
+ </div>
358
+ {/if}
359
+ </div>
360
+ {:else}
361
+ <!-- Launcher Button -->
362
+ <button type="button" class="chatbot__launcher" onclick={openChat} aria-label="Open chat">
363
+ <svg
364
+ xmlns="http://www.w3.org/2000/svg"
365
+ width="24"
366
+ height="24"
367
+ viewBox="0 0 24 24"
368
+ fill="none"
369
+ stroke="currentColor"
370
+ stroke-width="2"
371
+ stroke-linecap="round"
372
+ stroke-linejoin="round"
373
+ >
374
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
375
+ </svg>
376
+ </button>
377
+ {/if}
378
+ </div>
379
+
380
+ <style>
381
+ .chatbot {
382
+ position: fixed;
383
+ z-index: 9999;
384
+ font-family: inherit;
385
+ }
386
+
387
+ .chatbot--bottom-right {
388
+ bottom: var(--space-lg, 1.5rem);
389
+ right: var(--space-lg, 1.5rem);
390
+ }
391
+
392
+ .chatbot--bottom-left {
393
+ bottom: var(--space-lg, 1.5rem);
394
+ left: var(--space-lg, 1.5rem);
395
+ }
396
+
397
+ /* Launcher */
398
+ .chatbot__launcher {
399
+ width: 3.5rem;
400
+ height: 3.5rem;
401
+ border-radius: var(--radius-full, 9999px);
402
+ border: none;
403
+ background: var(--color-primary, #3b82f6);
404
+ color: var(--color-text-inverse, #ffffff);
405
+ cursor: pointer;
406
+ display: flex;
407
+ align-items: center;
408
+ justify-content: center;
409
+ box-shadow:
410
+ 0 4px 6px -1px rgba(0, 0, 0, 0.1),
411
+ 0 2px 4px -2px rgba(0, 0, 0, 0.1);
412
+ transition: all var(--transition-base, 200ms ease);
413
+ }
414
+
415
+ .chatbot__launcher:hover {
416
+ background: var(--color-primary-hover, #2563eb);
417
+ transform: scale(1.05);
418
+ }
419
+
420
+ .chatbot__launcher:focus-visible {
421
+ outline: 2px solid var(--color-primary, #3b82f6);
422
+ outline-offset: 2px;
423
+ }
424
+
425
+ /* Window */
426
+ .chatbot__window {
427
+ width: 380px;
428
+ max-width: calc(100vw - 2rem);
429
+ height: 500px;
430
+ max-height: calc(100vh - 6rem);
431
+ background: var(--color-bg, #ffffff);
432
+ border-radius: var(--radius-xl, 1rem);
433
+ box-shadow:
434
+ 0 25px 50px -12px rgba(0, 0, 0, 0.25),
435
+ 0 0 0 1px rgba(0, 0, 0, 0.05);
436
+ display: flex;
437
+ flex-direction: column;
438
+ overflow: hidden;
439
+ }
440
+
441
+ .chatbot--minimized .chatbot__window {
442
+ height: auto;
443
+ }
444
+
445
+ /* Header */
446
+ .chatbot__header {
447
+ display: flex;
448
+ align-items: center;
449
+ justify-content: space-between;
450
+ padding: var(--space-md, 1rem);
451
+ background: var(--color-primary, #3b82f6);
452
+ color: var(--color-text-inverse, #ffffff);
453
+ }
454
+
455
+ .chatbot__header-info {
456
+ flex: 1;
457
+ }
458
+
459
+ .chatbot__title {
460
+ font-size: var(--text-base, 1rem);
461
+ font-weight: var(--font-semibold, 600);
462
+ }
463
+
464
+ .chatbot__subtitle {
465
+ font-size: var(--text-xs, 0.75rem);
466
+ opacity: 0.9;
467
+ }
468
+
469
+ .chatbot__header-actions {
470
+ display: flex;
471
+ gap: var(--space-xs, 0.25rem);
472
+ }
473
+
474
+ .chatbot__header-btn {
475
+ width: 2rem;
476
+ height: 2rem;
477
+ border: none;
478
+ background: transparent;
479
+ color: inherit;
480
+ cursor: pointer;
481
+ display: flex;
482
+ align-items: center;
483
+ justify-content: center;
484
+ border-radius: var(--radius-md, 0.375rem);
485
+ transition: background var(--transition-fast, 150ms ease);
486
+ }
487
+
488
+ .chatbot__header-btn:hover {
489
+ background: rgba(255, 255, 255, 0.2);
490
+ }
491
+
492
+ /* Messages */
493
+ .chatbot__messages {
494
+ flex: 1;
495
+ overflow-y: auto;
496
+ padding: var(--space-md, 1rem);
497
+ display: flex;
498
+ flex-direction: column;
499
+ }
500
+
501
+ /* Typing indicator */
502
+ .chatbot__typing {
503
+ display: flex;
504
+ gap: 4px;
505
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
506
+ background: var(--color-bg-muted, #f3f4f6);
507
+ border-radius: var(--radius-lg, 0.75rem);
508
+ width: fit-content;
509
+ margin-bottom: var(--space-sm, 0.5rem);
510
+ }
511
+
512
+ .chatbot__typing-dot {
513
+ width: 8px;
514
+ height: 8px;
515
+ border-radius: var(--radius-full, 9999px);
516
+ background: var(--color-text-muted, #6b7280);
517
+ animation: typing 1.4s infinite ease-in-out both;
518
+ }
519
+
520
+ .chatbot__typing-dot:nth-child(1) {
521
+ animation-delay: 0s;
522
+ }
523
+
524
+ .chatbot__typing-dot:nth-child(2) {
525
+ animation-delay: 0.2s;
526
+ }
527
+
528
+ .chatbot__typing-dot:nth-child(3) {
529
+ animation-delay: 0.4s;
530
+ }
531
+
532
+ @keyframes typing {
533
+ 0%,
534
+ 80%,
535
+ 100% {
536
+ transform: scale(0.6);
537
+ opacity: 0.4;
538
+ }
539
+ 40% {
540
+ transform: scale(1);
541
+ opacity: 1;
542
+ }
543
+ }
544
+
545
+ /* Quick Actions */
546
+ .chatbot__actions {
547
+ padding: 0 var(--space-md, 1rem);
548
+ padding-bottom: var(--space-sm, 0.5rem);
549
+ }
550
+
551
+ .chatbot__action-btn {
552
+ padding: var(--space-xs, 0.25rem) var(--space-sm, 0.5rem);
553
+ border: 1px solid var(--color-border, #e5e7eb);
554
+ border-radius: var(--radius-full, 9999px);
555
+ background: transparent;
556
+ color: var(--color-text-muted, #6b7280);
557
+ font-size: var(--text-xs, 0.75rem);
558
+ cursor: pointer;
559
+ transition: all var(--transition-fast, 150ms ease);
560
+ }
561
+
562
+ .chatbot__action-btn:hover {
563
+ background: var(--color-bg-muted, #f3f4f6);
564
+ border-color: var(--color-border-hover, #d1d5db);
565
+ }
566
+
567
+ /* Input */
568
+ .chatbot__input-container {
569
+ display: flex;
570
+ align-items: flex-end;
571
+ gap: var(--space-sm, 0.5rem);
572
+ padding: var(--space-md, 1rem);
573
+ border-top: 1px solid var(--color-border, #e5e7eb);
574
+ }
575
+
576
+ .chatbot__input {
577
+ flex: 1;
578
+ min-height: 2.5rem;
579
+ max-height: 120px;
580
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
581
+ border: 1px solid var(--color-border, #e5e7eb);
582
+ border-radius: var(--radius-lg, 0.75rem);
583
+ background: var(--color-bg, #ffffff);
584
+ color: var(--color-text, #1f2937);
585
+ font-family: inherit;
586
+ font-size: var(--text-sm, 0.875rem);
587
+ line-height: 1.5;
588
+ resize: none;
589
+ transition: border-color var(--transition-fast, 150ms ease);
590
+ }
591
+
592
+ .chatbot__input:focus {
593
+ outline: none;
594
+ border-color: var(--color-primary, #3b82f6);
595
+ }
596
+
597
+ .chatbot__input::placeholder {
598
+ color: var(--color-text-muted, #9ca3af);
599
+ }
600
+
601
+ .chatbot__send-btn {
602
+ width: 2.5rem;
603
+ height: 2.5rem;
604
+ border: none;
605
+ border-radius: var(--radius-full, 9999px);
606
+ background: var(--color-primary, #3b82f6);
607
+ color: var(--color-text-inverse, #ffffff);
608
+ cursor: pointer;
609
+ display: flex;
610
+ align-items: center;
611
+ justify-content: center;
612
+ transition: all var(--transition-fast, 150ms ease);
613
+ flex-shrink: 0;
614
+ }
615
+
616
+ .chatbot__send-btn:hover:not(:disabled) {
617
+ background: var(--color-primary-hover, #2563eb);
618
+ }
619
+
620
+ .chatbot__send-btn:disabled {
621
+ opacity: 0.5;
622
+ cursor: not-allowed;
623
+ }
624
+
625
+ /* Mobile */
626
+ @media (max-width: 480px) {
627
+ .chatbot--bottom-right,
628
+ .chatbot--bottom-left {
629
+ bottom: var(--space-md, 1rem);
630
+ right: var(--space-md, 1rem);
631
+ left: var(--space-md, 1rem);
632
+ }
633
+
634
+ .chatbot__window {
635
+ width: 100%;
636
+ max-width: none;
637
+ height: calc(100vh - 6rem);
638
+ }
639
+ }
640
+ </style>
@@ -0,0 +1,22 @@
1
+ import type { ChatMessage as ChatMessageType, LeadData } from './types.js';
2
+ interface Props {
3
+ initialMessages?: ChatMessageType[];
4
+ sessionId?: string;
5
+ placeholder?: string;
6
+ welcomeMessage?: string;
7
+ title?: string;
8
+ subtitle?: string;
9
+ defaultOpen?: boolean;
10
+ inactivityTimeout?: number;
11
+ showTypingIndicator?: boolean;
12
+ position?: 'bottom-right' | 'bottom-left';
13
+ class?: string;
14
+ onSendMessage?: (message: string, sessionId: string) => Promise<string | null>;
15
+ onEscalate?: (sessionId: string, messages: ChatMessageType[]) => Promise<void>;
16
+ onLeadCapture?: (data: LeadData, sessionId: string) => Promise<void>;
17
+ onOpen?: () => void;
18
+ onClose?: () => void;
19
+ }
20
+ declare const Chatbot: import("svelte").Component<Props, {}, "">;
21
+ type Chatbot = ReturnType<typeof Chatbot>;
22
+ export default Chatbot;
@@ -0,0 +1,3 @@
1
+ export { default as Chatbot } from './Chatbot.svelte';
2
+ export { default as ChatMessage } from './ChatMessage.svelte';
3
+ export type { ChatMessage as ChatMessageType, MessageRole, LeadData, ChatbotProps } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { default as Chatbot } from './Chatbot.svelte';
2
+ export { default as ChatMessage } from './ChatMessage.svelte';
@@ -0,0 +1,48 @@
1
+ export type MessageRole = 'user' | 'assistant' | 'system';
2
+ export interface ChatMessage {
3
+ id: string;
4
+ role: MessageRole;
5
+ content: string;
6
+ timestamp?: Date;
7
+ }
8
+ export interface LeadData {
9
+ name?: string;
10
+ email?: string;
11
+ phone?: string;
12
+ company?: string;
13
+ [key: string]: string | undefined;
14
+ }
15
+ export interface ChatbotProps {
16
+ /** Initial messages to display */
17
+ initialMessages?: ChatMessage[];
18
+ /** Session ID for conversation tracking */
19
+ sessionId?: string;
20
+ /** Placeholder text for input */
21
+ placeholder?: string;
22
+ /** Welcome message shown when chat opens */
23
+ welcomeMessage?: string;
24
+ /** Title shown in header */
25
+ title?: string;
26
+ /** Subtitle shown in header */
27
+ subtitle?: string;
28
+ /** Whether chat starts open */
29
+ defaultOpen?: boolean;
30
+ /** Inactivity timeout in ms before auto-opening (0 to disable) */
31
+ inactivityTimeout?: number;
32
+ /** Whether to show typing indicator when waiting for response */
33
+ showTypingIndicator?: boolean;
34
+ /** Position of the chat widget */
35
+ position?: 'bottom-right' | 'bottom-left';
36
+ /** Custom class */
37
+ class?: string;
38
+ /** Called when user sends a message. Should return AI response or null */
39
+ onSendMessage?: (message: string, sessionId: string) => Promise<string | null>;
40
+ /** Called when user requests human agent */
41
+ onEscalate?: (sessionId: string, messages: ChatMessage[]) => Promise<void>;
42
+ /** Called when lead data is captured */
43
+ onLeadCapture?: (data: LeadData, sessionId: string) => Promise<void>;
44
+ /** Called when chat is opened */
45
+ onOpen?: () => void;
46
+ /** Called when chat is closed */
47
+ onClose?: () => void;
48
+ }
@@ -0,0 +1,2 @@
1
+ // Chatbot component types
2
+ export {};