@crestapps/ai-chat-ui 1.0.0-preview.90 → 1.0.0-preview.95

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.
package/dist/ai-chat.js CHANGED
@@ -22,7 +22,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
22
22
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
23
23
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
24
24
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
25
- window.openAIChatManager = function () {
25
+ window.coreAIChatManager = function () {
26
26
  // Defaults (can be overridden by instanceConfig)
27
27
  var defaultConfig = {
28
28
  // UI defaults for generated media
@@ -36,7 +36,9 @@ window.openAIChatManager = function () {
36
36
  thumbsUpTitle: 'Thumbs up',
37
37
  thumbsDownTitle: 'Thumbs down',
38
38
  copyTitle: 'Click here to copy response to clipboard.',
39
+ copiedTitle: 'Response copied to clipboard.',
39
40
  codeCopiedText: 'Copied!',
41
+ copyResetDelayMs: 2000,
40
42
  widget: {
41
43
  chatWidgetContainer: null,
42
44
  chatWidgetStateName: null,
@@ -55,7 +57,7 @@ window.openAIChatManager = function () {
55
57
  minWidth: 320,
56
58
  minHeight: 420
57
59
  },
58
- messageTemplate: "\n <div class=\"ai-chat-messages\">\n <div v-for=\"(message, index) in messages\" :key=\"'msg-' + index\" class=\"ai-chat-message-item\">\n <div>\n <div v-if=\"message.role === 'user'\" class=\"ai-chat-msg-role ai-chat-msg-role-user\">{{ userLabel }}</div>\n <div v-else-if=\"message.role !== 'indicator'\" :class=\"getAssistantRoleClasses(message)\">\n <span :class=\"getAssistantIconClasses(message, index)\"><i :class=\"getAssistantIcon(message)\"></i></span>\n {{ getAssistantLabel(message) }}\n </div>\n <div class=\"ai-chat-message-body lh-base\">\n <h4 v-if=\"message.title\">{{ message.title }}</h4>\n <div v-html=\"message.htmlContent\"></div>\n <ol v-if=\"message.citationReferences && message.citationReferences.length\" class=\"ai-chat-citation-list\">\n <li v-for=\"citation in message.citationReferences\" :key=\"'citation-' + (citation.referenceKey || citation.displayIndex)\" class=\"ai-chat-citation-item\">\n <a v-if=\"citation.link\" :href=\"citation.link\" :target=\"citation.isDownload ? null : '_blank'\" :rel=\"citation.isDownload ? null : 'noopener noreferrer'\">{{ citation.label }}</a>\n <span v-else>{{ citation.label }}</span>\n </li>\n </ol>\n <span class=\"message-buttons-container\" v-if=\"!isIndicator(message)\">\n <template v-if=\"metricsEnabled && message.role === 'assistant'\">\n <span class=\"ai-chat-message-assistant-feedback\" :data-message-id=\"message.id\">\n <button type=\"button\" class=\"btn btn-sm btn-link text-success p-0 me-2 button-message-toolbox rate-up-btn\" @click=\"rateMessage(message, true, $event)\" :title=\"thumbsUpTitle\">\n <i class=\"fa-regular fa-thumbs-up\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-link text-danger p-0 me-2 button-message-toolbox rate-down-btn\" @click=\"rateMessage(message, false, $event)\" :title=\"thumbsDownTitle\">\n <i class=\"fa-regular fa-thumbs-down\"></i>\n </button>\n </span>\n </template>\n <button type=\"button\" v-if=\"textToSpeechEnabled && !isConversationMode && message.role === 'assistant' && !message.isStreaming\" class=\"btn btn-sm btn-link text-secondary p-0 me-1 button-message-toolbox\" :class=\"{ 'tts-playing': ttsPlayingMessageIndex === index }\" :data-tts-message-index=\"index\" @click=\"toggleMessageTts(message, index)\" :title=\"ttsPlayingMessageIndex === index ? 'Pause audio' : 'Read aloud'\">\n <span :class=\"ttsPlayingMessageIndex === index ? 'fa-solid fa-circle-pause' : 'fa-solid fa-circle-play'\"></span>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-link text-secondary p-0 button-message-toolbox\" @click=\"copyResponse(message)\" :title=\"copyTitle\">\n <i class=\"fa-solid fa-copy\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div v-for=\"notification in notifications\" :key=\"'notif-' + notification.type\" class=\"ai-chat-notification\" :class=\"'ai-chat-notification-' + (notification.type || 'info') + ' ' + (notification.cssClass || '')\">\n <div class=\"ai-chat-notification-content\">\n <i v-if=\"notification.icon\" :class=\"notification.icon\" class=\"ai-chat-notification-icon\"></i>\n <span class=\"ai-chat-notification-text\">{{ notification.content }}</span>\n <button type=\"button\" v-if=\"notification.dismissible\" class=\"btn btn-sm btn-link p-0 ms-2 ai-chat-notification-dismiss\" @click=\"dismissNotification(notification.type)\" title=\"Dismiss\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div v-if=\"notification.actions && notification.actions.length\" class=\"ai-chat-notification-actions\">\n <button type=\"button\" v-for=\"action in notification.actions\" :key=\"action.name\" class=\"btn btn-sm\" :class=\"action.cssClass || 'btn-outline-secondary'\" @click=\"handleNotificationAction(notification.type, action.name)\">\n <i v-if=\"action.icon\" :class=\"action.icon\" class=\"me-1\"></i>\n {{ action.label }}\n </button>\n </div>\n </div>\n </div>\n ",
60
+ messageTemplate: "\n <div class=\"ai-chat-messages\">\n <div v-for=\"(message, index) in messages\" :key=\"'msg-' + index\" class=\"ai-chat-message-item\">\n <div>\n <div v-if=\"message.role === 'user'\" class=\"ai-chat-msg-role ai-chat-msg-role-user\">{{ userLabel }}</div>\n <div v-else-if=\"message.role !== 'indicator'\" :class=\"getAssistantRoleClasses(message)\">\n <span :class=\"getAssistantIconClasses(message, index)\"><i :class=\"getAssistantIcon(message)\"></i></span>\n {{ getAssistantLabel(message) }}\n </div>\n <div class=\"ai-chat-message-body lh-base\">\n <h4 v-if=\"message.title\">{{ message.title }}</h4>\n <div v-html=\"message.htmlContent\"></div>\n <ol v-if=\"message.citationReferences && message.citationReferences.length\" class=\"ai-chat-citation-list\">\n <li v-for=\"citation in message.citationReferences\" :key=\"'citation-' + (citation.referenceKey || citation.displayIndex)\" class=\"ai-chat-citation-item\">\n <a v-if=\"citation.link\" :href=\"citation.link\" :target=\"citation.isDownload ? null : '_blank'\" :rel=\"citation.isDownload ? null : 'noopener noreferrer'\">{{ citation.label }}</a>\n <span v-else>{{ citation.label }}</span>\n </li>\n </ol>\n <span class=\"message-buttons-container\" v-if=\"!isIndicator(message)\">\n <template v-if=\"metricsEnabled && message.role === 'assistant'\">\n <span class=\"ai-chat-message-assistant-feedback\" :data-message-id=\"message.id\">\n <button type=\"button\" class=\"btn btn-sm btn-link text-success p-0 me-2 button-message-toolbox rate-up-btn\" @click=\"rateMessage(message, true, $event)\" :title=\"thumbsUpTitle\">\n <i class=\"fa-regular fa-thumbs-up\"></i>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-link text-danger p-0 me-2 button-message-toolbox rate-down-btn\" @click=\"rateMessage(message, false, $event)\" :title=\"thumbsDownTitle\">\n <i class=\"fa-regular fa-thumbs-down\"></i>\n </button>\n </span>\n </template>\n <button type=\"button\" v-if=\"textToSpeechEnabled && !isConversationMode && message.role === 'assistant' && !message.isStreaming\" class=\"btn btn-sm btn-link text-secondary p-0 me-1 button-message-toolbox\" :class=\"{ 'tts-playing': ttsPlayingMessageIndex === index }\" :data-tts-message-index=\"index\" @click=\"toggleMessageTts(message, index)\" :title=\"ttsPlayingMessageIndex === index ? 'Pause audio' : 'Read aloud'\">\n <span :class=\"ttsPlayingMessageIndex === index ? 'fa-solid fa-circle-pause' : 'fa-solid fa-circle-play'\"></span>\n </button>\n <button type=\"button\" class=\"btn btn-sm btn-link p-0 button-message-toolbox ai-response-copy-btn\" :class=\"copiedMessageIndex === index ? 'text-success' : 'text-secondary'\" :data-copy-message-index=\"index\" @click=\"copyResponse(message, index, $event)\" :title=\"copiedMessageIndex === index ? copiedTitle : copyTitle\">\n <i :class=\"copiedMessageIndex === index ? 'fa-solid fa-check' : 'fa-solid fa-copy'\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div v-for=\"notification in notifications\" :key=\"'notif-' + notification.type\" class=\"ai-chat-notification\" :class=\"'ai-chat-notification-' + (notification.type || 'info') + ' ' + (notification.cssClass || '')\">\n <div class=\"ai-chat-notification-content\">\n <i v-if=\"notification.icon\" :class=\"notification.icon\" class=\"ai-chat-notification-icon\"></i>\n <span class=\"ai-chat-notification-text\">{{ notification.content }}</span>\n <button type=\"button\" v-if=\"notification.dismissible\" class=\"btn btn-sm btn-link p-0 ms-2 ai-chat-notification-dismiss\" @click=\"dismissNotification(notification.type)\" title=\"Dismiss\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div v-if=\"notification.actions && notification.actions.length\" class=\"ai-chat-notification-actions\">\n <button type=\"button\" v-for=\"action in notification.actions\" :key=\"action.name\" class=\"btn btn-sm\" :class=\"action.cssClass || 'btn-outline-secondary'\" @click=\"handleNotificationAction(notification.type, action.name)\">\n <i v-if=\"action.icon\" :class=\"action.icon\" class=\"me-1\"></i>\n {{ action.label }}\n </button>\n </div>\n </div>\n </div>\n ",
59
61
  indicatorTemplate: "\n <div class=\"ai-chat-msg-role ai-chat-msg-role-assistant\">\n <span class=\"ai-streaming-icon\"><i class=\"fa fa-robot\" style=\"display: inline-block;\"></i></span>\n Assistant\n </div>\n "
60
62
  };
61
63
 
@@ -125,7 +127,7 @@ window.openAIChatManager = function () {
125
127
  return Math.min(Math.max(value, min), max);
126
128
  }
127
129
  function normalizeReference(reference) {
128
- var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$title, _ref4, _normalized$link, _ref5, _normalized$reference;
130
+ var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$title, _ref4, _normalized$link, _ref5, _normalized$reference, _normalized$isGenerat;
129
131
  if (!reference || _typeof(reference) !== 'object') {
130
132
  return null;
131
133
  }
@@ -135,6 +137,7 @@ window.openAIChatManager = function () {
135
137
  normalized.title = (_ref3 = (_normalized$title = normalized.title) !== null && _normalized$title !== void 0 ? _normalized$title : normalized.Title) !== null && _ref3 !== void 0 ? _ref3 : null;
136
138
  normalized.link = sanitizeUrl((_ref4 = (_normalized$link = normalized.link) !== null && _normalized$link !== void 0 ? _normalized$link : normalized.Link) !== null && _ref4 !== void 0 ? _ref4 : null);
137
139
  normalized.referenceType = (_ref5 = (_normalized$reference = normalized.referenceType) !== null && _normalized$reference !== void 0 ? _normalized$reference : normalized.ReferenceType) !== null && _ref5 !== void 0 ? _ref5 : null;
140
+ normalized.isGenerated = ((_normalized$isGenerat = normalized.isGenerated) !== null && _normalized$isGenerat !== void 0 ? _normalized$isGenerat : normalized.IsGenerated) === true;
138
141
  return normalized;
139
142
  }
140
143
  function isDownloadCitationReference(reference) {
@@ -169,28 +172,42 @@ window.openAIChatManager = function () {
169
172
  function buildCitationDisplay(content, references) {
170
173
  var processedContent = (content || '').trim();
171
174
  var messageReferences = normalizeReferences(references);
172
- if (!processedContent || !Object.keys(messageReferences).length) {
175
+ var referenceEntries = Object.entries(messageReferences);
176
+ if (!referenceEntries.length) {
173
177
  return {
174
178
  content: processedContent,
175
179
  citations: []
176
180
  };
177
181
  }
178
- var citedRefs = Object.entries(messageReferences).filter(function (_ref6) {
182
+ var citedRefs = referenceEntries.filter(function (_ref6) {
179
183
  var _ref7 = _slicedToArray(_ref6, 1),
180
184
  key = _ref7[0];
181
185
  return processedContent.includes(key);
182
186
  });
183
- if (!citedRefs.length) {
187
+ var generatedRefs = referenceEntries.filter(function (_ref8) {
188
+ var _ref9 = _slicedToArray(_ref8, 2),
189
+ key = _ref9[0],
190
+ value = _ref9[1];
191
+ return value.isGenerated && !processedContent.includes(key);
192
+ });
193
+ if (!citedRefs.length && !generatedRefs.length) {
184
194
  return {
185
195
  content: processedContent,
186
196
  citations: []
187
197
  };
188
198
  }
189
- citedRefs.sort(function (_ref8, _ref9) {
190
- var _ref0 = _slicedToArray(_ref8, 2),
191
- a = _ref0[1];
192
- var _ref1 = _slicedToArray(_ref9, 2),
193
- b = _ref1[1];
199
+ citedRefs.sort(function (_ref0, _ref1) {
200
+ var _ref10 = _slicedToArray(_ref0, 2),
201
+ a = _ref10[1];
202
+ var _ref11 = _slicedToArray(_ref1, 2),
203
+ b = _ref11[1];
204
+ return a.index - b.index;
205
+ });
206
+ generatedRefs.sort(function (_ref12, _ref13) {
207
+ var _ref14 = _slicedToArray(_ref12, 2),
208
+ a = _ref14[1];
209
+ var _ref15 = _slicedToArray(_ref13, 2),
210
+ b = _ref15[1];
194
211
  return a.index - b.index;
195
212
  });
196
213
  var citations = [];
@@ -224,11 +241,36 @@ window.openAIChatManager = function () {
224
241
  processedContent = processedContent.replaceAll(citation.placeholder, "<sup>".concat(citation.displayIndex, "</sup>"));
225
242
  }
226
243
  processedContent = processedContent.replaceAll('</sup><sup>', '</sup><sup>,</sup><sup>');
244
+
245
+ // Generated files (such as exported tabular data) are always offered as a download even when
246
+ // the model does not cite them inline, so the user never loses access to the produced file.
247
+ var _iterator2 = _createForOfIteratorHelper(generatedRefs),
248
+ _step2;
249
+ try {
250
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
251
+ var _step2$value = _slicedToArray(_step2.value, 2),
252
+ _key = _step2$value[0],
253
+ _value = _step2$value[1];
254
+ citations.push({
255
+ referenceKey: _key,
256
+ displayIndex: displayIndex,
257
+ label: getCitationLabel(_value, _key),
258
+ link: _value.link || null,
259
+ isDownload: true,
260
+ placeholder: null
261
+ });
262
+ displayIndex++;
263
+ }
264
+ } catch (err) {
265
+ _iterator2.e(err);
266
+ } finally {
267
+ _iterator2.f();
268
+ }
227
269
  return {
228
270
  content: processedContent,
229
- citations: citations.map(function (_ref10) {
230
- var placeholder = _ref10.placeholder,
231
- citation = _objectWithoutProperties(_ref10, _excluded);
271
+ citations: citations.map(function (_ref16) {
272
+ var placeholder = _ref16.placeholder,
273
+ citation = _objectWithoutProperties(_ref16, _excluded);
232
274
  return citation;
233
275
  })
234
276
  };
@@ -238,24 +280,24 @@ window.openAIChatManager = function () {
238
280
  if (!copyContent || !Array.isArray(citations) || citations.length === 0) {
239
281
  return copyContent;
240
282
  }
241
- var _iterator2 = _createForOfIteratorHelper(citations),
242
- _step2;
283
+ var _iterator3 = _createForOfIteratorHelper(citations),
284
+ _step3;
243
285
  try {
244
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
245
- var citation = _step2.value;
286
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
287
+ var citation = _step3.value;
246
288
  copyContent = copyContent.replaceAll(citation.referenceKey, "[".concat(citation.displayIndex, "]"));
247
289
  }
248
290
  } catch (err) {
249
- _iterator2.e(err);
291
+ _iterator3.e(err);
250
292
  } finally {
251
- _iterator2.f();
293
+ _iterator3.f();
252
294
  }
253
295
  copyContent += '\n\nReferences:\n';
254
- var _iterator3 = _createForOfIteratorHelper(citations),
255
- _step3;
296
+ var _iterator4 = _createForOfIteratorHelper(citations),
297
+ _step4;
256
298
  try {
257
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
258
- var _citation = _step3.value;
299
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
300
+ var _citation = _step4.value;
259
301
  copyContent += "".concat(_citation.displayIndex, ". ").concat(_citation.label);
260
302
  if (_citation.link) {
261
303
  copyContent += " - ".concat(_citation.link);
@@ -263,9 +305,9 @@ window.openAIChatManager = function () {
263
305
  copyContent += '\n';
264
306
  }
265
307
  } catch (err) {
266
- _iterator3.e(err);
308
+ _iterator4.e(err);
267
309
  } finally {
268
- _iterator3.f();
310
+ _iterator4.f();
269
311
  }
270
312
  return copyContent.trimEnd();
271
313
  }
@@ -447,17 +489,17 @@ window.openAIChatManager = function () {
447
489
  // Defer to requestAnimationFrame so the browser has fully laid out the
448
490
  // canvas elements before Chart.js reads their dimensions.
449
491
  requestAnimationFrame(function () {
450
- var _iterator4 = _createForOfIteratorHelper(charts),
451
- _step4;
492
+ var _iterator5 = _createForOfIteratorHelper(charts),
493
+ _step5;
452
494
  try {
453
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
454
- var c = _step4.value;
495
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
496
+ var c = _step5.value;
455
497
  renderChartOnCanvas(c.chartId, c.config);
456
498
  }
457
499
  } catch (err) {
458
- _iterator4.e(err);
500
+ _iterator5.e(err);
459
501
  } finally {
460
- _iterator4.f();
502
+ _iterator5.f();
461
503
  }
462
504
  });
463
505
  }
@@ -531,11 +573,23 @@ window.openAIChatManager = function () {
531
573
  ADD_ATTR: ['target']
532
574
  });
533
575
  }
576
+ function compactObject(source) {
577
+ if (!source || _typeof(source) !== 'object') {
578
+ return {};
579
+ }
580
+ return Object.fromEntries(Object.entries(source).filter(function (_ref17) {
581
+ var _ref18 = _slicedToArray(_ref17, 2),
582
+ value = _ref18[1];
583
+ return value !== undefined;
584
+ }));
585
+ }
534
586
  var initialize = function initialize(instanceConfig) {
535
- var config = Object.assign({}, defaultConfig, instanceConfig);
536
- config.widget = Object.assign({}, defaultConfig.widget || {}, instanceConfig && instanceConfig.widget ? instanceConfig.widget : {});
537
- var hasWidgetConfig = !!(instanceConfig && instanceConfig.widget && instanceConfig.widget.chatWidgetContainer && instanceConfig.widget.chatWidgetStateName);
538
- var widgetBehavior = window.openAIChatWidgetBehavior || null;
587
+ var normalizedInstanceConfig = compactObject(instanceConfig);
588
+ var normalizedWidgetConfig = compactObject(normalizedInstanceConfig.widget);
589
+ var config = Object.assign({}, defaultConfig, normalizedInstanceConfig);
590
+ config.widget = Object.assign({}, defaultConfig.widget || {}, normalizedWidgetConfig);
591
+ var hasWidgetConfig = !!(normalizedWidgetConfig.chatWidgetContainer && normalizedWidgetConfig.chatWidgetStateName);
592
+ var widgetBehavior = window.coreAIChatWidgetBehavior || null;
539
593
  // Keep defaultConfig in sync so renderers use overridden values
540
594
  defaultConfig = config;
541
595
  if (!config.signalRHubUrl) {
@@ -587,6 +641,7 @@ window.openAIChatManager = function () {
587
641
  thumbsUpTitle: config.thumbsUpTitle,
588
642
  thumbsDownTitle: config.thumbsDownTitle,
589
643
  copyTitle: config.copyTitle,
644
+ copiedTitle: config.copiedTitle,
590
645
  isRecording: false,
591
646
  mediaRecorder: null,
592
647
  preRecordingPrompt: '',
@@ -611,7 +666,10 @@ window.openAIChatManager = function () {
611
666
  pendingSessionPromise: null,
612
667
  pendingSessionResolver: null,
613
668
  pendingSessionRejector: null,
614
- pendingSessionTimeoutId: null
669
+ pendingSessionTimeoutId: null,
670
+ copiedMessageIndex: -1,
671
+ copyResetTimeoutId: null,
672
+ activeCopyButton: null
615
673
  };
616
674
  },
617
675
  computed: {
@@ -1325,14 +1383,14 @@ window.openAIChatManager = function () {
1325
1383
  if (message.content && !message.htmlContent) {
1326
1384
  message.htmlContent = parseMarkdownContent(message.content, message);
1327
1385
  }
1328
- this.fireEvent(new CustomEvent("addingOpenAIPromotMessage", {
1386
+ this.fireEvent(new CustomEvent("addingCoreAIPromotMessage", {
1329
1387
  detail: {
1330
1388
  message: message
1331
1389
  }
1332
1390
  }));
1333
1391
  this.messages.push(message);
1334
1392
  this.$nextTick(function () {
1335
- _this4.fireEvent(new CustomEvent("addedOpenAIPromotMessage", {
1393
+ _this4.fireEvent(new CustomEvent("addedCoreAIPromotMessage", {
1336
1394
  detail: {
1337
1395
  message: message
1338
1396
  }
@@ -1653,9 +1711,9 @@ window.openAIChatManager = function () {
1653
1711
  var _this9 = this;
1654
1712
  references = normalizeReferences(references);
1655
1713
  if (Object.keys(references).length) {
1656
- var _ref15, _message$rawContent2;
1714
+ var _ref23, _message$rawContent2;
1657
1715
  var message = this.messages[messageIndex];
1658
- message.rawContent = (_ref15 = (_message$rawContent2 = message.rawContent) !== null && _message$rawContent2 !== void 0 ? _message$rawContent2 : message.content) !== null && _ref15 !== void 0 ? _ref15 : '';
1716
+ message.rawContent = (_ref23 = (_message$rawContent2 = message.rawContent) !== null && _message$rawContent2 !== void 0 ? _message$rawContent2 : message.content) !== null && _ref23 !== void 0 ? _ref23 : '';
1659
1717
  updateMessagePresentation(message, references);
1660
1718
  this.messages[messageIndex] = message;
1661
1719
  this.$nextTick(function () {
@@ -1723,8 +1781,38 @@ window.openAIChatManager = function () {
1723
1781
  button.setAttribute('title', isPlaying ? 'Pause audio' : 'Read aloud');
1724
1782
  });
1725
1783
  },
1726
- synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1784
+ updateCopyButtons: function updateCopyButtons() {
1727
1785
  var _this1 = this;
1786
+ if (!this.chatContainer) {
1787
+ return;
1788
+ }
1789
+ var buttons = this.chatContainer.querySelectorAll('[data-copy-message-index]');
1790
+ buttons.forEach(function (button) {
1791
+ var buttonIndex = Number(button.getAttribute('data-copy-message-index'));
1792
+ var isCopied = buttonIndex === _this1.copiedMessageIndex;
1793
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1794
+ button.classList.toggle('text-success', isCopied);
1795
+ button.classList.toggle('text-secondary', !isCopied);
1796
+ button.setAttribute('title', isCopied ? _this1.copiedTitle : _this1.copyTitle);
1797
+ button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1798
+ RETURN_DOM_FRAGMENT: true
1799
+ }));
1800
+ });
1801
+ },
1802
+ setCopyButtonState: function setCopyButtonState(button, isCopied) {
1803
+ if (!button) {
1804
+ return;
1805
+ }
1806
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1807
+ button.classList.toggle('text-success', isCopied);
1808
+ button.classList.toggle('text-secondary', !isCopied);
1809
+ button.setAttribute('title', isCopied ? this.copiedTitle : this.copyTitle);
1810
+ button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1811
+ RETURN_DOM_FRAGMENT: true
1812
+ }));
1813
+ },
1814
+ synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1815
+ var _this10 = this;
1728
1816
  if (!this.textToSpeechEnabled || !text || !this.connection) {
1729
1817
  return;
1730
1818
  }
@@ -1733,16 +1821,16 @@ window.openAIChatManager = function () {
1733
1821
  this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;
1734
1822
  this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)["catch"](function (err) {
1735
1823
  console.error("TTS synthesis error:", err);
1736
- _this1.isPlayingAudio = false;
1737
- _this1.ttsPlayingMessageIndex = -1;
1738
- _this1._ttsCacheIndex = -1;
1739
- _this1.$nextTick(function () {
1740
- return _this1.updateTtsPlaybackButtons();
1824
+ _this10.isPlayingAudio = false;
1825
+ _this10.ttsPlayingMessageIndex = -1;
1826
+ _this10._ttsCacheIndex = -1;
1827
+ _this10.$nextTick(function () {
1828
+ return _this10.updateTtsPlaybackButtons();
1741
1829
  });
1742
1830
  });
1743
1831
  },
1744
1832
  toggleMessageTts: function toggleMessageTts(message, index) {
1745
- var _this10 = this;
1833
+ var _this11 = this;
1746
1834
  if (this.ttsPlayingMessageIndex === index) {
1747
1835
  this.stopAudio();
1748
1836
  return;
@@ -1755,7 +1843,7 @@ window.openAIChatManager = function () {
1755
1843
  }));
1756
1844
  this.ttsPlayingMessageIndex = index;
1757
1845
  this.$nextTick(function () {
1758
- return _this10.updateTtsPlaybackButtons();
1846
+ return _this11.updateTtsPlaybackButtons();
1759
1847
  });
1760
1848
  if (this.ttsAudioCache[index]) {
1761
1849
  this.playAudioBlob(this.ttsAudioCache[index]);
@@ -1764,13 +1852,13 @@ window.openAIChatManager = function () {
1764
1852
  this.synthesizeSpeech(message.content, index);
1765
1853
  },
1766
1854
  playCollectedAudio: function playCollectedAudio() {
1767
- var _this11 = this;
1855
+ var _this12 = this;
1768
1856
  if (this.audioChunks.length === 0) {
1769
1857
  if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {
1770
1858
  this.isPlayingAudio = false;
1771
1859
  this.ttsPlayingMessageIndex = -1;
1772
1860
  this.$nextTick(function () {
1773
- return _this11.updateTtsPlaybackButtons();
1861
+ return _this12.updateTtsPlaybackButtons();
1774
1862
  });
1775
1863
  }
1776
1864
  return;
@@ -1780,18 +1868,18 @@ window.openAIChatManager = function () {
1780
1868
  }, 0);
1781
1869
  var combined = new Uint8Array(totalLength);
1782
1870
  var offset = 0;
1783
- var _iterator5 = _createForOfIteratorHelper(this.audioChunks),
1784
- _step5;
1871
+ var _iterator6 = _createForOfIteratorHelper(this.audioChunks),
1872
+ _step6;
1785
1873
  try {
1786
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
1787
- var chunk = _step5.value;
1874
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
1875
+ var chunk = _step6.value;
1788
1876
  combined.set(chunk, offset);
1789
1877
  offset += chunk.length;
1790
1878
  }
1791
1879
  } catch (err) {
1792
- _iterator5.e(err);
1880
+ _iterator6.e(err);
1793
1881
  } finally {
1794
- _iterator5.f();
1882
+ _iterator6.f();
1795
1883
  }
1796
1884
  this.audioChunks = [];
1797
1885
  var blob = new Blob([combined], {
@@ -1810,39 +1898,39 @@ window.openAIChatManager = function () {
1810
1898
  this.playAudioBlob(blob);
1811
1899
  },
1812
1900
  playAudioBlob: function playAudioBlob(blob) {
1813
- var _this12 = this;
1901
+ var _this13 = this;
1814
1902
  var url = URL.createObjectURL(blob);
1815
1903
  var audio = new Audio(url);
1816
1904
  this.currentAudioUrl = url;
1817
1905
  this.currentAudioElement = audio;
1818
1906
  this.isPlayingAudio = true;
1819
1907
  audio.addEventListener('ended', function () {
1820
- _this12.currentAudioElement = null;
1821
- _this12.currentAudioUrl = null;
1908
+ _this13.currentAudioElement = null;
1909
+ _this13.currentAudioUrl = null;
1822
1910
  URL.revokeObjectURL(url);
1823
- _this12.playNextInQueue();
1911
+ _this13.playNextInQueue();
1824
1912
  });
1825
1913
  audio.addEventListener('error', function () {
1826
- _this12.currentAudioElement = null;
1827
- _this12.currentAudioUrl = null;
1914
+ _this13.currentAudioElement = null;
1915
+ _this13.currentAudioUrl = null;
1828
1916
  URL.revokeObjectURL(url);
1829
- _this12.playNextInQueue();
1917
+ _this13.playNextInQueue();
1830
1918
  });
1831
1919
  audio.play()["catch"](function (err) {
1832
1920
  console.error("Audio playback error:", err);
1833
- _this12.currentAudioElement = null;
1834
- _this12.currentAudioUrl = null;
1921
+ _this13.currentAudioElement = null;
1922
+ _this13.currentAudioUrl = null;
1835
1923
  URL.revokeObjectURL(url);
1836
- _this12.audioPlayQueue = [];
1837
- _this12.isPlayingAudio = false;
1838
- _this12.ttsPlayingMessageIndex = -1;
1839
- _this12.$nextTick(function () {
1840
- return _this12.updateTtsPlaybackButtons();
1924
+ _this13.audioPlayQueue = [];
1925
+ _this13.isPlayingAudio = false;
1926
+ _this13.ttsPlayingMessageIndex = -1;
1927
+ _this13.$nextTick(function () {
1928
+ return _this13.updateTtsPlaybackButtons();
1841
1929
  });
1842
1930
  });
1843
1931
  },
1844
1932
  playNextInQueue: function playNextInQueue() {
1845
- var _this13 = this;
1933
+ var _this14 = this;
1846
1934
  if (this.audioPlayQueue.length > 0) {
1847
1935
  var nextBlob = this.audioPlayQueue.shift();
1848
1936
  this.playAudioBlob(nextBlob);
@@ -1850,13 +1938,13 @@ window.openAIChatManager = function () {
1850
1938
  this.isPlayingAudio = false;
1851
1939
  this.ttsPlayingMessageIndex = -1;
1852
1940
  this.$nextTick(function () {
1853
- return _this13.updateTtsPlaybackButtons();
1941
+ return _this14.updateTtsPlaybackButtons();
1854
1942
  });
1855
1943
  this.conversationModeOnAudioEnded();
1856
1944
  }
1857
1945
  },
1858
1946
  stopAudio: function stopAudio() {
1859
- var _this14 = this;
1947
+ var _this15 = this;
1860
1948
  if (this.currentAudioElement) {
1861
1949
  this.currentAudioElement.pause();
1862
1950
  this.currentAudioElement.currentTime = 0;
@@ -1871,7 +1959,7 @@ window.openAIChatManager = function () {
1871
1959
  this.isPlayingAudio = false;
1872
1960
  this.ttsPlayingMessageIndex = -1;
1873
1961
  this.$nextTick(function () {
1874
- return _this14.updateTtsPlaybackButtons();
1962
+ return _this15.updateTtsPlaybackButtons();
1875
1963
  });
1876
1964
  },
1877
1965
  toggleConversationMode: function toggleConversationMode() {
@@ -1882,7 +1970,7 @@ window.openAIChatManager = function () {
1882
1970
  }
1883
1971
  },
1884
1972
  startConversationMode: function startConversationMode() {
1885
- var _this15 = this;
1973
+ var _this16 = this;
1886
1974
  if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {
1887
1975
  return;
1888
1976
  }
@@ -1902,12 +1990,12 @@ window.openAIChatManager = function () {
1902
1990
  }
1903
1991
  }).then(function (stream) {
1904
1992
  var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus') ? 'audio/ogg;codecs=opus' : MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm';
1905
- _this15.mediaRecorder = new MediaRecorder(stream, {
1993
+ _this16.mediaRecorder = new MediaRecorder(stream, {
1906
1994
  mimeType: mimeType,
1907
1995
  audioBitsPerSecond: 128000
1908
1996
  });
1909
- _this15._conversationSubject = new signalR.Subject();
1910
- _this15._conversationStream = stream;
1997
+ _this16._conversationSubject = new signalR.Subject();
1998
+ _this16._conversationStream = stream;
1911
1999
 
1912
2000
  // Create an AnalyserNode for volume-based interrupt detection.
1913
2001
  // During TTS playback, detect when the user speaks above
@@ -1916,20 +2004,20 @@ window.openAIChatManager = function () {
1916
2004
  // speaker echo so the STT stream has no gaps.
1917
2005
  var AudioCtx = window.AudioContext || window.webkitAudioContext;
1918
2006
  if (AudioCtx) {
1919
- _this15._conversationAudioCtx = new AudioCtx();
1920
- _this15._conversationAnalyser = _this15._conversationAudioCtx.createAnalyser();
1921
- _this15._conversationAnalyser.fftSize = 256;
1922
- var micSource = _this15._conversationAudioCtx.createMediaStreamSource(stream);
1923
- micSource.connect(_this15._conversationAnalyser);
2007
+ _this16._conversationAudioCtx = new AudioCtx();
2008
+ _this16._conversationAnalyser = _this16._conversationAudioCtx.createAnalyser();
2009
+ _this16._conversationAnalyser.fftSize = 256;
2010
+ var micSource = _this16._conversationAudioCtx.createMediaStreamSource(stream);
2011
+ micSource.connect(_this16._conversationAnalyser);
1924
2012
  }
1925
2013
  var pendingChunk = Promise.resolve();
1926
- var analyser = _this15._conversationAnalyser;
2014
+ var analyser = _this16._conversationAnalyser;
1927
2015
  var interruptVolumeThreshold = 30;
1928
- _this15.mediaRecorder.addEventListener('dataavailable', function (e) {
2016
+ _this16.mediaRecorder.addEventListener('dataavailable', function (e) {
1929
2017
  if (e.data && e.data.size > 0) {
1930
2018
  // During TTS playback, check mic volume to detect
1931
2019
  // user interruption (speaking above threshold).
1932
- if (_this15.isPlayingAudio && analyser) {
2020
+ if (_this16.isPlayingAudio && analyser) {
1933
2021
  var freqData = new Uint8Array(analyser.frequencyBinCount);
1934
2022
  analyser.getByteFrequencyData(freqData);
1935
2023
  var sum = 0;
@@ -1939,7 +2027,7 @@ window.openAIChatManager = function () {
1939
2027
  var avg = sum / freqData.length;
1940
2028
  if (avg >= interruptVolumeThreshold) {
1941
2029
  // User is speaking — interrupt TTS playback.
1942
- _this15.stopAudio();
2030
+ _this16.stopAudio();
1943
2031
  }
1944
2032
  }
1945
2033
 
@@ -1961,7 +2049,7 @@ window.openAIChatManager = function () {
1961
2049
  }, '');
1962
2050
  base64 = btoa(binaryString);
1963
2051
  try {
1964
- _this15._conversationSubject.next(base64);
2052
+ _this16._conversationSubject.next(base64);
1965
2053
  } catch (err) {
1966
2054
  // Subject may have been completed already.
1967
2055
  }
@@ -1972,28 +2060,28 @@ window.openAIChatManager = function () {
1972
2060
  })));
1973
2061
  }
1974
2062
  });
1975
- _this15.mediaRecorder.addEventListener('stop', function () {
2063
+ _this16.mediaRecorder.addEventListener('stop', function () {
1976
2064
  stream.getTracks().forEach(function (track) {
1977
2065
  return track.stop();
1978
2066
  });
1979
2067
  pendingChunk.then(function () {
1980
2068
  try {
1981
- _this15._conversationSubject.complete();
2069
+ _this16._conversationSubject.complete();
1982
2070
  } catch (err) {
1983
2071
  // Already completed.
1984
2072
  }
1985
2073
  });
1986
2074
  });
1987
- var profileId = _this15.getProfileId();
1988
- var sessionId = _this15.getSessionId() || '';
2075
+ var profileId = _this16.getProfileId();
2076
+ var sessionId = _this16.getSessionId() || '';
1989
2077
  var language = navigator.language || document.documentElement.lang || 'en-US';
1990
- _this15.connection.send("StartConversation", profileId, sessionId, _this15._conversationSubject, mimeType, language);
1991
- _this15.mediaRecorder.start(250);
1992
- _this15.isRecording = true;
2078
+ _this16.connection.send("StartConversation", profileId, sessionId, _this16._conversationSubject, mimeType, language);
2079
+ _this16.mediaRecorder.start(250);
2080
+ _this16.isRecording = true;
1993
2081
  })["catch"](function (err) {
1994
2082
  console.error('Microphone access denied:', err);
1995
- _this15.isConversationMode = false;
1996
- _this15.updateConversationButton();
2083
+ _this16.isConversationMode = false;
2084
+ _this16.updateConversationButton();
1997
2085
  });
1998
2086
  },
1999
2087
  stopConversationMode: function stopConversationMode() {
@@ -2120,7 +2208,7 @@ window.openAIChatManager = function () {
2120
2208
  return removedCount;
2121
2209
  },
2122
2210
  receiveNotification: function receiveNotification(notification) {
2123
- var _this16 = this;
2211
+ var _this17 = this;
2124
2212
  if (!notification || !notification.type) {
2125
2213
  return;
2126
2214
  }
@@ -2135,7 +2223,7 @@ window.openAIChatManager = function () {
2135
2223
  }
2136
2224
  this.scheduleNotificationDismiss(notification);
2137
2225
  this.$nextTick(function () {
2138
- _this16.scrollToBottom();
2226
+ _this17.scrollToBottom();
2139
2227
  });
2140
2228
  },
2141
2229
  updateNotification: function updateNotification(notification) {
@@ -2152,12 +2240,12 @@ window.openAIChatManager = function () {
2152
2240
  }
2153
2241
  },
2154
2242
  scheduleNotificationDismiss: function scheduleNotificationDismiss(notification) {
2155
- var _this17 = this;
2243
+ var _this18 = this;
2156
2244
  if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {
2157
2245
  return;
2158
2246
  }
2159
2247
  this.notificationDismissTimers[notification.type] = setTimeout(function () {
2160
- _this17.removeNotification(notification.type);
2248
+ _this18.removeNotification(notification.type);
2161
2249
  }, notification.autoDismissMs);
2162
2250
  },
2163
2251
  clearNotificationDismiss: function clearNotificationDismiss(notificationType) {
@@ -2187,12 +2275,12 @@ window.openAIChatManager = function () {
2187
2275
  });
2188
2276
  },
2189
2277
  scrollToBottom: function scrollToBottom() {
2190
- var _this18 = this;
2278
+ var _this19 = this;
2191
2279
  if (!this.autoScroll) {
2192
2280
  return;
2193
2281
  }
2194
2282
  setTimeout(function () {
2195
- _this18.chatContainer.scrollTop = _this18.chatContainer.scrollHeight - _this18.chatContainer.clientHeight;
2283
+ _this19.chatContainer.scrollTop = _this19.chatContainer.scrollHeight - _this19.chatContainer.clientHeight;
2196
2284
  }, 50);
2197
2285
  },
2198
2286
  handleUserInput: function handleUserInput(event) {
@@ -2238,27 +2326,27 @@ window.openAIChatManager = function () {
2238
2326
  });
2239
2327
  },
2240
2328
  ensureSessionForDocuments: function ensureSessionForDocuments(profileId) {
2241
- var _this19 = this;
2329
+ var _this20 = this;
2242
2330
  return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2243
2331
  var sessionId;
2244
2332
  return _regenerator().w(function (_context9) {
2245
2333
  while (1) switch (_context9.n) {
2246
2334
  case 0:
2247
- sessionId = _this19.getSessionId();
2335
+ sessionId = _this20.getSessionId();
2248
2336
  if (!sessionId) {
2249
2337
  _context9.n = 1;
2250
2338
  break;
2251
2339
  }
2252
2340
  return _context9.a(2, sessionId);
2253
2341
  case 1:
2254
- if (!(!profileId || !_this19.connection)) {
2342
+ if (!(!profileId || !_this20.connection)) {
2255
2343
  _context9.n = 2;
2256
2344
  break;
2257
2345
  }
2258
2346
  return _context9.a(2, null);
2259
2347
  case 2:
2260
2348
  _context9.n = 3;
2261
- return _this19.requestNewSession(profileId);
2349
+ return _this20.requestNewSession(profileId);
2262
2350
  case 3:
2263
2351
  return _context9.a(2, _context9.v);
2264
2352
  }
@@ -2266,7 +2354,7 @@ window.openAIChatManager = function () {
2266
2354
  }))();
2267
2355
  },
2268
2356
  requestNewSession: function requestNewSession(profileId) {
2269
- var _this20 = this;
2357
+ var _this21 = this;
2270
2358
  if (this.pendingSessionPromise) {
2271
2359
  return this.pendingSessionPromise;
2272
2360
  }
@@ -2274,14 +2362,14 @@ window.openAIChatManager = function () {
2274
2362
  return Promise.resolve(null);
2275
2363
  }
2276
2364
  this.pendingSessionPromise = new Promise(function (resolve, reject) {
2277
- _this20.pendingSessionResolver = resolve;
2278
- _this20.pendingSessionRejector = reject;
2279
- _this20.pendingSessionTimeoutId = window.setTimeout(function () {
2280
- _this20.rejectPendingSessionRequest('Timed out while creating a chat session.');
2365
+ _this21.pendingSessionResolver = resolve;
2366
+ _this21.pendingSessionRejector = reject;
2367
+ _this21.pendingSessionTimeoutId = window.setTimeout(function () {
2368
+ _this21.rejectPendingSessionRequest('Timed out while creating a chat session.');
2281
2369
  }, 15000);
2282
2370
  });
2283
2371
  this.connection.invoke("StartSession", profileId, null)["catch"](function (err) {
2284
- _this20.rejectPendingSessionRequest(err);
2372
+ _this21.rejectPendingSessionRequest(err);
2285
2373
  });
2286
2374
  return this.pendingSessionPromise;
2287
2375
  },
@@ -2307,7 +2395,7 @@ window.openAIChatManager = function () {
2307
2395
  this.pendingSessionTimeoutId = null;
2308
2396
  },
2309
2397
  initializeApp: function initializeApp() {
2310
- var _this21 = this;
2398
+ var _this22 = this;
2311
2399
  this.inputElement = document.querySelector(config.inputElementSelector);
2312
2400
  this.buttonElement = document.querySelector(config.sendButtonElementSelector);
2313
2401
  this.chatContainer = document.querySelector(config.chatContainerElementSelector);
@@ -2345,7 +2433,7 @@ window.openAIChatManager = function () {
2345
2433
  fileInput.accept = config.allowedExtensions;
2346
2434
  }
2347
2435
  fileInput.addEventListener('change', function (e) {
2348
- return _this21.handleFileInputChange(e);
2436
+ return _this22.handleFileInputChange(e);
2349
2437
  });
2350
2438
  this.documentBar.parentElement.appendChild(fileInput);
2351
2439
 
@@ -2353,13 +2441,13 @@ window.openAIChatManager = function () {
2353
2441
  var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;
2354
2442
  if (inputArea) {
2355
2443
  inputArea.addEventListener('dragover', function (e) {
2356
- return _this21.handleDragOver(e);
2444
+ return _this22.handleDragOver(e);
2357
2445
  });
2358
2446
  inputArea.addEventListener('dragleave', function (e) {
2359
- return _this21.handleDragLeave(e);
2447
+ return _this22.handleDragLeave(e);
2360
2448
  });
2361
2449
  inputArea.addEventListener('drop', function (e) {
2362
- return _this21.handleDrop(e);
2450
+ return _this22.handleDrop(e);
2363
2451
  });
2364
2452
  }
2365
2453
  }
@@ -2367,55 +2455,55 @@ window.openAIChatManager = function () {
2367
2455
 
2368
2456
  // Pause auto-scroll when the user manually scrolls up during streaming.
2369
2457
  this.chatContainer.addEventListener('scroll', function () {
2370
- if (!_this21.stream) {
2458
+ if (!_this22.stream) {
2371
2459
  return;
2372
2460
  }
2373
2461
  var threshold = 30;
2374
- var atBottom = _this21.chatContainer.scrollHeight - _this21.chatContainer.clientHeight - _this21.chatContainer.scrollTop <= threshold;
2375
- _this21.autoScroll = atBottom;
2462
+ var atBottom = _this22.chatContainer.scrollHeight - _this22.chatContainer.clientHeight - _this22.chatContainer.scrollTop <= threshold;
2463
+ _this22.autoScroll = atBottom;
2376
2464
  });
2377
2465
  this.inputElement.addEventListener('keydown', function (event) {
2378
- if (_this21.stream != null) {
2466
+ if (_this22.stream != null) {
2379
2467
  return;
2380
2468
  }
2381
2469
  if (event.key === "Enter" && !event.shiftKey) {
2382
2470
  event.preventDefault();
2383
- _this21.buttonElement.click();
2471
+ _this22.buttonElement.click();
2384
2472
  }
2385
2473
  });
2386
2474
  this.inputElement.addEventListener('input', function (e) {
2387
- _this21.handleUserInput(e);
2475
+ _this22.handleUserInput(e);
2388
2476
  if (e.target.value.trim()) {
2389
- _this21.buttonElement.removeAttribute('disabled');
2477
+ _this22.buttonElement.removeAttribute('disabled');
2390
2478
  } else {
2391
- _this21.buttonElement.setAttribute('disabled', true);
2479
+ _this22.buttonElement.setAttribute('disabled', true);
2392
2480
  }
2393
2481
  });
2394
2482
  this.buttonElement.addEventListener('click', function () {
2395
- if (_this21.stream != null) {
2396
- _this21.stream.dispose();
2397
- _this21.stream = null;
2398
- _this21.streamingFinished();
2399
- _this21.hideTypingIndicator();
2483
+ if (_this22.stream != null) {
2484
+ _this22.stream.dispose();
2485
+ _this22.stream = null;
2486
+ _this22.streamingFinished();
2487
+ _this22.hideTypingIndicator();
2400
2488
 
2401
2489
  // Clean up: remove empty assistant message or stop streaming animation.
2402
- if (_this21.messages.length > 0) {
2403
- var lastMsg = _this21.messages[_this21.messages.length - 1];
2490
+ if (_this22.messages.length > 0) {
2491
+ var lastMsg = _this22.messages[_this22.messages.length - 1];
2404
2492
  if (lastMsg.role === 'assistant' && !lastMsg.content) {
2405
- _this21.messages.pop();
2493
+ _this22.messages.pop();
2406
2494
  } else if (lastMsg.isStreaming) {
2407
2495
  lastMsg.isStreaming = false;
2408
2496
  }
2409
2497
  }
2410
2498
  return;
2411
2499
  }
2412
- _this21.sendMessage();
2500
+ _this22.sendMessage();
2413
2501
  });
2414
2502
  var promptGenerators = document.getElementsByClassName('profile-generated-prompt');
2415
2503
  for (var i = 0; i < promptGenerators.length; i++) {
2416
2504
  promptGenerators[i].addEventListener('click', function (e) {
2417
2505
  e.preventDefault();
2418
- _this21.generatePrompt(e.target);
2506
+ _this22.generatePrompt(e.target);
2419
2507
  });
2420
2508
  }
2421
2509
  var chatSessions = document.getElementsByClassName('chat-session-history-item');
@@ -2427,17 +2515,18 @@ window.openAIChatManager = function () {
2427
2515
  console.error('an element with the class chat-session-history-item with no data-session-id set.');
2428
2516
  return;
2429
2517
  }
2430
- _this21.loadSession(sessionId);
2431
- _this21.showChatScreen();
2518
+ _this22.loadSession(sessionId);
2519
+ _this22.showChatScreen();
2432
2520
  });
2433
2521
  }
2434
- for (var _i5 = 0; _i5 < config.messages.length; _i5++) {
2435
- this.addMessage(config.messages[_i5]);
2522
+ var initialMessages = Array.isArray(config.messages) ? config.messages : [];
2523
+ for (var _i5 = 0; _i5 < initialMessages.length; _i5++) {
2524
+ this.addMessage(initialMessages[_i5]);
2436
2525
  }
2437
2526
 
2438
2527
  // Update feedback icons in the DOM after initial messages have rendered.
2439
2528
  this.$nextTick(function () {
2440
- _this21.refreshAllFeedbackIcons();
2529
+ _this22.refreshAllFeedbackIcons();
2441
2530
  });
2442
2531
 
2443
2532
  // Delegate click for code block copy buttons.
@@ -2469,7 +2558,7 @@ window.openAIChatManager = function () {
2469
2558
  if (this.micButton) {
2470
2559
  this.micButton.style.display = '';
2471
2560
  this.micButton.addEventListener('click', function () {
2472
- _this21.toggleRecording();
2561
+ _this22.toggleRecording();
2473
2562
  });
2474
2563
  }
2475
2564
  }
@@ -2479,7 +2568,7 @@ window.openAIChatManager = function () {
2479
2568
  this.conversationButton = document.querySelector(config.conversationButtonElementSelector);
2480
2569
  if (this.conversationButton) {
2481
2570
  this.conversationButton.addEventListener('click', function () {
2482
- _this21.toggleConversationMode();
2571
+ _this22.toggleConversationMode();
2483
2572
  });
2484
2573
  }
2485
2574
  }
@@ -2503,7 +2592,7 @@ window.openAIChatManager = function () {
2503
2592
  }
2504
2593
  return;
2505
2594
  }
2506
- this.fireEvent(new CustomEvent("initializingSessionOpenAIChat", {
2595
+ this.fireEvent(new CustomEvent("initializingSessionCoreAIChat", {
2507
2596
  detail: {
2508
2597
  sessionId: sessionId
2509
2598
  }
@@ -2522,10 +2611,45 @@ window.openAIChatManager = function () {
2522
2611
  }
2523
2612
  return sessionId;
2524
2613
  },
2525
- copyResponse: function copyResponse(message) {
2526
- var _ref17, _message$copyContent;
2527
- var text = message && _typeof(message) === 'object' ? (_ref17 = (_message$copyContent = message.copyContent) !== null && _message$copyContent !== void 0 ? _message$copyContent : message.content) !== null && _ref17 !== void 0 ? _ref17 : '' : message !== null && message !== void 0 ? message : '';
2528
- navigator.clipboard.writeText(text);
2614
+ clearCopiedMessageState: function clearCopiedMessageState() {
2615
+ if (this.copyResetTimeoutId) {
2616
+ window.clearTimeout(this.copyResetTimeoutId);
2617
+ this.copyResetTimeoutId = null;
2618
+ }
2619
+ if (this.activeCopyButton) {
2620
+ this.setCopyButtonState(this.activeCopyButton, false);
2621
+ this.activeCopyButton = null;
2622
+ }
2623
+ this.copiedMessageIndex = -1;
2624
+ },
2625
+ copyResponse: function copyResponse(message, index, event) {
2626
+ var _ref25,
2627
+ _message$copyContent,
2628
+ _event$target,
2629
+ _event$target$closest,
2630
+ _this23 = this;
2631
+ var text = message && _typeof(message) === 'object' ? (_ref25 = (_message$copyContent = message.copyContent) !== null && _message$copyContent !== void 0 ? _message$copyContent : message.content) !== null && _ref25 !== void 0 ? _ref25 : '' : message !== null && message !== void 0 ? message : '';
2632
+ var button = (event === null || event === void 0 ? void 0 : event.currentTarget) || (event === null || event === void 0 || (_event$target = event.target) === null || _event$target === void 0 || (_event$target$closest = _event$target.closest) === null || _event$target$closest === void 0 ? void 0 : _event$target$closest.call(_event$target, '[data-copy-message-index]')) || null;
2633
+ navigator.clipboard.writeText(text).then(function () {
2634
+ _this23.clearCopiedMessageState();
2635
+ _this23.copiedMessageIndex = typeof index === 'number' ? index : -1;
2636
+ _this23.activeCopyButton = button;
2637
+ if (button) {
2638
+ _this23.setCopyButtonState(button, true);
2639
+ } else {
2640
+ _this23.$nextTick(function () {
2641
+ return _this23.updateCopyButtons();
2642
+ });
2643
+ }
2644
+ _this23.copyResetTimeoutId = window.setTimeout(function () {
2645
+ _this23.clearCopiedMessageState();
2646
+ _this23.$nextTick(function () {
2647
+ return _this23.updateCopyButtons();
2648
+ });
2649
+ }, Number(config.copyResetDelayMs) || 2000);
2650
+ })["catch"](function (err) {
2651
+ return console.error('Failed to copy response:', err);
2652
+ });
2529
2653
  },
2530
2654
  updateFeedbackIcons: function updateFeedbackIcons(container, userRating) {
2531
2655
  if (!container) {
@@ -2620,6 +2744,12 @@ window.openAIChatManager = function () {
2620
2744
  // Reserved for future use — volume-based interrupt detection
2621
2745
  // no longer mutes tracks; browser echo cancellation handles echo.
2622
2746
  },
2747
+ copiedMessageIndex: function copiedMessageIndex() {
2748
+ var _this24 = this;
2749
+ this.$nextTick(function () {
2750
+ return _this24.updateCopyButtons();
2751
+ });
2752
+ },
2623
2753
  isConversationMode: function isConversationMode(active) {
2624
2754
  // Hide/show mic button.
2625
2755
  if (this.micButton) {
@@ -2641,22 +2771,23 @@ window.openAIChatManager = function () {
2641
2771
  }
2642
2772
  },
2643
2773
  mounted: function mounted() {
2644
- var _this22 = this;
2774
+ var _this25 = this;
2645
2775
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
2646
2776
  var isInitialized;
2647
2777
  return _regenerator().w(function (_context0) {
2648
2778
  while (1) switch (_context0.n) {
2649
2779
  case 0:
2650
2780
  _context0.n = 1;
2651
- return _this22.startConnection();
2781
+ return _this25.startConnection();
2652
2782
  case 1:
2653
- isInitialized = _this22.initializeApp();
2783
+ isInitialized = _this25.initializeApp();
2654
2784
  if (isInitialized && hasWidgetConfig && widgetBehavior && typeof widgetBehavior.onMounted === 'function') {
2655
- widgetBehavior.onMounted(_this22, config);
2785
+ widgetBehavior.onMounted(_this25, config);
2656
2786
  }
2657
- _this22.$nextTick(function () {
2658
- refreshFontAwesomeIcons(_this22.$el);
2659
- _this22.fontAwesomeObserver = observeFontAwesomeIcons(_this22.$el);
2787
+ _this25.$nextTick(function () {
2788
+ _this25.updateCopyButtons();
2789
+ refreshFontAwesomeIcons(_this25.$el);
2790
+ _this25.fontAwesomeObserver = observeFontAwesomeIcons(_this25.$el);
2660
2791
  });
2661
2792
  case 2:
2662
2793
  return _context0.a(2);
@@ -2673,6 +2804,7 @@ window.openAIChatManager = function () {
2673
2804
  this.fontAwesomeObserver.disconnect();
2674
2805
  this.fontAwesomeObserver = null;
2675
2806
  }
2807
+ this.clearCopiedMessageState();
2676
2808
  this.stopAudio(false);
2677
2809
  if (this.stream) {
2678
2810
  this.stream.dispose();
@@ -2693,8 +2825,151 @@ window.openAIChatManager = function () {
2693
2825
  }
2694
2826
  return app;
2695
2827
  };
2828
+ var autoInitializeSelector = '[data-coreai-chat-config],[data-coreai-chat-app-element-selector]';
2829
+ function getAttributeValue(element, attributeName) {
2830
+ var value = element.getAttribute(attributeName);
2831
+ return value === null || value === '' ? null : value;
2832
+ }
2833
+ function parseBooleanAttributeValue(value) {
2834
+ if (value === null || value === undefined) {
2835
+ return null;
2836
+ }
2837
+ if (typeof value === 'boolean') {
2838
+ return value;
2839
+ }
2840
+ var normalized = String(value).trim().toLowerCase();
2841
+ if (normalized === 'true') {
2842
+ return true;
2843
+ }
2844
+ if (normalized === 'false') {
2845
+ return false;
2846
+ }
2847
+ return null;
2848
+ }
2849
+ function parseJsonAttribute(element, attributeName, description) {
2850
+ var rawValue = getAttributeValue(element, attributeName);
2851
+ if (!rawValue) {
2852
+ return null;
2853
+ }
2854
+ try {
2855
+ return JSON.parse(rawValue);
2856
+ } catch (error) {
2857
+ console.error('Failed to parse ' + description + ' JSON.', error);
2858
+ return null;
2859
+ }
2860
+ }
2861
+ function buildConfigFromDataAttributes(element, options) {
2862
+ var _parseJsonAttribute, _parseJsonAttribute2;
2863
+ var settings = options || {};
2864
+ if (element.hasAttribute('data-coreai-chat-widget-container-selector') && settings.allowWidgetHost !== true) {
2865
+ return null;
2866
+ }
2867
+ var appElementSelector = getAttributeValue(element, 'data-coreai-chat-app-element-selector');
2868
+ if (!appElementSelector) {
2869
+ return null;
2870
+ }
2871
+ var config = {
2872
+ signalRHubUrl: getAttributeValue(element, 'data-coreai-chat-signalr-hub-url'),
2873
+ appElementSelector: appElementSelector,
2874
+ chatContainerElementSelector: getAttributeValue(element, 'data-coreai-chat-container-element-selector'),
2875
+ inputElementSelector: getAttributeValue(element, 'data-coreai-chat-input-element-selector'),
2876
+ sendButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-send-button-element-selector'),
2877
+ placeholderElementSelector: getAttributeValue(element, 'data-coreai-chat-placeholder-element-selector'),
2878
+ messages: (_parseJsonAttribute = parseJsonAttribute(element, 'data-coreai-chat-messages', 'CoreAI chat messages')) !== null && _parseJsonAttribute !== void 0 ? _parseJsonAttribute : undefined,
2879
+ chatMode: getAttributeValue(element, 'data-coreai-chat-mode'),
2880
+ micButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-mic-button-element-selector'),
2881
+ conversationButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-conversation-button-element-selector'),
2882
+ ttsVoiceName: getAttributeValue(element, 'data-coreai-chat-tts-voice-name'),
2883
+ documentBarSelector: getAttributeValue(element, 'data-coreai-chat-document-bar-selector'),
2884
+ uploadDocumentUrl: getAttributeValue(element, 'data-coreai-chat-upload-document-url'),
2885
+ removeDocumentUrl: getAttributeValue(element, 'data-coreai-chat-remove-document-url'),
2886
+ allowedExtensions: getAttributeValue(element, 'data-coreai-chat-allowed-extensions'),
2887
+ supportedExtensionsText: getAttributeValue(element, 'data-coreai-chat-supported-extensions-text'),
2888
+ existingDocuments: (_parseJsonAttribute2 = parseJsonAttribute(element, 'data-coreai-chat-existing-documents', 'CoreAI chat existing documents')) !== null && _parseJsonAttribute2 !== void 0 ? _parseJsonAttribute2 : undefined,
2889
+ initialPrompt: getAttributeValue(element, 'data-coreai-chat-initial-prompt'),
2890
+ userLabel: getAttributeValue(element, 'data-coreai-chat-user-label'),
2891
+ assistantLabel: getAttributeValue(element, 'data-coreai-chat-assistant-label'),
2892
+ copyTitle: getAttributeValue(element, 'data-coreai-chat-copy-title'),
2893
+ copiedTitle: getAttributeValue(element, 'data-coreai-chat-copied-title')
2894
+ };
2895
+ var booleanAttributes = {
2896
+ metricsEnabled: 'data-coreai-chat-metrics-enabled',
2897
+ textToSpeechEnabled: 'data-coreai-chat-text-to-speech-enabled',
2898
+ sessionDocumentsEnabled: 'data-coreai-chat-session-documents-enabled',
2899
+ autoCreateSession: 'data-coreai-chat-auto-create-session',
2900
+ singleResponseMode: 'data-coreai-chat-single-response-mode'
2901
+ };
2902
+ Object.keys(booleanAttributes).forEach(function (key) {
2903
+ var parsed = parseBooleanAttributeValue(getAttributeValue(element, booleanAttributes[key]));
2904
+ if (parsed !== null) {
2905
+ config[key] = parsed;
2906
+ }
2907
+ });
2908
+ return compactObject(config);
2909
+ }
2910
+ function buildConfigFromElement(element, options) {
2911
+ var jsonConfig = parseJsonAttribute(element, 'data-coreai-chat-config', 'CoreAI chat config');
2912
+ var attributeConfig = buildConfigFromDataAttributes(element, options);
2913
+ if (!jsonConfig && !attributeConfig) {
2914
+ return null;
2915
+ }
2916
+ return Object.assign({}, jsonConfig || {}, attributeConfig || {});
2917
+ }
2918
+ function initializeFromElement(element) {
2919
+ if (!element || element.dataset.coreAiChatInitialized === 'true') {
2920
+ return element ? element.__coreAIChatApp || null : null;
2921
+ }
2922
+ var config = buildConfigFromElement(element);
2923
+ if (!config) {
2924
+ return null;
2925
+ }
2926
+ var app = initialize(config);
2927
+ if (!app) {
2928
+ return null;
2929
+ }
2930
+ element.dataset.coreAiChatInitialized = 'true';
2931
+ element.__coreAIChatApp = app;
2932
+ return app;
2933
+ }
2934
+ function scanForAutoInitialization(root) {
2935
+ if (!root || typeof root.querySelectorAll !== 'function') {
2936
+ return;
2937
+ }
2938
+ if (typeof root.matches === 'function' && root.matches(autoInitializeSelector)) {
2939
+ initializeFromElement(root);
2940
+ }
2941
+ root.querySelectorAll(autoInitializeSelector).forEach(initializeFromElement);
2942
+ }
2943
+ function startAutoInitialization() {
2944
+ scanForAutoInitialization(document);
2945
+ if (typeof MutationObserver === 'undefined') {
2946
+ return;
2947
+ }
2948
+ var observer = new MutationObserver(function (mutations) {
2949
+ mutations.forEach(function (mutation) {
2950
+ mutation.addedNodes.forEach(function (node) {
2951
+ if (node && node.nodeType === 1) {
2952
+ scanForAutoInitialization(node);
2953
+ }
2954
+ });
2955
+ });
2956
+ });
2957
+ observer.observe(document.body, {
2958
+ childList: true,
2959
+ subtree: true
2960
+ });
2961
+ }
2962
+ if (document.readyState === 'loading') {
2963
+ document.addEventListener('DOMContentLoaded', startAutoInitialization, {
2964
+ once: true
2965
+ });
2966
+ } else {
2967
+ startAutoInitialization();
2968
+ }
2696
2969
  return {
2697
- initialize: initialize
2970
+ initialize: initialize,
2971
+ initializeFromElement: initializeFromElement,
2972
+ buildConfigFromElement: buildConfigFromElement
2698
2973
  };
2699
2974
  }();
2700
2975