test-chat-component-per 1.0.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.
@@ -0,0 +1,580 @@
1
+ /**
2
+ * DelaChat - Standalone Chat Component
3
+ * Version: 1.0.0
4
+ *
5
+ * A plug-and-play chat component for integrating DelaChat AI
6
+ * Pure vanilla JavaScript - zero dependencies
7
+ */
8
+
9
+ (function(window) {
10
+ 'use strict';
11
+
12
+ // Component version
13
+ const VERSION = '1.0.0';
14
+
15
+ /**
16
+ * DelaChat Constructor
17
+ */
18
+ function DelaChatInstance(container, config) {
19
+ this.container = container;
20
+ this.config = this._validateConfig(config);
21
+ this.state = {
22
+ messages: [],
23
+ isLoading: false,
24
+ isOpen: true,
25
+ error: null
26
+ };
27
+ this.elements = {};
28
+ this.eventListeners = {};
29
+
30
+ this._init();
31
+ }
32
+
33
+ /**
34
+ * Initialize the component
35
+ */
36
+ DelaChatInstance.prototype._init = function() {
37
+ this._render();
38
+ this._attachEventListeners();
39
+ this._loadHistory();
40
+
41
+ if (this.config.onReady) {
42
+ this.config.onReady.call(this);
43
+ }
44
+ };
45
+
46
+ /**
47
+ * Validate and set default configuration
48
+ */
49
+ DelaChatInstance.prototype._validateConfig = function(config) {
50
+ if (!config.apiUrl) throw new Error('apiUrl is required');
51
+ if (!config.sessionUid) throw new Error('sessionUid is required');
52
+ if (!config.datasource) throw new Error('datasource is required');
53
+ if (!config.productId) throw new Error('productId is required');
54
+ if (!config.token) throw new Error('token is required');
55
+
56
+ return {
57
+ apiUrl: config.apiUrl,
58
+ sessionUid: config.sessionUid,
59
+ datasource: config.datasource,
60
+ productId: config.productId,
61
+ token: config.token,
62
+ theme: config.theme || 'light',
63
+ height: config.height || '600px',
64
+ width: config.width || '400px',
65
+ position: config.position || 'inline',
66
+ autoOpen: config.autoOpen !== false,
67
+ avatarUrl: config.avatarUrl || null,
68
+ userName: config.userName || 'User',
69
+ onReady: config.onReady || null,
70
+ onMessageSent: config.onMessageSent || null,
71
+ onMessageReceived: config.onMessageReceived || null,
72
+ onError: config.onError || null,
73
+ onClose: config.onClose || null
74
+ };
75
+ };
76
+
77
+ /**
78
+ * Render the chat UI
79
+ */
80
+ DelaChatInstance.prototype._render = function() {
81
+ const html = `
82
+ <div class="delachat-window" data-theme="${this.config.theme}" style="height: ${this.config.height}; width: ${this.config.width};">
83
+ <!-- Header -->
84
+ <div class="delachat-header">
85
+ <h2 class="delachat-title">Ask Dela</h2>
86
+ <div class="delachat-header-buttons">
87
+ <button class="delachat-btn-icon" data-action="disclaimer" title="Disclaimer">
88
+ <span class="delachat-icon">ℹ</span>
89
+ </button>
90
+ <button class="delachat-btn-icon" data-action="new-chat" title="New Chat">
91
+ <span class="delachat-icon">+</span>
92
+ </button>
93
+ <button class="delachat-btn-icon" data-action="help" title="Help">
94
+ <span class="delachat-icon">?</span>
95
+ </button>
96
+ <button class="delachat-btn-icon" data-action="close" title="Close">
97
+ <span class="delachat-icon">✕</span>
98
+ </button>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- Disclaimer -->
103
+ <div class="delachat-disclaimer" style="display: none;">
104
+ <p>Dela is an AI-powered assistant. While I strive to provide accurate information, please verify important details independently.</p>
105
+ </div>
106
+
107
+ <!-- Error message -->
108
+ <div class="delachat-error" style="display: none;"></div>
109
+
110
+ <!-- Messages area -->
111
+ <div class="delachat-messages">
112
+ <div class="delachat-welcome">
113
+ <div class="delachat-ai-avatar-large">
114
+ <span>🤖</span>
115
+ </div>
116
+ <p>Hello! I'm Dela, your AI assistant. How can I help you today?</p>
117
+ </div>
118
+ </div>
119
+
120
+ <!-- Loading indicator -->
121
+ <div class="delachat-loading" style="display: none;">
122
+ <div class="delachat-loading-dots">
123
+ <span></span>
124
+ <span></span>
125
+ <span></span>
126
+ </div>
127
+ <span>Dela is thinking...</span>
128
+ </div>
129
+
130
+ <!-- Input area -->
131
+ <div class="delachat-input-container">
132
+ <div class="delachat-input-wrapper">
133
+ <textarea
134
+ class="delachat-input"
135
+ placeholder="Type your message..."
136
+ rows="1"
137
+ ></textarea>
138
+ <button class="delachat-btn-send" data-action="send" title="Send message">
139
+ <span class="delachat-icon">➤</span>
140
+ </button>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ `;
145
+
146
+ this.container.innerHTML = html;
147
+ this._cacheElements();
148
+ };
149
+
150
+ /**
151
+ * Cache DOM elements
152
+ */
153
+ DelaChatInstance.prototype._cacheElements = function() {
154
+ const win = this.container.querySelector('.delachat-window');
155
+ this.elements = {
156
+ window: win,
157
+ header: win.querySelector('.delachat-header'),
158
+ disclaimer: win.querySelector('.delachat-disclaimer'),
159
+ error: win.querySelector('.delachat-error'),
160
+ messages: win.querySelector('.delachat-messages'),
161
+ welcome: win.querySelector('.delachat-welcome'),
162
+ loading: win.querySelector('.delachat-loading'),
163
+ inputContainer: win.querySelector('.delachat-input-container'),
164
+ input: win.querySelector('.delachat-input'),
165
+ btnSend: win.querySelector('[data-action="send"]'),
166
+ btnClose: win.querySelector('[data-action="close"]'),
167
+ btnDisclaimer: win.querySelector('[data-action="disclaimer"]'),
168
+ btnNewChat: win.querySelector('[data-action="new-chat"]'),
169
+ btnHelp: win.querySelector('[data-action="help"]')
170
+ };
171
+ };
172
+
173
+ /**
174
+ * Attach event listeners
175
+ */
176
+ DelaChatInstance.prototype._attachEventListeners = function() {
177
+ var self = this;
178
+
179
+ // Send button
180
+ this.elements.btnSend.addEventListener('click', function() {
181
+ self._handleSend();
182
+ });
183
+
184
+ // Input - Enter key
185
+ this.elements.input.addEventListener('keydown', function(e) {
186
+ if (e.key === 'Enter' && !e.shiftKey) {
187
+ e.preventDefault();
188
+ self._handleSend();
189
+ }
190
+ });
191
+
192
+ // Auto-resize textarea
193
+ this.elements.input.addEventListener('input', function() {
194
+ this.style.height = 'auto';
195
+ this.style.height = (this.scrollHeight) + 'px';
196
+ });
197
+
198
+ // Close button
199
+ this.elements.btnClose.addEventListener('click', function() {
200
+ self.close();
201
+ });
202
+
203
+ // Disclaimer toggle
204
+ this.elements.btnDisclaimer.addEventListener('click', function() {
205
+ var disclaimer = self.elements.disclaimer;
206
+ disclaimer.style.display = disclaimer.style.display === 'none' ? 'block' : 'none';
207
+ });
208
+
209
+ // New chat
210
+ this.elements.btnNewChat.addEventListener('click', function() {
211
+ if (confirm('Start a new conversation? This will clear the current chat.')) {
212
+ self.clearHistory();
213
+ }
214
+ });
215
+
216
+ // Help
217
+ this.elements.btnHelp.addEventListener('click', function() {
218
+ alert('DelaChat Help\n\nType your question and press Enter or click Send.\n\nTips:\n- Ask specific questions\n- Provide context for better answers\n- Use "Regenerate" if you need a different response');
219
+ });
220
+ };
221
+
222
+ /**
223
+ * Handle send message
224
+ */
225
+ DelaChatInstance.prototype._handleSend = function() {
226
+ var message = this.elements.input.value.trim();
227
+ if (!message || this.state.isLoading) return;
228
+
229
+ this.sendMessage(message);
230
+ this.elements.input.value = '';
231
+ this.elements.input.style.height = 'auto';
232
+ };
233
+
234
+ /**
235
+ * Load chat history from API
236
+ */
237
+ DelaChatInstance.prototype._loadHistory = function() {
238
+ var self = this;
239
+ this._setLoading(true);
240
+
241
+ this._apiRequest('GET', '/delachat/history?limit=50&offset=0')
242
+ .then(function(data) {
243
+ self.state.messages = data.records || [];
244
+ self._renderMessages();
245
+ self._setLoading(false);
246
+ })
247
+ .catch(function(error) {
248
+ self._handleError(error);
249
+ self._setLoading(false);
250
+ });
251
+ };
252
+
253
+ /**
254
+ * Send user message
255
+ */
256
+ DelaChatInstance.prototype.sendMessage = function(message) {
257
+ var self = this;
258
+ this._setLoading(true);
259
+ this._hideWelcome();
260
+
261
+ // Add user message to UI immediately
262
+ var userMsg = {
263
+ historyId: 'temp-' + Date.now(),
264
+ message: message,
265
+ source: 0,
266
+ created: new Date().toISOString(),
267
+ userId: this.config.userName,
268
+ isUserMessage: true,
269
+ isAIMessage: false
270
+ };
271
+
272
+ this.state.messages.unshift(userMsg);
273
+ this._renderMessages();
274
+
275
+ // Send to API
276
+ this._apiRequest('POST', '/delachat/history', {
277
+ message: message,
278
+ source: 0,
279
+ productUid: this.config.productId
280
+ })
281
+ .then(function(data) {
282
+ // Replace temp message with real one
283
+ self.state.messages[0] = data.record;
284
+ self._renderMessages();
285
+
286
+ if (self.config.onMessageSent) {
287
+ self.config.onMessageSent.call(self, message);
288
+ }
289
+
290
+ // Simulate AI response (in real app, this would come from AI service)
291
+ self._simulateAIResponse(message);
292
+ })
293
+ .catch(function(error) {
294
+ self._handleError(error);
295
+ self._setLoading(false);
296
+ });
297
+ };
298
+
299
+ /**
300
+ * Simulate AI response (Replace with real AI integration)
301
+ */
302
+ DelaChatInstance.prototype._simulateAIResponse = function(userMessage) {
303
+ var self = this;
304
+
305
+ // Simulate delay
306
+ setTimeout(function() {
307
+ var aiResponse = "I received your message: \"" + userMessage + "\". This is a simulated response. In production, this would call your AI service.";
308
+
309
+ self._apiRequest('POST', '/delachat/history', {
310
+ message: aiResponse,
311
+ source: 1,
312
+ productUid: self.config.productId
313
+ })
314
+ .then(function(data) {
315
+ self.state.messages.unshift(data.record);
316
+ self._renderMessages();
317
+ self._setLoading(false);
318
+
319
+ if (self.config.onMessageReceived) {
320
+ self.config.onMessageReceived.call(self, aiResponse);
321
+ }
322
+ })
323
+ .catch(function(error) {
324
+ self._handleError(error);
325
+ self._setLoading(false);
326
+ });
327
+ }, 1500);
328
+ };
329
+
330
+ /**
331
+ * Render messages
332
+ */
333
+ DelaChatInstance.prototype._renderMessages = function() {
334
+ var self = this;
335
+ var messagesHtml = this.state.messages.map(function(msg) {
336
+ return self._renderMessage(msg);
337
+ }).join('');
338
+
339
+ this.elements.messages.innerHTML = messagesHtml;
340
+ this._scrollToBottom();
341
+ };
342
+
343
+ /**
344
+ * Render single message
345
+ */
346
+ DelaChatInstance.prototype._renderMessage = function(msg) {
347
+ var isUser = msg.source === 0;
348
+ var avatarIcon = isUser ? '👤' : '🤖';
349
+ var messageClass = isUser ? 'delachat-message-user' : 'delachat-message-ai';
350
+
351
+ var actions = '';
352
+ if (!isUser) {
353
+ actions = `
354
+ <div class="delachat-message-actions">
355
+ <button onclick="DelaChat._copyMessage('${msg.historyId}')" class="delachat-btn-action" title="Copy">
356
+ 📋 Copy
357
+ </button>
358
+ <button onclick="DelaChat._regenerate('${msg.historyId}')" class="delachat-btn-action" title="Regenerate">
359
+ 🔄 Regenerate
360
+ </button>
361
+ </div>
362
+ `;
363
+ }
364
+
365
+ return `
366
+ <div class="delachat-message ${messageClass}" data-id="${msg.historyId}">
367
+ <div class="delachat-avatar">
368
+ <span>${avatarIcon}</span>
369
+ </div>
370
+ <div class="delachat-message-content">
371
+ <div class="delachat-message-bubble">
372
+ ${this._escapeHtml(msg.message)}
373
+ </div>
374
+ ${actions}
375
+ </div>
376
+ </div>
377
+ `;
378
+ };
379
+
380
+ /**
381
+ * API Request helper
382
+ */
383
+ DelaChatInstance.prototype._apiRequest = function(method, endpoint, data) {
384
+ var self = this;
385
+ var url = this.config.apiUrl + endpoint;
386
+
387
+ return new Promise(function(resolve, reject) {
388
+ var xhr = new XMLHttpRequest();
389
+ xhr.open(method, url);
390
+ xhr.setRequestHeader('Content-Type', 'application/json');
391
+ xhr.setRequestHeader('X-SessionUid', self.config.sessionUid);
392
+ xhr.setRequestHeader('X-Datasource', self.config.datasource);
393
+ xhr.setRequestHeader('X-ProductId', self.config.productId.toString());
394
+ xhr.setRequestHeader('X-RequestVerificationToken', self.config.token);
395
+
396
+ xhr.onload = function() {
397
+ if (xhr.status >= 200 && xhr.status < 300) {
398
+ try {
399
+ var response = JSON.parse(xhr.responseText);
400
+ resolve(response);
401
+ } catch (e) {
402
+ reject(new Error('Invalid JSON response'));
403
+ }
404
+ } else if (xhr.status === 401) {
405
+ reject(new Error('Unauthorized: Session expired or invalid token'));
406
+ } else {
407
+ reject(new Error('Request failed: ' + xhr.status));
408
+ }
409
+ };
410
+
411
+ xhr.onerror = function() {
412
+ reject(new Error('Network error'));
413
+ };
414
+
415
+ xhr.send(data ? JSON.stringify(data) : null);
416
+ });
417
+ };
418
+
419
+ /**
420
+ * Set loading state
421
+ */
422
+ DelaChatInstance.prototype._setLoading = function(isLoading) {
423
+ this.state.isLoading = isLoading;
424
+ this.elements.loading.style.display = isLoading ? 'flex' : 'none';
425
+ this.elements.input.disabled = isLoading;
426
+ this.elements.btnSend.disabled = isLoading;
427
+ };
428
+
429
+ /**
430
+ * Handle error
431
+ */
432
+ DelaChatInstance.prototype._handleError = function(error) {
433
+ this.state.error = error.message;
434
+ this.elements.error.textContent = error.message;
435
+ this.elements.error.style.display = 'block';
436
+
437
+ if (this.config.onError) {
438
+ this.config.onError.call(this, error);
439
+ }
440
+
441
+ // Auto-hide error after 5 seconds
442
+ var self = this;
443
+ setTimeout(function() {
444
+ self.elements.error.style.display = 'none';
445
+ }, 5000);
446
+ };
447
+
448
+ /**
449
+ * Hide welcome message
450
+ */
451
+ DelaChatInstance.prototype._hideWelcome = function() {
452
+ if (this.elements.welcome) {
453
+ this.elements.welcome.style.display = 'none';
454
+ }
455
+ };
456
+
457
+ /**
458
+ * Scroll to bottom
459
+ */
460
+ DelaChatInstance.prototype._scrollToBottom = function() {
461
+ this.elements.messages.scrollTop = this.elements.messages.scrollHeight;
462
+ };
463
+
464
+ /**
465
+ * Escape HTML
466
+ */
467
+ DelaChatInstance.prototype._escapeHtml = function(text) {
468
+ var div = document.createElement('div');
469
+ div.textContent = text;
470
+ return div.innerHTML.replace(/\n/g, '<br>');
471
+ };
472
+
473
+ /**
474
+ * Public API Methods
475
+ */
476
+
477
+ DelaChatInstance.prototype.open = function() {
478
+ this.state.isOpen = true;
479
+ this.elements.window.style.display = 'flex';
480
+ };
481
+
482
+ DelaChatInstance.prototype.close = function() {
483
+ this.state.isOpen = false;
484
+ this.elements.window.style.display = 'none';
485
+
486
+ if (this.config.onClose) {
487
+ this.config.onClose.call(this);
488
+ }
489
+ };
490
+
491
+ DelaChatInstance.prototype.toggle = function() {
492
+ if (this.state.isOpen) {
493
+ this.close();
494
+ } else {
495
+ this.open();
496
+ }
497
+ };
498
+
499
+ DelaChatInstance.prototype.clearHistory = function() {
500
+ this.state.messages = [];
501
+ this._renderMessages();
502
+ this.elements.welcome.style.display = 'block';
503
+ };
504
+
505
+ DelaChatInstance.prototype.getMessages = function() {
506
+ return this.state.messages.slice();
507
+ };
508
+
509
+ DelaChatInstance.prototype.isOpen = function() {
510
+ return this.state.isOpen;
511
+ };
512
+
513
+ DelaChatInstance.prototype.getConfig = function() {
514
+ return Object.assign({}, this.config);
515
+ };
516
+
517
+ DelaChatInstance.prototype.updateConfig = function(newConfig) {
518
+ Object.assign(this.config, newConfig);
519
+ };
520
+
521
+ DelaChatInstance.prototype.destroy = function() {
522
+ this.container.innerHTML = '';
523
+ this.state = null;
524
+ this.config = null;
525
+ this.elements = null;
526
+ };
527
+
528
+ /**
529
+ * Global DelaChat object
530
+ */
531
+ var DelaChat = {
532
+ version: VERSION,
533
+ instances: [],
534
+
535
+ /**
536
+ * Create new chat instance
537
+ */
538
+ create: function(selector, config) {
539
+ var container = typeof selector === 'string'
540
+ ? document.querySelector(selector)
541
+ : selector;
542
+
543
+ if (!container) {
544
+ throw new Error('Container not found: ' + selector);
545
+ }
546
+
547
+ var instance = new DelaChatInstance(container, config);
548
+ this.instances.push(instance);
549
+ return instance;
550
+ },
551
+
552
+ /**
553
+ * Helper methods for global actions
554
+ */
555
+ _copyMessage: function(messageId) {
556
+ // Find message in all instances
557
+ for (var i = 0; i < this.instances.length; i++) {
558
+ var msg = this.instances[i].state.messages.find(function(m) {
559
+ return m.historyId === messageId;
560
+ });
561
+
562
+ if (msg) {
563
+ if (navigator.clipboard) {
564
+ navigator.clipboard.writeText(msg.message);
565
+ alert('Message copied to clipboard');
566
+ }
567
+ break;
568
+ }
569
+ }
570
+ },
571
+
572
+ _regenerate: function(messageId) {
573
+ alert('Regenerate feature - implement AI regeneration here');
574
+ }
575
+ };
576
+
577
+ // Export to global scope
578
+ window.DelaChat = DelaChat;
579
+
580
+ })(window);