quikchat 1.1.16 → 1.2.4

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 (42) hide show
  1. package/README.md +160 -354
  2. package/dist/build-manifest.json +157 -0
  3. package/dist/quikchat-md-full.cjs.js +2742 -0
  4. package/dist/quikchat-md-full.cjs.js.map +1 -0
  5. package/dist/quikchat-md-full.cjs.min.js +10 -0
  6. package/dist/quikchat-md-full.cjs.min.js.map +1 -0
  7. package/dist/quikchat-md-full.esm.js +2740 -0
  8. package/dist/quikchat-md-full.esm.js.map +1 -0
  9. package/dist/quikchat-md-full.esm.min.js +10 -0
  10. package/dist/quikchat-md-full.esm.min.js.map +1 -0
  11. package/dist/quikchat-md-full.umd.js +2748 -0
  12. package/dist/quikchat-md-full.umd.js.map +1 -0
  13. package/dist/quikchat-md-full.umd.min.js +10 -0
  14. package/dist/quikchat-md-full.umd.min.js.map +1 -0
  15. package/dist/quikchat-md.cjs.js +1641 -0
  16. package/dist/quikchat-md.cjs.js.map +1 -0
  17. package/dist/quikchat-md.cjs.min.js +8 -0
  18. package/dist/quikchat-md.cjs.min.js.map +1 -0
  19. package/dist/quikchat-md.esm.js +1639 -0
  20. package/dist/quikchat-md.esm.js.map +1 -0
  21. package/dist/quikchat-md.esm.min.js +8 -0
  22. package/dist/quikchat-md.esm.min.js.map +1 -0
  23. package/dist/quikchat-md.umd.js +1647 -0
  24. package/dist/quikchat-md.umd.js.map +1 -0
  25. package/dist/quikchat-md.umd.min.js +8 -0
  26. package/dist/quikchat-md.umd.min.js.map +1 -0
  27. package/dist/quikchat.cjs.js +449 -1421
  28. package/dist/quikchat.cjs.js.map +1 -1
  29. package/dist/quikchat.cjs.min.js +1 -1
  30. package/dist/quikchat.cjs.min.js.map +1 -1
  31. package/dist/quikchat.css +753 -226
  32. package/dist/quikchat.esm.js +449 -1421
  33. package/dist/quikchat.esm.js.map +1 -1
  34. package/dist/quikchat.esm.min.js +1 -1
  35. package/dist/quikchat.esm.min.js.map +1 -1
  36. package/dist/quikchat.min.css +1 -1
  37. package/dist/quikchat.umd.js +449 -1421
  38. package/dist/quikchat.umd.js.map +1 -1
  39. package/dist/quikchat.umd.min.js +1 -1
  40. package/dist/quikchat.umd.min.js.map +1 -1
  41. package/package.json +59 -34
  42. package/dist/quikchat.d.ts +0 -194
@@ -3,37 +3,76 @@ function _arrayLikeToArray(r, a) {
3
3
  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
4
4
  return n;
5
5
  }
6
- function _arrayWithoutHoles(r) {
7
- if (Array.isArray(r)) return _arrayLikeToArray(r);
8
- }
9
6
  function _classCallCheck(a, n) {
10
7
  if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
11
8
  }
12
9
  function _defineProperties(e, r) {
13
10
  for (var t = 0; t < r.length; t++) {
14
11
  var o = r[t];
15
- o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
12
+ o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
16
13
  }
17
14
  }
18
15
  function _createClass(e, r, t) {
19
16
  return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
20
- writable: !1
17
+ writable: false
21
18
  }), e;
22
19
  }
20
+ function _createForOfIteratorHelper(r, e) {
21
+ var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
22
+ if (!t) {
23
+ if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
24
+ t && (r = t);
25
+ var n = 0,
26
+ F = function () {};
27
+ return {
28
+ s: F,
29
+ n: function () {
30
+ return n >= r.length ? {
31
+ done: true
32
+ } : {
33
+ done: false,
34
+ value: r[n++]
35
+ };
36
+ },
37
+ e: function (r) {
38
+ throw r;
39
+ },
40
+ f: F
41
+ };
42
+ }
43
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
44
+ }
45
+ var o,
46
+ a = true,
47
+ u = false;
48
+ return {
49
+ s: function () {
50
+ t = t.call(r);
51
+ },
52
+ n: function () {
53
+ var r = t.next();
54
+ return a = r.done, r;
55
+ },
56
+ e: function (r) {
57
+ u = true, o = r;
58
+ },
59
+ f: function () {
60
+ try {
61
+ a || null == t.return || t.return();
62
+ } finally {
63
+ if (u) throw o;
64
+ }
65
+ }
66
+ };
67
+ }
23
68
  function _defineProperty(e, r, t) {
24
69
  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
25
70
  value: t,
26
- enumerable: !0,
27
- configurable: !0,
28
- writable: !0
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true
29
74
  }) : e[r] = t, e;
30
75
  }
