@crestapps/ai-chat-ui 1.0.0-rc.7 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: {
@@ -1120,10 +1186,12 @@ window.openAIChatManager = function () {
1120
1186
  });
1121
1187
 
1122
1188
  // When the session is new (no messages) and an initial prompt is configured,
1123
- // automatically send it as the first user message to trigger an AI response.
1189
+ // display it as the first assistant message without invoking the AI.
1124
1190
  if (_this3.messages.length === 0 && config.initialPrompt) {
1125
- _this3.prompt = config.initialPrompt;
1126
- _this3.sendMessage();
1191
+ _this3.addMessage({
1192
+ role: 'assistant',
1193
+ content: config.initialPrompt
1194
+ });
1127
1195
  }
1128
1196
  });
1129
1197
  _this3.connection.on("ReceiveError", function (error) {
@@ -1289,8 +1357,6 @@ window.openAIChatManager = function () {
1289
1357
  console.info("SignalR: reconnected.");
1290
1358
  if (_this3.isSessionStarted) {
1291
1359
  _this3.reloadCurrentSession();
1292
- } else if (config.autoCreateSession) {
1293
- _this3.startNewSession();
1294
1360
  }
1295
1361
  });
1296
1362
  _this3.connection.onclose(function (error) {
@@ -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) {
@@ -2215,12 +2311,17 @@ window.openAIChatManager = function () {
2215
2311
  this.sessionRating = null;
2216
2312
  this.messages = [];
2217
2313
  this.documents = [];
2218
- if (!config.autoCreateSession) {
2314
+
2315
+ // When an initial prompt is configured, show it as the first
2316
+ // assistant message instead of the generic placeholder.
2317
+ if (config.initialPrompt) {
2318
+ this.addMessage({
2319
+ role: 'assistant',
2320
+ content: config.initialPrompt
2321
+ });
2322
+ } else {
2219
2323
  this.showPlaceholder();
2220
2324
  }
2221
- if (config.autoCreateSession) {
2222
- this.startNewSession();
2223
- }
2224
2325
  if (widgetBehavior && typeof widgetBehavior.onSessionReset === 'function') {
2225
2326
  widgetBehavior.onSessionReset(this, config);
2226
2327
  }
@@ -2238,27 +2339,27 @@ window.openAIChatManager = function () {
2238
2339
  });
2239
2340
  },
2240
2341
  ensureSessionForDocuments: function ensureSessionForDocuments(profileId) {
2241
- var _this19 = this;
2342
+ var _this20 = this;
2242
2343
  return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2243
2344
  var sessionId;
2244
2345
  return _regenerator().w(function (_context9) {
2245
2346
  while (1) switch (_context9.n) {
2246
2347
  case 0:
2247
- sessionId = _this19.getSessionId();
2348
+ sessionId = _this20.getSessionId();
2248
2349
  if (!sessionId) {
2249
2350
  _context9.n = 1;
2250
2351
  break;
2251
2352
  }
2252
2353
  return _context9.a(2, sessionId);
2253
2354
  case 1:
2254
- if (!(!profileId || !_this19.connection)) {
2355
+ if (!(!profileId || !_this20.connection)) {
2255
2356
  _context9.n = 2;
2256
2357
  break;
2257
2358
  }
2258
2359
  return _context9.a(2, null);
2259
2360
  case 2:
2260
2361
  _context9.n = 3;
2261
- return _this19.requestNewSession(profileId);
2362
+ return _this20.requestNewSession(profileId);
2262
2363
  case 3:
2263
2364
  return _context9.a(2, _context9.v);
2264
2365
  }
@@ -2266,7 +2367,7 @@ window.openAIChatManager = function () {
2266
2367
  }))();
2267
2368
  },
2268
2369
  requestNewSession: function requestNewSession(profileId) {
2269
- var _this20 = this;
2370
+ var _this21 = this;
2270
2371
  if (this.pendingSessionPromise) {
2271
2372
  return this.pendingSessionPromise;
2272
2373
  }
@@ -2274,14 +2375,14 @@ window.openAIChatManager = function () {
2274
2375
  return Promise.resolve(null);
2275
2376
  }
2276
2377
  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.');
2378
+ _this21.pendingSessionResolver = resolve;
2379
+ _this21.pendingSessionRejector = reject;
2380
+ _this21.pendingSessionTimeoutId = window.setTimeout(function () {
2381
+ _this21.rejectPendingSessionRequest('Timed out while creating a chat session.');
2281
2382
  }, 15000);
2282
2383
  });
2283
2384
  this.connection.invoke("StartSession", profileId, null)["catch"](function (err) {
2284
- _this20.rejectPendingSessionRequest(err);
2385
+ _this21.rejectPendingSessionRequest(err);
2285
2386
  });
2286
2387
  return this.pendingSessionPromise;
2287
2388
  },
@@ -2307,7 +2408,7 @@ window.openAIChatManager = function () {
2307
2408
  this.pendingSessionTimeoutId = null;
2308
2409
  },
2309
2410
  initializeApp: function initializeApp() {
2310
- var _this21 = this;
2411
+ var _this22 = this;
2311
2412
  this.inputElement = document.querySelector(config.inputElementSelector);
2312
2413
  this.buttonElement = document.querySelector(config.sendButtonElementSelector);
2313
2414
  this.chatContainer = document.querySelector(config.chatContainerElementSelector);
@@ -2325,8 +2426,6 @@ window.openAIChatManager = function () {
2325
2426
  var sessionId = this.getSessionId();
2326
2427
  if (!hasWidgetConfig && sessionId) {
2327
2428
  this.loadSession(sessionId);
2328
- } else if (this.isOrchestratorAvailable() && config.autoCreateSession && !hasWidgetConfig && !sessionId) {
2329
- this.startNewSession();
2330
2429
  }
2331
2430
 
2332
2431
  // Initialize document bar if enabled.
@@ -2345,7 +2444,7 @@ window.openAIChatManager = function () {
2345
2444
  fileInput.accept = config.allowedExtensions;
2346
2445
  }
2347
2446
  fileInput.addEventListener('change', function (e) {
2348
- return _this21.handleFileInputChange(e);
2447
+ return _this22.handleFileInputChange(e);
2349
2448
  });
2350
2449
  this.documentBar.parentElement.appendChild(fileInput);
2351
2450
 
@@ -2353,13 +2452,13 @@ window.openAIChatManager = function () {
2353
2452
  var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;
2354
2453
  if (inputArea) {
2355
2454
  inputArea.addEventListener('dragover', function (e) {
2356
- return _this21.handleDragOver(e);
2455
+ return _this22.handleDragOver(e);
2357
2456
  });
2358
2457
  inputArea.addEventListener('dragleave', function (e) {
2359
- return _this21.handleDragLeave(e);
2458
+ return _this22.handleDragLeave(e);
2360
2459
  });
2361
2460
  inputArea.addEventListener('drop', function (e) {
2362
- return _this21.handleDrop(e);
2461
+ return _this22.handleDrop(e);
2363
2462
  });
2364
2463
  }
2365
2464
  }
@@ -2367,55 +2466,55 @@ window.openAIChatManager = function () {
2367
2466
 
2368
2467
  // Pause auto-scroll when the user manually scrolls up during streaming.
2369
2468
  this.chatContainer.addEventListener('scroll', function () {
2370
- if (!_this21.stream) {
2469
+ if (!_this22.stream) {
2371
2470
  return;
2372
2471
  }
2373
2472
  var threshold = 30;
2374
- var atBottom = _this21.chatContainer.scrollHeight - _this21.chatContainer.clientHeight - _this21.chatContainer.scrollTop <= threshold;
2375
- _this21.autoScroll = atBottom;
2473
+ var atBottom = _this22.chatContainer.scrollHeight - _this22.chatContainer.clientHeight - _this22.chatContainer.scrollTop <= threshold;
2474
+ _this22.autoScroll = atBottom;
2376
2475
  });
2377
2476
  this.inputElement.addEventListener('keydown', function (event) {
2378
- if (_this21.stream != null) {
2477
+ if (_this22.stream != null) {
2379
2478
  return;
2380
2479
  }
2381
2480
  if (event.key === "Enter" && !event.shiftKey) {
2382
2481
  event.preventDefault();
2383
- _this21.buttonElement.click();
2482
+ _this22.buttonElement.click();
2384
2483
  }
2385
2484
  });
2386
2485
  this.inputElement.addEventListener('input', function (e) {
2387
- _this21.handleUserInput(e);
2486
+ _this22.handleUserInput(e);
2388
2487
  if (e.target.value.trim()) {
2389
- _this21.buttonElement.removeAttribute('disabled');
2488
+ _this22.buttonElement.removeAttribute('disabled');
2390
2489
  } else {
2391
- _this21.buttonElement.setAttribute('disabled', true);
2490
+ _this22.buttonElement.setAttribute('disabled', true);
2392
2491
  }
2393
2492
  });
2394
2493
  this.buttonElement.addEventListener('click', function () {
2395
- if (_this21.stream != null) {
2396
- _this21.stream.dispose();
2397
- _this21.stream = null;
2398
- _this21.streamingFinished();
2399
- _this21.hideTypingIndicator();
2494
+ if (_this22.stream != null) {
2495
+ _this22.stream.dispose();
2496
+ _this22.stream = null;
2497
+ _this22.streamingFinished();
2498
+ _this22.hideTypingIndicator();
2400
2499
 
2401
2500
  // 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];
2501
+ if (_this22.messages.length > 0) {
2502
+ var lastMsg = _this22.messages[_this22.messages.length - 1];
2404
2503
  if (lastMsg.role === 'assistant' && !lastMsg.content) {
2405
- _this21.messages.pop();
2504
+ _this22.messages.pop();
2406
2505
  } else if (lastMsg.isStreaming) {
2407
2506
  lastMsg.isStreaming = false;
2408
2507
  }
2409
2508
  }
2410
2509
  return;
2411
2510
  }
2412
- _this21.sendMessage();
2511
+ _this22.sendMessage();
2413
2512
  });
2414
2513
  var promptGenerators = document.getElementsByClassName('profile-generated-prompt');
2415
2514
  for (var i = 0; i < promptGenerators.length; i++) {
2416
2515
  promptGenerators[i].addEventListener('click', function (e) {
2417
2516
  e.preventDefault();
2418
- _this21.generatePrompt(e.target);
2517
+ _this22.generatePrompt(e.target);
2419
2518
  });
2420
2519
  }
2421
2520
  var chatSessions = document.getElementsByClassName('chat-session-history-item');
@@ -2427,17 +2526,27 @@ window.openAIChatManager = function () {
2427
2526
  console.error('an element with the class chat-session-history-item with no data-session-id set.');
2428
2527
  return;
2429
2528
  }
2430
- _this21.loadSession(sessionId);
2431
- _this21.showChatScreen();
2529
+ _this22.loadSession(sessionId);
2530
+ _this22.showChatScreen();
2432
2531
  });
2433
2532
  }
2434
- for (var _i5 = 0; _i5 < config.messages.length; _i5++) {
2435
- this.addMessage(config.messages[_i5]);
2533
+ var initialMessages = Array.isArray(config.messages) ? config.messages : [];
2534
+ for (var _i5 = 0; _i5 < initialMessages.length; _i5++) {
2535
+ this.addMessage(initialMessages[_i5]);
2536
+ }
2537
+
2538
+ // When no messages were loaded and an initial prompt is configured,
2539
+ // display it as the first assistant message without invoking the AI.
2540
+ if (this.messages.length === 0 && config.initialPrompt) {
2541
+ this.addMessage({
2542
+ role: 'assistant',
2543
+ content: config.initialPrompt
2544
+ });
2436
2545
  }
2437
2546
 
2438
2547
  // Update feedback icons in the DOM after initial messages have rendered.
2439
2548
  this.$nextTick(function () {
2440
- _this21.refreshAllFeedbackIcons();
2549
+ _this22.refreshAllFeedbackIcons();
2441
2550
  });
2442
2551
 
2443
2552
  // Delegate click for code block copy buttons.
@@ -2469,7 +2578,7 @@ window.openAIChatManager = function () {
2469
2578
  if (this.micButton) {
2470
2579
  this.micButton.style.display = '';
2471
2580
  this.micButton.addEventListener('click', function () {
2472
- _this21.toggleRecording();
2581
+ _this22.toggleRecording();
2473
2582
  });
2474
2583
  }
2475
2584
  }
@@ -2479,7 +2588,7 @@ window.openAIChatManager = function () {
2479
2588
  this.conversationButton = document.querySelector(config.conversationButtonElementSelector);
2480
2589
  if (this.conversationButton) {
2481
2590
  this.conversationButton.addEventListener('click', function () {
2482
- _this21.toggleConversationMode();
2591
+ _this22.toggleConversationMode();
2483
2592
  });
2484
2593
  }
2485
2594
  }
@@ -2503,7 +2612,7 @@ window.openAIChatManager = function () {
2503
2612
  }
2504
2613
  return;
2505
2614
  }
2506
- this.fireEvent(new CustomEvent("initializingSessionOpenAIChat", {
2615
+ this.fireEvent(new CustomEvent("initializingSessionCoreAIChat", {
2507
2616
  detail: {
2508
2617
  sessionId: sessionId
2509
2618
  }
@@ -2514,6 +2623,13 @@ window.openAIChatManager = function () {
2514
2623
  if (widgetBehavior && typeof widgetBehavior.onSessionInitialized === 'function') {
2515
2624
  widgetBehavior.onSessionInitialized(this, sessionId, config);
2516
2625
  }
2626
+
2627
+ // For non-widget pages, update the browser URL so that a page
2628
+ // refresh reloads the correct session instead of starting fresh.
2629
+ if (!hasWidgetConfig && sessionId && config.sessionUrlTemplate && typeof history !== 'undefined' && history.replaceState) {
2630
+ var sessionUrl = config.sessionUrlTemplate.replace('{sessionId}', encodeURIComponent(sessionId));
2631
+ history.replaceState(null, '', sessionUrl);
2632
+ }
2517
2633
  },
2518
2634
  getSessionId: function getSessionId() {
2519
2635
  var sessionId = this.inputElement ? this.inputElement.getAttribute('data-session-id') : null;
@@ -2522,10 +2638,45 @@ window.openAIChatManager = function () {
2522
2638
  }
2523
2639
  return sessionId;
2524
2640
  },
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);
2641
+ clearCopiedMessageState: function clearCopiedMessageState() {
2642
+ if (this.copyResetTimeoutId) {
2643
+ window.clearTimeout(this.copyResetTimeoutId);
2644
+ this.copyResetTimeoutId = null;
2645
+ }
2646
+ if (this.activeCopyButton) {
2647
+ this.setCopyButtonState(this.activeCopyButton, false);
2648
+ this.activeCopyButton = null;
2649
+ }
2650
+ this.copiedMessageIndex = -1;
2651
+ },
2652
+ copyResponse: function copyResponse(message, index, event) {
2653
+ var _ref25,
2654
+ _message$copyContent,
2655
+ _event$target,
2656
+ _event$target$closest,
2657
+ _this23 = this;
2658
+ 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 : '';
2659
+ 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;
2660
+ navigator.clipboard.writeText(text).then(function () {
2661
+ _this23.clearCopiedMessageState();
2662
+ _this23.copiedMessageIndex = typeof index === 'number' ? index : -1;
2663
+ _this23.activeCopyButton = button;
2664
+ if (button) {
2665
+ _this23.setCopyButtonState(button, true);
2666
+ } else {
2667
+ _this23.$nextTick(function () {
2668
+ return _this23.updateCopyButtons();
2669
+ });
2670
+ }
2671
+ _this23.copyResetTimeoutId = window.setTimeout(function () {
2672
+ _this23.clearCopiedMessageState();
2673
+ _this23.$nextTick(function () {
2674
+ return _this23.updateCopyButtons();
2675
+ });
2676
+ }, Number(config.copyResetDelayMs) || 2000);
2677
+ })["catch"](function (err) {
2678
+ return console.error('Failed to copy response:', err);
2679
+ });
2529
2680
  },
2530
2681
  updateFeedbackIcons: function updateFeedbackIcons(container, userRating) {
2531
2682
  if (!container) {
@@ -2620,6 +2771,12 @@ window.openAIChatManager = function () {
2620
2771
  // Reserved for future use — volume-based interrupt detection
2621
2772
  // no longer mutes tracks; browser echo cancellation handles echo.
2622
2773
  },
2774
+ copiedMessageIndex: function copiedMessageIndex() {
2775
+ var _this24 = this;
2776
+ this.$nextTick(function () {
2777
+ return _this24.updateCopyButtons();
2778
+ });
2779
+ },
2623
2780
  isConversationMode: function isConversationMode(active) {
2624
2781
  // Hide/show mic button.
2625
2782
  if (this.micButton) {
@@ -2641,22 +2798,23 @@ window.openAIChatManager = function () {
2641
2798
  }
2642
2799
  },
2643
2800
  mounted: function mounted() {
2644
- var _this22 = this;
2801
+ var _this25 = this;
2645
2802
  _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
2646
2803
  var isInitialized;
2647
2804
  return _regenerator().w(function (_context0) {
2648
2805
  while (1) switch (_context0.n) {
2649
2806
  case 0:
2650
2807
  _context0.n = 1;
2651
- return _this22.startConnection();
2808
+ return _this25.startConnection();
2652
2809
  case 1:
2653
- isInitialized = _this22.initializeApp();
2810
+ isInitialized = _this25.initializeApp();
2654
2811
  if (isInitialized && hasWidgetConfig && widgetBehavior && typeof widgetBehavior.onMounted === 'function') {
2655
- widgetBehavior.onMounted(_this22, config);
2812
+ widgetBehavior.onMounted(_this25, config);
2656
2813
  }
2657
- _this22.$nextTick(function () {
2658
- refreshFontAwesomeIcons(_this22.$el);
2659
- _this22.fontAwesomeObserver = observeFontAwesomeIcons(_this22.$el);
2814
+ _this25.$nextTick(function () {
2815
+ _this25.updateCopyButtons();
2816
+ refreshFontAwesomeIcons(_this25.$el);
2817
+ _this25.fontAwesomeObserver = observeFontAwesomeIcons(_this25.$el);
2660
2818
  });
2661
2819
  case 2:
2662
2820
  return _context0.a(2);
@@ -2673,6 +2831,7 @@ window.openAIChatManager = function () {
2673
2831
  this.fontAwesomeObserver.disconnect();
2674
2832
  this.fontAwesomeObserver = null;
2675
2833
  }
2834
+ this.clearCopiedMessageState();
2676
2835
  this.stopAudio(false);
2677
2836
  if (this.stream) {
2678
2837
  this.stream.dispose();
@@ -2693,8 +2852,151 @@ window.openAIChatManager = function () {
2693
2852
  }
2694
2853
  return app;
2695
2854
  };
2855
+ var autoInitializeSelector = '[data-coreai-chat-config],[data-coreai-chat-app-element-selector]';
2856
+ function getAttributeValue(element, attributeName) {
2857
+ var value = element.getAttribute(attributeName);
2858
+ return value === null || value === '' ? null : value;
2859
+ }
2860
+ function parseBooleanAttributeValue(value) {
2861
+ if (value === null || value === undefined) {
2862
+ return null;
2863
+ }
2864
+ if (typeof value === 'boolean') {
2865
+ return value;
2866
+ }
2867
+ var normalized = String(value).trim().toLowerCase();
2868
+ if (normalized === 'true') {
2869
+ return true;
2870
+ }
2871
+ if (normalized === 'false') {
2872
+ return false;
2873
+ }
2874
+ return null;
2875
+ }
2876
+ function parseJsonAttribute(element, attributeName, description) {
2877
+ var rawValue = getAttributeValue(element, attributeName);
2878
+ if (!rawValue) {
2879
+ return null;
2880
+ }
2881
+ try {
2882
+ return JSON.parse(rawValue);
2883
+ } catch (error) {
2884
+ console.error('Failed to parse ' + description + ' JSON.', error);
2885
+ return null;
2886
+ }
2887
+ }
2888
+ function buildConfigFromDataAttributes(element, options) {
2889
+ var _parseJsonAttribute, _parseJsonAttribute2;
2890
+ var settings = options || {};
2891
+ if (element.hasAttribute('data-coreai-chat-widget-container-selector') && settings.allowWidgetHost !== true) {
2892
+ return null;
2893
+ }
2894
+ var appElementSelector = getAttributeValue(element, 'data-coreai-chat-app-element-selector');
2895
+ if (!appElementSelector) {
2896
+ return null;
2897
+ }
2898
+ var config = {
2899
+ signalRHubUrl: getAttributeValue(element, 'data-coreai-chat-signalr-hub-url'),
2900
+ appElementSelector: appElementSelector,
2901
+ chatContainerElementSelector: getAttributeValue(element, 'data-coreai-chat-container-element-selector'),
2902
+ inputElementSelector: getAttributeValue(element, 'data-coreai-chat-input-element-selector'),
2903
+ sendButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-send-button-element-selector'),
2904
+ placeholderElementSelector: getAttributeValue(element, 'data-coreai-chat-placeholder-element-selector'),
2905
+ messages: (_parseJsonAttribute = parseJsonAttribute(element, 'data-coreai-chat-messages', 'CoreAI chat messages')) !== null && _parseJsonAttribute !== void 0 ? _parseJsonAttribute : undefined,
2906
+ chatMode: getAttributeValue(element, 'data-coreai-chat-mode'),
2907
+ micButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-mic-button-element-selector'),
2908
+ conversationButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-conversation-button-element-selector'),
2909
+ ttsVoiceName: getAttributeValue(element, 'data-coreai-chat-tts-voice-name'),
2910
+ documentBarSelector: getAttributeValue(element, 'data-coreai-chat-document-bar-selector'),
2911
+ uploadDocumentUrl: getAttributeValue(element, 'data-coreai-chat-upload-document-url'),
2912
+ removeDocumentUrl: getAttributeValue(element, 'data-coreai-chat-remove-document-url'),
2913
+ allowedExtensions: getAttributeValue(element, 'data-coreai-chat-allowed-extensions'),
2914
+ supportedExtensionsText: getAttributeValue(element, 'data-coreai-chat-supported-extensions-text'),
2915
+ existingDocuments: (_parseJsonAttribute2 = parseJsonAttribute(element, 'data-coreai-chat-existing-documents', 'CoreAI chat existing documents')) !== null && _parseJsonAttribute2 !== void 0 ? _parseJsonAttribute2 : undefined,
2916
+ initialPrompt: getAttributeValue(element, 'data-coreai-chat-initial-prompt'),
2917
+ sessionUrlTemplate: getAttributeValue(element, 'data-coreai-chat-session-url-template'),
2918
+ userLabel: getAttributeValue(element, 'data-coreai-chat-user-label'),
2919
+ assistantLabel: getAttributeValue(element, 'data-coreai-chat-assistant-label'),
2920
+ copyTitle: getAttributeValue(element, 'data-coreai-chat-copy-title'),
2921
+ copiedTitle: getAttributeValue(element, 'data-coreai-chat-copied-title')
2922
+ };
2923
+ var booleanAttributes = {
2924
+ metricsEnabled: 'data-coreai-chat-metrics-enabled',
2925
+ textToSpeechEnabled: 'data-coreai-chat-text-to-speech-enabled',
2926
+ sessionDocumentsEnabled: 'data-coreai-chat-session-documents-enabled',
2927
+ singleResponseMode: 'data-coreai-chat-single-response-mode'
2928
+ };
2929
+ Object.keys(booleanAttributes).forEach(function (key) {
2930
+ var parsed = parseBooleanAttributeValue(getAttributeValue(element, booleanAttributes[key]));
2931
+ if (parsed !== null) {
2932
+ config[key] = parsed;
2933
+ }
2934
+ });
2935
+ return compactObject(config);
2936
+ }
2937
+ function buildConfigFromElement(element, options) {
2938
+ var jsonConfig = parseJsonAttribute(element, 'data-coreai-chat-config', 'CoreAI chat config');
2939
+ var attributeConfig = buildConfigFromDataAttributes(element, options);
2940
+ if (!jsonConfig && !attributeConfig) {
2941
+ return null;
2942
+ }
2943
+ return Object.assign({}, jsonConfig || {}, attributeConfig || {});
2944
+ }
2945
+ function initializeFromElement(element) {
2946
+ if (!element || element.dataset.coreAiChatInitialized === 'true') {
2947
+ return element ? element.__coreAIChatApp || null : null;
2948
+ }
2949
+ var config = buildConfigFromElement(element);
2950
+ if (!config) {
2951
+ return null;
2952
+ }
2953
+ var app = initialize(config);
2954
+ if (!app) {
2955
+ return null;
2956
+ }
2957
+ element.dataset.coreAiChatInitialized = 'true';
2958
+ element.__coreAIChatApp = app;
2959
+ return app;
2960
+ }
2961
+ function scanForAutoInitialization(root) {
2962
+ if (!root || typeof root.querySelectorAll !== 'function') {
2963
+ return;
2964
+ }
2965
+ if (typeof root.matches === 'function' && root.matches(autoInitializeSelector)) {
2966
+ initializeFromElement(root);
2967
+ }
2968
+ root.querySelectorAll(autoInitializeSelector).forEach(initializeFromElement);
2969
+ }
2970
+ function startAutoInitialization() {
2971
+ scanForAutoInitialization(document);
2972
+ if (typeof MutationObserver === 'undefined') {
2973
+ return;
2974
+ }
2975
+ var observer = new MutationObserver(function (mutations) {
2976
+ mutations.forEach(function (mutation) {
2977
+ mutation.addedNodes.forEach(function (node) {
2978
+ if (node && node.nodeType === 1) {
2979
+ scanForAutoInitialization(node);
2980
+ }
2981
+ });
2982
+ });
2983
+ });
2984
+ observer.observe(document.body, {
2985
+ childList: true,
2986
+ subtree: true
2987
+ });
2988
+ }
2989
+ if (document.readyState === 'loading') {
2990
+ document.addEventListener('DOMContentLoaded', startAutoInitialization, {
2991
+ once: true
2992
+ });
2993
+ } else {
2994
+ startAutoInitialization();
2995
+ }
2696
2996
  return {
2697
- initialize: initialize
2997
+ initialize: initialize,
2998
+ initializeFromElement: initializeFromElement,
2999
+ buildConfigFromElement: buildConfigFromElement
2698
3000
  };
2699
3001
  }();
2700
3002