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

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,8 +57,8 @@ 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 ",
59
- 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
+ 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-message-heading ai-chat-message-heading-user\">\n <span class=\"ai-chat-msg-role ai-chat-msg-role-user\">{{ userLabel }}</span>\n </div>\n <div v-else-if=\"message.role !== 'indicator'\" class=\"ai-chat-message-heading ai-chat-message-heading-assistant\">\n <div :class=\"getAssistantRoleClasses(message)\">\n <span :class=\"getAssistantIconClasses(message, index)\"><i :class=\"getAssistantIcon(message)\"></i></span>\n <span class=\"ai-chat-message-role-label\">{{ getAssistantLabel(message) }}</span>\n </div>\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 ",
61
+ indicatorTemplate: "\n <div class=\"ai-chat-message-heading ai-chat-message-heading-assistant\">\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 <span class=\"ai-chat-message-role-label\">Assistant</span>\n </div>\n </div>\n "
60
62
  };
61
63
 
62
64
  // Sanitize URLs to prevent javascript: protocol injection.
@@ -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,31 @@ 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
+ }
586
+ function resolveIndicatorTemplate(template, assistantLabel) {
587
+ if (!template) {
588
+ return template;
589
+ }
590
+ var encodedAssistantLabel = escapeHtmlEntities(assistantLabel || 'Assistant');
591
+ return template.replace(/\{\{\s*assistantLabel\s*\}\}/g, encodedAssistantLabel);
592
+ }
534
593
  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;
594
+ var normalizedInstanceConfig = compactObject(instanceConfig);
595
+ var normalizedWidgetConfig = compactObject(normalizedInstanceConfig.widget);
596
+ var config = Object.assign({}, defaultConfig, normalizedInstanceConfig);
597
+ config.widget = Object.assign({}, defaultConfig.widget || {}, normalizedWidgetConfig);
598
+ config.indicatorTemplate = resolveIndicatorTemplate(config.indicatorTemplate, config.assistantLabel);
599
+ var hasWidgetConfig = !!(normalizedWidgetConfig.chatWidgetContainer && normalizedWidgetConfig.chatWidgetStateName);
600
+ var widgetBehavior = window.coreAIChatWidgetBehavior || null;
539
601
  // Keep defaultConfig in sync so renderers use overridden values
540
602
  defaultConfig = config;
