acecoderz-chat-ui 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.
Files changed (62) hide show
  1. package/README.md +309 -0
  2. package/browser/index.ts +15 -0
  3. package/dist/adapters/react/ChatUI.d.ts +32 -0
  4. package/dist/adapters/react/ChatUI.d.ts.map +1 -0
  5. package/dist/adapters/react/ChatUI.js +170 -0
  6. package/dist/adapters/react/index.d.ts +5 -0
  7. package/dist/adapters/react/index.d.ts.map +1 -0
  8. package/dist/adapters/react/index.js +2 -0
  9. package/dist/adapters/react/useChat.d.ts +14 -0
  10. package/dist/adapters/react/useChat.d.ts.map +1 -0
  11. package/dist/adapters/react/useChat.js +64 -0
  12. package/dist/adapters/solid/createChat.d.ts +13 -0
  13. package/dist/adapters/solid/createChat.d.ts.map +1 -0
  14. package/dist/adapters/solid/createChat.js +34 -0
  15. package/dist/adapters/solid/index.d.ts +3 -0
  16. package/dist/adapters/solid/index.d.ts.map +1 -0
  17. package/dist/adapters/solid/index.js +1 -0
  18. package/dist/adapters/vanilla/index.d.ts +31 -0
  19. package/dist/adapters/vanilla/index.d.ts.map +1 -0
  20. package/dist/adapters/vanilla/index.js +346 -0
  21. package/dist/browser/Archive.zip +0 -0
  22. package/dist/browser/cdn-example.html +177 -0
  23. package/dist/browser/chatbot-ui.css +508 -0
  24. package/dist/browser/chatbot-ui.js +878 -0
  25. package/dist/browser/chatbot-ui.js.map +7 -0
  26. package/dist/browser/chatbot-ui.min.js +71 -0
  27. package/dist/browser/chatbot.html +100 -0
  28. package/dist/core/src/ChatEngine.d.ts +30 -0
  29. package/dist/core/src/ChatEngine.d.ts.map +1 -0
  30. package/dist/core/src/ChatEngine.js +357 -0
  31. package/dist/core/src/apiLogger.d.ts +47 -0
  32. package/dist/core/src/apiLogger.d.ts.map +1 -0
  33. package/dist/core/src/apiLogger.js +199 -0
  34. package/dist/core/src/index.d.ts +7 -0
  35. package/dist/core/src/index.d.ts.map +1 -0
  36. package/dist/core/src/index.js +3 -0
  37. package/dist/core/src/types.d.ts +62 -0
  38. package/dist/core/src/types.d.ts.map +1 -0
  39. package/dist/core/src/types.js +1 -0
  40. package/dist/core/src/urlWhitelist.d.ts +19 -0
  41. package/dist/core/src/urlWhitelist.d.ts.map +1 -0
  42. package/dist/core/src/urlWhitelist.js +66 -0
  43. package/dist/src/ChatUI.stories.d.ts +37 -0
  44. package/dist/src/ChatUI.stories.d.ts.map +1 -0
  45. package/dist/src/ChatUI.stories.js +65 -0
  46. package/dist/src/ChatUIThemes.stories.d.ts +28 -0
  47. package/dist/src/ChatUIThemes.stories.d.ts.map +1 -0
  48. package/dist/src/ChatUIThemes.stories.js +109 -0
  49. package/dist/src/ThemeProperties.stories.d.ts +92 -0
  50. package/dist/src/ThemeProperties.stories.d.ts.map +1 -0
  51. package/dist/src/ThemeProperties.stories.js +195 -0
  52. package/dist/src/UseChat.stories.d.ts +21 -0
  53. package/dist/src/UseChat.stories.d.ts.map +1 -0
  54. package/dist/src/UseChat.stories.js +66 -0
  55. package/dist/src/VanillaAdapter.stories.d.ts +39 -0
  56. package/dist/src/VanillaAdapter.stories.d.ts.map +1 -0
  57. package/dist/src/VanillaAdapter.stories.js +78 -0
  58. package/dist/src/index.d.ts +9 -0
  59. package/dist/src/index.d.ts.map +1 -0
  60. package/dist/src/index.js +8 -0
  61. package/package.json +117 -0
  62. package/styles/chat.css +508 -0