31
- function _iterableToArray(r) {
32
- if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
33
- }
34
- function _nonIterableSpread() {
35
- throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
36
- }
37
76
  function ownKeys(e, r) {
38
77
  var t = Object.keys(e);
39
78
  if (Object.getOwnPropertySymbols) {
@@ -47,7 +86,7 @@ function ownKeys(e, r) {
47
86
  function _objectSpread2(e) {
48
87
  for (var r = 1; r < arguments.length; r++) {
49
88
  var t = null != arguments[r] ? arguments[r] : {};
50
- r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
89
+ r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
51
90
  _defineProperty(e, r, t[r]);
52
91
  }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
53
92
  Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
@@ -55,14 +94,11 @@ function _objectSpread2(e) {
55
94
  }
56
95
  return e;
57
96
  }
58
- function _toConsumableArray(r) {
59
- return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
60
- }
61
97
  function _toPrimitive(t, r) {
62
98
  if ("object" != typeof t || !t) return t;
63
99
  var e = t[Symbol.toPrimitive];
64
100
  if (void 0 !== e) {
65
- var i = e.call(t, r );
101
+ var i = e.call(t, r);
66
102
  if ("object" != typeof i) return i;
67
103
  throw new TypeError("@@toPrimitive must return a primitive value.");
68
104
  }
@@ -80,358 +116,11 @@ function _unsupportedIterableToArray(r, a) {
80
116
  }
81
117
  }
82
118
 
83
- // Auto-generated version file - DO NOT EDIT MANUALLY
84
- // This file is automatically updated by tools/updateVersion.js
85
-
86
- var quikchatVersion = {
87
- version: "1.1.16",
88
- license: "BSD-2",
89
- url: "https://github/deftio/quikchat",
90
- fun: true
91
- };
92
-
93
- /**
94
- * Simplified virtual scrolling implementation for QuikChat
95
- * @private
96
- */
97
- var SimpleVirtualScroller = /*#__PURE__*/function () {
98
- function SimpleVirtualScroller(container) {
99
- var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
100
- _classCallCheck(this, SimpleVirtualScroller);
101
- this.container = container;
102
- this.items = [];
103
- this.itemHeight = options.itemHeight || 80; // Default/estimate height
104
- this.buffer = options.buffer || 5;
105
- this.visibleRange = {
106
- start: 0,
107
- end: 0
108
- };
109
- this.renderedElements = new Map();
110
- this.itemHeights = new Map(); // Cache actual heights
111
- this.itemPositions = new Map(); // Cache positions
112
- this.totalHeight = 0;
113
- this.onRenderItem = options.onRenderItem || function () {};
114
- this._initStructure();
115
- this._attachScrollListener();
116
- }
117
- return _createClass(SimpleVirtualScroller, [{
118
- key: "_initStructure",
119
- value: function _initStructure() {
120
- var _this = this;
121
- var existingClasses = this.container.className;
122
- this.container.innerHTML = '';
123
- this.container.className = existingClasses;
124
- this.container.style.position = 'relative';
125
- this.container.style.overflow = 'auto';
126
-
127
- // Ensure container has height
128
- if (!this.container.style.height && this.container.offsetHeight === 0) {
129
- this.container.style.height = '100%';
130
- }
131
- this.spacer = document.createElement('div');
132
- this.spacer.style.cssText = 'position: absolute; top: 0; left: 0; width: 1px; pointer-events: none; z-index: -1;';
133
- this.content = document.createElement('div');
134
- this.content.style.cssText = 'position: relative; width: 100%;';
135
- this.container.appendChild(this.spacer);
136
- this.container.appendChild(this.content);
137
-
138
- // Initial render after structure is set up
139
- setTimeout(function () {
140
- _this._updateVisibleRange();
141
- _this._renderVisibleItems();
142
- }, 0);
143
- }
144
- }, {
145
- key: "_attachScrollListener",
146
- value: function _attachScrollListener() {
147
- var _this2 = this;
148
- var ticking = false;
149
- this.container.addEventListener('scroll', function () {
150
- if (!ticking) {
151
- requestAnimationFrame(function () {
152
- _this2._updateVisibleRange();
153
- _this2._renderVisibleItems();
154
- ticking = false;
155
- });
156
- ticking = true;
157
- }
158
- });
159
- }
160
- }, {
161
- key: "_getItemHeight",
162
- value: function _getItemHeight(index) {
163
- // Return cached height or estimate
164
- return this.itemHeights.get(index) || this.itemHeight;
165
- }
166
- }, {
167
- key: "_getItemPosition",
168
- value: function _getItemPosition(index) {
169
- // Return cached position or calculate
170
- if (this.itemPositions.has(index)) {
171
- return this.itemPositions.get(index);
172
- }
173
-
174
- // Calculate position based on previous items
175
- var position = 0;
176
- for (var i = 0; i < index; i++) {
177
- position += this._getItemHeight(i);
178
- }
179
- this.itemPositions.set(index, position);
180
- return position;
181
- }
182
- }, {
183
- key: "_recalculatePositions",
184
- value: function _recalculatePositions(fromIndex) {
185
- // Recalculate positions from a specific index onwards
186
- var position = fromIndex > 0 ? this._getItemPosition(fromIndex) : 0;
187
- for (var i = fromIndex; i < this.items.length; i++) {
188
- this.itemPositions.set(i, position);
189
- position += this._getItemHeight(i);
190
-
191
- // Update position of rendered elements
192
- var element = this.renderedElements.get(i);
193
- if (element) {
194
- element.style.top = "".concat(this.itemPositions.get(i), "px");
195
- }
196
- }
197
-
198
- // Update total height
199
- this.totalHeight = position;
200
- this.spacer.style.height = "".concat(this.totalHeight, "px");
201
- }
202
- }, {
203
- key: "_updateVisibleRange",
204
- value: function _updateVisibleRange() {
205
- var scrollTop = this.container.scrollTop;
206
- var viewportHeight = this.container.clientHeight;
207
-
208
- // Find first visible item based on positions
209
- var startIndex = 0;
210
- var endIndex = this.items.length;
211
-
212
- // Use cached positions when available
213
- if (this.itemPositions.size > 0) {
214
- // Find start index
215
- for (var i = 0; i < this.items.length; i++) {
216
- var pos = this._getItemPosition(i);
217
- if (pos + this._getItemHeight(i) > scrollTop) {
218
- startIndex = Math.max(0, i - this.buffer);
219
- break;
220
- }
221
- }
222
-
223
- // Find end index
224
- for (var _i = startIndex; _i < this.items.length; _i++) {
225
- var _pos = this._getItemPosition(_i);
226
- if (_pos > scrollTop + viewportHeight) {
227
- endIndex = Math.min(this.items.length, _i + this.buffer);
228
- break;
229
- }
230
- }
231
- } else {
232
- // Fallback to estimate if no positions cached yet
233
- startIndex = Math.max(0, Math.floor(scrollTop / this.itemHeight) - this.buffer);
234
- endIndex = Math.min(this.items.length, Math.ceil((scrollTop + viewportHeight) / this.itemHeight) + this.buffer);
235
- }
236
- this.visibleRange = {
237
- start: startIndex,
238
- end: endIndex
239
- };
240
- }
241
- }, {
242
- key: "_renderVisibleItems",
243
- value: function _renderVisibleItems() {
244
- var _this3 = this;
245
- var _this$visibleRange = this.visibleRange,
246
- start = _this$visibleRange.start,
247
- end = _this$visibleRange.end;
248
-
249
- // Remove elements outside range
250
- this.renderedElements.forEach(function (element, index) {
251
- if (index < start || index >= end) {
252
- element.remove();
253
- _this3.renderedElements["delete"](index);
254
- // Don't clear height cache - we might need it again
255
- }
256
- });
257
-
258
- // Add new visible elements
259
- var _loop = function _loop(i) {
260
- var item = _this3.items[i];
261
- if (!item || _this3.renderedElements.has(i)) return 1; // continue
262
- var element = _this3._createItemElement(item, i);
263
- element.style.position = 'absolute';
264
- element.style.top = "".concat(_this3._getItemPosition(i), "px");
265
- element.style.left = '0';
266
- element.style.right = '0';
267
- _this3.renderedElements.set(i, element);
268
- _this3.content.appendChild(element);
269
-
270
- // Measure actual height after rendering
271
- requestAnimationFrame(function () {
272
- if (_this3.renderedElements.has(i)) {
273
- var actualHeight = element.offsetHeight;
274
- if (actualHeight && actualHeight !== _this3._getItemHeight(i)) {
275
- _this3.itemHeights.set(i, actualHeight);
276
- _this3._recalculatePositions(i);
277
- }
278
- }
279
- });
280
- };
281
- for (var i = start; i < end; i++) {
282
- if (_loop(i)) continue;
283
- }
284
- this.onRenderItem(this.content);
285
- }
286
- }, {
287
- key: "_createItemElement",
288
- value: function _createItemElement(item, index) {
289
- var messageDiv = document.createElement('div');
290
- messageDiv.className = "quikchat-message quikchat-message-".concat(item.align || 'left', " quikchat-msgid-").concat(String(item.msgid).padStart(10, '0'));
291
- messageDiv.setAttribute('data-index', index);
292
- messageDiv.setAttribute('data-msgid', item.msgid);
293
- if (item.tags && item.tags.length > 0) {
294
- item.tags.forEach(function (tag) {
295
- return messageDiv.classList.add("quikchat-tag-".concat(tag));
296
- });
297
- }
298
- var userDiv = document.createElement('div');
299
- userDiv.className = 'quikchat-message-user';
300
- userDiv.innerHTML = item.userString || '';
301
- var contentDiv = document.createElement('div');
302
- contentDiv.className = 'quikchat-message-content';
303
- contentDiv.innerHTML = item.content || '';
304
- messageDiv.appendChild(userDiv);
305
- messageDiv.appendChild(contentDiv);
306
- if (!item.visible) {
307
- messageDiv.style.display = 'none';
308
- }
309
- return messageDiv;
310
- }
311
- }, {
312
- key: "addItem",
313
- value: function addItem(item) {
314
- this.items.push(item);
315
- var index = this.items.length - 1;
316
-
317
- // Calculate position for new item
318
- var position = index > 0 ? this._getItemPosition(index - 1) + this._getItemHeight(index - 1) : 0;
319
- this.itemPositions.set(index, position);
320
-
321
- // Update total height (using estimate for new item)
322
- this.totalHeight = position + this.itemHeight;
323
- this.spacer.style.height = "".concat(this.totalHeight, "px");
324
- if (index >= this.visibleRange.start && index < this.visibleRange.end) {
325
- this._renderVisibleItems();
326
- }
327
- if (item.scrollIntoView) {
328
- this.container.scrollTop = this.container.scrollHeight;
329
- }
330
- return index;
331
- }
332
- }, {
333
- key: "addItems",
334
- value: function addItems(items) {
335
- var _this$items;
336
- // Batch add items for better performance
337
- var startLength = this.items.length;
338
- (_this$items = this.items).push.apply(_this$items, _toConsumableArray(items));
339
-
340
- // Calculate positions for new items
341
- var position = startLength > 0 ? this._getItemPosition(startLength - 1) + this._getItemHeight(startLength - 1) : 0;
342
- for (var i = startLength; i < this.items.length; i++) {
343
- this.itemPositions.set(i, position);
344
- position += this.itemHeight; // Use estimate for new items
345
- }
346
- this.totalHeight = position;
347
- this.spacer.style.height = "".concat(this.totalHeight, "px");
348
-
349
- // Update visible range and render
350
- this._updateVisibleRange();
351
- this._renderVisibleItems();
352
-
353
- // Handle scrollIntoView for last item if needed
354
- if (items.length > 0 && items[items.length - 1].scrollIntoView) {
355
- this.container.scrollTop = this.container.scrollHeight;
356
- }
357
- }
358
- }, {
359
- key: "clear",
360
- value: function clear() {
361
- this.items = [];
362
- this.renderedElements.clear();
363
- this.itemHeights.clear();
364
- this.itemPositions.clear();
365
- this.totalHeight = 0;
366
- this.content.innerHTML = '';
367
- this.spacer.style.height = '0px';
368
- this.visibleRange = {
369
- start: 0,
370
- end: 0
371
- };
372
-
373
- // Force update after clear
374
- this._updateVisibleRange();
375
- this._renderVisibleItems();
376
- }
377
- }, {
378
- key: "updateItem",
379
- value: function updateItem(index, updates) {
380
- if (index >= 0 && index < this.items.length) {
381
- this.items[index] = _objectSpread2(_objectSpread2({}, this.items[index]), updates);
382
- if (this.renderedElements.has(index)) {
383
- this._renderVisibleItems();
384
- }
385
- }
386
- }
387
- }, {
388
- key: "destroy",
389
- value: function destroy() {
390
- this.container.innerHTML = '';
391
- this.items = [];
392
- this.renderedElements.clear();
393
- }
394
- }]);
395
- }();
396
- /**
397
- * QuikChat - A zero-dependency JavaScript chat widget for modern web applications
398
- * @class quikchat
399
- * @version 1.1.16-dev1
400
- */
401
119
  var quikchat = /*#__PURE__*/function () {
402
120
  /**
403
- * Creates a new QuikChat instance
404
- * @constructor
405
- * @param {string|HTMLElement} parentElement - CSS selector or DOM element to attach the chat widget to
406
- * @param {Function} [onSend] - Callback function triggered when user sends a message
407
- * @param {Object} [options] - Configuration options
408
- * @param {string} [options.theme='quikchat-theme-light'] - CSS theme class name
409
- * @param {boolean} [options.trackHistory=true] - Whether to track message history
410
- * @param {Object} [options.titleArea] - Title area configuration
411
- * @param {string} [options.titleArea.title='Chat'] - Title text/HTML
412
- * @param {boolean} [options.titleArea.show=false] - Whether to show title area initially
413
- * @param {'left'|'center'|'right'} [options.titleArea.align='center'] - Title alignment
414
- * @param {Object} [options.messagesArea] - Messages area configuration
415
- * @param {boolean} [options.messagesArea.alternating=true] - Alternate message colors
416
- * @param {Object} [options.inputArea] - Input area configuration
417
- * @param {boolean} [options.inputArea.show=true] - Whether to show input area initially
418
- * @param {boolean} [options.sendOnEnter=true] - Send message on Enter key
419
- * @param {boolean} [options.sendOnShiftEnter=false] - Send message on Shift+Enter
420
- * @param {string} [options.instanceClass=''] - Additional CSS class for the widget instance
421
- * @example
422
- * // Basic usage
423
- * const chat = new quikchat('#chat-container', (instance, message) => {
424
- * console.log('User sent:', message);
425
- * });
426
- *
427
- * @example
428
- * // With options
429
- * const chat = new quikchat('#chat', handleMessage, {
430
- * theme: 'quikchat-theme-dark',
431
- * titleArea: { title: 'Support Chat', show: true },
432
- * sendOnEnter: false,
433
- * sendOnShiftEnter: true
434
- * });
121
+ *
122
+ * @param string or DOM element parentElement
123
+ * @param {*} meta
435
124
  */
436
125
  function quikchat(parentElement) {
437
126
  var onSend = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
@@ -440,6 +129,7 @@ var quikchat = /*#__PURE__*/function () {
440
129
  var defaultOpts = {
441
130
  theme: 'quikchat-theme-light',
442
131
  trackHistory: true,
132
+ showTimestamps: false,
443
133
  titleArea: {
444
134
  title: "Chat",
445
135
  show: false,
@@ -447,31 +137,19 @@ var quikchat = /*#__PURE__*/function () {
447
137
  },
448
138
  messagesArea: {
449
139
  alternating: true
450
- },
451
- inputArea: {
452
- show: true
453
- },
454
- sendOnEnter: true,
455
- sendOnShiftEnter: false,
456
- instanceClass: '',
457
- virtualScrolling: true,
458
- // Default to true for better performance
459
- virtualScrollingThreshold: 500 // Lower threshold since it performs so well
140
+ }
460
141
  };
461
142
  var meta = _objectSpread2(_objectSpread2({}, defaultOpts), options); // merge options with defaults
462
143
 
463
144
  if (typeof parentElement === 'string') {
464
145
  parentElement = document.querySelector(parentElement);
465
146
  }
466
- //console.log(parentElement, meta);
467
147
  this._parentElement = parentElement;
468
148
  this._theme = meta.theme;
469
149
  this._onSend = onSend ? onSend : function () {}; // call back function for onSend
150
+ this._messageFormatter = meta.messageFormatter || null;
151
+ this._sanitize = meta.sanitize || false;
470
152
  this._createWidget();
471
- if (meta.instanceClass) {
472
- this._chatWidget.classList.add(meta.instanceClass);
473
- }
474
-
475
153
  // title area
476
154
  if (meta.titleArea) {
477
155
  this.titleAreaSetContents(meta.titleArea.title, meta.titleArea.align);
@@ -485,94 +163,30 @@ var quikchat = /*#__PURE__*/function () {
485
163
  if (meta.messagesArea) {
486
164
  this.messagesAreaAlternateColors(meta.messagesArea.alternating);
487
165
  }
488
-
489
- // input area
490
- if (meta.inputArea) {
491
- if (meta.inputArea.show === true) this.inputAreaShow();else this.inputAreaHide();
166
+ // timestamps
167
+ if (meta.showTimestamps) {
168
+ this.messagesAreaShowTimestamps(true);
169
+ }
170
+ // direction (ltr/rtl)
171
+ if (meta.direction) {
172
+ this.setDirection(meta.direction);
492
173
  }
493
174
  // plumbing
494
175
  this._attachEventListeners();
495
- this.trackHistory = meta.trackHistory || true;
176
+ this.trackHistory = meta.trackHistory !== false;
496
177
  this._historyLimit = 10000000;
497
178
  this._history = [];
498
- this._activeTags = new Set();
499
-
500
- // send on enter / shift enter
501
- this.sendOnEnter = meta.sendOnEnter;
502
- this.sendOnShiftEnter = meta.sendOnShiftEnter;
503
-
504
- // Virtual scrolling setup
505
- this.virtualScrollingEnabled = meta.virtualScrolling;
506
- this.virtualScrollingThreshold = meta.virtualScrollingThreshold;
507
- this.virtualScroller = null;
508
-
509
- // Don't initialize virtual scrolling immediately - wait for threshold
510
- // Virtual scrolling will be initialized when message count exceeds threshold
511
179
  }
512
-
513
- /**
514
- * Initialize virtual scrolling
515
- * @private
516
- */
517
180
  return _createClass(quikchat, [{
518
- key: "_initVirtualScrolling",
519
- value: function _initVirtualScrolling() {
520
- var _this4 = this;
521
- if (this.virtualScrollingEnabled && this._messagesArea && !this.virtualScroller) {
522
- // Check if we've hit the threshold (or about to with the next message)
523
- if (this._history.length >= this.virtualScrollingThreshold - 1) {
524
- this.virtualScroller = new SimpleVirtualScroller(this._messagesArea, {
525
- itemHeight: 80,
526
- buffer: 5,
527
- onRenderItem: function onRenderItem(content) {
528
- // Apply alternating colors if enabled
529
- if (_this4._messagesArea.classList.contains('quikchat-messages-area-alt')) {
530
- _this4._updateMessageStyles();
531
- }
532
- }
533
- });
534
-
535
- // Migrate existing messages to virtual scroller
536
- this._migrateToVirtualScrolling();
537
- }
538
- }
539
- }
540
-
541
- /**
542
- * Migrate existing messages to virtual scrolling
543
- * @private
544
- */
545
- }, {
546
- key: "_migrateToVirtualScrolling",
547
- value: function _migrateToVirtualScrolling() {
548
- if (!this.virtualScroller) return;
549
-
550
- // Clear DOM but keep history
551
- this._messagesArea.innerHTML = '';
552
-
553
- // Add all messages to virtual scroller
554
- var items = this._history.map(function (msg) {
555
- return {
556
- msgid: msg.msgid,
557
- content: msg.content,
558
- userString: msg.userString,
559
- align: msg.align,
560
- role: msg.role,
561
- visible: msg.visible,
562
- tags: msg.tags || [],
563
- scrollIntoView: false
564
- };
565
- });
566
- this.virtualScroller.addItems(items);
567
- }
568
- }, {
569
181
  key: "_createWidget",
570
182
  value: function _createWidget() {
571
- var widgetHTML = "\n <div class=\"quikchat-base ".concat(this.theme, "\">\n <div class=\"quikchat-title-area\">\n <span style=\"font-size: 1.5em; font-weight: 600;\">Title Area</span>\n </div>\n <div class=\"quikchat-messages-area\"></div>\n <div class=\"quikchat-input-area\">\n <textarea class=\"quikchat-input-textbox\"></textarea>\n <button class=\"quikchat-input-send-btn\">Send</button>\n </div>\n </div>\n ");
183
+ var widgetHTML = "\n <div class=\"quikchat-base ".concat(this.theme, "\">\n <div class=\"quikchat-title-area\"></div>\n <div class=\"quikchat-messages-wrapper\"><div class=\"quikchat-messages-area\" role=\"log\" aria-live=\"polite\" aria-label=\"Chat messages\"></div><button class=\"quikchat-scroll-bottom\" aria-label=\"Scroll to bottom\"></button></div>\n <div class=\"quikchat-input-area\">\n <textarea class=\"quikchat-input-textbox\" rows=\"1\" aria-label=\"Type a message\"></textarea>\n <button class=\"quikchat-input-send-btn\">Send</button>\n </div>\n </div>\n ");
572
184
  this._parentElement.innerHTML = widgetHTML;
573
185
  this._chatWidget = this._parentElement.querySelector('.quikchat-base');
574
186
  this._titleArea = this._chatWidget.querySelector('.quikchat-title-area');
187
+ this._messagesWrapper = this._chatWidget.querySelector('.quikchat-messages-wrapper');
575
188
  this._messagesArea = this._chatWidget.querySelector('.quikchat-messages-area');
189
+ this._scrollBottomBtn = this._messagesWrapper.querySelector('.quikchat-scroll-bottom');
576
190
  this._inputArea = this._chatWidget.querySelector('.quikchat-input-area');
577
191
  this._textEntry = this._inputArea.querySelector('.quikchat-input-textbox');
578
192
  this._sendButton = this._inputArea.querySelector('.quikchat-input-send-btn');
@@ -585,40 +199,55 @@ var quikchat = /*#__PURE__*/function () {
585
199
  }, {
586
200
  key: "_attachEventListeners",
587
201
  value: function _attachEventListeners() {
588
- var _this5 = this;
589
- this._sendButton.addEventListener('click', function (event) {
590
- event.preventDefault();
591
- _this5._onSend(_this5, _this5._textEntry.value.trim());
592
- });
593
- window.addEventListener('resize', function () {
594
- return _this5._handleContainerResize();
595
- });
596
- this._chatWidget.addEventListener('resize', function () {
597
- return _this5._handleContainerResize();
202
+ var _this = this;
203
+ this._sendButton.addEventListener('click', function () {
204
+ var text = _this._textEntry.value.trim();
205
+ if (text === '') return;
206
+ _this._onSend(_this, text);
598
207
  });
599
208
  this._textEntry.addEventListener('keydown', function (event) {
600
- // Check if Shift + Enter is pressed then we just do carraige
209
+ // Check if Shift + Enter is pressed
601
210
  if (event.shiftKey && event.keyCode === 13) {
602
- // Prevent default behavior (adding new line)
603
- if (_this5.sendOnShiftEnter) {
604
- event.preventDefault();
605
- _this5._onSend(_this5, _this5._textEntry.value.trim());
606
- }
607
- } else if (event.keyCode === 13) {
608
- // Enter but not Shift + Enter
609
- if (_this5.sendOnEnter) {
610
- event.preventDefault();
611
- _this5._onSend(_this5, _this5._textEntry.value.trim());
612
- }
211
+ event.preventDefault();
212
+ var text = _this._textEntry.value.trim();
213
+ if (text === '') return;
214
+ _this._onSend(_this, text);
613
215
  }
614
216
  });
217
+
218
+ // Auto-grow textarea
219
+ this._textEntry.addEventListener('input', function () {
220
+ return _this._autoGrowTextarea();
221
+ });
615
222
  this._messagesArea.addEventListener('scroll', function () {
616
- var _this5$_messagesArea = _this5._messagesArea,
617
- scrollTop = _this5$_messagesArea.scrollTop,
618
- scrollHeight = _this5$_messagesArea.scrollHeight,
619
- clientHeight = _this5$_messagesArea.clientHeight;
620
- _this5.userScrolledUp = scrollTop + clientHeight < scrollHeight;
223
+ var _this$_messagesArea = _this._messagesArea,
224
+ scrollTop = _this$_messagesArea.scrollTop,
225
+ scrollHeight = _this$_messagesArea.scrollHeight,
226
+ clientHeight = _this$_messagesArea.clientHeight;
227
+ _this.userScrolledUp = scrollTop + clientHeight < scrollHeight - 1;
228
+ _this._updateScrollBottomBtn();
229
+ });
230
+
231
+ // Scroll-to-bottom button
232
+ this._scrollBottomBtn.addEventListener('click', function () {
233
+ return _this.scrollToBottom();
621
234
  });
235
+
236
+ // Ctrl+End to scroll to bottom
237
+ this._chatWidget.addEventListener('keydown', function (event) {
238
+ if (event.ctrlKey && event.key === 'End') {
239
+ event.preventDefault();
240
+ _this.scrollToBottom();
241
+ }
242
+ });
243
+
244
+ // Use ResizeObserver to detect parent container resize
245
+ if (typeof ResizeObserver !== 'undefined') {
246
+ this._resizeObserver = new ResizeObserver(function () {
247
+ return _this._handleContainerResize();
248
+ });
249
+ this._resizeObserver.observe(this._parentElement);
250
+ }
622
251
  }
623
252
 
624
253
  // set the onSend function callback.
@@ -633,54 +262,16 @@ var quikchat = /*#__PURE__*/function () {
633
262
  value: function setCallbackonMessageAdded(callback) {
634
263
  this._onMessageAdded = callback;
635
264
  }
636
-
637
- /**
638
- * Sets the callback function for when content is appended to a message
639
- * @param {Function} callback - Function to call when content is appended
640
- * @param {quikchat} callback.instance - The QuikChat instance
641
- * @param {number} callback.msgId - The ID of the message being appended to
642
- * @param {string} callback.content - The content being appended
643
- * @since 1.1.15
644
- * @example
645
- * chat.setCallbackonMessageAppend((instance, msgId, content) => {
646
- * console.log(`Appended "${content}" to message ${msgId}`);
647
- * });
648
- */
649
265
  }, {
650
266
  key: "setCallbackonMessageAppend",
651
267
  value: function setCallbackonMessageAppend(callback) {
652
268
  this._onMessageAppend = callback;
653
269
  }
654
-
655
- /**
656
- * Sets the callback function for when a message's content is replaced
657
- * @param {Function} callback - Function to call when content is replaced
658
- * @param {quikchat} callback.instance - The QuikChat instance
659
- * @param {number} callback.msgId - The ID of the message being replaced
660
- * @param {string} callback.content - The new content
661
- * @since 1.1.15
662
- * @example
663
- * chat.setCallbackonMessageReplace((instance, msgId, content) => {
664
- * console.log(`Message ${msgId} replaced with: ${content}`);
665
- * });
666
- */
667
270
  }, {
668
271
  key: "setCallbackonMessageReplace",
669
272
  value: function setCallbackonMessageReplace(callback) {
670
273
  this._onMessageReplace = callback;
671
274
  }
672
-
673
- /**
674
- * Sets the callback function for when a message is deleted
675
- * @param {Function} callback - Function to call when a message is deleted
676
- * @param {quikchat} callback.instance - The QuikChat instance
677
- * @param {number} callback.msgId - The ID of the deleted message
678
- * @since 1.1.15
679
- * @example
680
- * chat.setCallbackonMessageDelete((instance, msgId) => {
681
- * console.log(`Message ${msgId} was deleted`);
682
- * });
683
- */
684
275
  }, {
685
276
  key: "setCallbackonMessageDelete",
686
277
  value: function setCallbackonMessageDelete(callback) {
@@ -688,46 +279,25 @@ var quikchat = /*#__PURE__*/function () {
688
279
  }
689
280
 
690
281
  // Public methods
691
- /**
692
- * Toggles the visibility of the title area
693
- * @returns {void}
694
- */
695
282
  }, {
696
283
  key: "titleAreaToggle",
697
284
  value: function titleAreaToggle() {
698
- this._titleArea.style.display === 'none' ? this.titleAreaShow() : this.titleAreaHide();
285
+ if (this._titleArea.style.display === 'none') {
286
+ this.titleAreaShow();
287
+ } else {
288
+ this.titleAreaHide();
289
+ }
699
290
  }
700
-
701
- /**
702
- * Shows the title area
703
- * @returns {void}
704
- */
705
291
  }, {
706
292
  key: "titleAreaShow",
707
293
  value: function titleAreaShow() {
708
294
  this._titleArea.style.display = '';
709
- this._adjustMessagesAreaHeight();
710
295
  }
711
-
712
- /**
713
- * Hides the title area
714
- * @returns {void}
715
- */
716
296
  }, {
717
297
  key: "titleAreaHide",
718
298
  value: function titleAreaHide() {
719
299
  this._titleArea.style.display = 'none';
720
- this._adjustMessagesAreaHeight();
721
300
  }
722
-
723
- /**
724
- * Sets the contents of the title area
725
- * @param {string} title - HTML content to display in the title area
726
- * @param {'left'|'center'|'right'} [align='center'] - Text alignment
727
- * @returns {void}
728
- * @example
729
- * chat.titleAreaSetContents('<h2>Support Chat</h2>', 'center');
730
- */
731
301
  }, {
732
302
  key: "titleAreaSetContents",
733
303
  value: function titleAreaSetContents(title) {
@@ -735,11 +305,6 @@ var quikchat = /*#__PURE__*/function () {
735
305
  this._titleArea.innerHTML = title;
736
306
  this._titleArea.style.textAlign = align;
737
307
  }
738
-
739
- /**
740
- * Gets the current contents of the title area
741
- * @returns {string} The HTML content of the title area
742
- */
743
308
  }, {
744
309
  key: "titleAreaGetContents",
745
310
  value: function titleAreaGetContents() {
@@ -748,48 +313,133 @@ var quikchat = /*#__PURE__*/function () {
748
313
  }, {
749
314
  key: "inputAreaToggle",
750
315
  value: function inputAreaToggle() {
751
- this._inputArea.classList.toggle('hidden');
752
- this._inputArea.style.display === 'none' ? this.inputAreaShow() : this.inputAreaHide();
316
+ if (this._inputArea.style.display === 'none') {
317
+ this.inputAreaShow();
318
+ } else {
319
+ this.inputAreaHide();
320
+ }
753
321
  }
754
322
  }, {
755
323
  key: "inputAreaShow",
756
324
  value: function inputAreaShow() {
757
325
  this._inputArea.style.display = '';
758
- this._adjustMessagesAreaHeight();
759
326
  }
760
327
  }, {
761
328
  key: "inputAreaHide",
762
329
  value: function inputAreaHide() {
763
330
  this._inputArea.style.display = 'none';
764
- this._adjustMessagesAreaHeight();
765
331
  }
766
332
  }, {
767
- key: "_adjustMessagesAreaHeight",
768
- value: function _adjustMessagesAreaHeight() {
769
- var hiddenElements = _toConsumableArray(this._chatWidget.children).filter(function (child) {
770
- return child.classList.contains('hidden');
771
- });
772
- var totalHiddenHeight = hiddenElements.reduce(function (sum, child) {
773
- return sum + child.offsetHeight;
774
- }, 0);
775
- var containerHeight = this._chatWidget.offsetHeight;
776
- this._messagesArea.style.height = "calc(100% - ".concat(containerHeight - totalHiddenHeight, "px)");
333
+ key: "inputAreaSetEnabled",
334
+ value: function inputAreaSetEnabled(enabled) {
335
+ this._textEntry.disabled = !enabled;
336
+ this._sendButton.disabled = !enabled;
337
+ }
338
+ }, {
339
+ key: "inputAreaSetButtonText",
340
+ value: function inputAreaSetButtonText(text) {
341
+ this._sendButton.textContent = text;
342
+ }
343
+ }, {
344
+ key: "inputAreaGetButtonText",
345
+ value: function inputAreaGetButtonText() {
346
+ return this._sendButton.textContent;
347
+ }
348
+ }, {
349
+ key: "setDirection",
350
+ value: function setDirection(dir) {
351
+ var d = dir === 'rtl' ? 'rtl' : 'ltr';
352
+ this._chatWidget.setAttribute('dir', d);
353
+ if (d === 'rtl') {
354
+ this._chatWidget.classList.add('quikchat-rtl');
355
+ } else {
356
+ this._chatWidget.classList.remove('quikchat-rtl');
357
+ }
358
+ }
359
+ }, {
360
+ key: "getDirection",
361
+ value: function getDirection() {
362
+ return this._chatWidget.getAttribute('dir') || 'ltr';
777
363
  }
778
364
  }, {
779
365
  key: "_handleContainerResize",
780
366
  value: function _handleContainerResize() {
781
- this._adjustMessagesAreaHeight();
782
- this._adjustSendButtonWidth();
783
- return true;
367
+ // Layout is handled by CSS flexbox — no JS height calculation needed.
368
+ // This hook exists for future use or custom resize callbacks.
784
369
  }
785
370
  }, {
786
- key: "_adjustSendButtonWidth",
787
- value: function _adjustSendButtonWidth() {
788
- var sendButtonText = this._sendButton.textContent.trim();
789
- var fontSize = parseFloat(getComputedStyle(this._sendButton).fontSize);
790
- var minWidth = fontSize * sendButtonText.length + 16;
791
- this._sendButton.style.minWidth = "".concat(minWidth, "px");
792
- return true;
371
+ key: "scrollToBottom",
372
+ value: function scrollToBottom() {
373
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
374
+ this.userScrolledUp = false;
375
+ this._updateScrollBottomBtn();
376
+ }
377
+ }, {
378
+ key: "_updateScrollBottomBtn",
379
+ value: function _updateScrollBottomBtn() {
380
+ if (this.userScrolledUp) {
381
+ this._scrollBottomBtn.classList.add('quikchat-scroll-bottom-visible');
382
+ } else {
383
+ this._scrollBottomBtn.classList.remove('quikchat-scroll-bottom-visible');
384
+ }
385
+ }
386
+ }, {
387
+ key: "_autoGrowTextarea",
388
+ value: function _autoGrowTextarea() {
389
+ var el = this._textEntry;
390
+ el.style.height = 'auto';
391
+ var maxHeight = parseInt(getComputedStyle(el).getPropertyValue('--quikchat-input-max-height')) || 120;
392
+ el.style.height = Math.min(el.scrollHeight, maxHeight) + 'px';
393
+ el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';
394
+ }
395
+ }, {
396
+ key: "_formatTimestamp",
397
+ value: function _formatTimestamp(isoString) {
398
+ var d = new Date(isoString);
399
+ var h = d.getHours();
400
+ var m = String(d.getMinutes()).padStart(2, '0');
401
+ var ampm = h >= 12 ? 'PM' : 'AM';
402
+ var h12 = h % 12 || 12;
403
+ return h12 + ':' + m + ' ' + ampm;
404
+ }
405
+ }, {
406
+ key: "messagesAreaShowTimestamps",
407
+ value: function messagesAreaShowTimestamps(show) {
408
+ if (show) {
409
+ this._messagesArea.classList.add('quikchat-show-timestamps');
410
+ } else {
411
+ this._messagesArea.classList.remove('quikchat-show-timestamps');
412
+ }
413
+ }
414
+ }, {
415
+ key: "messagesAreaShowTimestampsGet",
416
+ value: function messagesAreaShowTimestampsGet() {
417
+ return this._messagesArea.classList.contains('quikchat-show-timestamps');
418
+ }
419
+ }, {
420
+ key: "messagesAreaShowTimestampsToggle",
421
+ value: function messagesAreaShowTimestampsToggle() {
422
+ this._messagesArea.classList.toggle('quikchat-show-timestamps');
423
+ }
424
+ }, {
425
+ key: "_escapeHTML",
426
+ value: function _escapeHTML(str) {
427
+ var div = document.createElement('div');
428
+ div.textContent = str;
429
+ return div.innerHTML;
430
+ }
431
+ }, {
432
+ key: "_processContent",
433
+ value: function _processContent(content) {
434
+ if (this._sanitize === true) {
435
+ content = this._escapeHTML(content);
436
+ } else if (typeof this._sanitize === 'function') {
437
+ content = this._sanitize(content);
438
+ }
439
+ if (this._messageFormatter) {
440
+ content = this._messageFormatter(content);
441
+ }
442
+ return content;
793
443
  }
794
444
 
795
445
  //messagesArea functions
@@ -814,149 +464,67 @@ var quikchat = /*#__PURE__*/function () {
814
464
  value: function messagesAreaAlternateColorsGet() {
815
465
  return this._messagesArea.classList.contains('quikchat-messages-area-alt');
816
466
  }
817
- /**
818
- * Adds a new message to the chat with full configuration options
819
- * @param {Object} input - Message configuration object
820
- * @param {string} [input.content=''] - Message content (HTML allowed)
821
- * @param {string} [input.userString='user'] - Display name for the message sender
822
- * @param {'left'|'right'|'center'} [input.align='right'] - Message alignment
823
- * @param {string} [input.role='user'] - Role identifier (user, assistant, system)
824
- * @param {number} [input.userID=-1] - User ID for the message
825
- * @param {string|false} [input.timestamp=false] - ISO timestamp or false for auto
826
- * @param {string|false} [input.updatedtime=false] - Last updated timestamp
827
- * @param {boolean|'smart'} [input.scrollIntoView=true] - Scroll behavior (true/false/'smart')
828
- * @param {boolean} [input.visible=true] - Whether message is initially visible
829
- * @param {string[]} [input.tags=[]] - Tags for message categorization
830
- * @returns {number} Message ID for the newly added message
831
- * @example
832
- * const msgId = chat.messageAddFull({
833
- * content: 'Hello!',
834
- * userString: 'Bot',
835
- * align: 'left',
836
- * scrollIntoView: 'smart',
837
- * tags: ['greeting']
838
- * });
839
- */
467
+ // message functions
840
468
  }, {
841
469
  key: "messageAddFull",
842
470
  value: function messageAddFull() {
843
- var _this6 = this;
844
471
  var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
845
472
  content: "",
846
473
  userString: "user",
847
474
  align: "right",
848
475
  role: "user",
849
- userID: -1,
850
- timestamp: false,
851
- updatedtime: false,
852
- scrollIntoView: true,
853
- visible: true,
854
- tags: []
476
+ userID: -1
855
477
  };
856
478
  var msgid = this.msgid;
479
+ var messageDiv = document.createElement('div');
480
+ var msgidClass = 'quikchat-msgid-' + String(msgid).padStart(10, '0');
481
+ messageDiv.classList.add('quikchat-message', msgidClass);
482
+ messageDiv.classList.add('quikchat-role-' + (input.role || 'user'));
483
+ messageDiv.classList.add('quikchat-align-' + (input.align || 'right'));
857
484
  this.msgid++;
858
- var messageDiv = null; // Initialize messageDiv to null
485
+ messageDiv.classList.add(this._messagesArea.children.length % 2 === 1 ? 'quikchat-message-1' : 'quikchat-message-2');
859
486
 
860
- // Check if we should initialize virtual scrolling
861
- if (this.virtualScrollingEnabled && !this.virtualScroller && this._history.length >= this.virtualScrollingThreshold - 1) {
862
- this._initVirtualScrolling();
487
+ // Visibility: default true, hidden messages get display:none
488
+ var visible = input.visible !== false;
489
+ if (!visible) {
490
+ messageDiv.style.display = 'none';
863
491
  }
864
492
 
865
- // If virtual scrolling is enabled, use the virtual scroller
866
- if (this.virtualScroller) {
867
- var messageData = {
868
- msgid: msgid,
869
- content: input.content,
870
- userString: input.userString,
871
- align: input.align,
872
- role: input.role,
873
- visible: input.visible !== undefined ? input.visible : true,
874
- tags: input.tags || [],
875
- scrollIntoView: input.scrollIntoView
876
- };
877
-
878
- // Add tags to active tags set
879
- if (Array.isArray(input.tags)) {
880
- input.tags.forEach(function (tag) {
881
- if (typeof tag === 'string' && /^[a-zA-Z0-9-]+$/.test(tag)) {
882
- _this6._activeTags.add(tag);
883
- }
884
- });
885
- }
886
-
887
- // Add to virtual scroller
888
- this.virtualScroller.addItem(messageData);
889
-
890
- // Clear text entry
891
- this._textEntry.value = '';
892
- this._adjustMessagesAreaHeight();
893
- } else {
894
- // Original DOM-based implementation
895
- messageDiv = document.createElement('div');
896
- var msgidClass = 'quikchat-msgid-' + String(msgid).padStart(10, '0');
897
- 'quikchat-userid-' + String(input.userString).padStart(10, '0'); // hash this..
898
- messageDiv.classList.add('quikchat-message', msgidClass, 'quikchat-structure');
899
- if (Array.isArray(input.tags)) {
900
- input.tags.forEach(function (tag) {
901
- if (typeof tag === 'string' && /^[a-zA-Z0-9-]+$/.test(tag)) {
902
- messageDiv.classList.add("quikchat-tag-".concat(tag));
903
- _this6._activeTags.add(tag);
904
- }
905
- });
906
- }
907
- var userDiv = document.createElement('div');
908
- userDiv.innerHTML = input.userString;
909
- userDiv.classList.add('quikchat-user-label');
910
- userDiv.style.textAlign = input.align;
911
- var contentDiv = document.createElement('div');
912
- contentDiv.classList.add('quikchat-message-content');
913
-
914
- // Determine text alignment for right-aligned messages
915
- if (input.align === "right") {
916
- var isMultiLine = input.content.includes("\n");
917
- var isLong = input.content.length > 50; // Adjust length threshold
918
-
919
- if (isMultiLine || isLong) {
920
- contentDiv.classList.add("quikchat-right-multiline");
921
- } else {
922
- contentDiv.classList.add("quikchat-right-singleline");
923
- }
924
- }
925
- contentDiv.innerHTML = input.content;
926
- messageDiv.appendChild(userDiv);
927
- messageDiv.appendChild(contentDiv);
928
- this._messagesArea.appendChild(messageDiv);
929
- if (input.visible === false) {
930
- messageDiv.style.display = 'none';
931
- }
932
-
933
- // Handle scroll behavior based on scrollIntoView parameter
934
- // 'smart' = only scroll if near bottom, true = always scroll, false = never scroll
935
- if (input.scrollIntoView === true) {
936
- this.messageScrollToBottom();
937
- } else if (input.scrollIntoView === 'smart' && !this.userScrolledUp) {
938
- this.messageScrollToBottom();
939
- }
940
- // If scrollIntoView is false, don't scroll at all
493
+ // Tags: array of strings for group-based visibility control
494
+ var tags = Array.isArray(input.tags) ? input.tags.slice() : [];
495
+ var userDiv = document.createElement('div');
496
+ userDiv.classList.add('quikchat-user-label');
497
+ userDiv.style.textAlign = input.align;
498
+ userDiv.innerHTML = input.userString;
499
+ var contentDiv = document.createElement('div');
500
+ contentDiv.classList.add('quikchat-message-content');
501
+ contentDiv.style.textAlign = input.align;
502
+ contentDiv.innerHTML = this._processContent(input.content);
503
+ var timestamp = new Date().toISOString();
504
+ var timestampSpan = document.createElement('span');
505
+ timestampSpan.classList.add('quikchat-timestamp');
506
+ timestampSpan.textContent = this._formatTimestamp(timestamp);
507
+ messageDiv.appendChild(userDiv);
508
+ messageDiv.appendChild(contentDiv);
509
+ messageDiv.appendChild(timestampSpan);
510
+ this._messagesArea.appendChild(messageDiv);
941
511
 
942
- this._textEntry.value = '';
943
- this._adjustMessagesAreaHeight();
944
- this._handleShortLongMessageCSS(messageDiv, input.align); // Handle CSS for short/long messages
945
- this._updateMessageStyles();
512
+ // Scroll to the last message only if the user is not actively scrolling up
513
+ if (!this.userScrolledUp) {
514
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
946
515
  }
947
-
948
- // Add timestamp now, unless it is passed in
949
- var timestamp = input.timestamp ? input.timestamp : new Date().toISOString();
950
- var updatedtime = input.updatedtime ? input.updatedtime : timestamp;
951
- var visible = input.visible !== undefined ? input.visible : true;
516
+ this._textEntry.value = '';
517
+ this._autoGrowTextarea();
518
+ var updatedtime = timestamp;
952
519
  if (this.trackHistory) {
953
520
  this._history.push(_objectSpread2(_objectSpread2({
954
521
  msgid: msgid
955
522
  }, input), {}, {
956
523
  visible: visible,
524
+ tags: tags,
957
525
  timestamp: timestamp,
958
526
  updatedtime: updatedtime,
959
- messageDiv: messageDiv || null
527
+ messageDiv: messageDiv
960
528
  }));
961
529
  if (this._history.length > this._historyLimit) {
962
530
  this._history.shift();
@@ -967,24 +535,6 @@ var quikchat = /*#__PURE__*/function () {
967
535
  }
968
536
  return msgid;
969
537
  }
970
-
971
- /**
972
- * Adds a new message to the chat (simplified version of messageAddFull)
973
- * @param {string} [content=''] - Message content (HTML allowed)
974
- * @param {string} [userString='user'] - Display name for the message sender
975
- * @param {'left'|'right'|'center'} [align='right'] - Message alignment
976
- * @param {string} [role='user'] - Role identifier (user, assistant, system)
977
- * @param {boolean|'smart'} [scrollIntoView=true] - Scroll behavior
978
- * @param {boolean} [visible=true] - Whether message is initially visible
979
- * @param {string[]} [tags=[]] - Tags for message categorization
980
- * @returns {number} Message ID for the newly added message
981
- * @example
982
- * // Simple message
983
- * chat.messageAddNew('Hello!', 'User', 'right');
984
- *
985
- * // Bot message with smart scroll
986
- * chat.messageAddNew('Hi there!', 'Bot', 'left', 'assistant', 'smart');
987
- */
988
538
  }, {
989
539
  key: "messageAddNew",
990
540
  value: function messageAddNew() {
@@ -992,115 +542,125 @@ var quikchat = /*#__PURE__*/function () {
992
542
  var userString = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "user";
993
543
  var align = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "right";
994
544
  var role = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : "user";
995
- var scrollIntoView = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
996
- var visible = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
997
- var tags = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : [];
998
- var retvalue = this.messageAddFull({
545
+ return this.messageAddFull({
999
546
  content: content,
1000
547
  userString: userString,
1001
548
  align: align,
1002
- role: role,
1003
- scrollIntoView: scrollIntoView,
1004
- visible: visible,
1005
- tags: tags
549
+ role: role
1006
550
  });
1007
- // this.messageScrollToBottom();
1008
- return retvalue;
1009
551
  }
1010
-
1011
- /**
1012
- * Removes a message from the chat by its ID
1013
- * @param {number} n - Message ID to remove
1014
- * @returns {boolean} True if message was removed, false if not found
1015
- * @example
1016
- * const success = chat.messageRemove(5);
1017
- */
1018
552
  }, {
1019
553
  key: "messageRemove",
1020
554
  value: function messageRemove(n) {
1021
- // use css selector to remove the message
1022
- var sucess = false;
555
+ var success = false;
1023
556
  try {
1024
557
  this._messagesArea.removeChild(this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))));
1025
- sucess = true;
1026
- } catch (error) {
1027
- console.log("{String(n)} : Message ID not found");
558
+ success = true;
559
+ } catch (_error) {
560
+ // Message ID not found
1028
561
  }
1029
- if (sucess) {
1030
- // slow way to remove from history
1031
- //this._history = this._history.filter((item) => item.msgid !== n); // todo make this more efficient
1032
-
1033
- // better way to delete this from history
562
+ if (success) {
1034
563
  this._history.splice(this._history.findIndex(function (item) {
1035
564
  return item.msgid === n;
1036
565
  }), 1);
1037
-
1038
- // Call the onMessageDelete callback if it exists
1039
566
  if (this._onMessageDelete) {
1040
567
  this._onMessageDelete(this, n);
1041
568
  }
1042
569
  }
1043
- return sucess;
570
+ return success;
1044
571
  }
1045
572
  /* returns the message html object from the DOM
1046
573
  */
1047
- /**
1048
- * Gets the DOM element for a message by its ID
1049
- * @param {number} n - Message ID
1050
- * @returns {HTMLElement|null} The message DOM element or null if not found
1051
- */
1052
574
  }, {
1053
575
  key: "messageGetDOMObject",
1054
576
  value: function messageGetDOMObject(n) {
1055
577
  var msg = null;
1056
- // now use css selector to get the message
1057
578
  try {
1058
579
  msg = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
1059
- } catch (error) {
1060
- console.log("{String(n)} : Message ID not found");
580
+ } catch (_error) {
581
+ // Message ID not found
1061
582
  }
1062
583
  return msg;
1063
584
  }
1064
585
  /* returns the message content only
1065
586
  */
1066
- /**
1067
- * Gets the content of a message by its ID
1068
- * @param {number} n - Message ID
1069
- * @returns {string} The message content or empty string if not found
1070
- */
1071
587
  }, {
1072
588
  key: "messageGetContent",
1073
589
  value: function messageGetContent(n) {
1074
590
  var content = "";
1075
- // now use css selector to get the message
1076
591
  try {
1077
- // get from history..
1078
592
  content = this._history.filter(function (item) {
1079
593
  return item.msgid === n;
1080
594
  })[0].content;
1081
- //content = this._messagesArea.querySelector(`.quikchat-msgid-${String(n).padStart(10, '0')}`).lastChild.textContent;
1082
- } catch (error) {
1083
- console.log("{String(n)} : Message ID not found");
595
+ } catch (_error) {
596
+ // Message ID not found
1084
597
  }
1085
598
  return content;
1086
599
  }
1087
-
1088
- /* returns the DOM Content element of a given message
1089
- */
1090
600
  }, {
1091
- key: "messageGetContentDOMElement",
1092
- value: function messageGetContentDOMElement(n) {
1093
- var contentElement = null;
1094
- // now use css selector to get the message
601
+ key: "messageSetVisible",
602
+ value: function messageSetVisible(n, visible) {
603
+ var msgEl = this.messageGetDOMObject(n);
604
+ if (!msgEl) return false;
605
+ msgEl.style.display = visible ? '' : 'none';
606
+ var item = this._history.find(function (entry) {
607
+ return entry.msgid === n;
608
+ });
609
+ if (item) item.visible = visible;
610
+ return true;
611
+ }
612
+ }, {
613
+ key: "messageGetVisible",
614
+ value: function messageGetVisible(n) {
615
+ var item = this._history.find(function (entry) {
616
+ return entry.msgid === n;
617
+ });
618
+ return item ? item.visible !== false : false;
619
+ }
620
+ }, {
621
+ key: "messageToggleVisible",
622
+ value: function messageToggleVisible(n) {
623
+ var current = this.messageGetVisible(n);
624
+ return this.messageSetVisible(n, !current);
625
+ }
626
+ }, {
627
+ key: "messageSetVisibleByTag",
628
+ value: function messageSetVisibleByTag(tag, visible) {
629
+ var count = 0;
630
+ var _iterator = _createForOfIteratorHelper(this._history),
631
+ _step;
1095
632
  try {
1096
- //contentElement = this._messagesArea.querySelector(`.quikchat-msgid-${String(n).padStart(10, '0')}`).lastChild;
1097
- contentElement = this._history.filter(function (item) {
1098
- return item.msgid === n;
1099
- })[0].messageDiv.lastChild;
1100
- } catch (error) {
1101
- console.log("{String(n)} : Message ID not found");
633
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
634
+ var item = _step.value;
635
+ if (item.tags && item.tags.includes(tag)) {
636
+ this.messageSetVisible(item.msgid, visible);
637
+ count++;
638
+ }
639
+ }
640
+ } catch (err) {
641
+ _iterator.e(err);
642
+ } finally {
643
+ _iterator.f();
1102
644
  }
1103
- return contentElement;
645
+ return count;
646
+ }
647
+ }, {
648
+ key: "messageGetTags",
649
+ value: function messageGetTags(n) {
650
+ var item = this._history.find(function (entry) {
651
+ return entry.msgid === n;
652
+ });
653
+ return item && item.tags ? item.tags.slice() : [];
654
+ }
655
+ }, {
656
+ key: "messageSetTags",
657
+ value: function messageSetTags(n, tags) {
658
+ var item = this._history.find(function (entry) {
659
+ return entry.msgid === n;
660
+ });
661
+ if (!item) return false;
662
+ item.tags = Array.isArray(tags) ? tags.slice() : [];
663
+ return true;
1104
664
  }
1105
665
 
1106
666
  /* append message to the message content
@@ -1110,41 +670,23 @@ var quikchat = /*#__PURE__*/function () {
1110
670
  value: function messageAppendContent(n, content) {
1111
671
  var success = false;
1112
672
  try {
1113
- // Update history first
1114
- var item = this._history.filter(function (item) {
1115
- return item.msgid === n;
673
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
674
+ var item = this._history.filter(function (entry) {
675
+ return entry.msgid === n;
1116
676
  })[0];
1117
677
  item.content += content;
1118
678
  item.updatedtime = new Date().toISOString();
1119
-
1120
- // If virtual scrolling is active, update the virtual scroller
1121
- if (this.virtualScroller) {
1122
- // Find the item index in virtual scroller
1123
- var index = this.virtualScroller.items.findIndex(function (item) {
1124
- return item.msgid === n;
1125
- });
1126
- if (index >= 0) {
1127
- this.virtualScroller.items[index].content += content;
1128
- // Re-render if the item is currently visible
1129
- this.virtualScroller.updateItem(index, {
1130
- content: this.virtualScroller.items[index].content
1131
- });
1132
- }
1133
- } else {
1134
- // Regular DOM manipulation
1135
- this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))).lastChild.innerHTML += content;
1136
- }
679
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(item.content);
680
+ msgEl.classList.remove('quikchat-typing');
1137
681
  success = true;
1138
-
1139
- // Call the onMessageAppend callback if it exists
682
+ if (!this.userScrolledUp) {
683
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
684
+ }
1140
685
  if (this._onMessageAppend) {
1141
686
  this._onMessageAppend(this, n, content);
1142
687
  }
1143
-
1144
- // Don't auto-scroll on append - let user control this
1145
- // Users can call messageScrollToBottom() if they want to scroll
1146
- } catch (error) {
1147
- console.log("".concat(String(n), " : Message ID not found"));
688
+ } catch (_error) {
689
+ // Message ID not found
1148
690
  }
1149
691
  return success;
1150
692
  }
@@ -1156,405 +698,80 @@ var quikchat = /*#__PURE__*/function () {
1156
698
  value: function messageReplaceContent(n, content) {
1157
699
  var success = false;
1158
700
  try {
1159
- // Update history first
1160
- var item = this._history.filter(function (item) {
1161
- return item.msgid === n;
701
+ var msgEl = this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0')));
702
+ var item = this._history.filter(function (entry) {
703
+ return entry.msgid === n;
1162
704
  })[0];
1163
705
  item.content = content;
1164
706
  item.updatedtime = new Date().toISOString();
1165
-
1166
- // If virtual scrolling is active, update the virtual scroller
1167
- if (this.virtualScroller) {
1168
- // Find the item index in virtual scroller
1169
- var index = this.virtualScroller.items.findIndex(function (item) {
1170
- return item.msgid === n;
1171
- });
1172
- if (index >= 0) {
1173
- this.virtualScroller.items[index].content = content;
1174
- // Re-render if the item is currently visible
1175
- this.virtualScroller.updateItem(index, {
1176
- content: content
1177
- });
1178
- }
1179
- } else {
1180
- // Regular DOM manipulation
1181
- this._messagesArea.querySelector(".quikchat-msgid-".concat(String(n).padStart(10, '0'))).lastChild.innerHTML = content;
1182
- }
707
+ msgEl.querySelector('.quikchat-message-content').innerHTML = this._processContent(content);
708
+ msgEl.classList.remove('quikchat-typing');
1183
709
  success = true;
1184
-
1185
- // Call the onMessageReplace callback if it exists
710
+ if (!this.userScrolledUp) {
711
+ this._messagesArea.scrollTop = this._messagesArea.scrollHeight;
712
+ }
1186
713
  if (this._onMessageReplace) {
1187
714
  this._onMessageReplace(this, n, content);
1188
715
  }
1189
-
1190
- // Don't auto-scroll on append - let user control this
1191
- // Users can call messageScrollToBottom() if they want to scroll
1192
- } catch (error) {
1193
- console.log("".concat(String(n), " : Message ID not found"));
716
+ } catch (_error) {
717
+ // Message ID not found
1194
718
  }
1195
719
  return success;
1196
720
  }
1197
-
1198
- /**
1199
- * Scrolls the messages area to the bottom.
1200
- */
1201
- }, {
1202
- key: "messageScrollToBottom",
1203
- value: function messageScrollToBottom() {
1204
- if (this._messagesArea.lastElementChild) {
1205
- this._messagesArea.lastElementChild.scrollIntoView();
1206
- }
1207
- }
1208
-
1209
- /**
1210
- * Removes the last message from the messages area.
1211
- */
1212
- }, {
1213
- key: "messageRemoveLast",
1214
- value: function messageRemoveLast() {
1215
- // find the last message by id:
1216
- if (this._history.length >= 0) {
1217
- var lastMsgId = this._history[this._history.length - 1].msgid;
1218
- return this.messageRemove(lastMsgId);
1219
- }
1220
- return false;
1221
- }
1222
721
  }, {
1223
- key: "messageSetVisibility",
1224
- value: function messageSetVisibility(msgid, isVisible) {
1225
- var message = this._history.find(function (item) {
1226
- return item.msgid === msgid;
722
+ key: "messageAddTypingIndicator",
723
+ value: function messageAddTypingIndicator() {
724
+ var userString = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
725
+ var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'left';
726
+ var msgid = this.messageAddFull({
727
+ content: '',
728
+ userString: userString,
729
+ align: align,
730
+ role: 'assistant'
1227
731
  });
1228
- if (message && message.messageDiv) {
1229
- message.messageDiv.style.display = isVisible ? '' : 'none';
1230
- message.visible = isVisible;
1231
- this._updateMessageStyles();
1232
- return true;
1233
- }
1234
- return false;
732
+ var msgEl = this.messageGetDOMObject(msgid);
733
+ msgEl.classList.add('quikchat-typing');
734
+ var contentDiv = msgEl.querySelector('.quikchat-message-content');
735
+ contentDiv.innerHTML = '<span class="quikchat-typing-dots"><span>.</span><span>.</span><span>.</span></span>';
736
+ return msgid;
1235
737
  }
1236
738
  }, {
1237
- key: "messageGetVisibility",
1238
- value: function messageGetVisibility(msgid) {
1239
- var message = this._history.find(function (item) {
1240
- return item.msgid === msgid;
1241
- });
1242
- if (message && message.messageDiv) {
1243
- return message.messageDiv.style.display !== 'none';
1244
- }
1245
- return false; // Return false if not found or no messageDiv
739
+ key: "setMessageFormatter",
740
+ value: function setMessageFormatter(formatter) {
741
+ this._messageFormatter = formatter;
1246
742
  }
1247
743
  }, {
1248
- key: "_updateMessageStyles",
1249
- value: function _updateMessageStyles() {
1250
- var visibleMessages = _toConsumableArray(this._messagesArea.children).filter(function (child) {
1251
- return child.style.display !== 'none';
1252
- });
1253
- visibleMessages.forEach(function (messageDiv, index) {
1254
- messageDiv.classList.remove('quikchat-message-1', 'quikchat-message-2');
1255
- messageDiv.classList.add(index % 2 === 0 ? 'quikchat-message-1' : 'quikchat-message-2');
1256
- });
744
+ key: "setSanitize",
745
+ value: function setSanitize(sanitize) {
746
+ this._sanitize = sanitize;
1257
747
  }
1258
748
 
1259
- /**
1260
- * For right sided or centered messages, we need to handle the CSS for short and long messages.
1261
- * for short messages we use simple justifying, for long messages we need to wrap and perform multiline
1262
- * formatting.
1263
- *
1264
- * @param {*} messageElement
1265
- * @returns nothing
1266
- */
1267
- }, {
1268
- key: "_handleShortLongMessageCSS",
1269
- value: function _handleShortLongMessageCSS(messageElement, align) {
1270
- // console.log(messageElement);
1271
- // Reset classes
1272
- messageElement.classList.remove('left-singleline', 'left-multiline', 'center-singleline', 'center-multiline', 'right-singleline', 'right-multiline');
1273
- var contentDiv = messageElement.lastChild;
1274
- window.lastDiv = contentDiv; // for debugging
1275
- // Determine if the message is short or long
1276
-
1277
- var computedStyle = window.getComputedStyle(contentDiv);
1278
-
1279
- // Get the element's height
1280
- var elementHeight = contentDiv.offsetHeight;
1281
-
1282
- // Calculate or estimate line height
1283
- var lineHeight;
1284
- if (computedStyle.lineHeight === "normal") {
1285
- var fontSize = parseFloat(computedStyle.fontSize);
1286
- lineHeight = fontSize * 1.2; // approximate "normal" as 1.2 times font-size
1287
- } else {
1288
- lineHeight = parseFloat(computedStyle.lineHeight);
1289
- }
1290
-
1291
- // Check if the element height is more than one line-height
1292
- var isMultiLine = elementHeight > lineHeight;
1293
-
1294
- // Using scrollHeight and clientHeight to check for overflow (multi-line)
1295
- switch (align) {
1296
- case 'center':
1297
- if (isMultiLine) {
1298
- messageElement.classList.add('center-multiline');
1299
- } else {
1300
- messageElement.classList.add('center-singleline');
1301
- }
1302
- break;
1303
- case 'right':
1304
- if (isMultiLine) {
1305
- messageElement.classList.add('right-multiline');
1306
- } else {
1307
- messageElement.classList.add('right-singleline');
1308
- }
1309
- break;
1310
- case 'left':
1311
- default:
1312
- if (isMultiLine) {
1313
- messageElement.classList.add('left-multiline');
1314
- } else {
1315
- messageElement.classList.add('left-singleline');
1316
- }
1317
- break;
1318
- }
1319
- }
1320
749
  // history functions
1321
750
  /**
1322
- *
1323
- * @param {*} n
1324
- * @param {*} m
751
+ *
752
+ * @param {*} n
753
+ * @param {*} m
1325
754
  * @returns array of history messages
1326
755
  */
1327
- /**
1328
- * Gets a slice of message history
1329
- * @param {number} [n] - Start index (defaults to 0)
1330
- * @param {number} [m] - End index (defaults to history length)
1331
- * @returns {Array} Array of message objects
1332
- * @example
1333
- * // Get first 10 messages
1334
- * const messages = chat.historyGet(0, 10);
1335
- *
1336
- * // Get all messages
1337
- * const allMessages = chat.historyGet();
1338
- */
1339
756
  }, {
1340
757
  key: "historyGet",
1341
758
  value: function historyGet(n, m) {
1342
- if (n == undefined) {
1343
- n = 0;
1344
- m = this._history.length;
759
+ if (n === undefined) {
760
+ return this._history.slice();
1345
761
  }
1346
762
  if (m === undefined) {
1347
- m = n < 0 ? m : n + 1;
1348
- }
1349
- // remember that entries could be deleted. TODO: So we need to return the actual history entries
1350
- // so now we need to find the array index that correspondes to messageIds n (start) and m (end)
1351
-
1352
- return this._history.slice(n, m);
1353
- }
1354
-
1355
- /**
1356
- * Gets a copy of the entire message history
1357
- * @returns {Array} Complete array of all message objects
1358
- * @example
1359
- * const history = chat.historyGetAllCopy();
1360
- * console.log(`Total messages: ${history.length}`);
1361
- */
1362
- }, {
1363
- key: "historyGetAllCopy",
1364
- value: function historyGetAllCopy() {
1365
- return this._history.slice();
1366
- }
1367
-
1368
- /**
1369
- * Get a page of history messages with pagination support
1370
- * @param {number} page - Page number (1-based)
1371
- * @param {number} pageSize - Number of messages per page (default 50)
1372
- * @param {string} order - 'asc' for oldest first, 'desc' for newest first (default 'asc')
1373
- * @returns {object} Object with messages array, pagination info
1374
- */
1375
- }, {
1376
- key: "historyGetPage",
1377
- value: function historyGetPage() {
1378
- var page = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
1379
- var pageSize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 50;
1380
- var order = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'asc';
1381
- var totalMessages = this._history.length;
1382
- var totalPages = Math.ceil(totalMessages / pageSize);
1383
- var currentPage = Math.max(1, Math.min(page, totalPages || 1));
1384
- var start, end;
1385
- if (order === 'desc') {
1386
- // For descending order, page 1 shows the newest messages
1387
- start = Math.max(0, totalMessages - currentPage * pageSize);
1388
- end = totalMessages - (currentPage - 1) * pageSize;
1389
- } else {
1390
- // For ascending order, page 1 shows the oldest messages
1391
- start = (currentPage - 1) * pageSize;
1392
- end = Math.min(start + pageSize, totalMessages);
1393
- }
1394
- var messages = this._history.slice(start, end);
1395
-
1396
- // Reverse messages array if descending order requested
1397
- if (order === 'desc') {
1398
- messages.reverse();
1399
- }
1400
- return {
1401
- messages: messages,
1402
- pagination: {
1403
- currentPage: currentPage,
1404
- pageSize: pageSize,
1405
- totalPages: totalPages,
1406
- totalMessages: totalMessages,
1407
- hasNext: currentPage < totalPages,
1408
- hasPrevious: currentPage > 1,
1409
- order: order
763
+ if (n < 0) {
764
+ return this._history.slice(n);
1410
765
  }
1411
- };
1412
- }
1413
-
1414
- /**
1415
- * Get information about history size and pagination
1416
- * @param {number} pageSize - Size to calculate pages for (default 50)
1417
- * @returns {object} History metadata
1418
- */
1419
- /**
1420
- * Search history for messages matching criteria
1421
- * @param {object} criteria - Search criteria object
1422
- * @param {string} criteria.text - Text to search for in message content
1423
- * @param {string} criteria.userString - Filter by user name
1424
- * @param {string} criteria.role - Filter by role
1425
- * @param {array} criteria.tags - Filter by tags (messages with any of these tags)
1426
- * @param {number} criteria.limit - Maximum results to return (default 100)
1427
- * @returns {array} Array of matching messages
1428
- */
1429
- /**
1430
- * Searches through message history with various filters
1431
- * @param {Object} [criteria={}] - Search criteria
1432
- * @param {string} [criteria.text] - Text to search for in message content
1433
- * @param {string} [criteria.userString] - Filter by specific user
1434
- * @param {string} [criteria.role] - Filter by role (user, assistant, system)
1435
- * @param {string[]} [criteria.tags] - Filter by tags (messages must have at least one)
1436
- * @param {number} [criteria.limit=100] - Maximum number of results
1437
- * @returns {Array} Array of matching messages
1438
- * @since 1.1.15
1439
- * @example
1440
- * // Search for messages containing 'error'
1441
- * const errors = chat.historySearch({ text: 'error' });
1442
- *
1443
- * // Find all bot messages
1444
- * const botMessages = chat.historySearch({ role: 'assistant' });
1445
- *
1446
- * // Complex search
1447
- * const results = chat.historySearch({
1448
- * text: 'help',
1449
- * userString: 'Support',
1450
- * tags: ['urgent'],
1451
- * limit: 20
1452
- * });
1453
- */
1454
- }, {
1455
- key: "historySearch",
1456
- value: function historySearch() {
1457
- var criteria = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
1458
- var results = this._history;
1459
-
1460
- // Filter by text content (case-insensitive)
1461
- if (criteria.text) {
1462
- var searchText = criteria.text.toLowerCase();
1463
- results = results.filter(function (msg) {
1464
- return msg.content.toLowerCase().includes(searchText);
1465
- });
1466
- }
1467
-
1468
- // Filter by user
1469
- if (criteria.userString) {
1470
- results = results.filter(function (msg) {
1471
- return msg.userString === criteria.userString;
1472
- });
766
+ return this._history.slice(n, n + 1);
1473
767
  }
1474
-
1475
- // Filter by role
1476
- if (criteria.role) {
1477
- results = results.filter(function (msg) {
1478
- return msg.role === criteria.role;
1479
- });
1480
- }
1481
-
1482
- // Filter by tags (match any tag)
1483
- if (criteria.tags && criteria.tags.length > 0) {
1484
- results = results.filter(function (msg) {
1485
- return msg.tags && msg.tags.some(function (tag) {
1486
- return criteria.tags.includes(tag);
1487
- });
1488
- });
1489
- }
1490
-
1491
- // Limit results
1492
- var limit = criteria.limit || 100;
1493
- if (results.length > limit) {
1494
- results = results.slice(0, limit);
1495
- }
1496
- return results;
1497
- }
1498
-
1499
- /**
1500
- * Gets metadata and statistics about the message history
1501
- * @param {number} [pageSize=50] - Page size for calculating total pages
1502
- * @returns {Object} History information object
1503
- * @returns {number} returns.totalMessages - Total number of messages
1504
- * @returns {number} returns.totalPages - Total pages based on page size
1505
- * @returns {Object|null} returns.oldestMessage - First message info
1506
- * @returns {Object|null} returns.newestMessage - Last message info
1507
- * @returns {Object} returns.memoryUsage - Memory usage statistics
1508
- * @since 1.1.15
1509
- * @example
1510
- * const info = chat.historyGetInfo();
1511
- * console.log(`Messages: ${info.totalMessages}`);
1512
- * console.log(`Memory: ${info.memoryUsage.estimatedSize} bytes`);
1513
- */
1514
- }, {
1515
- key: "historyGetInfo",
1516
- value: function historyGetInfo() {
1517
- var pageSize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 50;
1518
- var totalMessages = this._history.length;
1519
- return {
1520
- totalMessages: totalMessages,
1521
- totalPages: Math.ceil(totalMessages / pageSize),
1522
- oldestMessage: totalMessages > 0 ? {
1523
- msgid: this._history[0].msgid,
1524
- timestamp: this._history[0].timestamp,
1525
- userString: this._history[0].userString
1526
- } : null,
1527
- newestMessage: totalMessages > 0 ? {
1528
- msgid: this._history[totalMessages - 1].msgid,
1529
- timestamp: this._history[totalMessages - 1].timestamp,
1530
- userString: this._history[totalMessages - 1].userString
1531
- } : null,
1532
- memoryUsage: {
1533
- estimatedSize: JSON.stringify(this._history).length,
1534
- averageMessageSize: totalMessages > 0 ? Math.round(JSON.stringify(this._history).length / totalMessages) : 0
1535
- }
1536
- };
768
+ return this._history.slice(n, m);
1537
769
  }
1538
-
1539
- /**
1540
- * Clears all messages and resets the chat
1541
- * @returns {void}
1542
- * @example
1543
- * chat.historyClear(); // Removes all messages
1544
- */
1545
770
  }, {
1546
771
  key: "historyClear",
1547
772
  value: function historyClear() {
1548
773
  this.msgid = 0;
1549
-
1550
- // Handle virtual scroller
1551
- if (this.virtualScroller) {
1552
- this.virtualScroller.clear();
1553
- } else {
1554
- this._messagesArea.innerHTML = "";
1555
- }
1556
774
  this._history = [];
1557
- this._activeTags.clear();
1558
775
  }
1559
776
  }, {
1560
777
  key: "historyGetLength",
@@ -1572,29 +789,57 @@ var quikchat = /*#__PURE__*/function () {
1572
789
  }, {
1573
790
  key: "historyGetMessageContent",
1574
791
  value: function historyGetMessageContent(n) {
1575
- if (n >= 0 && n < this._history.length) return this._history[n].content;else return "";
792
+ if (n >= 0 && n < this._history.length) {
793
+ return this._history[n].content;
794
+ }
795
+ return "";
1576
796
  }
1577
-
1578
- // expects an array of messages to be in the format of the history object
1579
797
  }, {
1580
- key: "historyRestoreAll",
1581
- value: function historyRestoreAll(messageList) {
1582
- var _this7 = this;
1583
- // clear all messages and history
1584
- this.historyClear();
1585
-
1586
- // clear the messages div
1587
- this._messagesArea.innerHTML = "";
1588
-
1589
- // add all messages
1590
- messageList.forEach(function (message) {
1591
- _this7.messageAddFull(message);
798
+ key: "historyExport",
799
+ value: function historyExport() {
800
+ return this._history.map(function (item) {
801
+ return {
802
+ msgid: item.msgid,
803
+ content: item.content,
804
+ userString: item.userString,
805
+ align: item.align,
806
+ role: item.role,
807
+ userID: item.userID,
808
+ visible: item.visible,
809
+ tags: item.tags ? item.tags.slice() : [],
810
+ timestamp: item.timestamp,
811
+ updatedtime: item.updatedtime
812
+ };
1592
813
  });
1593
814
  }
1594
- /**
1595
- *
1596
- * @param {string} newTheme
1597
- */
815
+ }, {
816
+ key: "historyImport",
817
+ value: function historyImport(data) {
818
+ // Clear existing messages from DOM and history
819
+ this._messagesArea.innerHTML = '';
820
+ this._history = [];
821
+ this.msgid = 0;
822
+ var _iterator2 = _createForOfIteratorHelper(data),
823
+ _step2;
824
+ try {
825
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
826
+ var entry = _step2.value;
827
+ this.messageAddFull({
828
+ content: entry.content || '',
829
+ userString: entry.userString || 'user',
830
+ align: entry.align || 'right',
831
+ role: entry.role || 'user',
832
+ userID: entry.userID,
833
+ visible: entry.visible,
834
+ tags: entry.tags
835
+ });
836
+ }
837
+ } catch (err) {
838
+ _iterator2.e(err);
839
+ } finally {
840
+ _iterator2.f();
841
+ }
842
+ }
1598
843
  }, {
1599
844
  key: "changeTheme",
1600
845
  value: function changeTheme(newTheme) {
@@ -1602,164 +847,38 @@ var quikchat = /*#__PURE__*/function () {
1602
847
  this._chatWidget.classList.add(newTheme);
1603
848
  this._theme = newTheme;
1604
849
  }
1605
-
1606
- /**
1607
- * Get the current theme
1608
- * @returns {string} - The current theme
1609
- */
1610
850
  }, {
1611
851
  key: "theme",
1612
852
  get: function get() {
1613
853
  return this._theme;
1614
854
  }
1615
-
1616
- /**
1617
- *
1618
- * @returns {object} - Returns the version and license information for the library.
1619
- */
1620
- /**
1621
- * Gets the QuikChat version information
1622
- * @static
1623
- * @returns {Object} Version information object
1624
- * @returns {string} returns.version - Version number (e.g., '1.1.15')
1625
- * @returns {string} returns.license - License type (e.g., 'BSD-2')
1626
- * @returns {string} returns.url - Project URL
1627
- * @returns {boolean} returns.fun - Easter egg flag
1628
- * @example
1629
- * const version = quikchat.version();
1630
- * console.log(`QuikChat v${version.version}`);
1631
- */
1632
- }, {
1633
- key: "setTagVisibility",
1634
- value:
1635
- /**
1636
- * Sets visibility for all messages with a specific tag
1637
- * @param {string} tagName - Tag name to control
1638
- * @param {boolean} isVisible - Whether to show or hide messages with this tag
1639
- * @returns {boolean} True if successful, false if invalid tag name
1640
- * @since 1.1.14
1641
- * @example
1642
- * // Hide all system messages
1643
- * chat.setTagVisibility('system', false);
1644
- *
1645
- * // Show urgent messages
1646
- * chat.setTagVisibility('urgent', true);
1647
- */
1648
- function setTagVisibility(tagName, isVisible) {
1649
- if (typeof tagName !== 'string' || !/^[a-zA-Z0-9-]+$/.test(tagName)) {
1650
- return false;
1651
- }
1652
- var className = "quikchat-show-tag-".concat(tagName);
1653
- if (isVisible) {
1654
- this._chatWidget.classList.add(className);
1655
- } else {
1656
- this._chatWidget.classList.remove(className);
1657
- }
1658
- this._updateMessageStyles();
1659
- return true;
1660
- }
1661
-
1662
- /**
1663
- * Gets the visibility state of a tag
1664
- * @param {string} tagName - Tag name to check
1665
- * @returns {boolean} True if tag is visible, false otherwise
1666
- * @since 1.1.14
1667
- * @example
1668
- * const isVisible = chat.getTagVisibility('system');
1669
- */
1670
- }, {
1671
- key: "getTagVisibility",
1672
- value: function getTagVisibility(tagName) {
1673
- if (typeof tagName !== 'string' || !/^[a-zA-Z0-9-]+$/.test(tagName)) {
1674
- return false;
1675
- }
1676
- return this._chatWidget.classList.contains("quikchat-show-tag-".concat(tagName));
1677
- }
1678
-
1679
- /**
1680
- * Gets all active tags in the chat
1681
- * @returns {string[]} Array of all tags currently in use
1682
- * @since 1.1.14
1683
- * @example
1684
- * const tags = chat.getActiveTags();
1685
- * console.log('Active tags:', tags);
1686
- */
1687
- }, {
1688
- key: "getActiveTags",
1689
- value: function getActiveTags() {
1690
- return Array.from(this._activeTags);
1691
- }
1692
-
1693
- /**
1694
- * Checks if virtual scrolling is currently enabled
1695
- * @returns {boolean} True if virtual scrolling is enabled, false otherwise
1696
- * @since 1.1.16
1697
- * @example
1698
- * if (chat.isVirtualScrollingEnabled()) {
1699
- * console.log('Virtual scrolling is active');
1700
- * }
1701
- */
1702
- }, {
1703
- key: "isVirtualScrollingEnabled",
1704
- value: function isVirtualScrollingEnabled() {
1705
- return this.virtualScrollingEnabled && this.virtualScroller !== null;
1706
- }
1707
-
1708
- /**
1709
- * Gets the virtual scrolling configuration
1710
- * @returns {Object} Virtual scrolling configuration with enabled status and threshold
1711
- * @since 1.1.16
1712
- * @example
1713
- * const config = chat.getVirtualScrollingConfig();
1714
- * console.log(`Virtual scrolling: ${config.enabled}, threshold: ${config.threshold}`);
1715
- */
1716
- }, {
1717
- key: "getVirtualScrollingConfig",
1718
- value: function getVirtualScrollingConfig() {
1719
- return {
1720
- enabled: this.virtualScrollingEnabled,
1721
- active: this.virtualScroller !== null,
1722
- threshold: this.virtualScrollingThreshold
1723
- };
1724
- }
1725
855
  }], [{
1726
856
  key: "version",
1727
857
  value: function version() {
1728
- return quikchatVersion;
858
+ return {
859
+ "version": "1.2.4",
860
+ "license": "BSD-2",
861
+ "url": "https://github/deftio/quikchat"
862
+ };
1729
863
  }
1730
864
 
1731
865
  /**
1732
866
  * quikchat.loremIpsum() - Generate a simple string of Lorem Ipsum text (sample typographer's text) of numChars in length.
1733
867
  * borrowed from github.com/deftio/bitwrench.js
1734
- * @param {number} numChars - The number of characters to generate (random btw 25 and 150 if undefined).
868
+ * @param {number} numChars - The number of characters to generate (random btw 25 and 150 if undefined).
1735
869
  * @param {number} [startSpot=0] - The starting index in the Lorem Ipsum text. If undefined, a random startSpot will be generated.
1736
870
  * @param {boolean} [startWithCapitalLetter=true] - If true, capitalize the first character or inject a capital letter if the first character isn't a capital letter.
1737
- *
871
+ *
1738
872
  * @returns {string} A string of Lorem Ipsum text.
1739
- *
1740
- * @example
873
+ *
874
+ * @example
1741
875
  * // Returns 200 characters of Lorem Ipsum starting from index 50
1742
876
  * loremIpsum(200, 50);
1743
- *
1744
- * @example
877
+ *
878
+ * @example
1745
879
  * //Returns a 200 Lorem Ipsum characters starting from a random index
1746
880
  * loremIpsum(200);
1747
881
  */
1748
-
1749
- /**
1750
- * Generates Lorem Ipsum placeholder text
1751
- * @static
1752
- * @param {number} [numChars] - Length of text to generate (random if not specified)
1753
- * @param {number} [startSpot] - Starting offset in Lorem text (random if not specified)
1754
- * @param {boolean} [startWithCapitalLetter=true] - Whether to capitalize first letter
1755
- * @returns {string} Generated Lorem Ipsum text
1756
- * @example
1757
- * // Generate 100 characters
1758
- * const text = quikchat.loremIpsum(100);
1759
- *
1760
- * // Generate random length
1761
- * const randomText = quikchat.loremIpsum();
1762
- */
1763
882
  }, {
1764
883
  key: "loremIpsum",
1765
884
  value: function loremIpsum(numChars) {
@@ -1767,7 +886,7 @@ var quikchat = /*#__PURE__*/function () {
1767
886
  var startWithCapitalLetter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1768
887
  var loremText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ";
1769
888
  if (typeof numChars !== "number") {
1770
- numChars = Math.floor(Math.random() * 150) + 25;
889
+ numChars = Math.floor(Math.random() * 126) + 25;
1771
890
  }
1772
891
  if (startSpot === undefined) {
1773
892
  startSpot = Math.floor(Math.random() * loremText.length);
@@ -1779,9 +898,6 @@ var quikchat = /*#__PURE__*/function () {
1779
898
  startSpot = (startSpot + 1) % loremText.length;
1780
899
  }
1781
900
  var l = loremText.substring(startSpot) + loremText.substring(0, startSpot);
1782
- if (typeof numChars !== "number") {
1783
- numChars = l.length;
1784
- }
1785
901
  var s = "";
1786
902
  while (numChars > 0) {
1787
903
  s += numChars < l.length ? l.substring(0, numChars) : l;
@@ -1791,98 +907,10 @@ var quikchat = /*#__PURE__*/function () {
1791
907
  s = s.substring(0, s.length - 1) + "."; // always end on non-whitespace. "." was chosen arbitrarily.
1792
908
  }
1793
909
  if (startWithCapitalLetter) {
1794
- var c = s[0].toUpperCase();
1795
- c = /[A-Z]/.test(c) ? c : "M";
1796
- s = c + s.substring(1);
910
+ s = s[0].toUpperCase() + s.substring(1);
1797
911
  }
1798
912
  return s;
1799
913
  }
1800
- }, {
1801
- key: "tempMessageGenerator",
1802
- value:
1803
- /**
1804
- * Creates a temporary message that updates periodically
1805
- * @static
1806
- * @param {string|HTMLElement} domElement - Element selector or DOM element
1807
- * @param {string} content - Initial message content
1808
- * @param {number} interval - Update interval in milliseconds (min 100ms)
1809
- * @param {Function} [cb=null] - Callback to generate new content
1810
- * @param {string} cb.message - Current message
1811
- * @param {number} cb.count - Update count
1812
- * @returns {void}
1813
- * @example
1814
- * // Simple loading indicator
1815
- * quikchat.tempMessageGenerator('#loading', 'Loading', 500);
1816
- *
1817
- * // Custom update function
1818
- * quikchat.tempMessageGenerator('#status', 'Processing', 1000, (msg, count) => {
1819
- * return `Processing... ${count}%`;
1820
- * });
1821
- */
1822
- function tempMessageGenerator(domElement, content, interval) {
1823
- var cb = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
1824
- interval = Math.max(interval, 100); // Ensure at least 100ms interval
1825
-
1826
- var count = 0;
1827
- var defaultCB = function defaultCB(msg, count) {
1828
- msg += ".";
1829
- return msg;
1830
- };
1831
- if (cb && typeof cb !== 'function') {
1832
- cb = null;
1833
- }
1834
- cb = cb || defaultCB;
1835
-
1836
- // if its a string, then get the element (css sel) or its an DOM element already
1837
- var el = domElement;
1838
- if (typeof el === 'string') {
1839
- el = document.querySelector(el);
1840
- }
1841
- var element = el;
1842
-
1843
- // Ensure the element exists
1844
- if (!element) return;
1845
- element.innerHTML = content;
1846
- var currentMsg = content;
1847
- var intervalId = setInterval(function () {
1848
- if (element.innerHTML !== currentMsg) {
1849
- clearInterval(intervalId); // Stop updating if content is changed externally
1850
- return;
1851
- }
1852
- currentMsg = String(cb(currentMsg, count)); // Use callback return value if provided
1853
-
1854
- count++;
1855
- element.innerHTML = currentMsg;
1856
- }, interval);
1857
- }
1858
- }, {
1859
- key: "createTempMessageDOMStr",
1860
- value: function createTempMessageDOMStr(initialContent) {
1861
- var updateInterval = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000;
1862
- var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
1863
- var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1864
- // Make sure the interval is at least 100ms
1865
- updateInterval = Math.max(updateInterval, 100);
1866
-
1867
- // Validate callback; if not a function, ignore it.
1868
- if (callback && typeof callback !== 'function') {
1869
- callback = null;
1870
- }
1871
- // Default callback simply appends a dot.
1872
- callback = callback || function (msg, count) {
1873
- return msg + ".";
1874
- };
1875
-
1876
- // Allow an optional CSS class for the container element
1877
- var containerClass = options.containerClass ? options.containerClass : '';
1878
-
1879
- // Generate a unique id so that the inline script can reliably find the container.
1880
- var uniqueId = "tempMsg_" + Date.now() + "_" + Math.floor(Math.random() * 1000000);
1881
-
1882
- // Build and return the HTML string.
1883
- // Note the use of <\/script> (with a backslash) so that the inline script is not terminated early.
1884
- return "\n <span id=\"".concat(uniqueId, "\" ").concat(containerClass ? "class=\"".concat(containerClass, "\"") : '', ">\n ").concat(initialContent, "\n </span>\n <script>\n (function(){\n // Get our container element by its unique id.\n var container = document.getElementById(\"").concat(uniqueId, "\");\n if (!container) return;\n var count = 0;\n var currentMsg = container.innerHTML;\n var interval = ").concat(updateInterval, ";\n // Convert the callback function into its string representation.\n var cb = ").concat(callback.toString(), ";\n var intervalId = setInterval(function(){\n // If the content has been replaced, stop updating.\n if(container.innerHTML !== currentMsg){\n clearInterval(intervalId);\n return;\n }\n // Use the callback to generate the new message.\n currentMsg = String(cb(currentMsg, count));\n count++;\n container.innerHTML = currentMsg;\n }, interval);\n })();\n </script>\n ");
1885
- }
1886
914
  }]);
1887
915
  }();
1888
916