541
603
  if (!config.signalRHubUrl) {
@@ -587,6 +649,7 @@ window.openAIChatManager = function () {
587
649
  thumbsUpTitle: config.thumbsUpTitle,
588
650
  thumbsDownTitle: config.thumbsDownTitle,
589
651
  copyTitle: config.copyTitle,
652
+ copiedTitle: config.copiedTitle,
590
653
  isRecording: false,
591
654
  mediaRecorder: null,
592
655
  preRecordingPrompt: '',
@@ -611,7 +674,10 @@ window.openAIChatManager = function () {
611
674
  pendingSessionPromise: null,
612
675
  pendingSessionResolver: null,
613
676
  pendingSessionRejector: null,
614
- pendingSessionTimeoutId: null
677
+ pendingSessionTimeoutId: null,
678
+ copiedMessageIndex: -1,
679
+ copyResetTimeoutId: null,
680
+ activeCopyButton: null
615
681
  };
616
682
  },
617
683
  computed: {
@@ -1325,14 +1391,14 @@ window.openAIChatManager = function () {
1325
1391
  if (message.content && !message.htmlContent) {
1326
1392
  message.htmlContent = parseMarkdownContent(message.content, message);
1327
1393
  }
1328
- this.fireEvent(new CustomEvent("addingOpenAIPromotMessage", {
1394
+ this.fireEvent(new CustomEvent("addingCoreAIPromotMessage", {
1329
1395
  detail: {
1330
1396
  message: message
1331
1397
  }
1332
1398
  }));
1333
1399
  this.messages.push(message);
1334
1400
  this.$nextTick(function () {
1335
- _this4.fireEvent(new CustomEvent("addedOpenAIPromotMessage", {
1401
+ _this4.fireEvent(new CustomEvent("addedCoreAIPromotMessage", {
1336
1402
  detail: {
1337
1403
  message: message
1338
1404
  }
@@ -1653,9 +1719,9 @@ window.openAIChatManager = function () {
1653
1719
  var _this9 = this;
1654
1720
  references = normalizeReferences(references);
1655
1721
  if (Object.keys(references).length) {
1656
- var _ref15, _message$rawContent2;
1722
+ var _ref23, _message$rawContent2;
1657
1723
  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 : '';
1724
+ message.rawContent = (_ref23 = (_message$rawContent2 = message.rawContent) !== null && _message$rawContent2 !== void 0 ? _message$rawContent2 : message.content) !== null && _ref23 !== void 0 ? _ref23 : '';
1659
1725
  updateMessagePresentation(message, references);
1660
1726
  this.messages[messageIndex] = message;
1661
1727
  this.$nextTick(function () {
@@ -1723,8 +1789,38 @@ window.openAIChatManager = function () {
1723
1789
  button.setAttribute('title', isPlaying ? 'Pause audio' : 'Read aloud');
1724
1790
  });
1725
1791
  },
1726
- synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1792
+ updateCopyButtons: function updateCopyButtons() {
1727
1793
  var _this1 = this;
1794
+ if (!this.chatContainer) {
1795
+ return;
1796
+ }
1797
+ var buttons = this.chatContainer.querySelectorAll('[data-copy-message-index]');
1798
+ buttons.forEach(function (button) {
1799
+ var buttonIndex = Number(button.getAttribute('data-copy-message-index'));
1800
+ var isCopied = buttonIndex === _this1.copiedMessageIndex;
1801
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1802
+ button.classList.toggle('text-success', isCopied);
1803
+ button.classList.toggle('text-secondary', !isCopied);
1804
+ button.setAttribute('title', isCopied ? _this1.copiedTitle : _this1.copyTitle);
1805
+ button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1806
+ RETURN_DOM_FRAGMENT: true
1807
+ }));
1808
+ });
1809
+ },
1810
+ setCopyButtonState: function setCopyButtonState(button, isCopied) {
1811
+ if (!button) {
1812
+ return;
1813
+ }
1814
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1815
+ button.classList.toggle('text-success', isCopied);
1816
+ button.classList.toggle('text-secondary', !isCopied);
1817
+ button.setAttribute('title', isCopied ? this.copiedTitle : this.copyTitle);
1818
+ button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1819
+ RETURN_DOM_FRAGMENT: true
1820
+ }));
1821
+ },
1822
+ synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1823
+ var _this10 = this;
1728
1824
  if (!this.textToSpeechEnabled || !text || !this.connection) {
1729
1825
  return;
1730
1826
  }
@@ -1733,16 +1829,16 @@ window.openAIChatManager = function () {
1733
1829
  this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;
1734
1830
  this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)["catch"](function (err) {
1735
1831
  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();
1832
+ _this10.isPlayingAudio = false;
1833
+ _this10.ttsPlayingMessageIndex = -1;
1834
+ _this10._ttsCacheIndex = -1;
1835
+ _this10.$nextTick(function () {
1836
+ return _this10.updateTtsPlaybackButtons();
1741
1837
  });
1742
1838
  });
1743
1839
  },
1744
1840
  toggleMessageTts: function toggleMessageTts(message, index) {
1745
- var _this10 = this;
1841
+ var _this11 = this;
1746
1842
  if (this.ttsPlayingMessageIndex === index) {
1747
1843
  this.stopAudio();
1748
1844
  return;
@@ -1755,7 +1851,7 @@ window.openAIChatManager = function () {
1755
1851
  }));
1756
1852
  this.ttsPlayingMessageIndex = index;
1757
1853
  this.$nextTick(function () {
1758
- return _this10.updateTtsPlaybackButtons();
1854
+ return _this11.updateTtsPlaybackButtons();
1759
1855
  });
1760
1856
  if (this.ttsAudioCache[index]) {
1761
1857
  this.playAudioBlob(this.ttsAudioCache[index]);
@@ -1764,13 +1860,13 @@ window.openAIChatManager = function () {
1764
1860
  this.synthesizeSpeech(message.content, index);
1765
1861
  },
1766
1862
  playCollectedAudio: function playCollectedAudio() {
1767
- var _this11 = this;
1863
+ var _this12 = this;
1768
1864
  if (this.audioChunks.length === 0) {
1769
1865
  if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {
1770
1866
  this.isPlayingAudio = false;
1771
1867
  this.ttsPlayingMessageIndex = -1;
1772
1868
  this.$nextTick(function () {
1773
- return _this11.updateTtsPlaybackButtons();
1869
+ return _this12.updateTtsPlaybackButtons();
1774
1870
  });
1775
1871
  }
1776
1872
  return;
@@ -1780,18 +1876,18 @@ window.openAIChatManager = function () {
1780
1876
  }, 0);
1781
1877
  var combined = new Uint8Array(totalLength);
1782
1878
  var offset = 0;
1783
- var _iterator5 = _createForOfIteratorHelper(this.audioChunks),
1784
- _step5;
1879
+ var _iterator6 = _createForOfIteratorHelper(this.audioChunks),
1880
+ _step6;
1785
1881
  try {
1786
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
1787
- var chunk = _step5.value;
1882
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
1883
+ var chunk = _step6.value;
1788
1884
  combined.set(chunk, offset);
1789
1885
  offset += chunk.length;
1790
1886
  }
1791
1887
  } catch (err) {
1792
- _iterator5.e(err);
1888
+ _iterator6.e(err);
1793
1889
  } finally {
1794
- _iterator5.f();
1890
+ _iterator6.f();
1795
1891
  }
1796
1892
  this.audioChunks = [];
1797
1893
  var blob = new Blob([combined], {
@@ -1810,39 +1906,39 @@ window.openAIChatManager = function () {
1810
1906
  this.playAudioBlob(blob);
1811
1907
  },
1812
1908
  playAudioBlob: function playAudioBlob(blob) {
1813
- var _this12 = this;
1909
+ var _this13 = this;
1814
1910
  var url = URL.createObjectURL(blob);
1815
1911
  var audio = new Audio(url);
1816
1912
  this.currentAudioUrl = url;
1817
1913
  this.currentAudioElement = audio;
1818
1914
  this.isPlayingAudio = true;
1819
1915
  audio.addEventListener('ended', function () {
1820
- _this12.currentAudioElement = null;
1821
- _this12.currentAudioUrl = null;
1916
+ _this13.currentAudioElement = null;
1917
+ _this13.currentAudioUrl = null;
1822
1918
  URL.revokeObjectURL(url);
1823
- _this12.playNextInQueue();
1919
+ _this13.playNextInQueue();
1824
1920
  });
1825
1921
  audio.addEventListener('error', function () {
1826
- _this12.currentAudioElement = null;
1827
- _this12.currentAudioUrl = null;
1922
+ _this13.currentAudioElement = null;
1923
+ _this13.currentAudioUrl = null;
1828
1924
  URL.revokeObjectURL(url);
1829
- _this12.playNextInQueue();
1925
+ _this13.playNextInQueue();
1830
1926
  });
1831
1927
  audio.play()["catch"](function (err) {
1832
1928
  console.error("Audio playback error:", err);
1833
- _this12.currentAudioElement = null;
1834
- _this12.currentAudioUrl = null;
1929
+ _this13.currentAudioElement = null;
1930
+ _this13.currentAudioUrl = null;
1835
1931
  URL.revokeObjectURL(url);
1836
- _this12.audioPlayQueue = [];
1837
- _this12.isPlayingAudio = false;
1838
- _this12.ttsPlayingMessageIndex = -1;
1839
- _this12.$nextTick(function () {
1840
- return _this12.updateTtsPlaybackButtons();
1932
+ _this13.audioPlayQueue = [];
1933
+ _this13.isPlayingAudio = false;
1934
+ _this13.ttsPlayingMessageIndex = -1;
1935
+ _this13.$nextTick(function () {
1936
+ return _this13.updateTtsPlaybackButtons();
1841
1937
  });
1842
1938
  });
1843
1939
  },
1844
1940
  playNextInQueue: function playNextInQueue() {
1845
- var _this13 = this;
1941
+ var _this14 = this;
1846
1942
  if (this.audioPlayQueue.length > 0) {
1847
1943
  var nextBlob = this.audioPlayQueue.shift();
1848
1944
  this.playAudioBlob(nextBlob);
@@ -1850,13 +1946,13 @@ window.openAIChatManager = function () {
1850
1946
  this.isPlayingAudio = false;
1851
1947
  this.ttsPlayingMessageIndex = -1;
1852
1948
  this.$nextTick(function () {
1853
- return _this13.updateTtsPlaybackButtons();
1949
+ return _this14.updateTtsPlaybackButtons();
1854
1950
  });
1855
1951
  this.conversationModeOnAudioEnded();
1856
1952
  }
1857
1953
  },
1858
1954
  stopAudio: function stopAudio() {
1859
- var _this14 = this;
1955
+ var _this15 = this;
1860
1956
  if (this.currentAudioElement) {
1861
1957
  this.currentAudioElement.pause();
1862
1958
  this.currentAudioElement.currentTime = 0;
@@ -1871,7 +1967,7 @@ window.openAIChatManager = function () {
1871
1967
  this.isPlayingAudio = false;
1872
1968
  this.ttsPlayingMessageIndex = -1;
1873
1969
  this.$nextTick(function () {
1874
- return _this14.updateTtsPlaybackButtons();
1970
+ return _this15.updateTtsPlaybackButtons();
1875
1971
  });
1876
1972
  },
1877
1973
  toggleConversationMode: function toggleConversationMode() {
@@ -1882,7 +1978,7 @@ window.openAIChatManager = function () {
1882
1978
  }
1883
1979
  },
1884
1980
  startConversationMode: function startConversationMode() {
1885
- var _this15 = this;
1981
+ var _this16 = this;
1886
1982
  if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {
1887
1983
  return;
1888
1984
  }
@@ -1902,12 +1998,12 @@ window.openAIChatManager = function () {
1902
1998
  }
1903
1999
  }).then(function (stream) {
1904
2000
  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, {
2001
+ _this16.mediaRecorder = new MediaRecorder(stream, {
1906
2002
  mimeType: mimeType,
1907
2003
  audioBitsPerSecond: 128000
1908
2004
  });
1909
- _this15._conversationSubject = new signalR.Subject();
1910
- _this15._conversationStream = stream;
2005
+ _this16._conversationSubject = new signalR.Subject();
2006
+ _this16._conversationStream = stream;
1911
2007
 
1912
2008
  // Create an AnalyserNode for volume-based interrupt detection.
1913
2009
  // During TTS playback, detect when the user speaks above
@@ -1916,20 +2012,20 @@ window.openAIChatManager = function () {
1916
2012
  // speaker echo so the STT stream has no gaps.
1917
2013
  var AudioCtx = window.AudioContext || window.webkitAudioContext;
1918
2014
  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);
2015
+ _this16._conversationAudioCtx = new AudioCtx();
2016
+ _this16._conversationAnalyser = _this16._conversationAudioCtx.createAnalyser();
2017
+ _this16._conversationAnalyser.fftSize = 256;
2018
+ var micSource = _this16._conversationAudioCtx.createMediaStreamSource(stream);
2019
+ micSource.connect(_this16._conversationAnalyser);
1924
2020
  }
1925
2021
  var pendingChunk = Promise.resolve();
1926
- var analyser = _this15._conversationAnalyser;
2022
+ var analyser = _this16._conversationAnalyser;
1927
2023
  var interruptVolumeThreshold = 30;
1928
- _this15.mediaRecorder.addEventListener('dataavailable', function (e) {
2024
+ _this16.mediaRecorder.addEventListener('dataavailable', function (e) {
1929
2025
  if (e.data && e.data.size > 0) {
1930
2026
  // During TTS playback, check mic volume to detect
1931
2027
  // user interruption (speaking above threshold).
1932
- if (_this15.isPlayingAudio && analyser) {
2028
+ if (_this16.isPlayingAudio && analyser) {
1933
2029
  var freqData = new Uint8Array(analyser.frequencyBinCount);
1934
2030
  analyser.getByteFrequencyData(freqData);
1935
2031
  var sum = 0;
@@ -1939,7 +2035,7 @@ window.openAIChatManager = function () {
1939
2035
  var avg = sum / freqData.length;
1940
2036
  if (avg >= interruptVolumeThreshold) {
1941
2037
  // User is speaking — interrupt TTS playback.
1942
- _this15.stopAudio();
2038
+ _this16.stopAudio();
1943
2039
  }
1944
2040
  }
1945
2041
 
@@ -1961,7 +2057,7 @@ window.openAIChatManager = function () {
1961
2057
  }, '');
1962
2058
  base64 = btoa(binaryString);
1963
2059
  try {
1964
- _this15._conversationSubject.next(base64);
2060
+ _this16._conversationSubject.next(base64);
1965
2061
  } catch (err) {
1966
2062
  // Subject may have been completed already.
1967
2063
  }
@@ -1972,28 +2068,28 @@ window.openAIChatManager = function () {
1972
2068
  })));
1973
2069
  }
1974
2070
  });
1975
- _this15.mediaRecorder.addEventListener('stop', function () {
2071
+ _this16.mediaRecorder.addEventListener('stop', function () {
1976
2072
  stream.getTracks().forEach(function (track) {
1977
2073
  return track.stop();
1978
2074
  });
1979
2075
  pendingChunk.then(function () {
1980
2076
  try {
1981
- _this15._conversationSubject.complete();
2077
+ _this16._conversationSubject.complete();
1982
2078
  } catch (err) {
1983
2079
  // Already completed.
1984
2080
  }
1985
2081
  });
1986
2082
  });
1987
- var profileId = _this15.getProfileId();
1988
- var sessionId = _this15.getSessionId() || '';
2083
+ var profileId = _this16.getProfileId();
2084
+ var sessionId = _this16.getSessionId() || '';
1989
2085
  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;
2086
+ _this16.connection.send("StartConversation", profileId, sessionId, _this16._conversationSubject, mimeType, language);
2087
+ _this16.mediaRecorder.start(250);
2088
+ _this16.isRecording = true;
1993
2089
  })["catch"](function (err) {
1994
2090
  console.error('Microphone access denied:', err);
1995
- _this15.isConversationMode = false;
1996
- _this15.updateConversationButton();
2091
+ _this16.isConversationMode = false;
2092
+ _this16.updateConversationButton();
1997
2093
  });
1998
2094
  },
1999
2095
  stopConversationMode: function stopConversationMode() {
@@ -2120,7 +2216,7 @@ window.openAIChatManager = function () {
2120
2216
  return removedCount;
2121
2217
  },
2122
2218
  receiveNotification: function receiveNotification(notification) {
2123
- var _this16 = this;
2219
+ var _this17 = this;
2124
2220
  if (!notification || !notification.type) {
2125
2221
  return;
2126
2222
  }
@@ -2135,7 +2231,7 @@ window.openAIChatManager = function () {
2135
2231
  }
2136
2232
  this.scheduleNotificationDismiss(notification);
2137
2233
  this.$nextTick(function () {
2138
- _this16.scrollToBottom();
2234
+ _this17.scrollToBottom();
2139
2235
  });
2140
2236
  },
2141
2237
  updateNotification: function updateNotification(notification) {
@@ -2152,12 +2248,12 @@ window.openAIChatManager = function () {
2152
2248
  }
2153
2249
  },
2154
2250
  scheduleNotificationDismiss: function scheduleNotificationDismiss(notification) {
2155
- var _this17 = this;
2251
+ var _this18 = this;
2156
2252
  if (!notification || !notification.type || !notification.autoDismissMs || notification.autoDismissMs <= 0) {
2157
2253
  return;
2158
2254
  }
2159
2255
  this.notificationDismissTimers[notification.type] = setTimeout(function () {
2160
- _this17.removeNotification(notification.type);
2256
+ _this18.removeNotification(notification.type);
2161
2257
  }, notification.autoDismissMs);
2162
2258
  },
2163
2259
  clearNotificationDismiss: function clearNotificationDismiss(notificationType) {
@@ -2187,12 +2283,12 @@ window.openAIChatManager = function () {
2187
2283
  });
2188
2284
  },
2189
2285
  scrollToBottom: function scrollToBottom() {
2190
- var _this18 = this;
2286
+ var _this19 = this;
2191
2287
  if (!this.autoScroll) {
2192
2288
  return;
2193
2289
  }
2194
2290
  setTimeout(function () {
2195
- _this18.chatContainer.scrollTop = _this18.chatContainer.scrollHeight - _this18.chatContainer.clientHeight;
2291
+ _this19.chatContainer.scrollTop = _this19.chatContainer.scrollHeight - _this19.chatContainer.clientHeight;
2196
2292
  }, 50);
2197
2293
  },
2198
2294
  handleUserInput: function handleUserInput(event) {
@@ -2238,27 +2334,27 @@ window.openAIChatManager = function () {
2238
2334
  });
2239
2335
  },
2240
2336
  ensureSessionForDocuments: function ensureSessionForDocuments(profileId) {
2241
- var _this19 = this;
2337
+ var _this20 = this;
2242
2338
  return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2243
2339
  var sessionId;
2244
2340
  return _regenerator().w(function (_context9) {
2245
2341
  while (1) switch (_context9.n) {
2246
2342
  case 0:
2247
- sessionId = _this19.getSessionId();
2343
+ sessionId = _this20.getSessionId();
2248
2344
  if (!sessionId) {
2249
2345
  _context9.n = 1;
2250
2346
  break;
2251
2347
  }
2252
2348
  return _context9.a(2, sessionId);
2253
2349
  case 1:
2254
- if (!(!profileId || !_this19.connection)) {
2350
+ if (!(!profileId || !_this20.connection)) {
2255
2351
  _context9.n = 2;
2256
2352
  break;
2257
2353
  }
2258
2354
  return _context9.a(2, null);
2259
2355
  case 2:
2260
2356
  _context9.n = 3;
2261
- return _this19.requestNewSession(profileId);
2357
+ return _this20.requestNewSession(profileId);
2262
2358
  case 3:
2263
2359
  return _context9.a(2, _context9.v);
2264
2360
  }
@@ -2266,7 +2362,7 @@ window.openAIChatManager = function () {
2266
2362
  }))();
2267
2363
  },
2268
2364
  requestNewSession: function requestNewSession(profileId) {
2269
- var _this20 = this;
2365
+ var _this21 = this;
2270
2366
  if (this.pendingSessionPromise) {
2271
2367
  return this.pendingSessionPromise;
2272
2368
  }
@@ -2274,14 +2370,14 @@ window.openAIChatManager = function () {
2274
2370
  return Promise.resolve(null);
2275
2371
  }
2276
2372
  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.');
2373
+ _this21.pendingSessionResolver = resolve;
2374
+ _this21.pendingSessionRejector = reject;
2375
+ _this21.pendingSessionTimeoutId = window.setTimeout(function () {
2376
+ _this21.rejectPendingSessionRequest('Timed out while creating a chat session.');
2281
2377
  }, 15000);
2282
2378
  });
2283
2379
  this.connection.invoke("StartSession", profileId, null)["catch"](function (err) {
2284
- _this20.rejectPendingSessionRequest(err);
2380
+ _this21.rejectPendingSessionRequest(err);
2285
2381
  });
2286
2382
  return this.pendingSessionPromise;
2287
2383
  },
@@ -2307,7 +2403,7 @@ window.openAIChatManager = function () {
2307
2403
  this.pendingSessionTimeoutId = null;
2308
2404
  },
2309
2405
  initializeApp: function initializeApp() {
2310
- var _this21 = this;
2406
+ var _this22 = this;
2311
2407
  this.inputElement = document.querySelector(config.inputElementSelector);
2312
2408
  this.buttonElement = document.querySelector(config.sendButtonElementSelector);
2313
2409
  this.chatContainer = document.querySelector(config.chatContainerElementSelector);
@@ -2345,7 +2441,7 @@ window.openAIChatManager = function () {
2345
2441
  fileInput.accept = config.allowedExtensions;
2346
2442
  }
2347
2443
  fileInput.addEventListener('change', function (e) {
2348
- return _this21.handleFileInputChange(e);
2444
+ return _this22.handleFileInputChange(e);
2349
2445
  });
2350
2446
  this.documentBar.parentElement.appendChild(fileInput);
2351
2447
 
@@ -2353,13 +2449,13 @@ window.openAIChatManager = function () {
2353
2449
  var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;
2354
2450
  if (inputArea) {
2355
2451
  inputArea.addEventListener('dragover', function (e) {
2356
- return _this21.handleDragOver(e);
2452
+ return _this22.handleDragOver(e);
2357
2453
  });
2358
2454
  inputArea.addEventListener('dragleave', function (e) {
2359
- return _this21.handleDragLeave(e);
2455
+ return _this22.handleDragLeave(e);
2360
2456
  });
2361
2457
  inputArea.addEventListener('drop', function (e) {
2362
- return _this21.handleDrop(e);
2458
+ return _this22.handleDrop(e);
2363
2459
  });
2364
2460
  }
2365
2461
  }
@@ -2367,55 +2463,55 @@ window.openAIChatManager = function () {
2367
2463
 
2368
2464
  // Pause auto-scroll when the user manually scrolls up during streaming.
2369
2465
  this.chatContainer.addEventListener('scroll', function () {
2370
- if (!_this21.stream) {
2466
+ if (!_this22.stream) {
2371
2467
  return;
2372
2468
  }
2373
2469
  var threshold = 30;
2374
- var atBottom = _this21.chatContainer.scrollHeight - _this21.chatContainer.clientHeight - _this21.chatContainer.scrollTop <= threshold;
2375
- _this21.autoScroll = atBottom;
2470
+ var atBottom = _this22.chatContainer.scrollHeight - _this22.chatContainer.clientHeight - _this22.chatContainer.scrollTop <= threshold;
2471
+ _this22.autoScroll = atBottom;
2376
2472
  });
2377
2473
  this.inputElement.addEventListener('keydown', function (event) {
2378
- if (_this21.stream != null) {
2474
+ if (_this22.stream != null) {
2379
2475
  return;
2380
2476
  }
2381
2477
  if (event.key === "Enter" && !event.shiftKey) {
2382
2478
  event.preventDefault();
2383
- _this21.buttonElement.click();
2479
+ _this22.buttonElement.click();
2384
2480
  }
2385
2481
  });
2386
2482
  this.inputElement.addEventListener('input', function (e) {
2387
- _this21.handleUserInput(e);
2483
+ _this22.handleUserInput(e);
2388
2484
  if (e.target.value.trim()) {
2389
- _this21.buttonElement.removeAttribute('disabled');
2485
+ _this22.buttonElement.removeAttribute('disabled');
2390
2486
  } else {
2391
- _this21.buttonElement.setAttribute('disabled', true);
2487
+ _this22.buttonElement.setAttribute('disabled', true);
2392
2488
  }
2393
2489
  });
2394
2490
  this.buttonElement.addEventListener('click', function () {
2395
- if (_this21.stream != null) {
2396
- _this21.stream.dispose();
2397
- _this21.stream = null;
2398
- _this21.streamingFinished();
2399
- _this21.hideTypingIndicator();
2491
+ if (_this22.stream != null) {
2492
+ _this22.stream.dispose();
2493
+ _this22.stream = null;
2494
+ _this22.streamingFinished();
2495
+ _this22.hideTypingIndicator();
2400
2496
 
2401
2497
  // 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];
2498
+ if (_this22.messages.length > 0) {
2499
+ var lastMsg = _this22.messages[_this22.messages.length - 1];
2404
2500
  if (lastMsg.role === 'assistant' && !lastMsg.content) {
2405
- _this21.messages.pop();
2501
+ _this22.messages.pop();
2406
2502
  } else if (lastMsg.isStreaming) {
2407
2503
  lastMsg.isStreaming = false;
2408
2504
  }
2409
2505
  }
2410
2506
  return;
2411
2507
  }
2412
- _this21.sendMessage();
2508
+ _this22.sendMessage();
2413
2509
  });
2414
2510
  var promptGenerators = document.getElementsByClassName('profile-generated-prompt');
2415
2511
  for (var i = 0; i < promptGenerators.length; i++) {
2416
2512
  promptGenerators[i].addEventListener('click', function (e) {
2417
2513
  e.preventDefault();
2418
- _this21.generatePrompt(e.target);
2514
+ _this22.generatePrompt(e.target);
2419
2515
  });
2420
2516
  }
2421
2517
  var chatSessions = document.getElementsByClassName('chat-session-history-item');
@@ -2427,17 +2523,18 @@ window.openAIChatManager = function () {
2427
2523
  console.error('an element with the class chat-session-history-item with no data-session-id set.');
2428
2524
  return;
2429
2525
  }
2430
- _this21.loadSession(sessionId);
2431
- _this21.showChatScreen();
2526
+ _this22.loadSession(sessionId);
2527
+ _this22.showChatScreen();
2432
2528
  });
2433
2529
  }
2434
- for (var _i5 = 0; _i5 < config.messages.length; _i5++) {
2435
- this.addMessage(config.messages[_i5]);
2530
+ var initialMessages = Array.isArray(config.messages) ? config.messages : [];
2531
+ for (var _i5 = 0; _i5 < initialMessages.length; _i5++) {
2532
+ this.addMessage(initialMessages[_i5]);
2436
2533
  }
2437
2534
 
2438
2535
  // Update feedback icons in the DOM after initial messages have rendered.
2439
2536
  this.$nextTick(function () {
2440
- _this21.refreshAllFeedbackIcons();
2537
+ _this22.refreshAllFeedbackIcons();
2441
2538
  });
2442
2539
 
2443
2540
  // Delegate click for code block copy buttons.
@@ -2469,7 +2566,7 @@ window.openAIChatManager = function () {
2469
2566
  if (this.micButton) {
2470
2567
  this.micButton.style.display = '';
2471
2568
  this.micButton.addEventListener('click', function () {
2472
- _this21.toggleRecording();
2569
+ _this22.toggleRecording();
2473
2570
  });
2474
2571
  }
2475
2572
  }
@@ -2479,7 +2576,7 @@ window.openAIChatManager = function () {
2479
2576
  this.conversationButton = document.querySelector(config.conversationButtonElementSelector);
2480
2577
  if (this.conversationButton) {
2481
2578
  this.conversationButton.addEventListener('click', function () {
2482
- _this21.toggleConversationMode();
2579
+ _this22.toggleConversationMode();
2483
2580
  });
2484
2581
  }
2485
2582
  }
@@ -2503,7 +2600,7 @@ window.openAIChatManager = function () {
2503
2600
  }
2504
2601
  return;
2505
2602
  }
2506
- this.fireEvent(new CustomEvent("initializingSessionOpenAIChat", {
2603
+ this.fireEvent(new CustomEvent("initializingSessionCoreAIChat", {
2507
2604
  detail: {
2508
2605
  sessionId: sessionId
2509
2606
  }
@@ -2522,10 +2619,45 @@ window.openAIChatManager = function () {
2522
2619
  }
2523
2620
  return sessionId;
2524
2621
  },
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);
2622
+ clearCopiedMessageState: function clearCopiedMessageState() {
2623
+ if (this.copyResetTimeoutId) {
2624
+ window.clearTimeout(this.copyResetTimeoutId);
2625
+ this.copyResetTimeoutId = null;
2626
+ }
2627
+ if (this.activeCopyButton) {
2628
+ this.setCopyButtonState(this.activeCopyButton, false);
2629
+ this.activeCopyButton = null;
2630
+ }
2631
+ this.copiedMessageIndex = -1;
2632
+ },
2633
+ copyResponse: function copyResponse(message, index, event) {
2634
+ var _ref25,
2635
+ _message$copyContent,
2636
+ _event$target,
2637
+ _event$target$closest,
2638
+ _this23 = this;
2639
+ 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 : '';
2640
+ 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;
2641
+ navigator.clipboard.writeText(text).then(function () {
2642
+ _this23.clearCopiedMessageState();
2643
+ _this23.copiedMessageIndex = typeof index === 'number' ? index : -1;
2644
+ _this23.activeCopyButton = button;
2645
+ if (button) {
2646
+ _this23.setCopyButtonState(button, true);
2647
+ } else {
2648
+ _this23.$nextTick(function () {
2649
+ return _this23.updateCopyButtons();
2650
+ });
2651
+ }
2652
+ _this23.copyResetTimeoutId = window.setTimeout(function () {
2653
+ _this23.clearCopiedMessageState();
2654
+ _this23.$nextTick(function () {
2655
+ return _this23.updateCopyButtons();
2656
+ });
2657
+ }, Number(config.copyResetDelayMs) || 2000);
2658
+ })["catch"](function (err) {
2659
+ return console.error('Failed to copy response:', err);
2660
+ });
2529
2661
  },
2530
2662
  updateFeedbackIcons: function updateFeedbackIcons(container, userRating) {
2531
2663
  if (!container) {
@@ -2620,6 +2752,12 @@ window.openAIChatManager = function () {
2620
2752
  // Reserved for future use — volume-based interrupt detection
2621
2753
  // no longer mutes tracks; browser echo cancellation handles echo.
2622
2754
  },
2755
+ copiedMessageIndex: function copiedMessageIndex() {
2756
+ var _this24 = this;
2757
+ this.$nextTick(function () {
2758
+ return _this24.updateCopyButtons();
2759
+ });
2760
+ },
2623
2761
  isConversationMode: function isConversationMode(active) {
2624
2762
  // Hide/show mic button.
2625
2763
  if (this.micButton) {
@@ -2641,22 +2779,23 @@ window.openAIChatManager = function () {
2641
2779
  }
2642
2780
  },
2643
2781
  mounted: function mounted() {
2644
- var _this22 = this;
2782
+ var _this25 = this;
2645
2783
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
2646
2784
  var isInitialized;
2647
2785
  return _regenerator().w(function (_context0) {
2648
2786
  while (1) switch (_context0.n) {
2649
2787
  case 0:
2650
2788
  _context0.n = 1;
2651
- return _this22.startConnection();
2789
+ return _this25.startConnection();
2652
2790
  case 1:
2653
- isInitialized = _this22.initializeApp();
2791
+ isInitialized = _this25.initializeApp();
2654
2792
  if (isInitialized && hasWidgetConfig && widgetBehavior && typeof widgetBehavior.onMounted === 'function') {
2655
- widgetBehavior.onMounted(_this22, config);
2793
+ widgetBehavior.onMounted(_this25, config);
2656
2794
  }
2657
- _this22.$nextTick(function () {
2658
- refreshFontAwesomeIcons(_this22.$el);
2659
- _this22.fontAwesomeObserver = observeFontAwesomeIcons(_this22.$el);
2795
+ _this25.$nextTick(function () {
2796
+ _this25.updateCopyButtons();
2797
+ refreshFontAwesomeIcons(_this25.$el);
2798
+ _this25.fontAwesomeObserver = observeFontAwesomeIcons(_this25.$el);
2660
2799
  });
2661
2800
  case 2:
2662
2801
  return _context0.a(2);
@@ -2673,6 +2812,7 @@ window.openAIChatManager = function () {
2673
2812
  this.fontAwesomeObserver.disconnect();
2674
2813
  this.fontAwesomeObserver = null;
2675
2814
  }
2815
+ this.clearCopiedMessageState();
2676
2816
  this.stopAudio(false);
2677
2817
  if (this.stream) {
2678
2818
  this.stream.dispose();
@@ -2693,8 +2833,151 @@ window.openAIChatManager = function () {
2693
2833
  }
2694
2834
  return app;
2695
2835
  };
2836
+ var autoInitializeSelector = '[data-coreai-chat-config],[data-coreai-chat-app-element-selector]';
2837
+ function getAttributeValue(element, attributeName) {
2838
+ var value = element.getAttribute(attributeName);
2839
+ return value === null || value === '' ? null : value;
2840
+ }
2841
+ function parseBooleanAttributeValue(value) {
2842
+ if (value === null || value === undefined) {
2843
+ return null;
2844
+ }
2845
+ if (typeof value === 'boolean') {
2846
+ return value;
2847
+ }
2848
+ var normalized = String(value).trim().toLowerCase();
2849
+ if (normalized === 'true') {
2850
+ return true;
2851
+ }
2852
+ if (normalized === 'false') {
2853
+ return false;
2854
+ }
2855
+ return null;
2856
+ }
2857
+ function parseJsonAttribute(element, attributeName, description) {
2858
+ var rawValue = getAttributeValue(element, attributeName);
2859
+ if (!rawValue) {
2860
+ return null;
2861
+ }
2862
+ try {
2863
+ return JSON.parse(rawValue);
2864
+ } catch (error) {
2865
+ console.error('Failed to parse ' + description + ' JSON.', error);
2866
+ return null;
2867
+ }
2868
+ }
2869
+ function buildConfigFromDataAttributes(element, options) {
2870
+ var _parseJsonAttribute, _parseJsonAttribute2;
2871
+ var settings = options || {};
2872
+ if (element.hasAttribute('data-coreai-chat-widget-container-selector') && settings.allowWidgetHost !== true) {
2873
+ return null;
2874
+ }
2875
+ var appElementSelector = getAttributeValue(element, 'data-coreai-chat-app-element-selector');
2876
+ if (!appElementSelector) {
2877
+ return null;
2878
+ }
2879
+ var config = {
2880
+ signalRHubUrl: getAttributeValue(element, 'data-coreai-chat-signalr-hub-url'),
2881
+ appElementSelector: appElementSelector,
2882
+ chatContainerElementSelector: getAttributeValue(element, 'data-coreai-chat-container-element-selector'),
2883
+ inputElementSelector: getAttributeValue(element, 'data-coreai-chat-input-element-selector'),
2884
+ sendButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-send-button-element-selector'),
2885
+ placeholderElementSelector: getAttributeValue(element, 'data-coreai-chat-placeholder-element-selector'),
2886
+ messages: (_parseJsonAttribute = parseJsonAttribute(element, 'data-coreai-chat-messages', 'CoreAI chat messages')) !== null && _parseJsonAttribute !== void 0 ? _parseJsonAttribute : undefined,
2887
+ chatMode: getAttributeValue(element, 'data-coreai-chat-mode'),
2888
+ micButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-mic-button-element-selector'),
2889
+ conversationButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-conversation-button-element-selector'),
2890
+ ttsVoiceName: getAttributeValue(element, 'data-coreai-chat-tts-voice-name'),
2891
+ documentBarSelector: getAttributeValue(element, 'data-coreai-chat-document-bar-selector'),
2892
+ uploadDocumentUrl: getAttributeValue(element, 'data-coreai-chat-upload-document-url'),
2893
+ removeDocumentUrl: getAttributeValue(element, 'data-coreai-chat-remove-document-url'),
2894
+ allowedExtensions: getAttributeValue(element, 'data-coreai-chat-allowed-extensions'),
2895
+ supportedExtensionsText: getAttributeValue(element, 'data-coreai-chat-supported-extensions-text'),
2896
+ existingDocuments: (_parseJsonAttribute2 = parseJsonAttribute(element, 'data-coreai-chat-existing-documents', 'CoreAI chat existing documents')) !== null && _parseJsonAttribute2 !== void 0 ? _parseJsonAttribute2 : undefined,
2897
+ initialPrompt: getAttributeValue(element, 'data-coreai-chat-initial-prompt'),
2898
+ userLabel: getAttributeValue(element, 'data-coreai-chat-user-label'),
2899
+ assistantLabel: getAttributeValue(element, 'data-coreai-chat-assistant-label'),
2900
+ copyTitle: getAttributeValue(element, 'data-coreai-chat-copy-title'),
2901
+ copiedTitle: getAttributeValue(element, 'data-coreai-chat-copied-title')
2902
+ };
2903
+ var booleanAttributes = {
2904
+ metricsEnabled: 'data-coreai-chat-metrics-enabled',
2905
+ textToSpeechEnabled: 'data-coreai-chat-text-to-speech-enabled',
2906
+ sessionDocumentsEnabled: 'data-coreai-chat-session-documents-enabled',
2907
+ autoCreateSession: 'data-coreai-chat-auto-create-session',
2908
+ singleResponseMode: 'data-coreai-chat-single-response-mode'
2909
+ };
2910
+ Object.keys(booleanAttributes).forEach(function (key) {
2911
+ var parsed = parseBooleanAttributeValue(getAttributeValue(element, booleanAttributes[key]));
2912
+ if (parsed !== null) {
2913
+ config[key] = parsed;
2914
+ }
2915
+ });
2916
+ return compactObject(config);
2917
+ }
2918
+ function buildConfigFromElement(element, options) {
2919
+ var jsonConfig = parseJsonAttribute(element, 'data-coreai-chat-config', 'CoreAI chat config');
2920
+ var attributeConfig = buildConfigFromDataAttributes(element, options);
2921
+ if (!jsonConfig && !attributeConfig) {
2922
+ return null;
2923
+ }
2924
+ return Object.assign({}, jsonConfig || {}, attributeConfig || {});
2925
+ }
2926
+ function initializeFromElement(element) {
2927
+ if (!element || element.dataset.coreAiChatInitialized === 'true') {
2928
+ return element ? element.__coreAIChatApp || null : null;
2929
+ }
2930
+ var config = buildConfigFromElement(element);
2931
+ if (!config) {
2932
+ return null;
2933
+ }
2934
+ var app = initialize(config);
2935
+ if (!app) {
2936
+ return null;
2937
+ }
2938
+ element.dataset.coreAiChatInitialized = 'true';
2939
+ element.__coreAIChatApp = app;
2940
+ return app;
2941
+ }
2942
+ function scanForAutoInitialization(root) {
2943
+ if (!root || typeof root.querySelectorAll !== 'function') {
2944
+ return;
2945
+ }
2946
+ if (typeof root.matches === 'function' && root.matches(autoInitializeSelector)) {
2947
+ initializeFromElement(root);
2948
+ }
2949
+ root.querySelectorAll(autoInitializeSelector).forEach(initializeFromElement);
2950
+ }
2951
+ function startAutoInitialization() {
2952
+ scanForAutoInitialization(document);
2953
+ if (typeof MutationObserver === 'undefined') {
2954
+ return;
2955
+ }
2956
+ var observer = new MutationObserver(function (mutations) {
2957
+ mutations.forEach(function (mutation) {
2958
+ mutation.addedNodes.forEach(function (node) {
2959
+ if (node && node.nodeType === 1) {
2960
+ scanForAutoInitialization(node);
2961
+ }
2962
+ });
2963
+ });
2964
+ });
2965
+ observer.observe(document.body, {
2966
+ childList: true,
2967
+ subtree: true
2968
+ });
2969
+ }
2970
+ if (document.readyState === 'loading') {
2971
+ document.addEventListener('DOMContentLoaded', startAutoInitialization, {
2972
+ once: true
2973
+ });
2974
+ } else {
2975
+ startAutoInitialization();
2976
+ }
2696
2977
  return {
2697
- initialize: initialize
2978
+ initialize: initialize,
2979
+ initializeFromElement: initializeFromElement,
2980
+ buildConfigFromElement: buildConfigFromElement
2698
2981
  };
2699
2982
  }();
2700
2983