@@ -0,0 +1,3 @@
1
+ export { createChat } from './createChat';
2
+ export type { CreateChatReturn } from './createChat';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../adapters/solid/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export { createChat } from './createChat';
@@ -0,0 +1,31 @@
1
+ import { ChatEngine } from '../../core/src/ChatEngine';
2
+ import type { ChatConfig, Message, ChatTheme } from '../../core/src/types';
3
+ export interface ChatUIOptions {
4
+ config: ChatConfig;
5
+ theme?: ChatTheme;
6
+ container: HTMLElement | string;
7
+ placeholder?: string;
8
+ showTimestamp?: boolean;
9
+ showAvatar?: boolean;
10
+ userAvatar?: string;
11
+ assistantAvatar?: string;
12
+ maxHeight?: string;
13
+ disabled?: boolean;
14
+ autoScroll?: boolean;
15
+ enableMarkdown?: boolean;
16
+ showClearButton?: boolean;
17
+ emptyStateMessage?: string;
18
+ loadingMessage?: string;
19
+ customMessageRenderer?: (message: Message) => string;
20
+ onInit?: (instance: ChatUIInstance) => void;
21
+ }
22
+ export interface ChatUIInstance {
23
+ engine: ChatEngine;
24
+ render: () => void;
25
+ destroy: () => void;
26
+ clear: () => void;
27
+ scrollToBottom: () => void;
28
+ getContainer: () => HTMLElement;
29
+ }
30
+ export declare function createChatUI(options: ChatUIOptions): ChatUIInstance;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../adapters/vanilla/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,EAAE,WAAW,GAAG,MAAM,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qBAAqB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IACrD,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,WAAW,CAAC;CACjC;AA2DD,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,cAAc,CAyUnE"}
@@ -0,0 +1,346 @@
1
+ import { ChatEngine } from '../../core/src/ChatEngine';
2
+ /**
3
+ * Escape HTML to prevent XSS
4
+ */
5
+ function escapeHtml(text) {
6
+ const div = document.createElement('div');
7
+ div.textContent = text;
8
+ return div.innerHTML;
9
+ }
10
+ /**
11
+ * Simple markdown-like text formatting (basic support)
12
+ */
13
+ function formatText(text, enableMarkdown) {
14
+ if (!enableMarkdown) {
15
+ return escapeHtml(text);
16
+ }
17
+ // Basic markdown support
18
+ let formatted = escapeHtml(text);
19
+ // Bold: **text** or __text__
20
+ formatted = formatted.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
21
+ formatted = formatted.replace(/__(.+?)__/g, '<strong>$1</strong>');
22
+ // Italic: *text* or _text_
23
+ formatted = formatted.replace(/\*(.+?)\*/g, '<em>$1</em>');
24
+ formatted = formatted.replace(/_(.+?)_/g, '<em>$1</em>');
25
+ // Code: `code`
26
+ formatted = formatted.replace(/`([^`]+)`/g, '<code>$1</code>');
27
+ // Links: [text](url)
28
+ formatted = formatted.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
29
+ // Line breaks
30
+ formatted = formatted.replace(/\n/g, '<br>');
31
+ return formatted;
32
+ }
33
+ /**
34
+ * Get container element from selector or element
35
+ */
36
+ function getContainerElement(container) {
37
+ if (typeof container === 'string') {
38
+ const element = document.querySelector(container);
39
+ if (!element) {
40
+ throw new Error(`Chatbot container not found: ${container}`);
41
+ }
42
+ if (!(element instanceof HTMLElement)) {
43
+ throw new Error(`Chatbot container is not an HTMLElement: ${container}`);
44
+ }
45
+ return element;
46
+ }
47
+ return container;
48
+ }
49
+ export function createChatUI(options) {
50
+ const { config, theme = {}, container, placeholder = 'Type your message...', showTimestamp = true, showAvatar = true, userAvatar, assistantAvatar, maxHeight = '600px', disabled = false, autoScroll = true, enableMarkdown = true, showClearButton = true, emptyStateMessage = 'Start a conversation...', loadingMessage = 'Thinking...', customMessageRenderer, onInit, } = options;
51
+ // Get container element
52
+ const containerElement = getContainerElement(container);
53
+ const engine = new ChatEngine(config);
54
+ // Store references for cleanup
55
+ let messagesContainer = null;
56
+ let inputElement = null;
57
+ let formElement = null;
58
+ let sendButtonElement = null;
59
+ let clearButtonElement = null;
60
+ let isUpdatingInputProgrammatically = false; // Flag to prevent input event loop
61
+ // Apply CSS variables for theming
62
+ const applyTheme = () => {
63
+ const root = containerElement;
64
+ root.style.setProperty('--chat-primary-color', theme.primaryColor || '#3b82f6');
65
+ root.style.setProperty('--chat-secondary-color', theme.secondaryColor || '#64748b');
66
+ root.style.setProperty('--chat-background-color', theme.backgroundColor || '#ffffff');
67
+ root.style.setProperty('--chat-text-color', theme.textColor || '#1e293b');
68
+ root.style.setProperty('--chat-user-message-color', theme.userMessageColor || '#3b82f6');
69
+ root.style.setProperty('--chat-assistant-message-color', theme.assistantMessageColor || '#f1f5f9');
70
+ root.style.setProperty('--chat-input-background-color', theme.inputBackgroundColor || '#f8fafc');
71
+ root.style.setProperty('--chat-input-text-color', theme.inputTextColor || '#1e293b');
72
+ root.style.setProperty('--chat-border-radius', theme.borderRadius || '0.5rem');
73
+ root.style.setProperty('--chat-font-family', theme.fontFamily || 'system-ui, sans-serif');
74
+ root.style.setProperty('--chat-font-size', theme.fontSize || '1rem');
75
+ root.style.setProperty('--chat-max-height', maxHeight);
76
+ };
77
+ // Scroll to bottom helper
78
+ const scrollToBottom = () => {
79
+ if (messagesContainer && autoScroll) {
80
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
81
+ }
82
+ };
83
+ const formatTimestamp = (date) => {
84
+ return new Intl.DateTimeFormat('en-US', {
85
+ hour: '2-digit',
86
+ minute: '2-digit',
87
+ }).format(date);
88
+ };
89
+ const renderAvatar = (role) => {
90
+ if (!showAvatar)
91
+ return '';
92
+ const avatar = role === 'user' ? userAvatar : assistantAvatar;
93
+ if (avatar) {
94
+ return `<div class="chat-avatar"><img src="${escapeHtml(avatar)}" alt="${role}" /></div>`;
95
+ }
96
+ return `<div class="chat-avatar">${role === 'user' ? 'U' : 'A'}</div>`;
97
+ };
98
+ const renderMessage = (message) => {
99
+ // Use custom renderer if provided
100
+ if (customMessageRenderer) {
101
+ return customMessageRenderer(message);
102
+ }
103
+ const isUser = message.role === 'user';
104
+ const isSystem = message.role === 'system';
105
+ const role = isUser ? 'user' : 'assistant';
106
+ const avatarHtml = !isSystem ? renderAvatar(role) : '';
107
+ const messageContent = enableMarkdown
108
+ ? `<div class="chat-markdown">${formatText(message.content, enableMarkdown)}</div>`
109
+ : `<div class="chat-message-text">${formatText(message.content, false)}</div>`;
110
+ return `
111
+ <div class="chat-message ${isUser ? 'chat-message-user' : isSystem ? 'chat-message-system' : 'chat-message-assistant'}" data-message-id="${escapeHtml(message.id)}">
112
+ ${!isUser && !isSystem ? avatarHtml : ''}
113
+ <div class="chat-message-content ${isUser ? 'chat-message-content-user' : isSystem ? 'chat-message-content-system' : 'chat-message-content-assistant'}">
114
+ ${messageContent}
115
+ ${showTimestamp ? `<div class="chat-message-timestamp">${formatTimestamp(message.timestamp)}</div>` : ''}
116
+ </div>
117
+ ${isUser ? avatarHtml : ''}
118
+ </div>
119
+ `;
120
+ };
121
+ const render = () => {
122
+ const messages = engine.getMessages();
123
+ const input = engine.getInput();
124
+ const isLoading = engine.getIsLoading();
125
+ const error = engine.getError();
126
+ // Preserve input focus and cursor position before re-render
127
+ const wasInputFocused = inputElement && document.activeElement === inputElement;
128
+ const inputCursorPosition = inputElement ? inputElement.selectionStart || 0 : 0;
129
+ applyTheme();
130
+ containerElement.className = `chat-container ${containerElement.className || ''}`.trim();
131
+ containerElement.setAttribute('data-chat-ui', 'true');
132
+ containerElement.innerHTML = `
133
+ <div class="chat-messages-container">
134
+ ${messages.length === 0 ? `<div class="chat-empty-state">${escapeHtml(emptyStateMessage)}</div>` : ''}
135
+ ${messages.map(renderMessage).join('')}
136
+ ${isLoading ? `
137
+ <div class="chat-message chat-message-assistant chat-message-loading">
138
+ ${renderAvatar('assistant')}
139
+ <div class="chat-message-content chat-message-content-assistant">
140
+ <div class="chat-message-text">${escapeHtml(loadingMessage)}</div>
141
+ </div>
142
+ </div>
143
+ ` : ''}
144
+ </div>
145
+ ${error ? `<div class="chat-error">
146
+ <div class="chat-error-message">Error: ${escapeHtml(error.message)}</div>
147
+ <button class="chat-retry-button" type="button">Retry</button>
148
+ </div>` : ''}
149
+ ${showClearButton && messages.length > 0 ? `
150
+ <div class="chat-actions">
151
+ <button class="chat-clear-button" type="button" title="Clear conversation">Clear</button>
152
+ </div>
153
+ ` : ''}
154
+ <div class="chat-input-container">
155
+ <form class="chat-input-form">
156
+ <input
157
+ type="text"
158
+ class="chat-input"
159
+ value="${escapeHtml(input)}"
160
+ placeholder="${escapeHtml(placeholder)}"
161
+ ${disabled || isLoading ? 'disabled' : ''}
162
+ aria-label="Chat input"
163
+ />
164
+ <button
165
+ type="submit"
166
+ class="chat-send-button"
167
+ ${disabled || isLoading || !input.trim() ? 'disabled' : ''}
168
+ aria-label="Send message"
169
+ >
170
+ Send
171
+ </button>
172
+ </form>
173
+ </div>
174
+ `;
175
+ // Store references
176
+ messagesContainer = containerElement.querySelector('.chat-messages-container');
177
+ inputElement = containerElement.querySelector('.chat-input');
178
+ formElement = containerElement.querySelector('.chat-input-form');
179
+ sendButtonElement = containerElement.querySelector('.chat-send-button');
180
+ clearButtonElement = containerElement.querySelector('.chat-clear-button');
181
+ const retryButton = containerElement.querySelector('.chat-retry-button');
182
+ // Restore input focus and cursor position if it was focused before re-render
183
+ if (wasInputFocused && inputElement) {
184
+ requestAnimationFrame(() => {
185
+ if (inputElement) {
186
+ inputElement.focus();
187
+ const newCursorPosition = Math.min(inputCursorPosition, inputElement.value.length);
188
+ inputElement.setSelectionRange(newCursorPosition, newCursorPosition);
189
+ }
190
+ });
191
+ }
192
+ // Attach event listeners
193
+ if (formElement) {
194
+ formElement.addEventListener('submit', (e) => {
195
+ e.preventDefault();
196
+ if (!disabled && !isLoading && input.trim()) {
197
+ engine.sendMessage();
198
+ }
199
+ });
200
+ }
201
+ if (inputElement) {
202
+ // Handle input changes
203
+ inputElement.addEventListener('input', (e) => {
204
+ // Skip if we're programmatically updating the input
205
+ if (isUpdatingInputProgrammatically) {
206
+ return;
207
+ }
208
+ const target = e.target;
209
+ engine.setInput(target.value);
210
+ });
211
+ // Keyboard shortcuts: Enter to send, Shift+Enter for newline
212
+ inputElement.addEventListener('keydown', (e) => {
213
+ if (e.key === 'Enter' && !e.shiftKey) {
214
+ e.preventDefault();
215
+ if (!disabled && !isLoading && input.trim()) {
216
+ engine.sendMessage();
217
+ }
218
+ }
219
+ // Shift+Enter allows default behavior (newline if textarea, ignored if input)
220
+ });
221
+ }
222
+ // Clear button handler
223
+ if (clearButtonElement) {
224
+ clearButtonElement.addEventListener('click', () => {
225
+ if (confirm('Are you sure you want to clear the conversation?')) {
226
+ engine.clearMessages();
227
+ }
228
+ });
229
+ }
230
+ // Retry button handler
231
+ if (retryButton) {
232
+ retryButton.addEventListener('click', () => {
233
+ engine.retryLastMessage();
234
+ });
235
+ }
236
+ // Auto-scroll to bottom after render
237
+ // Use setTimeout to ensure DOM is updated
238
+ setTimeout(() => {
239
+ scrollToBottom();
240
+ }, 0);
241
+ };
242
+ // Subscribe to engine events
243
+ engine.on('messagesChange', () => {
244
+ render();
245
+ // Scroll after messages change
246
+ setTimeout(() => {
247
+ scrollToBottom();
248
+ }, 100);
249
+ });
250
+ // Helper function to update send button state
251
+ const updateSendButtonState = () => {
252
+ if (sendButtonElement) {
253
+ const input = engine.getInput();
254
+ const isLoading = engine.getIsLoading();
255
+ const shouldDisable = disabled || isLoading || !input.trim();
256
+ sendButtonElement.disabled = shouldDisable;
257
+ }
258
+ };
259
+ engine.on('inputChange', (value) => {
260
+ // Only update input value without full re-render to preserve focus
261
+ if (inputElement && inputElement.isConnected) {
262
+ const wasFocused = document.activeElement === inputElement;
263
+ const cursorPosition = inputElement.selectionStart || 0;
264
+ // Set flag to prevent input event from firing
265
+ isUpdatingInputProgrammatically = true;
266
+ inputElement.value = value || '';
267
+ isUpdatingInputProgrammatically = false;
268
+ // Update send button state
269
+ updateSendButtonState();
270
+ // Restore focus and cursor position if input was focused
271
+ if (wasFocused) {
272
+ // Use requestAnimationFrame to ensure DOM is ready
273
+ requestAnimationFrame(() => {
274
+ if (inputElement) {
275
+ inputElement.focus();
276
+ // Restore cursor position, accounting for value length changes
277
+ const newCursorPosition = Math.min(cursorPosition, inputElement.value.length);
278
+ inputElement.setSelectionRange(newCursorPosition, newCursorPosition);
279
+ }
280
+ });
281
+ }
282
+ }
283
+ else {
284
+ // If input element doesn't exist yet, do a full render
285
+ render();
286
+ }
287
+ });
288
+ engine.on('loadingChange', () => {
289
+ // Update send button state without full re-render if possible
290
+ updateSendButtonState();
291
+ // Still do full render for loading indicator
292
+ render();
293
+ setTimeout(() => {
294
+ scrollToBottom();
295
+ }, 100);
296
+ });
297
+ engine.on('error', render);
298
+ // Initial render
299
+ render();
300
+ const instance = {
301
+ engine,
302
+ render,
303
+ destroy: () => {
304
+ engine.destroy();
305
+ containerElement.innerHTML = '';
306
+ // Clear references
307
+ messagesContainer = null;
308
+ inputElement = null;
309
+ formElement = null;
310
+ sendButtonElement = null;
311
+ clearButtonElement = null;
312
+ },
313
+ clear: () => {
314
+ engine.clearMessages();
315
+ },
316
+ scrollToBottom,
317
+ getContainer: () => containerElement,
318
+ };
319
+ // Call onInit callback if provided
320
+ if (onInit) {
321
+ onInit(instance);
322
+ }
323
+ return instance;
324
+ }
325
+ /**
326
+ * Global initialization helper for PHP/Laravel integration
327
+ * Usage: window.ChatbotUI.init('container-id', { apiUrl: '...' })
328
+ */
329
+ if (typeof window !== 'undefined') {
330
+ window.ChatbotUI = {
331
+ create: createChatUI,
332
+ init: (containerId, options) => {
333
+ const container = typeof containerId === 'string'
334
+ ? document.getElementById(containerId) || document.querySelector(containerId)
335
+ : containerId;
336
+ if (!container) {
337
+ console.error(`Chatbot container not found: ${containerId}`);
338
+ return null;
339
+ }
340
+ return createChatUI({
341
+ container: container,
342
+ ...options,
343
+ });
344
+ },
345
+ };
346
+ }
Binary file
@@ -0,0 +1,177 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Chatbot UI - CDN Example</title>
7
+
8
+ <!-- Include Chatbot UI CSS -->
9
+ <link rel="stylesheet" href="./chatbot-ui.css">
10
+
11
+ <style>
12
+ body {
13
+ font-family: system-ui, -apple-system, sans-serif;
14
+ margin: 0;
15
+ padding: 20px;
16
+ background: #f5f5f5;
17
+ }
18
+
19
+ .container {
20
+ max-width: 800px;
21
+ margin: 0 auto;
22
+ background: white;
23
+ border-radius: 8px;
24
+ padding: 20px;
25
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
26
+ }
27
+
28
+ h1 {
29
+ margin-top: 0;
30
+ color: #1e293b;
31
+ }
32
+
33
+ .info {
34
+ background: #f0f9ff;
35
+ border-left: 4px solid #3b82f6;
36
+ padding: 12px;
37
+ margin-bottom: 20px;
38
+ border-radius: 4px;
39
+ }
40
+
41
+ .code-block {
42
+ background: #1e293b;
43
+ color: #f1f5f9;
44
+ padding: 16px;
45
+ border-radius: 6px;
46
+ overflow-x: auto;
47
+ margin: 16px 0;
48
+ font-family: 'Monaco', 'Menlo', monospace;
49
+ font-size: 14px;
50
+ }
51
+
52
+ #chatbot-container {
53
+ width: 100%;
54
+ height: 600px;
55
+ border: 1px solid #e2e8f0;
56
+ border-radius: 8px;
57
+ margin-top: 20px;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <div class="container">
63
+ <h1>🤖 Chatbot UI - CDN Example</h1>
64
+
65
+ <div class="info">
66
+ <strong>📝 Usage:</strong> Include the chatbot-ui.css and chatbot-ui.min.js files in your HTML,
67
+ then initialize the chatbot using <code>ChatbotUI.init()</code> or <code>ChatbotUI.create()</code>.
68
+ </div>
69
+
70
+ <h3>📋 HTML Setup:</h3>
71
+ <div class="code-block">&lt;!-- Include CSS --&gt;
72
+ &lt;link rel="stylesheet" href="https://cdn.example.com/chatbot-ui.css"&gt;
73
+
74
+ &lt;!-- Include JavaScript --&gt;
75
+ &lt;script src="https://cdn.example.com/chatbot-ui.min.js"&gt;&lt;/script&gt;
76
+
77
+ &lt;!-- Container element --&gt;
78
+ &lt;div id="chatbot-container"&gt;&lt;/div&gt;
79
+
80
+ &lt;!-- Initialize --&gt;
81
+ &lt;script&gt;
82
+ ChatbotUI.init('chatbot-container', {
83
+ config: {
84
+ apiUrl: 'http://localhost:3003/api',
85
+ },
86
+ theme: {
87
+ primaryColor: '#3b82f6',
88
+ userMessageColor: '#3b82f6',
89
+ assistantMessageColor: '#f1f5f9',
90
+ },
91
+ placeholder: 'Type your message...',
92
+ showTimestamp: true,
93
+ showAvatar: true,
94
+ });
95
+ &lt;/script&gt;</div>
96
+
97
+ <h3>🎨 Customization Example:</h3>
98
+ <div class="code-block">ChatbotUI.init('chatbot-container', {
99
+ config: {
100
+ apiUrl: 'https://your-api.com/api/chat',
101
+ // Optional: Add headers
102
+ headers: {
103
+ 'Authorization': 'Bearer YOUR_TOKEN',
104
+ },
105
+ },
106
+ theme: {
107
+ primaryColor: '#8b5cf6',
108
+ userMessageColor: '#8b5cf6',
109
+ assistantMessageColor: '#f3e8ff',
110
+ backgroundColor: '#ffffff',
111
+ borderRadius: '12px',
112
+ },
113
+ placeholder: 'Ask me anything...',
114
+ showTimestamp: true,
115
+ showAvatar: true,
116
+ maxHeight: '600px',
117
+ enableMarkdown: true,
118
+ showClearButton: true,
119
+ });</div>
120
+
121
+ <h3>🎯 Advanced Usage:</h3>
122
+ <div class="code-block">// Using create() for more control
123
+ const chatInstance = ChatbotUI.create({
124
+ container: document.getElementById('chatbot-container'),
125
+ config: {
126
+ apiUrl: 'https://your-api.com/api/chat',
127
+ },
128
+ theme: {
129
+ primaryColor: '#10b981',
130
+ },
131
+ onInit: (instance) => {
132
+ console.log('Chatbot initialized!', instance);
133
+ // Access the engine
134
+ instance.engine.on('messagesChange', () => {
135
+ console.log('Messages updated');
136
+ });
137
+ },
138
+ });
139
+
140
+ // Later, you can interact with it
141
+ chatInstance.clear(); // Clear conversation
142
+ chatInstance.scrollToBottom(); // Scroll to bottom
143
+ chatInstance.destroy(); // Clean up</div>
144
+
145
+ <hr style="margin: 30px 0; border: none; border-top: 1px solid #e2e8f0;">
146
+
147
+ <h3>💬 Live Example:</h3>
148
+ <div id="chatbot-container"></div>
149
+ </div>
150
+
151
+ <!-- Initialize the chatbot -->
152
+ <script>
153
+ // Wait for ChatbotUI to be available
154
+ if (typeof ChatbotUI !== 'undefined') {
155
+ ChatbotUI.init('chatbot-container', {
156
+ config: {
157
+ apiUrl: 'http://localhost:3003/api',
158
+ },
159
+ theme: {
160
+ primaryColor: '#3b82f6',
161
+ userMessageColor: '#3b82f6',
162
+ assistantMessageColor: '#f1f5f9',
163
+ borderRadius: '8px',
164
+ },
165
+ placeholder: 'Type your message...',
166
+ showTimestamp: true,
167
+ showAvatar: true,
168
+ maxHeight: '600px',
169
+ enableMarkdown: true,
170
+ showClearButton: true,
171
+ });
172
+ } else {
173
+ console.error('ChatbotUI not loaded. Make sure chatbot-ui.min.js is included.');
174
+ }
175
+ </script>
176
+ </body>
177
+ </html>