@crestapps/ai-chat-ui 1.0.0-preview.10 → 1.0.0-preview.108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/ai-chat-widget.js +961 -0
  2. package/dist/ai-chat-widget.js.map +1 -0
  3. package/dist/ai-chat-widget.min.js +2 -0
  4. package/dist/ai-chat-widget.min.js.map +1 -0
  5. package/dist/ai-chat.css +67 -17
  6. package/dist/ai-chat.css.map +1 -1
  7. package/dist/ai-chat.js +1082 -1027
  8. package/dist/ai-chat.js.map +1 -1
  9. package/dist/ai-chat.min.css +1 -1
  10. package/dist/ai-chat.min.js +1 -1
  11. package/dist/ai-chat.min.js.map +1 -1
  12. package/dist/chat-interaction-settings.js +297 -0
  13. package/dist/chat-interaction-settings.js.map +1 -0
  14. package/dist/chat-interaction-settings.min.js +2 -0
  15. package/dist/chat-interaction-settings.min.js.map +1 -0
  16. package/dist/chat-interaction.js +1198 -338
  17. package/dist/chat-interaction.js.map +1 -1
  18. package/dist/chat-interaction.min.js +1 -1
  19. package/dist/chat-interaction.min.js.map +1 -1
  20. package/dist/chat-widget.css +88 -35
  21. package/dist/chat-widget.css.map +1 -1
  22. package/dist/chat-widget.min.css +1 -1
  23. package/dist/document-drop-zone.css +10 -10
  24. package/dist/document-drop-zone.css.map +1 -1
  25. package/dist/document-drop-zone.js +92 -14
  26. package/dist/document-drop-zone.js.map +1 -1
  27. package/dist/document-drop-zone.min.css +1 -1
  28. package/dist/document-drop-zone.min.js +1 -1
  29. package/dist/document-drop-zone.min.js.map +1 -1
  30. package/dist/technical-name-generator.js +36 -8
  31. package/dist/technical-name-generator.js.map +1 -1
  32. package/dist/technical-name-generator.min.js +1 -1
  33. package/dist/technical-name-generator.min.js.map +1 -1
  34. package/package.json +9 -1
  35. package/README.md +0 -145
package/dist/ai-chat.js CHANGED
@@ -3,15 +3,18 @@
3
3
  ** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp.
4
4
  */
5
5
 
6
+ var _excluded = ["placeholder"];
6
7
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
7
8
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
8
9
  function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
9
10
  function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
10
- function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
11
11
  function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
12
12
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
13
13
  function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
14
14
  function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
15
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
16
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
17
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
15
18
  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
16
19
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
17
20
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -19,7 +22,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
19
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; } }
20
23
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
21
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); }
22
- window.openAIChatManager = function () {
25
+ window.coreAIChatManager = function () {
23
26
  // Defaults (can be overridden by instanceConfig)
24
27
  var defaultConfig = {
25
28
  // UI defaults for generated media
@@ -33,7 +36,9 @@ window.openAIChatManager = function () {
33
36
  thumbsUpTitle: 'Thumbs up',
34
37
  thumbsDownTitle: 'Thumbs down',
35
38
  copyTitle: 'Click here to copy response to clipboard.',
39
+ copiedTitle: 'Response copied to clipboard.',
36
40
  codeCopiedText: 'Copied!',
41
+ copyResetDelayMs: 2000,
37
42
  widget: {
38
43
  chatWidgetContainer: null,
39
44
  chatWidgetStateName: null,
@@ -52,8 +57,8 @@ window.openAIChatManager = function () {
52
57
  minWidth: 320,
53
58
  minHeight: 420
54
59
  },
55
- 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)\"><span :class=\"getAssistantIcon(message)\"></span></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 <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 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 <span class=\"fa-regular fa-thumbs-up\"></span>\n </button>\n <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 <span class=\"fa-regular fa-thumbs-down\"></span>\n </button>\n </span>\n </template>\n <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 class=\"btn btn-sm btn-link text-secondary p-0 button-message-toolbox\" @click=\"copyResponse(message.content)\" :title=\"copyTitle\">\n <span class=\"fa-solid fa-copy\"></span>\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 <span v-if=\"notification.icon\" :class=\"notification.icon\" class=\"ai-chat-notification-icon\"></span>\n <span class=\"ai-chat-notification-text\">{{ notification.content }}</span>\n <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 <span class=\"fa-solid fa-xmark\"></span>\n </button>\n </div>\n <div v-if=\"notification.actions && notification.actions.length\" class=\"ai-chat-notification-actions\">\n <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 <span v-if=\"action.icon\" :class=\"action.icon\" class=\"me-1\"></span>\n {{ action.label }}\n </button>\n </div>\n </div>\n </div>\n ",
56
- indicatorTemplate: "\n <div class=\"ai-chat-msg-role ai-chat-msg-role-assistant\">\n <span class=\"ai-streaming-icon\"><span class=\"fa fa-robot\" style=\"display: inline-block;\"></span></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 "
57
62
  };
58
63
 
59
64
  // Sanitize URLs to prevent javascript: protocol injection.
@@ -72,6 +77,42 @@ window.openAIChatManager = function () {
72
77
  span.textContent = text;
73
78
  return span.innerHTML;
74
79
  }
80
+ function containsFontAwesomePlaceholders(root) {
81
+ if (!root || root.nodeType !== 1) {
82
+ return false;
83
+ }
84
+ var selector = 'i[class*="fa-"],i.fa,i.fas,i.far,i.fab';
85
+ if (typeof root.matches === 'function' && root.matches(selector)) {
86
+ return true;
87
+ }
88
+ return typeof root.querySelector === 'function' && !!root.querySelector(selector);
89
+ }
90
+ function refreshFontAwesomeIcons(root) {
91
+ var fontAwesome = window.FontAwesome;
92
+ if (!containsFontAwesomePlaceholders(root) || !fontAwesome || !fontAwesome.dom || typeof fontAwesome.dom.i2svg !== 'function') {
93
+ return;
94
+ }
95
+ fontAwesome.dom.i2svg({
96
+ node: root
97
+ });
98
+ }
99
+ function observeFontAwesomeIcons(root) {
100
+ if (!root || typeof MutationObserver === 'undefined') {
101
+ return null;
102
+ }
103
+ var observer = new MutationObserver(function (mutations) {
104
+ mutations.forEach(function (mutation) {
105
+ mutation.addedNodes.forEach(function (node) {
106
+ refreshFontAwesomeIcons(node);
107
+ });
108
+ });
109
+ });
110
+ observer.observe(root, {
111
+ childList: true,
112
+ subtree: true
113
+ });
114
+ return observer;
115
+ }
75
116
  function parsePixelValue(value) {
76
117
  if (typeof value === 'number' && Number.isFinite(value)) {
77
118
  return value;
@@ -86,16 +127,31 @@ window.openAIChatManager = function () {
86
127
  return Math.min(Math.max(value, min), max);
87
128
  }
88
129
  function normalizeReference(reference) {
89
- var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$link;
130
+ var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$title, _ref4, _normalized$link, _ref5, _normalized$reference, _normalized$isGenerat;
90
131
  if (!reference || _typeof(reference) !== 'object') {
91
132
  return null;
92
133
  }
93
134
  var normalized = Object.assign({}, reference);
94
135
  normalized.index = (_ref = (_normalized$index = normalized.index) !== null && _normalized$index !== void 0 ? _normalized$index : normalized.Index) !== null && _ref !== void 0 ? _ref : 0;
95
136
  normalized.text = (_ref2 = (_normalized$text = normalized.text) !== null && _normalized$text !== void 0 ? _normalized$text : normalized.Text) !== null && _ref2 !== void 0 ? _ref2 : null;
96
- normalized.link = (_ref3 = (_normalized$link = normalized.link) !== null && _normalized$link !== void 0 ? _normalized$link : normalized.Link) !== null && _ref3 !== void 0 ? _ref3 : null;
137
+ normalized.title = (_ref3 = (_normalized$title = normalized.title) !== null && _normalized$title !== void 0 ? _normalized$title : normalized.Title) !== null && _ref3 !== void 0 ? _ref3 : null;
138
+ normalized.link = sanitizeUrl((_ref4 = (_normalized$link = normalized.link) !== null && _normalized$link !== void 0 ? _normalized$link : normalized.Link) !== null && _ref4 !== void 0 ? _ref4 : null);
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;
97
141
  return normalized;
98
142
  }
143
+ function isDownloadCitationReference(reference) {
144
+ if (!reference || _typeof(reference) !== 'object') {
145
+ return false;
146
+ }
147
+ if (typeof reference.referenceType === 'string' && reference.referenceType.toLowerCase() === 'document') {
148
+ return true;
149
+ }
150
+ if (typeof reference.link === 'string' && /\/ai\/documents\/.+\/download(?:$|\?)/i.test(reference.link)) {
151
+ return true;
152
+ }
153
+ return false;
154
+ }
99
155
  function normalizeReferences(references) {
100
156
  if (!references || _typeof(references) !== 'object') {
101
157
  return {};
@@ -110,6 +166,164 @@ window.openAIChatManager = function () {
110
166
  }
111
167
  return normalized;
112
168
  }
169
+ function getCitationLabel(reference, key) {
170
+ return reference.title || reference.text || key;
171
+ }
172
+ function buildCitationDisplay(content, references) {
173
+ var processedContent = (content || '').trim();
174
+ var messageReferences = normalizeReferences(references);
175
+ var referenceEntries = Object.entries(messageReferences);
176
+ if (!referenceEntries.length) {
177
+ return {
178
+ content: processedContent,
179
+ citations: []
180
+ };
181
+ }
182
+ var citedRefs = referenceEntries.filter(function (_ref6) {
183
+ var _ref7 = _slicedToArray(_ref6, 1),
184
+ key = _ref7[0];
185
+ return processedContent.includes(key);
186
+ });
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) {
194
+ return {
195
+ content: processedContent,
196
+ citations: []
197
+ };
198
+ }
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];
211
+ return a.index - b.index;
212
+ });
213
+ var citations = [];
214
+ var displayIndex = 1;
215
+ var _iterator = _createForOfIteratorHelper(citedRefs),
216
+ _step;
217
+ try {
218
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
219
+ var _step$value = _slicedToArray(_step.value, 2),
220
+ key = _step$value[0],
221
+ value = _step$value[1];
222
+ var placeholder = "__CITE_".concat(displayIndex, "_").concat(value.index || displayIndex, "__");
223
+ processedContent = processedContent.replaceAll(key, placeholder);
224
+ citations.push({
225
+ referenceKey: key,
226
+ displayIndex: displayIndex,
227
+ label: getCitationLabel(value, key),
228
+ link: value.link || null,
229
+ isDownload: isDownloadCitationReference(value),
230
+ placeholder: placeholder
231
+ });
232
+ displayIndex++;
233
+ }
234
+ } catch (err) {
235
+ _iterator.e(err);
236
+ } finally {
237
+ _iterator.f();
238
+ }
239
+ for (var _i2 = 0, _citations = citations; _i2 < _citations.length; _i2++) {
240
+ var citation = _citations[_i2];
241
+ processedContent = processedContent.replaceAll(citation.placeholder, "<sup>".concat(citation.displayIndex, "</sup>"));
242
+ }
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
+ }
269
+ return {
270
+ content: processedContent,
271
+ citations: citations.map(function (_ref16) {
272
+ var placeholder = _ref16.placeholder,
273
+ citation = _objectWithoutProperties(_ref16, _excluded);
274
+ return citation;
275
+ })
276
+ };
277
+ }
278
+ function buildCopyContent(content, citations) {
279
+ var copyContent = (content || '').trim();
280
+ if (!copyContent || !Array.isArray(citations) || citations.length === 0) {
281
+ return copyContent;
282
+ }
283
+ var _iterator3 = _createForOfIteratorHelper(citations),
284
+ _step3;
285
+ try {
286
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
287
+ var citation = _step3.value;
288
+ copyContent = copyContent.replaceAll(citation.referenceKey, "[".concat(citation.displayIndex, "]"));
289
+ }
290
+ } catch (err) {
291
+ _iterator3.e(err);
292
+ } finally {
293
+ _iterator3.f();
294
+ }
295
+ copyContent += '\n\nReferences:\n';
296
+ var _iterator4 = _createForOfIteratorHelper(citations),
297
+ _step4;
298
+ try {
299
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
300
+ var _citation = _step4.value;
301
+ copyContent += "".concat(_citation.displayIndex, ". ").concat(_citation.label);
302
+ if (_citation.link) {
303
+ copyContent += " - ".concat(_citation.link);
304
+ }
305
+ copyContent += '\n';
306
+ }
307
+ } catch (err) {
308
+ _iterator4.e(err);
309
+ } finally {
310
+ _iterator4.f();
311
+ }
312
+ return copyContent.trimEnd();
313
+ }
314
+ function updateMessagePresentation(message, references) {
315
+ var messageReferences = normalizeReferences(references !== null && references !== void 0 ? references : message.references);
316
+ var rawContent = typeof message.rawContent === 'string' ? message.rawContent : typeof message.content === 'string' ? message.content : '';
317
+ var citationDisplay = buildCitationDisplay(rawContent, messageReferences);
318
+ message.rawContent = rawContent;
319
+ message.content = rawContent;
320
+ message.displayContent = citationDisplay.content;
321
+ message.references = messageReferences;
322
+ message.citationReferences = citationDisplay.citations;
323
+ message.copyContent = buildCopyContent(rawContent, citationDisplay.citations);
324
+ message.htmlContent = parseMarkdownContent(citationDisplay.content, message);
325
+ return message;
326
+ }
113
327
  var renderer = new marked.Renderer();
114
328
 
115
329
  // Modify the link rendering to open in a new tab
@@ -140,7 +354,7 @@ window.openAIChatManager = function () {
140
354
  highlighted = escapeHtmlEntities(code);
141
355
  }
142
356
  var langDisplay = lang ? escapeHtmlEntities(lang) : 'code';
143
- return "<div class=\"ai-code-block\"><div class=\"ai-code-header\"><span class=\"ai-code-lang\"><span class=\"fa-solid fa-code\"></span> ".concat(langDisplay, "</span><button type=\"button\" class=\"ai-code-copy-btn\" title=\"Copy code\"><span class=\"fa-regular fa-copy\"></span></button></div><pre><code class=\"hljs").concat(lang ? ' language-' + lang : '', "\">").concat(highlighted, "</code></pre></div>");
357
+ return "<div class=\"ai-code-block\"><div class=\"ai-code-header\"><span class=\"ai-code-lang\"><i class=\"fa-solid fa-code\"></i> ".concat(langDisplay, "</span><button type=\"button\" class=\"ai-code-copy-btn\" title=\"Copy code\"><i class=\"fa-regular fa-copy\"></i></button></div><pre><code class=\"hljs").concat(lang ? ' language-' + lang : '', "\">").concat(highlighted, "</code></pre></div>");
144
358
  };
145
359
 
146
360
  // Custom image renderer for generated images with thumbnail styling and download button.
@@ -150,7 +364,7 @@ window.openAIChatManager = function () {
150
364
  if (!src) return '';
151
365
  var alt = data.text || defaultConfig.generatedImageAltText;
152
366
  var maxWidth = defaultConfig.generatedImageMaxWidth;
153
- return "<div class=\"generated-image-container\">\n <img src=\"".concat(src, "\" alt=\"").concat(alt, "\" class=\"img-thumbnail\" style=\"max-width: ").concat(maxWidth, "px; height: auto;\" />\n <div class=\"mt-2\">\n <a href=\"").concat(src, "\" target=\"_blank\" download=\"").concat(alt, "\" title=\"").concat(defaultConfig.downloadImageTitle, "\" class=\"btn btn-sm btn-outline-secondary ai-download-image\">\n <span class=\"fa-solid fa-download\"></span>\n </a>\n </div>\n </div>");
367
+ return "<div class=\"generated-image-container\">\n <img src=\"".concat(src, "\" alt=\"").concat(alt, "\" class=\"img-thumbnail\" style=\"max-width: ").concat(maxWidth, "px; height: auto;\" />\n <div class=\"mt-2\">\n <a href=\"").concat(src, "\" target=\"_blank\" download=\"").concat(alt, "\" title=\"").concat(defaultConfig.downloadImageTitle, "\" class=\"btn btn-sm btn-outline-secondary ai-download-image\">\n <i class=\"fa-solid fa-download\"></i>\n </a>\n </div>\n </div>");
154
368
  };
155
369
 
156
370
  // Chart counter for unique IDs
@@ -164,7 +378,7 @@ window.openAIChatManager = function () {
164
378
  // its DOM update to render charts it didn't create itself.
165
379
  window.__chartConfigs = window.__chartConfigs || {};
166
380
  function createChartHtml(chartId) {
167
- return "<div class=\"chart-container\" style=\"position: relative; width: 100%; max-width: 560px; min-height: 420px;\">" + "<canvas id=\"".concat(chartId, "\"></canvas>") + "</div>" + "<div class=\"mt-2\">" + "<button type=\"button\" class=\"btn btn-sm btn-outline-secondary download-chart-btn\" data-chart-id=\"".concat(chartId, "\" title=\"").concat(defaultConfig.downloadChartTitle, "\">") + "<span class=\"fa-solid fa-download\"></span> ".concat(defaultConfig.downloadChartButtonText) + "</button>" + "</div>";
381
+ return "<div class=\"chart-container\" style=\"position: relative; width: 100%; max-width: 560px; min-height: 420px;\">" + "<canvas id=\"".concat(chartId, "\"></canvas>") + "</div>" + "<div class=\"mt-2\">" + "<button type=\"button\" class=\"btn btn-sm btn-outline-secondary download-chart-btn\" data-chart-id=\"".concat(chartId, "\" title=\"").concat(defaultConfig.downloadChartTitle, "\">") + "<i class=\"fa-solid fa-download\"></i> ".concat(defaultConfig.downloadChartButtonText) + "</button>" + "</div>";
168
382
  }
169
383
 
170
384
  // Register [chart:{...json...}] as a native marked block extension so the
@@ -275,17 +489,17 @@ window.openAIChatManager = function () {
275
489
  // Defer to requestAnimationFrame so the browser has fully laid out the
276
490
  // canvas elements before Chart.js reads their dimensions.
277
491
  requestAnimationFrame(function () {
278
- var _iterator = _createForOfIteratorHelper(charts),
279
- _step;
492
+ var _iterator5 = _createForOfIteratorHelper(charts),
493
+ _step5;
280
494
  try {
281
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
282
- var c = _step.value;
495
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
496
+ var c = _step5.value;
283
497
  renderChartOnCanvas(c.chartId, c.config);
284
498
  }
285
499
  } catch (err) {
286
- _iterator.e(err);
500
+ _iterator5.e(err);
287
501
  } finally {
288
- _iterator.f();
502
+ _iterator5.f();
289
503
  }
290
504
  });
291
505
  }
@@ -338,8 +552,8 @@ window.openAIChatManager = function () {
338
552
  return;
339
553
  }
340
554
  requestAnimationFrame(function () {
341
- for (var _i2 = 0, _Object$keys = Object.keys(configs); _i2 < _Object$keys.length; _i2++) {
342
- var chartId = _Object$keys[_i2];
555
+ for (var _i3 = 0, _Object$keys = Object.keys(configs); _i3 < _Object$keys.length; _i3++) {
556
+ var chartId = _Object$keys[_i3];
343
557
  renderChartOnCanvas(chartId, configs[chartId]);
344
558
  }
345
559
  });
@@ -359,10 +573,31 @@ window.openAIChatManager = function () {
359
573
  ADD_ATTR: ['target']
360
574
  });
361
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
+ }
362
593
  var initialize = function initialize(instanceConfig) {
363
- var config = Object.assign({}, defaultConfig, instanceConfig);
364
- config.widget = Object.assign({}, defaultConfig.widget || {}, instanceConfig && instanceConfig.widget ? instanceConfig.widget : {});
365
- var hasWidgetConfig = !!(instanceConfig && instanceConfig.widget && instanceConfig.widget.chatWidgetContainer && instanceConfig.widget.chatWidgetStateName);
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;
366
601
  // Keep defaultConfig in sync so renderers use overridden values
367
602
  defaultConfig = config;
368
603
  if (!config.signalRHubUrl) {
@@ -385,7 +620,7 @@ window.openAIChatManager = function () {
385
620
  console.error('The sendButtonElementSelector is required.');
386
621
  return;
387
622
  }
388
- var app = Vue.createApp({
623
+ var appDefinition = {
389
624
  data: function data() {
390
625
  return {
391
626
  inputElement: null,
@@ -394,18 +629,6 @@ window.openAIChatManager = function () {
394
629
  placeholder: null,
395
630
  isSessionStarted: false,
396
631
  isPlaceholderVisible: true,
397
- chatWidgetStateName: null,
398
- chatWidgetStateSession: null,
399
- chatWidgetLayoutKey: null,
400
- chatHistorySection: null,
401
- widgetContainerElement: null,
402
- widgetToggleButtonElement: null,
403
- widgetResetSizeButtonElement: null,
404
- widgetResizeObserver: null,
405
- widgetViewportResizeHandler: null,
406
- widgetDefaultWidth: null,
407
- widgetDefaultHeight: null,
408
- widgetIsInitialized: false,
409
632
  isStreaming: false,
410
633
  isNavigatingAway: false,
411
634
  autoScroll: true,
@@ -415,6 +638,8 @@ window.openAIChatManager = function () {
415
638
  prompt: '',
416
639
  documents: config.existingDocuments || [],
417
640
  isUploading: false,
641
+ isDocumentOperationPending: false,
642
+ documentOperationQueue: null,
418
643
  uploadErrors: [],
419
644
  isDragOver: false,
420
645
  documentBar: null,
@@ -424,6 +649,7 @@ window.openAIChatManager = function () {
424
649
  thumbsUpTitle: config.thumbsUpTitle,
425
650
  thumbsDownTitle: config.thumbsDownTitle,
426
651
  copyTitle: config.copyTitle,
652
+ copiedTitle: config.copiedTitle,
427
653
  isRecording: false,
428
654
  mediaRecorder: null,
429
655
  preRecordingPrompt: '',
@@ -444,7 +670,14 @@ window.openAIChatManager = function () {
444
670
  conversationModeEnabled: config.chatMode === 'Conversation',
445
671
  conversationButton: null,
446
672
  isConversationMode: false,
447
- notificationDismissTimers: {}
673
+ notificationDismissTimers: {},
674
+ pendingSessionPromise: null,
675
+ pendingSessionResolver: null,
676
+ pendingSessionRejector: null,
677
+ pendingSessionTimeoutId: null,
678
+ copiedMessageIndex: -1,
679
+ copyResetTimeoutId: null,
680
+ activeCopyButton: null
448
681
  };
449
682
  },
450
683
  computed: {
@@ -461,336 +694,6 @@ window.openAIChatManager = function () {
461
694
  handleBeforeUnload: function handleBeforeUnload() {
462
695
  this.isNavigatingAway = true;
463
696
  },
464
- getStoredWidgetLayout: function getStoredWidgetLayout() {
465
- if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) {
466
- return {};
467
- }
468
- try {
469
- var storedLayout = localStorage.getItem(this.chatWidgetLayoutKey);
470
- return storedLayout ? JSON.parse(storedLayout) || {} : {};
471
- } catch (error) {
472
- console.warn('Failed to read widget layout state.', error);
473
- return {};
474
- }
475
- },
476
- saveStoredWidgetLayout: function saveStoredWidgetLayout(partialLayout) {
477
- if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) {
478
- return;
479
- }
480
- var currentLayout = this.getStoredWidgetLayout();
481
- var nextLayout = Object.assign({}, currentLayout);
482
- if (partialLayout.panel !== undefined) {
483
- nextLayout.panel = Object.assign({}, currentLayout.panel || {}, partialLayout.panel || {});
484
- }
485
- if (partialLayout.position !== undefined) {
486
- nextLayout.position = Object.assign({}, currentLayout.position || {}, partialLayout.position || {});
487
- }
488
- localStorage.setItem(this.chatWidgetLayoutKey, JSON.stringify(nextLayout));
489
- },
490
- getElementSize: function getElementSize(element, fallbackWidth, fallbackHeight) {
491
- if (!element) {
492
- return {
493
- width: fallbackWidth || 0,
494
- height: fallbackHeight || 0
495
- };
496
- }
497
- var rect = element.getBoundingClientRect();
498
- var computed = window.getComputedStyle(element);
499
- return {
500
- width: Math.round(rect.width || parsePixelValue(computed.width) || fallbackWidth || 0),
501
- height: Math.round(rect.height || parsePixelValue(computed.height) || fallbackHeight || 0)
502
- };
503
- },
504
- getElementPosition: function getElementPosition(element) {
505
- if (!element) {
506
- return {
507
- left: 0,
508
- top: 0
509
- };
510
- }
511
- var rect = element.getBoundingClientRect();
512
- var computed = window.getComputedStyle(element);
513
- var inlineLeft = parsePixelValue(element.style.left);
514
- var inlineTop = parsePixelValue(element.style.top);
515
- var computedLeft = parsePixelValue(computed.left);
516
- var computedTop = parsePixelValue(computed.top);
517
- var isHidden = computed.display === 'none';
518
- return {
519
- left: Math.round(isHidden && Number.isFinite(inlineLeft) ? inlineLeft : rect.left || inlineLeft || computedLeft || 0),
520
- top: Math.round(isHidden && Number.isFinite(inlineTop) ? inlineTop : rect.top || inlineTop || computedTop || 0)
521
- };
522
- },
523
- clampWidgetPosition: function clampWidgetPosition(left, top, width, height) {
524
- var viewportPadding = 8;
525
- var maxLeft = Math.max(viewportPadding, window.innerWidth - width - viewportPadding);
526
- var maxTop = Math.max(viewportPadding, window.innerHeight - height - viewportPadding);
527
- return {
528
- left: Math.round(clamp(left, viewportPadding, maxLeft)),
529
- top: Math.round(clamp(top, viewportPadding, maxTop))
530
- };
531
- },
532
- applyAbsolutePosition: function applyAbsolutePosition(element, left, top) {
533
- if (!element) {
534
- return;
535
- }
536
- element.style.left = left + 'px';
537
- element.style.top = top + 'px';
538
- element.style.right = 'auto';
539
- element.style.bottom = 'auto';
540
- },
541
- getWidgetDefaultSize: function getWidgetDefaultSize() {
542
- return {
543
- width: this.widgetDefaultWidth || parsePixelValue(config.widget.defaultWidth) || 380,
544
- height: this.widgetDefaultHeight || parsePixelValue(config.widget.defaultHeight) || 520
545
- };
546
- },
547
- getWidgetToggleGap: function getWidgetToggleGap() {
548
- return 8;
549
- },
550
- isWidgetPanelVisible: function isWidgetPanelVisible() {
551
- if (!this.widgetContainerElement) {
552
- return false;
553
- }
554
- var computed = window.getComputedStyle(this.widgetContainerElement);
555
- return this.widgetContainerElement.classList.contains('open') && computed.display !== 'none';
556
- },
557
- persistWidgetPanelPosition: function persistWidgetPanelPosition() {
558
- if (!this.widgetContainerElement || !config.widget.persistLayout) {
559
- return;
560
- }
561
- var panelDefaultSize = this.getWidgetDefaultSize();
562
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
563
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
564
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
565
- var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
566
- this.saveStoredWidgetLayout({
567
- position: {
568
- left: linkedPosition.left,
569
- top: linkedPosition.top
570
- }
571
- });
572
- },
573
- persistWidgetTogglePosition: function persistWidgetTogglePosition() {
574
- if (!this.widgetToggleButtonElement || !config.widget.persistLayout) {
575
- return;
576
- }
577
- var toggleRect = this.widgetToggleButtonElement.getBoundingClientRect();
578
- this.saveStoredWidgetLayout({
579
- position: {
580
- left: this.getElementPosition(this.widgetToggleButtonElement).left,
581
- top: this.getElementPosition(this.widgetToggleButtonElement).top
582
- }
583
- });
584
- },
585
- persistWidgetPanelSize: function persistWidgetPanelSize() {
586
- if (!this.widgetContainerElement || !config.widget.enableResizing || !config.widget.persistLayout) {
587
- return;
588
- }
589
- var defaultSize = this.getWidgetDefaultSize();
590
- var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
591
- this.saveStoredWidgetLayout({
592
- panel: {
593
- width: Math.abs(currentSize.width - defaultSize.width) <= 1 ? null : currentSize.width,
594
- height: Math.abs(currentSize.height - defaultSize.height) <= 1 ? null : currentSize.height
595
- }
596
- });
597
- },
598
- syncTogglePositionToPanel: function syncTogglePositionToPanel() {
599
- var persistLayout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
600
- if (!this.widgetContainerElement || !this.widgetToggleButtonElement) {
601
- return;
602
- }
603
- var panelDefaultSize = this.getWidgetDefaultSize();
604
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
605
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
606
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
607
- var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
608
- this.applyAbsolutePosition(this.widgetToggleButtonElement, linkedPosition.left, linkedPosition.top);
609
- if (persistLayout && config.widget.persistLayout) {
610
- this.persistWidgetTogglePosition();
611
- }
612
- },
613
- syncPanelPositionToToggle: function syncPanelPositionToToggle() {
614
- var persistLayout = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
615
- if (!this.widgetContainerElement || !this.widgetToggleButtonElement) {
616
- return;
617
- }
618
- var panelDefaultSize = this.getWidgetDefaultSize();
619
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
620
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
621
- var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
622
- var linkedPosition = this.clampWidgetPosition(togglePosition.left + toggleSize.width - panelSize.width, togglePosition.top - panelSize.height - this.getWidgetToggleGap(), panelSize.width, panelSize.height);
623
- this.applyAbsolutePosition(this.widgetContainerElement, linkedPosition.left, linkedPosition.top);
624
- if (persistLayout && config.widget.persistLayout) {
625
- this.persistWidgetPanelPosition();
626
- }
627
- },
628
- updateWidgetResetSizeButtonVisibility: function updateWidgetResetSizeButtonVisibility() {
629
- if (!this.widgetResetSizeButtonElement) {
630
- return;
631
- }
632
- if (!config.widget.enableResizing || !this.widgetContainerElement) {
633
- this.widgetResetSizeButtonElement.classList.add('d-none');
634
- return;
635
- }
636
- var defaultSize = this.getWidgetDefaultSize();
637
- var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
638
- var isResized = Math.abs(currentSize.width - defaultSize.width) > 1 || Math.abs(currentSize.height - defaultSize.height) > 1 || !!this.widgetContainerElement.style.width || !!this.widgetContainerElement.style.height;
639
- this.widgetResetSizeButtonElement.classList.toggle('d-none', !isResized);
640
- },
641
- resetWidgetSize: function resetWidgetSize() {
642
- if (!this.widgetContainerElement || !config.widget.enableResizing) {
643
- return;
644
- }
645
- this.widgetContainerElement.style.removeProperty('width');
646
- this.widgetContainerElement.style.removeProperty('height');
647
- this.persistWidgetPanelSize();
648
- this.constrainWidgetElementsToViewport();
649
- this.updateWidgetResetSizeButtonVisibility();
650
- },
651
- restoreWidgetLayout: function restoreWidgetLayout() {
652
- var storedLayout = this.getStoredWidgetLayout();
653
- if (this.widgetContainerElement && config.widget.enableResizing) {
654
- if (Number.isFinite(storedLayout.panel && storedLayout.panel.width)) {
655
- this.widgetContainerElement.style.width = storedLayout.panel.width + 'px';
656
- } else {
657
- this.widgetContainerElement.style.removeProperty('width');
658
- }
659
- if (Number.isFinite(storedLayout.panel && storedLayout.panel.height)) {
660
- this.widgetContainerElement.style.height = storedLayout.panel.height + 'px';
661
- } else {
662
- this.widgetContainerElement.style.removeProperty('height');
663
- }
664
- }
665
- if (this.widgetToggleButtonElement && config.widget.enableDragging && Number.isFinite(storedLayout.position && storedLayout.position.left) && Number.isFinite(storedLayout.position && storedLayout.position.top)) {
666
- this.applyAbsolutePosition(this.widgetToggleButtonElement, storedLayout.position.left, storedLayout.position.top);
667
- this.syncPanelPositionToToggle(false);
668
- } else if (this.widgetToggleButtonElement && config.widget.enableDragging && Number.isFinite(storedLayout.toggle && storedLayout.toggle.left) && Number.isFinite(storedLayout.toggle && storedLayout.toggle.top)) {
669
- this.applyAbsolutePosition(this.widgetToggleButtonElement, storedLayout.toggle.left, storedLayout.toggle.top);
670
- this.syncPanelPositionToToggle(false);
671
- } else if (this.widgetContainerElement && config.widget.enableDragging && Number.isFinite(storedLayout.panel && storedLayout.panel.left) && Number.isFinite(storedLayout.panel && storedLayout.panel.top)) {
672
- this.applyAbsolutePosition(this.widgetContainerElement, storedLayout.panel.left, storedLayout.panel.top);
673
- this.syncTogglePositionToPanel(false);
674
- }
675
- this.constrainWidgetElementsToViewport();
676
- this.updateWidgetResetSizeButtonVisibility();
677
- },
678
- attachDraggableWidgetElement: function attachDraggableWidgetElement(element, handle, layoutKey, suppressClickOnDrag) {
679
- var _this = this;
680
- if (!element || !handle || !config.widget.enableDragging) {
681
- return;
682
- }
683
- var activePointerId = null;
684
- var startX = 0;
685
- var startY = 0;
686
- var startLeft = 0;
687
- var startTop = 0;
688
- var dragged = false;
689
- var dragThreshold = 4;
690
- var interactiveSelector = 'button, a, input, textarea, select, option, label';
691
- var _stopDragging = function stopDragging(event) {
692
- if (activePointerId === null) {
693
- return;
694
- }
695
- if (event && event.pointerId !== undefined && event.pointerId !== activePointerId) {
696
- return;
697
- }
698
- document.removeEventListener('pointermove', onPointerMove);
699
- document.removeEventListener('pointerup', _stopDragging);
700
- document.removeEventListener('pointercancel', _stopDragging);
701
- element.classList.remove('ai-chat-widget-dragging');
702
- activePointerId = null;
703
- if (!dragged) {
704
- return;
705
- }
706
- if (layoutKey === 'panel') {
707
- _this.syncTogglePositionToPanel(false);
708
- _this.persistWidgetPanelPosition();
709
- } else if (layoutKey === 'toggle') {
710
- _this.syncPanelPositionToToggle(false);
711
- _this.persistWidgetTogglePosition();
712
- }
713
- if (config.widget.persistLayout) {
714
- _this.persistWidgetPanelPosition();
715
- _this.persistWidgetTogglePosition();
716
- }
717
- if (suppressClickOnDrag) {
718
- element.dataset.dragSuppressClick = 'true';
719
- window.setTimeout(function () {
720
- if (element.dataset.dragSuppressClick === 'true') {
721
- element.dataset.dragSuppressClick = 'false';
722
- }
723
- }, 0);
724
- }
725
- };
726
- var onPointerMove = function onPointerMove(event) {
727
- if (activePointerId === null || event.pointerId !== activePointerId) {
728
- return;
729
- }
730
- var deltaX = event.clientX - startX;
731
- var deltaY = event.clientY - startY;
732
- if (!dragged && Math.hypot(deltaX, deltaY) < dragThreshold) {
733
- return;
734
- }
735
- dragged = true;
736
- var defaultSize = _this.getWidgetDefaultSize();
737
- var currentSize = _this.getElementSize(element, layoutKey === 'panel' ? defaultSize.width : 52, layoutKey === 'panel' ? defaultSize.height : 52);
738
- var nextPosition = _this.clampWidgetPosition(startLeft + deltaX, startTop + deltaY, currentSize.width, currentSize.height);
739
- _this.applyAbsolutePosition(element, nextPosition.left, nextPosition.top);
740
- if (layoutKey === 'panel') {
741
- _this.syncTogglePositionToPanel(false);
742
- } else if (layoutKey === 'toggle') {
743
- _this.syncPanelPositionToToggle(false);
744
- }
745
- };
746
- handle.addEventListener('pointerdown', function (event) {
747
- if (event.button !== 0) {
748
- return;
749
- }
750
- if (layoutKey === 'panel' && event.target.closest(interactiveSelector)) {
751
- return;
752
- }
753
- var rect = element.getBoundingClientRect();
754
- startX = event.clientX;
755
- startY = event.clientY;
756
- startLeft = rect.left;
757
- startTop = rect.top;
758
- dragged = false;
759
- activePointerId = event.pointerId;
760
- element.classList.add('ai-chat-widget-dragging');
761
- if (handle.setPointerCapture) {
762
- handle.setPointerCapture(event.pointerId);
763
- }
764
- document.addEventListener('pointermove', onPointerMove);
765
- document.addEventListener('pointerup', _stopDragging);
766
- document.addEventListener('pointercancel', _stopDragging);
767
- event.preventDefault();
768
- });
769
- },
770
- constrainWidgetElementsToViewport: function constrainWidgetElementsToViewport() {
771
- if (this.widgetContainerElement && this.widgetContainerElement.style.left && this.isWidgetPanelVisible()) {
772
- var panelDefaultSize = this.getWidgetDefaultSize();
773
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
774
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
775
- var clampedPanel = this.clampWidgetPosition(panelPosition.left, panelPosition.top, panelSize.width, panelSize.height);
776
- this.applyAbsolutePosition(this.widgetContainerElement, clampedPanel.left, clampedPanel.top);
777
- this.syncTogglePositionToPanel(false);
778
- if (config.widget.persistLayout) {
779
- this.persistWidgetPanelPosition();
780
- this.persistWidgetTogglePosition();
781
- }
782
- } else if (this.widgetToggleButtonElement && this.widgetToggleButtonElement.style.left) {
783
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
784
- var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
785
- var clampedToggle = this.clampWidgetPosition(togglePosition.left, togglePosition.top, toggleSize.width, toggleSize.height);
786
- this.applyAbsolutePosition(this.widgetToggleButtonElement, clampedToggle.left, clampedToggle.top);
787
- this.syncPanelPositionToToggle(false);
788
- if (config.widget.persistLayout) {
789
- this.persistWidgetTogglePosition();
790
- this.persistWidgetPanelPosition();
791
- }
792
- }
793
- },
794
697
  handleDragOver: function handleDragOver(e) {
795
698
  if (!config.sessionDocumentsEnabled) return;
796
699
  e.preventDefault();
@@ -815,180 +718,272 @@ window.openAIChatManager = function () {
815
718
  var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;
816
719
  if (inputArea) inputArea.classList.remove('ai-chat-drag-over');
817
720
  if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
818
- this.uploadFiles(e.dataTransfer.files);
721
+ this.uploadFiles(Array.from(e.dataTransfer.files));
819
722
  }
820
723
  },
821
724
  triggerFileInput: function triggerFileInput() {
822
- if (!config.sessionDocumentsEnabled) return;
725
+ if (!config.sessionDocumentsEnabled || this.isDocumentOperationPending) return;
823
726
  var fileInput = document.getElementById('ai-chat-doc-input');
824
727
  if (fileInput) fileInput.click();
825
728
  },
826
729
  handleFileInputChange: function handleFileInputChange(e) {
827
- var files = e.target.files;
730
+ var files = e.target.files ? Array.from(e.target.files) : [];
828
731
  if (files && files.length > 0) {
829
732
  this.uploadFiles(files);
830
733
  }
831
734
  e.target.value = '';
832
735
  },
833
- uploadFiles: function uploadFiles(files) {
834
- var _this2 = this;
835
- return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
836
- var sessionId, profileId, formData, i, response, errorText, uploadError, result, j, _t;
736
+ queueDocumentOperation: function queueDocumentOperation(operation) {
737
+ var self = this;
738
+ var previousOperation = this.documentOperationQueue || Promise.resolve();
739
+ var nextOperation = previousOperation["catch"](function () {}).then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
837
740
  return _regenerator().w(function (_context) {
838
741
  while (1) switch (_context.p = _context.n) {
742
+ case 0:
743
+ self.isDocumentOperationPending = true;
744
+ _context.p = 1;
745
+ _context.n = 2;
746
+ return operation();
747
+ case 2:
748
+ return _context.a(2, _context.v);
749
+ case 3:
750
+ _context.p = 3;
751
+ self.isDocumentOperationPending = false;
752
+ return _context.f(3);
753
+ case 4:
754
+ return _context.a(2);
755
+ }
756
+ }, _callee, null, [[1,, 3, 4]]);
757
+ })));
758
+ this.documentOperationQueue = nextOperation["finally"](function () {
759
+ if (self.documentOperationQueue === nextOperation) {
760
+ self.documentOperationQueue = null;
761
+ }
762
+ });
763
+ return this.documentOperationQueue;
764
+ },
765
+ uploadFiles: function uploadFiles(files) {
766
+ var _this = this;
767
+ return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
768
+ var filesToUpload;
769
+ return _regenerator().w(function (_context3) {
770
+ while (1) switch (_context3.n) {
839
771
  case 0:
840
772
  if (config.uploadDocumentUrl) {
841
- _context.n = 1;
773
+ _context3.n = 1;
842
774
  break;
843
775
  }
844
- return _context.a(2);
776
+ return _context3.a(2);
845
777
  case 1:
846
- sessionId = _this2.getSessionId();
847
- profileId = _this2.getProfileId();
848
- if (!(!sessionId && !profileId)) {
849
- _context.n = 2;
778
+ filesToUpload = Array.isArray(files) ? files.slice() : Array.from(files || []);
779
+ if (!(filesToUpload.length === 0)) {
780
+ _context3.n = 2;
850
781
  break;
851
782
  }
852
- console.warn('Cannot upload documents without a session or profile.');
853
- return _context.a(2);
783
+ return _context3.a(2);
854
784
  case 2:
855
- _this2.isUploading = true;
856
- _this2.uploadErrors = [];
857
- _this2.renderDocumentBar();
858
- _context.p = 3;
859
- formData = new FormData();
860
- if (sessionId) {
861
- formData.append('sessionId', sessionId);
862
- } else {
863
- formData.append('profileId', profileId);
864
- }
865
- for (i = 0; i < files.length; i++) {
866
- formData.append('files', files[i]);
867
- }
868
- _context.n = 4;
869
- return fetch(config.uploadDocumentUrl, {
870
- method: 'POST',
871
- body: formData
872
- });
873
- case 4:
874
- response = _context.v;
875
- if (response.ok) {
876
- _context.n = 6;
877
- break;
878
- }
879
- _context.n = 5;
880
- return response.text();
881
- case 5:
882
- errorText = _context.v;
883
- uploadError = _this2.extractReadableErrorMessage(errorText, 'Upload failed. Please try again.');
884
- console.error('Upload failed:', errorText);
885
- _this2.uploadErrors = [{
886
- fileName: '',
887
- error: uploadError
888
- }];
889
- return _context.a(2);
890
- case 6:
891
- _context.n = 7;
892
- return response.json();
893
- case 7:
894
- result = _context.v;
895
- // If the server created a new session, initialize it.
896
- if (result.sessionId && !sessionId) {
897
- _this2.initializeSession(result.sessionId);
898
- }
899
- if (result.uploaded && result.uploaded.length > 0) {
900
- for (j = 0; j < result.uploaded.length; j++) {
901
- _this2.documents.push(result.uploaded[j]);
902
- }
903
- }
904
- if (result.failed && result.failed.length > 0) {
905
- _this2.uploadErrors = result.failed;
906
- }
907
- _context.n = 9;
908
- break;
909
- case 8:
910
- _context.p = 8;
911
- _t = _context.v;
912
- console.error('Upload error:', _t);
913
- _this2.uploadErrors = [{
914
- fileName: '',
915
- error: 'Upload failed. Please try again.'
916
- }];
917
- case 9:
918
- _context.p = 9;
919
- _this2.isUploading = false;
920
- _this2.renderDocumentBar();
921
- return _context.f(9);
922
- case 10:
923
- return _context.a(2);
785
+ return _context3.a(2, _this.queueDocumentOperation(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
786
+ var sessionId, profileId, formData, i, response, errorText, uploadError, result, _t, _t2;
787
+ return _regenerator().w(function (_context2) {
788
+ while (1) switch (_context2.p = _context2.n) {
789
+ case 0:
790
+ sessionId = _this.getSessionId();
791
+ profileId = _this.getProfileId();
792
+ if (sessionId) {
793
+ _context2.n = 4;
794
+ break;
795
+ }
796
+ _context2.p = 1;
797
+ _context2.n = 2;
798
+ return _this.ensureSessionForDocuments(profileId);
799
+ case 2:
800
+ sessionId = _context2.v;
801
+ _context2.n = 4;
802
+ break;
803
+ case 3:
804
+ _context2.p = 3;
805
+ _t = _context2.v;
806
+ console.error('Failed to create a chat session for document upload:', _t);
807
+ _this.uploadErrors = [{
808
+ fileName: '',
809
+ error: 'Could not create a chat session for the upload.'
810
+ }];
811
+ _this.renderDocumentBar();
812
+ return _context2.a(2);
813
+ case 4:
814
+ if (sessionId) {
815
+ _context2.n = 5;
816
+ break;
817
+ }
818
+ console.warn('Cannot upload documents without a session or profile.');
819
+ _this.uploadErrors = [{
820
+ fileName: '',
821
+ error: 'Could not create a chat session for the upload.'
822
+ }];
823
+ _this.renderDocumentBar();
824
+ return _context2.a(2);
825
+ case 5:
826
+ _this.isUploading = true;
827
+ _this.uploadErrors = [];
828
+ _this.renderDocumentBar();
829
+ _context2.p = 6;
830
+ formData = new FormData();
831
+ formData.append('sessionId', sessionId);
832
+ for (i = 0; i < filesToUpload.length; i++) {
833
+ formData.append('files', filesToUpload[i]);
834
+ }
835
+ _context2.n = 7;
836
+ return fetch(config.uploadDocumentUrl, {
837
+ method: 'POST',
838
+ body: formData
839
+ });
840
+ case 7:
841
+ response = _context2.v;
842
+ if (response.ok) {
843
+ _context2.n = 9;
844
+ break;
845
+ }
846
+ _context2.n = 8;
847
+ return response.text();
848
+ case 8:
849
+ errorText = _context2.v;
850
+ uploadError = _this.extractReadableErrorMessage(errorText, 'Upload failed. Please try again.');
851
+ console.error('Upload failed:', errorText);
852
+ _this.uploadErrors = [{
853
+ fileName: '',
854
+ error: uploadError
855
+ }];
856
+ return _context2.a(2);
857
+ case 9:
858
+ _context2.n = 10;
859
+ return response.json();
860
+ case 10:
861
+ result = _context2.v;
862
+ if (result.sessionId && result.sessionId !== _this.getSessionId()) {
863
+ _this.initializeSession(result.sessionId);
864
+ }
865
+ if (Array.isArray(result.documents)) {
866
+ _this.documents = result.documents;
867
+ } else if (result.uploaded && result.uploaded.length > 0) {
868
+ _this.documents = _this.documents.concat(result.uploaded);
869
+ }
870
+ if (result.failed && result.failed.length > 0) {
871
+ _this.uploadErrors = result.failed;
872
+ }
873
+ _context2.n = 12;
874
+ break;
875
+ case 11:
876
+ _context2.p = 11;
877
+ _t2 = _context2.v;
878
+ console.error('Upload error:', _t2);
879
+ _this.uploadErrors = [{
880
+ fileName: '',
881
+ error: 'Upload failed. Please try again.'
882
+ }];
883
+ if (_this.getSessionId()) {
884
+ _this.reloadCurrentSession();
885
+ }
886
+ case 12:
887
+ _context2.p = 12;
888
+ _this.isUploading = false;
889
+ _this.renderDocumentBar();
890
+ return _context2.f(12);
891
+ case 13:
892
+ return _context2.a(2);
893
+ }
894
+ }, _callee2, null, [[6, 11, 12, 13], [1, 3]]);
895
+ }))));
924
896
  }
925
- }, _callee, null, [[3, 8, 9, 10]]);
897
+ }, _callee3);
926
898
  }))();
927
899
  },
928
900
  removeDocument: function removeDocument(doc) {
929
- var _this3 = this;
930
- return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2() {
931
- var sessionId, response, idx, errorText, removeError, _t2;
932
- return _regenerator().w(function (_context2) {
933
- while (1) switch (_context2.p = _context2.n) {
901
+ var _this2 = this;
902
+ return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
903
+ return _regenerator().w(function (_context5) {
904
+ while (1) switch (_context5.n) {
934
905
  case 0:
935
906
  if (config.removeDocumentUrl) {
936
- _context2.n = 1;
907
+ _context5.n = 1;
937
908
  break;
938
909
  }
939
- return _context2.a(2);
910
+ return _context5.a(2);
940
911
  case 1:
941
- _context2.p = 1;
942
- sessionId = _this3.getSessionId();
943
- _context2.n = 2;
944
- return fetch(config.removeDocumentUrl, {
945
- method: 'POST',
946
- headers: {
947
- 'Content-Type': 'application/json'
948
- },
949
- body: JSON.stringify({
950
- itemId: sessionId,
951
- documentId: doc.documentId
952
- })
953
- });
954
- case 2:
955
- response = _context2.v;
956
- if (!response.ok) {
957
- _context2.n = 3;
958
- break;
959
- }
960
- idx = _this3.documents.indexOf(doc);
961
- if (idx > -1) _this3.documents.splice(idx, 1);
962
- _context2.n = 5;
963
- break;
964
- case 3:
965
- _context2.n = 4;
966
- return response.text();
967
- case 4:
968
- errorText = _context2.v;
969
- removeError = _this3.extractReadableErrorMessage(errorText, 'Failed to remove document. Please try again.');
970
- console.error('Failed to remove document:', response.status, errorText);
971
- _this3.uploadErrors = [{
972
- fileName: doc.fileName || '',
973
- error: removeError
974
- }];
975
- _this3.renderDocumentBar();
976
- case 5:
977
- _context2.n = 7;
978
- break;
979
- case 6:
980
- _context2.p = 6;
981
- _t2 = _context2.v;
982
- console.error('Remove document error:', _t2);
983
- _this3.uploadErrors = [{
984
- fileName: doc.fileName || '',
985
- error: 'Failed to remove document. Please try again.'
986
- }];
987
- _this3.renderDocumentBar();
988
- case 7:
989
- return _context2.a(2);
912
+ return _context5.a(2, _this2.queueDocumentOperation(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
913
+ var sessionId, response, result, idx, errorText, removeError, _t3;
914
+ return _regenerator().w(function (_context4) {
915
+ while (1) switch (_context4.p = _context4.n) {
916
+ case 0:
917
+ _context4.p = 0;
918
+ sessionId = _this2.getSessionId();
919
+ _context4.n = 1;
920
+ return fetch(config.removeDocumentUrl, {
921
+ method: 'POST',
922
+ headers: {
923
+ 'Content-Type': 'application/json'
924
+ },
925
+ body: JSON.stringify({
926
+ itemId: sessionId,
927
+ documentId: doc.documentId
928
+ })
929
+ });
930
+ case 1:
931
+ response = _context4.v;
932
+ if (!response.ok) {
933
+ _context4.n = 3;
934
+ break;
935
+ }
936
+ _context4.n = 2;
937
+ return response.json();
938
+ case 2:
939
+ result = _context4.v;
940
+ if (Array.isArray(result.documents)) {
941
+ _this2.documents = result.documents;
942
+ } else {
943
+ idx = _this2.documents.indexOf(doc);
944
+ if (idx > -1) {
945
+ _this2.documents.splice(idx, 1);
946
+ }
947
+ }
948
+ _context4.n = 5;
949
+ break;
950
+ case 3:
951
+ _context4.n = 4;
952
+ return response.text();
953
+ case 4:
954
+ errorText = _context4.v;
955
+ removeError = _this2.extractReadableErrorMessage(errorText, 'Failed to remove document. Please try again.');
956
+ console.error('Failed to remove document:', response.status, errorText);
957
+ _this2.uploadErrors = [{
958
+ fileName: doc.fileName || '',
959
+ error: removeError
960
+ }];
961
+ if (sessionId) {
962
+ _this2.reloadCurrentSession();
963
+ }
964
+ _this2.renderDocumentBar();
965
+ case 5:
966
+ _context4.n = 7;
967
+ break;
968
+ case 6:
969
+ _context4.p = 6;
970
+ _t3 = _context4.v;
971
+ console.error('Remove document error:', _t3);
972
+ _this2.uploadErrors = [{
973
+ fileName: doc.fileName || '',
974
+ error: 'Failed to remove document. Please try again.'
975
+ }];
976
+ if (_this2.getSessionId()) {
977
+ _this2.reloadCurrentSession();
978
+ }
979
+ _this2.renderDocumentBar();
980
+ case 7:
981
+ return _context4.a(2);
982
+ }
983
+ }, _callee4, null, [[0, 6]]);
984
+ }))));
990
985
  }
991
- }, _callee2, null, [[1, 6]]);
986
+ }, _callee5);
992
987
  }))();
993
988
  },
994
989
  formatFileSize: function formatFileSize(bytes) {
@@ -1010,9 +1005,9 @@ window.openAIChatManager = function () {
1010
1005
  var name = doc.fileName || 'Document';
1011
1006
  if (name.length > 20) name = name.substring(0, 17) + '...';
1012
1007
  html += '<span class="badge bg-secondary bg-opacity-25 text-dark d-inline-flex align-items-center gap-1 px-2 py-1" style="font-size: 0.8rem;" title="' + this.escapeHtml(doc.fileName || '') + '">';
1013
- html += '<span class="fa-solid fa-file-lines" style="font-size: 0.7rem;"></span> ';
1008
+ html += '<i class="fa-solid fa-file-lines" style="font-size: 0.7rem;"></i> ';
1014
1009
  html += this.escapeHtml(name);
1015
- html += ' <button type="button" class="btn-close btn-close-sm ms-1" style="font-size: 0.5rem;" data-doc-index="' + i + '" aria-label="Remove"></button>';
1010
+ html += ' <button type="button" class="btn-close btn-close-sm ms-1" style="font-size: 0.5rem;" data-doc-index="' + i + '" aria-label="Remove"' + (this.isDocumentOperationPending ? ' disabled' : '') + '></button>';
1016
1011
  html += '</span>';
1017
1012
  }
1018
1013
  for (var m = 0; m < this.uploadErrors.length; m++) {
@@ -1021,7 +1016,7 @@ window.openAIChatManager = function () {
1021
1016
  var errorMsg = failedItem.error || 'Upload failed';
1022
1017
  if (failedName.length > 15) failedName = failedName.substring(0, 12) + '...';
1023
1018
  html += '<span class="badge bg-danger bg-opacity-25 text-danger d-inline-flex align-items-center gap-1 px-2 py-1" style="font-size: 0.8rem;" title="' + this.escapeHtml((failedItem.fileName || '') + ': ' + errorMsg) + '">';
1024
- html += '<span class="fa-solid fa-circle-exclamation" style="font-size: 0.7rem;"></span> ';
1019
+ html += '<i class="fa-solid fa-circle-exclamation" style="font-size: 0.7rem;"></i> ';
1025
1020
  html += this.escapeHtml(failedName);
1026
1021
  html += ' <button type="button" class="btn-close btn-close-sm ms-1" style="font-size: 0.5rem;" data-error-index="' + m + '" aria-label="Dismiss"></button>';
1027
1022
  html += '</span>';
@@ -1031,8 +1026,8 @@ window.openAIChatManager = function () {
1031
1026
  html += '<span class="spinner-border spinner-border-sm" style="width: 0.7rem; height: 0.7rem;"></span> Uploading...';
1032
1027
  html += '</span>';
1033
1028
  }
1034
- html += '<button type="button" class="btn btn-sm btn-outline-secondary rounded-pill ai-chat-doc-add-btn d-inline-flex align-items-center gap-1" style="font-size: 0.75rem; padding: 0.15rem 0.5rem;" title="Attach documents">';
1035
- html += '<span class="fa-solid fa-paperclip"></span>';
1029
+ html += '<button type="button" class="btn btn-sm btn-outline-secondary rounded-pill ai-chat-doc-add-btn d-inline-flex align-items-center gap-1" style="font-size: 0.75rem; padding: 0.15rem 0.5rem;" title="Attach documents"' + (this.isDocumentOperationPending ? ' disabled' : '') + '>';
1030
+ html += '<i class="fa-solid fa-paperclip"></i>';
1036
1031
  if (this.documents.length === 0 && !this.isUploading) {
1037
1032
  html += ' <span>Attach files</span>';
1038
1033
  }
@@ -1054,6 +1049,9 @@ window.openAIChatManager = function () {
1054
1049
  return function (e) {
1055
1050
  e.preventDefault();
1056
1051
  e.stopPropagation();
1052
+ if (self.isDocumentOperationPending) {
1053
+ return;
1054
+ }
1057
1055
  var docToRemove = self.documents[idx];
1058
1056
  if (docToRemove) self.removeDocument(docToRemove);
1059
1057
  };
@@ -1078,6 +1076,9 @@ window.openAIChatManager = function () {
1078
1076
  if (addBtn) {
1079
1077
  addBtn.addEventListener('click', function (e) {
1080
1078
  e.preventDefault();
1079
+ if (self.isDocumentOperationPending) {
1080
+ return;
1081
+ }
1081
1082
  self.triggerFileInput();
1082
1083
  });
1083
1084
  }
@@ -1154,139 +1155,135 @@ window.openAIChatManager = function () {
1154
1155
  return !!message && message.isStreaming && index === this.lastAssistantIndex && !(appearance && appearance.disableStreamingAnimation);
1155
1156
  },
1156
1157
  startConnection: function startConnection() {
1157
- var _this4 = this;
1158
- return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
1159
- var _t3;
1160
- return _regenerator().w(function (_context3) {
1161
- while (1) switch (_context3.p = _context3.n) {
1158
+ var _this3 = this;
1159
+ return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
1160
+ var _t4;
1161
+ return _regenerator().w(function (_context6) {
1162
+ while (1) switch (_context6.p = _context6.n) {
1162
1163
  case 0:
1163
- _this4.connection = new signalR.HubConnectionBuilder().withUrl(config.signalRHubUrl).withAutomaticReconnect().build();
1164
+ _this3.connection = new signalR.HubConnectionBuilder().withUrl(config.signalRHubUrl).withAutomaticReconnect().build();
1164
1165
 
1165
1166
  // Allow long-running operations (e.g., multi-step MCP tool calls)
1166
1167
  // without the client disconnecting prematurely.
1167
- _this4.connection.serverTimeoutInMilliseconds = 600000;
1168
- _this4.connection.keepAliveIntervalInMilliseconds = 15000;
1169
- _this4.connection.on("LoadSession", function (data) {
1168
+ _this3.connection.serverTimeoutInMilliseconds = 600000;
1169
+ _this3.connection.keepAliveIntervalInMilliseconds = 15000;
1170
+ _this3.connection.on("LoadSession", function (data) {
1170
1171
  var _data$messages;
1171
- _this4.initializeSession(data.sessionId, true);
1172
- _this4.messages = [];
1173
- _this4.documents = data.documents || [];
1172
+ _this3.initializeSession(data.sessionId, true);
1173
+ _this3.messages = [];
1174
+ _this3.documents = data.documents || [];
1175
+ _this3.uploadErrors = [];
1174
1176
  ((_data$messages = data.messages) !== null && _data$messages !== void 0 ? _data$messages : []).forEach(function (msg) {
1175
- _this4.addMessage(msg);
1176
- _this4.$nextTick(function () {
1177
+ _this3.addMessage(msg);
1178
+ _this3.$nextTick(function () {
1177
1179
  renderChartsInMessage(msg);
1178
1180
  });
1179
1181
  });
1180
1182
 
1181
1183
  // Update feedback icons in the DOM after all messages have rendered.
1182
- _this4.$nextTick(function () {
1183
- _this4.refreshAllFeedbackIcons();
1184
+ _this3.$nextTick(function () {
1185
+ _this3.refreshAllFeedbackIcons();
1184
1186
  });
1185
1187
 
1186
1188
  // When the session is new (no messages) and an initial prompt is configured,
1187
1189
  // automatically send it as the first user message to trigger an AI response.
1188
- if (_this4.messages.length === 0 && config.initialPrompt) {
1189
- _this4.prompt = config.initialPrompt;
1190
- _this4.sendMessage();
1190
+ if (_this3.messages.length === 0 && config.initialPrompt) {
1191
+ _this3.prompt = config.initialPrompt;
1192
+ _this3.sendMessage();
1191
1193
  }
1192
1194
  });
1193
- _this4.connection.on("ReceiveError", function (error) {
1195
+ _this3.connection.on("ReceiveError", function (error) {
1194
1196
  console.error("SignalR Error: ", error);
1195
- if (_this4.isRecording) {
1196
- _this4.stopRecording();
1197
+ if (_this3.isRecording) {
1198
+ _this3.stopRecording();
1197
1199
  }
1198
-
1199
- // If this is a widget with a stale cached session (e.g., profile was deleted),
1200
- // clear the cached session and start fresh with the current profile.
1201
- if (_this4.widgetIsInitialized && !_this4.isSessionStarted && !_this4._attemptedSessionRecovery) {
1202
- _this4._attemptedSessionRecovery = true;
1203
- localStorage.removeItem(_this4.chatWidgetStateSession);
1204
- _this4.startNewSession();
1200
+ if (widgetBehavior && typeof widgetBehavior.handleReceiveError === 'function') {
1201
+ widgetBehavior.handleReceiveError(_this3, error, config);
1205
1202
  }
1206
1203
  });
1207
- _this4.connection.on("MessageRated", function (messageId, userRating) {
1208
- var msg = _this4.messages.find(function (m) {
1204
+ _this3.connection.on("MessageRated", function (messageId, userRating) {
1205
+ var msg = _this3.messages.find(function (m) {
1209
1206
  return m.id === messageId;
1210
1207
  });
1211
1208
  if (msg) {
1212
1209
  msg.userRating = userRating;
1213
1210
  }
1214
1211
  });
1215
- _this4.connection.on("ReceiveTranscript", function (sessionId, text, isFinal) {
1216
- if (_this4.isConversationMode) {
1212
+ _this3.connection.on("ReceiveTranscript", function (sessionId, text, isFinal) {
1213
+ if (_this3.isConversationMode) {
1217
1214
  if (!isFinal && text) {
1218
- _this4._conversationPartialTranscript = text;
1215
+ _this3._conversationPartialTranscript = text;
1219
1216
  var escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1220
1217
  var html = '<p class="ai-partial-transcript">' + escaped + '</p>';
1221
1218
 
1222
1219
  // Show partial transcript as a live user message.
1223
- if (!_this4._conversationPartialMessage) {
1224
- _this4.hidePlaceholder();
1225
- _this4._conversationPartialMessage = {
1220
+ if (!_this3._conversationPartialMessage) {
1221
+ _this3.hidePlaceholder();
1222
+ _this3._conversationPartialMessage = {
1226
1223
  role: 'user',
1227
1224
  content: text,
1228
1225
  htmlContent: html,
1229
1226
  isPartial: true
1230
1227
  };
1231
- _this4.messages.push(_this4._conversationPartialMessage);
1228
+ _this3.messages.push(_this3._conversationPartialMessage);
1232
1229
  } else {
1233
- _this4._conversationPartialMessage.content = text;
1234
- _this4._conversationPartialMessage.htmlContent = html;
1230
+ _this3._conversationPartialMessage.content = text;
1231
+ _this3._conversationPartialMessage.htmlContent = html;
1235
1232
  }
1236
- _this4.scrollToBottom();
1233
+ _this3.scrollToBottom();
1237
1234
  }
1238
1235
  return;
1239
1236
  }
1240
- if (text && !_this4._audioInputSent) {
1241
- _this4.prompt = _this4.preRecordingPrompt + text;
1242
- if (_this4.inputElement) {
1243
- _this4.inputElement.value = _this4.prompt;
1244
- _this4.inputElement.dispatchEvent(new Event('input'));
1237
+ if (text && !_this3._audioInputSent) {
1238
+ _this3.prompt = _this3.preRecordingPrompt + text;
1239
+ if (_this3.inputElement) {
1240
+ _this3.inputElement.value = _this3.prompt;
1241
+ _this3.inputElement.dispatchEvent(new Event('input'));
1245
1242
  }
1246
1243
  }
1247
1244
  });
1248
- _this4.connection.on("ReceiveConversationUserMessage", function (sessionId, text) {
1245
+ _this3.connection.on("ReceiveConversationUserMessage", function (sessionId, text) {
1249
1246
  if (text) {
1250
- _this4.stopAudio();
1247
+ _this3.stopAudio();
1251
1248
 
1252
1249
  // If there's an interrupted assistant message still streaming,
1253
1250
  // mark it as done to stop the spinner animation.
1254
- if (_this4._conversationAssistantMessage) {
1255
- var oldMsg = _this4.messages[_this4._conversationAssistantMessage.index];
1251
+ if (_this3._conversationAssistantMessage) {
1252
+ var oldMsg = _this3.messages[_this3._conversationAssistantMessage.index];
1256
1253
  if (oldMsg) {
1257
1254
  oldMsg.isStreaming = false;
1258
1255
  }
1259
- _this4._conversationAssistantMessage = null;
1256
+ _this3._conversationAssistantMessage = null;
1260
1257
  }
1261
1258
 
1262
1259
  // Replace the partial transcript message with the final one.
1263
- if (_this4._conversationPartialMessage) {
1260
+ if (_this3._conversationPartialMessage) {
1264
1261
  var escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1265
- _this4._conversationPartialMessage.content = text;
1266
- _this4._conversationPartialMessage.htmlContent = '<p>' + escaped + '</p>';
1267
- _this4._conversationPartialMessage.isPartial = false;
1268
- _this4._conversationPartialMessage = null;
1262
+ _this3._conversationPartialMessage.content = text;
1263
+ _this3._conversationPartialMessage.htmlContent = '<p>' + escaped + '</p>';
1264
+ _this3._conversationPartialMessage.isPartial = false;
1265
+ _this3._conversationPartialMessage = null;
1269
1266
  } else {
1270
- _this4.addMessage({
1267
+ _this3.addMessage({
1271
1268
  role: 'user',
1272
1269
  content: text
1273
1270
  });
1274
1271
  }
1275
- _this4.scrollToBottom();
1272
+ _this3.scrollToBottom();
1276
1273
  }
1277
1274
  });
1278
- _this4.connection.on("ReceiveConversationAssistantToken", function (sessionId, messageId, token, responseId, appearance) {
1279
- if (!_this4._conversationAssistantMessage) {
1280
- _this4.stopAudio();
1281
- _this4.hideTypingIndicator();
1275
+ _this3.connection.on("ReceiveConversationAssistantToken", function (sessionId, messageId, token, responseId, references, appearance) {
1276
+ if (!_this3._conversationAssistantMessage) {
1277
+ _this3.stopAudio();
1278
+ _this3.hideTypingIndicator();
1282
1279
 
1283
1280
  // Ensure no stale streaming indicators remain from prior messages.
1284
- for (var j = 0; j < _this4.messages.length; j++) {
1285
- if (_this4.messages[j].isStreaming) {
1286
- _this4.messages[j].isStreaming = false;
1281
+ for (var j = 0; j < _this3.messages.length; j++) {
1282
+ if (_this3.messages[j].isStreaming) {
1283
+ _this3.messages[j].isStreaming = false;
1287
1284
  }
1288
1285
  }
1289
- var msgIndex = _this4.messages.length;
1286
+ var msgIndex = _this3.messages.length;
1290
1287
  var newMessage = {
1291
1288
  id: messageId,
1292
1289
  role: "assistant",
@@ -1294,110 +1291,112 @@ window.openAIChatManager = function () {
1294
1291
  htmlContent: "",
1295
1292
  isStreaming: true,
1296
1293
  userRating: null,
1297
- appearance: _this4.normalizeAssistantAppearance(appearance)
1294
+ appearance: _this3.normalizeAssistantAppearance(appearance),
1295
+ references: {}
1298
1296
  };
1299
- _this4.messages.push(newMessage);
1300
- _this4._conversationAssistantMessage = {
1297
+ _this3.messages.push(newMessage);
1298
+ _this3._conversationAssistantMessage = {
1301
1299
  index: msgIndex,
1302
1300
  content: ''
1303
1301
  };
1304
1302
  }
1305
- _this4._conversationAssistantMessage.content += token;
1306
- var msg = _this4.messages[_this4._conversationAssistantMessage.index];
1303
+ _this3._conversationAssistantMessage.content += token;
1304
+ var msg = _this3.messages[_this3._conversationAssistantMessage.index];
1307
1305
  if (msg) {
1308
1306
  if (!msg.appearance) {
1309
- msg.appearance = _this4.normalizeAssistantAppearance(appearance);
1307
+ msg.appearance = _this3.normalizeAssistantAppearance(appearance);
1310
1308
  }
1311
- msg.content = _this4._conversationAssistantMessage.content;
1312
- msg.htmlContent = parseMarkdownContent(msg.content, msg);
1313
- _this4.$nextTick(function () {
1309
+ msg.rawContent = _this3._conversationAssistantMessage.content;
1310
+ msg.references = normalizeReferences(Object.assign({}, msg.references || {}, references || {}));
1311
+ updateMessagePresentation(msg, msg.references);
1312
+ _this3.$nextTick(function () {
1314
1313
  renderChartsInMessage(msg);
1315
- _this4.scrollToBottom();
1314
+ _this3.scrollToBottom();
1316
1315
  });
1317
1316
  }
1318
1317
  });
1319
- _this4.connection.on("ReceiveConversationAssistantComplete", function (sessionId, messageId) {
1320
- if (_this4._conversationAssistantMessage) {
1321
- var msg = _this4.messages[_this4._conversationAssistantMessage.index];
1318
+ _this3.connection.on("ReceiveConversationAssistantComplete", function (sessionId, messageId, references) {
1319
+ if (_this3._conversationAssistantMessage) {
1320
+ var msg = _this3.messages[_this3._conversationAssistantMessage.index];
1322
1321
  if (msg) {
1323
1322
  msg.isStreaming = false;
1323
+ msg.references = normalizeReferences(Object.assign({}, msg.references || {}, references || {}));
1324
+ updateMessagePresentation(msg, msg.references);
1324
1325
  }
1325
- _this4._conversationAssistantMessage = null;
1326
+ _this3._conversationAssistantMessage = null;
1326
1327
  }
1327
1328
  });
1328
- _this4.connection.on("ReceiveAudioChunk", function (sessionId, base64Audio, contentType) {
1329
+ _this3.connection.on("ReceiveAudioChunk", function (sessionId, base64Audio, contentType) {
1329
1330
  if (base64Audio) {
1330
1331
  var binaryString = atob(base64Audio);
1331
1332
  var bytes = new Uint8Array(binaryString.length);
1332
1333
  for (var i = 0; i < binaryString.length; i++) {
1333
1334
  bytes[i] = binaryString.charCodeAt(i);
1334
1335
  }
1335
- _this4.audioChunks.push(bytes);
1336
+ _this3.audioChunks.push(bytes);
1336
1337
  }
1337
1338
  });
1338
- _this4.connection.on("ReceiveAudioComplete", function (sessionId) {
1339
- _this4.playCollectedAudio();
1339
+ _this3.connection.on("ReceiveAudioComplete", function (sessionId) {
1340
+ _this3.playCollectedAudio();
1340
1341
  });
1341
- _this4.connection.on("ReceiveNotification", function (notification) {
1342
- _this4.receiveNotification(notification);
1342
+ _this3.connection.on("ReceiveNotification", function (notification) {
1343
+ _this3.receiveNotification(notification);
1343
1344
  });
1344
- _this4.connection.on("UpdateNotification", function (notification) {
1345
- _this4.updateNotification(notification);
1345
+ _this3.connection.on("UpdateNotification", function (notification) {
1346
+ _this3.updateNotification(notification);
1346
1347
  });
1347
- _this4.connection.on("RemoveNotification", function (notificationType) {
1348
- _this4.removeNotification(notificationType);
1348
+ _this3.connection.on("RemoveNotification", function (notificationType) {
1349
+ _this3.removeNotification(notificationType);
1349
1350
  });
1350
- _this4.connection.onreconnecting(function () {
1351
+ _this3.connection.onreconnecting(function () {
1351
1352
  console.warn("SignalR: reconnecting...");
1352
1353
  });
1353
- _this4.connection.onreconnected(function () {
1354
+ _this3.connection.onreconnected(function () {
1354
1355
  console.info("SignalR: reconnected.");
1355
- if (_this4.isSessionStarted) {
1356
- _this4.reloadCurrentSession();
1357
- } else if (config.autoCreateSession) {
1358
- _this4.startNewSession();
1356
+ if (_this3.isSessionStarted) {
1357
+ _this3.reloadCurrentSession();
1359
1358
  }
1360
1359
  });
1361
- _this4.connection.onclose(function (error) {
1362
- if (_this4.isNavigatingAway) {
1360
+ _this3.connection.onclose(function (error) {
1361
+ if (_this3.isNavigatingAway) {
1363
1362
  return;
1364
1363
  }
1365
1364
  if (error) {
1366
1365
  console.warn("SignalR connection closed with error:", error.message || error);
1367
1366
  }
1368
1367
  });
1369
- _context3.p = 1;
1370
- _context3.n = 2;
1371
- return _this4.connection.start();
1368
+ _context6.p = 1;
1369
+ _context6.n = 2;
1370
+ return _this3.connection.start();
1372
1371
  case 2:
1373
- _context3.n = 4;
1372
+ _context6.n = 4;
1374
1373
  break;
1375
1374
  case 3:
1376
- _context3.p = 3;
1377
- _t3 = _context3.v;
1378
- console.error("SignalR Connection Error: ", _t3);
1375
+ _context6.p = 3;
1376
+ _t4 = _context6.v;
1377
+ console.error("SignalR Connection Error: ", _t4);
1379
1378
  case 4:
1380
- return _context3.a(2);
1379
+ return _context6.a(2);
1381
1380
  }
1382
- }, _callee3, null, [[1, 3]]);
1381
+ }, _callee6, null, [[1, 3]]);
1383
1382
  }))();
1384
1383
  },
1385
1384
  addMessageInternal: function addMessageInternal(message) {
1386
- var _this5 = this;
1385
+ var _this4 = this;
1387
1386
  if (message.role === 'assistant') {
1388
1387
  message.appearance = this.normalizeAssistantAppearance(message.appearance);
1389
1388
  }
1390
1389
  if (message.content && !message.htmlContent) {
1391
1390
  message.htmlContent = parseMarkdownContent(message.content, message);
1392
1391
  }
1393
- this.fireEvent(new CustomEvent("addingOpenAIPromotMessage", {
1392
+ this.fireEvent(new CustomEvent("addingCoreAIPromotMessage", {
1394
1393
  detail: {
1395
1394
  message: message
1396
1395
  }
1397
1396
  }));
1398
1397
  this.messages.push(message);
1399
1398
  this.$nextTick(function () {
1400
- _this5.fireEvent(new CustomEvent("addedOpenAIPromotMessage", {
1399
+ _this4.fireEvent(new CustomEvent("addedCoreAIPromotMessage", {
1401
1400
  detail: {
1402
1401
  message: message
1403
1402
  }
@@ -1405,105 +1404,32 @@ window.openAIChatManager = function () {
1405
1404
  });
1406
1405
  },
1407
1406
  addMessage: function addMessage(message) {
1408
- var _this6 = this;
1407
+ var _this5 = this;
1409
1408
  // Ensure userRating is always defined for Vue reactivity.
1410
1409
  if (message.userRating === undefined) {
1411
1410
  message.userRating = null;
1412
1411
  }
1413
1412
  if (message.content) {
1414
- var processedContent = message.content.trim();
1415
- message.references = normalizeReferences(message.references);
1416
- if (message.references && _typeof(message.references) === "object" && Object.keys(message.references).length) {
1417
- // Only include references that were actually cited in the response.
1418
- var citedRefs = Object.entries(message.references).filter(function (_ref4) {
1419
- var _ref5 = _slicedToArray(_ref4, 1),
1420
- key = _ref5[0];
1421
- return processedContent.includes(key);
1422
- });
1423
- if (citedRefs.length) {
1424
- // Sort by original index so display indices follow a natural order.
1425
- citedRefs.sort(function (_ref6, _ref7) {
1426
- var _ref8 = _slicedToArray(_ref6, 2),
1427
- a = _ref8[1];
1428
- var _ref9 = _slicedToArray(_ref7, 2),
1429
- b = _ref9[1];
1430
- return a.index - b.index;
1431
- });
1432
-
1433
- // Phase 1: Replace all markers with unique placeholders.
1434
- var displayIndex = 1;
1435
- var _iterator2 = _createForOfIteratorHelper(citedRefs),
1436
- _step2;
1437
- try {
1438
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
1439
- var _step2$value = _slicedToArray(_step2.value, 2),
1440
- key = _step2$value[0],
1441
- value = _step2$value[1];
1442
- var placeholder = "__CITE_".concat(value.index, "__");
1443
- processedContent = processedContent.replaceAll(key, placeholder);
1444
- value._displayIndex = displayIndex++;
1445
- value._placeholder = placeholder;
1446
- }
1447
-
1448
- // Phase 2: Replace placeholders with sequential display indices.
1449
- } catch (err) {
1450
- _iterator2.e(err);
1451
- } finally {
1452
- _iterator2.f();
1453
- }
1454
- var _iterator3 = _createForOfIteratorHelper(citedRefs),
1455
- _step3;
1456
- try {
1457
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
1458
- var _step3$value = _slicedToArray(_step3.value, 2),
1459
- _value = _step3$value[1];
1460
- processedContent = processedContent.replaceAll(_value._placeholder, "<sup><strong>".concat(_value._displayIndex, "</strong></sup>"));
1461
- }
1462
-
1463
- // if we have multiple references, add a comma to ensure we don't concatenate numbers.
1464
- } catch (err) {
1465
- _iterator3.e(err);
1466
- } finally {
1467
- _iterator3.f();
1468
- }
1469
- processedContent = processedContent.replaceAll('</strong></sup><sup>', '</strong></sup><sup>,</sup><sup>');
1470
- processedContent += '<br><br>';
1471
- var _iterator4 = _createForOfIteratorHelper(citedRefs),
1472
- _step4;
1473
- try {
1474
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
1475
- var _step4$value = _slicedToArray(_step4.value, 2),
1476
- _key = _step4$value[0],
1477
- _value2 = _step4$value[1];
1478
- var label = _value2.text || _key;
1479
- processedContent += _value2.link ? "**".concat(_value2._displayIndex, "**. [").concat(label, "](").concat(_value2.link, ")<br>") : "**".concat(_value2._displayIndex, "**. ").concat(label, "<br>");
1480
- }
1481
- } catch (err) {
1482
- _iterator4.e(err);
1483
- } finally {
1484
- _iterator4.f();
1485
- }
1486
- }
1487
- }
1488
- message.content = processedContent;
1489
- message.htmlContent = parseMarkdownContent(processedContent, message);
1413
+ var _message$rawContent;
1414
+ message.rawContent = (_message$rawContent = message.rawContent) !== null && _message$rawContent !== void 0 ? _message$rawContent : message.content;
1415
+ updateMessagePresentation(message, message.references);
1490
1416
  }
1491
1417
  this.addMessageInternal(message);
1492
1418
  this.hidePlaceholder();
1493
1419
  this.$nextTick(function () {
1494
1420
  // Render any pending charts once the DOM is updated
1495
1421
  renderChartsInMessage(message);
1496
- _this6.scrollToBottom();
1422
+ _this5.scrollToBottom();
1497
1423
  });
1498
1424
  },
1499
1425
  addMessages: function addMessages(messages) {
1500
- var _this7 = this;
1426
+ var _this6 = this;
1501
1427
  for (var i = 0; i < messages.length; i++) {
1502
1428
  this.addMessageInternal(messages[i]);
1503
1429
  }
1504
1430
  this.hidePlaceholder();
1505
1431
  this.$nextTick(function () {
1506
- _this7.scrollToBottom();
1432
+ _this6.scrollToBottom();
1507
1433
  });
1508
1434
  },
1509
1435
  hidePlaceholder: function hidePlaceholder() {
@@ -1580,7 +1506,7 @@ window.openAIChatManager = function () {
1580
1506
  this.prompt = '';
1581
1507
  },
1582
1508
  startRecording: function startRecording() {
1583
- var _this8 = this;
1509
+ var _this7 = this;
1584
1510
  if (this.isRecording || !this.connection) {
1585
1511
  return;
1586
1512
  }
@@ -1592,27 +1518,27 @@ window.openAIChatManager = function () {
1592
1518
  }
1593
1519
  }).then(function (stream) {
1594
1520
  var mimeType = MediaRecorder.isTypeSupported('audio/ogg;codecs=opus') ? 'audio/ogg;codecs=opus' : MediaRecorder.isTypeSupported('audio/webm;codecs=opus') ? 'audio/webm;codecs=opus' : 'audio/webm';
1595
- _this8.mediaRecorder = new MediaRecorder(stream, {
1521
+ _this7.mediaRecorder = new MediaRecorder(stream, {
1596
1522
  mimeType: mimeType,
1597
1523
  audioBitsPerSecond: 128000
1598
1524
  });
1599
- _this8.preRecordingPrompt = _this8.prompt;
1600
- _this8._audioInputSent = false;
1525
+ _this7.preRecordingPrompt = _this7.prompt;
1526
+ _this7._audioInputSent = false;
1601
1527
  var subject = new signalR.Subject();
1602
- var profileId = _this8.getProfileId();
1603
- var sessionId = _this8.getSessionId() || '';
1528
+ var profileId = _this7.getProfileId();
1529
+ var sessionId = _this7.getSessionId() || '';
1604
1530
  var pendingChunk = Promise.resolve();
1605
- _this8.mediaRecorder.addEventListener('dataavailable', function (e) {
1531
+ _this7.mediaRecorder.addEventListener('dataavailable', function (e) {
1606
1532
  if (e.data && e.data.size > 0) {
1607
- pendingChunk = pendingChunk.then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
1533
+ pendingChunk = pendingChunk.then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
1608
1534
  var data, uint8Array, binaryString, base64;
1609
- return _regenerator().w(function (_context4) {
1610
- while (1) switch (_context4.n) {
1535
+ return _regenerator().w(function (_context7) {
1536
+ while (1) switch (_context7.n) {
1611
1537
  case 0:
1612
- _context4.n = 1;
1538
+ _context7.n = 1;
1613
1539
  return e.data.arrayBuffer();
1614
1540
  case 1:
1615
- data = _context4.v;
1541
+ data = _context7.v;
1616
1542
  uint8Array = new Uint8Array(data);
1617
1543
  binaryString = uint8Array.reduce(function (str, _byte) {
1618
1544
  return str + String.fromCharCode(_byte);
@@ -1620,13 +1546,13 @@ window.openAIChatManager = function () {
1620
1546
  base64 = btoa(binaryString);
1621
1547
  subject.next(base64);
1622
1548
  case 2:
1623
- return _context4.a(2);
1549
+ return _context7.a(2);
1624
1550
  }
1625
- }, _callee4);
1551
+ }, _callee7);
1626
1552
  })));
1627
1553
  }
1628
1554
  });
1629
- _this8.mediaRecorder.addEventListener('stop', function () {
1555
+ _this7.mediaRecorder.addEventListener('stop', function () {
1630
1556
  stream.getTracks().forEach(function (track) {
1631
1557
  return track.stop();
1632
1558
  });
@@ -1635,10 +1561,10 @@ window.openAIChatManager = function () {
1635
1561
  });
1636
1562
  });
1637
1563
  var language = navigator.language || document.documentElement.lang || 'en-US';
1638
- _this8.connection.send("SendAudioStream", profileId, sessionId, subject, mimeType, language);
1639
- _this8.mediaRecorder.start(250);
1640
- _this8.isRecording = true;
1641
- _this8.updateMicButton();
1564
+ _this7.connection.send("SendAudioStream", profileId, sessionId, subject, mimeType, language);
1565
+ _this7.mediaRecorder.start(250);
1566
+ _this7.isRecording = true;
1567
+ _this7.updateMicButton();
1642
1568
  })["catch"](function (err) {
1643
1569
  console.error('Microphone access denied:', err);
1644
1570
  });
@@ -1664,14 +1590,14 @@ window.openAIChatManager = function () {
1664
1590
  }
1665
1591
  if (this.isRecording) {
1666
1592
  this.micButton.classList.add('stt-recording');
1667
- this.micButton.innerHTML = '<span class="fa-solid fa-stop"></span>';
1593
+ this.micButton.innerHTML = '<i class="fa-solid fa-stop"></i>';
1668
1594
  } else {
1669
1595
  this.micButton.classList.remove('stt-recording');
1670
- this.micButton.innerHTML = '<span class="fa-solid fa-microphone"></span>';
1596
+ this.micButton.innerHTML = '<i class="fa-solid fa-microphone"></i>';
1671
1597
  }
1672
1598
  },
1673
1599
  streamMessage: function streamMessage(profileId, trimmedPrompt, sessionProfileId) {
1674
- var _this9 = this;
1600
+ var _this8 = this;
1675
1601
  if (this.stream) {
1676
1602
  this.stream.dispose();
1677
1603
  this.stream = null;
@@ -1688,14 +1614,14 @@ window.openAIChatManager = function () {
1688
1614
  var currentSessionId = this.getSessionId();
1689
1615
  this.stream = this.connection.stream("SendMessage", profileId, trimmedPrompt, currentSessionId, sessionProfileId).subscribe({
1690
1616
  next: function next(chunk) {
1691
- var message = _this9.messages[messageIndex];
1617
+ var message = _this8.messages[messageIndex];
1692
1618
  if (!message) {
1693
1619
  if (chunk.sessionId && !currentSessionId) {
1694
- _this9.initializeSession(chunk.sessionId);
1620
+ _this8.initializeSession(chunk.sessionId);
1695
1621
  }
1696
- _this9.hideTypingIndicator();
1622
+ _this8.hideTypingIndicator();
1697
1623
  // Re-assign the index after hiding the typing indicator.
1698
- messageIndex = _this9.messages.length;
1624
+ messageIndex = _this8.messages.length;
1699
1625
  var newMessage = {
1700
1626
  id: chunk.messageId,
1701
1627
  role: "assistant",
@@ -1705,16 +1631,16 @@ window.openAIChatManager = function () {
1705
1631
  isStreaming: true,
1706
1632
  userRating: null
1707
1633
  };
1708
- _this9.messages.push(newMessage);
1634
+ _this8.messages.push(newMessage);
1709
1635
  message = newMessage;
1710
1636
  }
1711
1637
  if (chunk.title && (!message.title || message.title !== chunk.title)) {
1712
1638
  message.title = chunk.title;
1713
1639
  }
1714
1640
  if (chunk.references && _typeof(chunk.references) === "object" && Object.keys(chunk.references).length) {
1715
- for (var _i3 = 0, _Object$entries2 = Object.entries(chunk.references); _i3 < _Object$entries2.length; _i3++) {
1641
+ for (var _i4 = 0, _Object$entries2 = Object.entries(chunk.references); _i4 < _Object$entries2.length; _i4++) {
1716
1642
  var _normalizeReference2;
1717
- var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i3], 2),
1643
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i4], 2),
1718
1644
  key = _Object$entries2$_i[0],
1719
1645
  value = _Object$entries2$_i[1];
1720
1646
  references[key] = (_normalizeReference2 = normalizeReference(value)) !== null && _normalizeReference2 !== void 0 ? _normalizeReference2 : {};
@@ -1729,62 +1655,52 @@ window.openAIChatManager = function () {
1729
1655
  if (chunk.responseId) {
1730
1656
  lastResponseId = chunk.responseId;
1731
1657
  }
1732
- var processedContent = chunk.content;
1733
- for (var _i4 = 0, _Object$entries3 = Object.entries(references); _i4 < _Object$entries3.length; _i4++) {
1734
- var _Object$entries3$_i = _slicedToArray(_Object$entries3[_i4], 2),
1735
- _key2 = _Object$entries3$_i[0],
1736
- _value3 = _Object$entries3$_i[1];
1737
- processedContent = processedContent.replaceAll(_key2, "<sup><strong>".concat(_value3.index, "</strong></sup>"));
1738
- }
1739
-
1740
- // Append processed content to the message.
1741
- // if we have multiple references, add a comma to ensure we don't concatenate numbers.
1742
- content += processedContent.replaceAll('</strong></sup><sup>', '</strong></sup><sup>,</sup><sup>');
1658
+ content += chunk.content;
1743
1659
  }
1744
1660
 
1745
1661
  // Update the existing message
1746
- message.content = content;
1747
- message.htmlContent = parseMarkdownContent(content, message);
1748
- _this9.messages[messageIndex] = message;
1749
- _this9.$nextTick(function () {
1662
+ message.rawContent = content;
1663
+ updateMessagePresentation(message, references);
1664
+ _this8.messages[messageIndex] = message;
1665
+ _this8.$nextTick(function () {
1750
1666
  renderChartsInMessage(message);
1751
- _this9.scrollToBottom();
1667
+ _this8.scrollToBottom();
1752
1668
  });
1753
1669
  },
1754
1670
  complete: function complete() {
1755
- var _this9$stream;
1756
- _this9.processReferences(references, messageIndex);
1757
- _this9.streamingFinished();
1758
- var msg = _this9.messages[messageIndex];
1671
+ var _this8$stream;
1672
+ _this8.processReferences(references, messageIndex);
1673
+ _this8.streamingFinished();
1674
+ var msg = _this8.messages[messageIndex];
1759
1675
  if (msg) {
1760
1676
  msg.isStreaming = false;
1761
1677
  }
1762
1678
  if (!msg || !msg.content) {
1763
1679
  // No content received at all.
1764
- _this9.hideTypingIndicator();
1680
+ _this8.hideTypingIndicator();
1765
1681
  }
1766
1682
 
1767
1683
  // Trigger text-to-speech only in conversation mode.
1768
- if (_this9.isConversationMode && _this9.textToSpeechEnabled && msg && msg.content) {
1769
- _this9.synthesizeSpeech(msg.content);
1684
+ if (_this8.isConversationMode && _this8.textToSpeechEnabled && msg && msg.content) {
1685
+ _this8.synthesizeSpeech(msg.content);
1770
1686
  }
1771
- (_this9$stream = _this9.stream) === null || _this9$stream === void 0 || _this9$stream.dispose();
1772
- _this9.stream = null;
1687
+ (_this8$stream = _this8.stream) === null || _this8$stream === void 0 || _this8$stream.dispose();
1688
+ _this8.stream = null;
1773
1689
  },
1774
1690
  error: function error(err) {
1775
- var _this9$stream2;
1776
- _this9.processReferences(references, messageIndex);
1777
- _this9.streamingFinished();
1778
- var msg = _this9.messages[messageIndex];
1691
+ var _this8$stream2;
1692
+ _this8.processReferences(references, messageIndex);
1693
+ _this8.streamingFinished();
1694
+ var msg = _this8.messages[messageIndex];
1779
1695
  if (msg) {
1780
1696
  msg.isStreaming = false;
1781
1697
  }
1782
- _this9.hideTypingIndicator();
1783
- if (!_this9.isNavigatingAway) {
1784
- _this9.addMessage(_this9.getServiceDownMessage());
1698
+ _this8.hideTypingIndicator();
1699
+ if (!_this8.isNavigatingAway) {
1700
+ _this8.addMessage(_this8.getServiceDownMessage());
1785
1701
  }
1786
- (_this9$stream2 = _this9.stream) === null || _this9$stream2 === void 0 || _this9$stream2.dispose();
1787
- _this9.stream = null;
1702
+ (_this8$stream2 = _this8.stream) === null || _this8$stream2 === void 0 || _this8$stream2.dispose();
1703
+ _this8.stream = null;
1788
1704
  console.error("Stream error:", err);
1789
1705
  }
1790
1706
  });
@@ -1798,92 +1714,17 @@ window.openAIChatManager = function () {
1798
1714
  return newMessage;
1799
1715
  },
1800
1716
  processReferences: function processReferences(references, messageIndex) {
1801
- var _this0 = this;
1717
+ var _this9 = this;
1802
1718
  references = normalizeReferences(references);
1803
1719
  if (Object.keys(references).length) {
1720
+ var _ref23, _message$rawContent2;
1804
1721
  var message = this.messages[messageIndex];
1805
- var content = message.content || '';
1806
-
1807
- // Only include references that were actually cited in the response.
1808
- // Check both raw [doc:N] markers and already-rendered <sup> tags from streaming.
1809
- var citedRefs = Object.entries(references).filter(function (_ref1) {
1810
- var _ref10 = _slicedToArray(_ref1, 2),
1811
- key = _ref10[0],
1812
- value = _ref10[1];
1813
- return content.includes(key) || content.includes("<sup><strong>".concat(value.index, "</strong></sup>"));
1814
- });
1815
- if (!citedRefs.length) {
1816
- return;
1817
- }
1818
-
1819
- // Sort by original index so display indices follow a natural order.
1820
- citedRefs.sort(function (_ref11, _ref12) {
1821
- var _ref13 = _slicedToArray(_ref11, 2),
1822
- a = _ref13[1];
1823
- var _ref14 = _slicedToArray(_ref12, 2),
1824
- b = _ref14[1];
1825
- return a.index - b.index;
1826
- });
1827
-
1828
- // Phase 1: Replace all markers with unique placeholders to avoid collisions during remapping.
1829
- var processed = content.trim();
1830
- var displayIndex = 1;
1831
- var _iterator5 = _createForOfIteratorHelper(citedRefs),
1832
- _step5;
1833
- try {
1834
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
1835
- var _step5$value = _slicedToArray(_step5.value, 2),
1836
- key = _step5$value[0],
1837
- value = _step5$value[1];
1838
- var placeholder = "__CITE_".concat(value.index, "__");
1839
- processed = processed.replaceAll(key, placeholder);
1840
- processed = processed.replaceAll("<sup><strong>".concat(value.index, "</strong></sup>"), placeholder);
1841
- value._displayIndex = displayIndex++;
1842
- value._placeholder = placeholder;
1843
- }
1844
-
1845
- // Phase 2: Replace placeholders with sequential display indices.
1846
- } catch (err) {
1847
- _iterator5.e(err);
1848
- } finally {
1849
- _iterator5.f();
1850
- }
1851
- var _iterator6 = _createForOfIteratorHelper(citedRefs),
1852
- _step6;
1853
- try {
1854
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
1855
- var _step6$value = _slicedToArray(_step6.value, 2),
1856
- _value4 = _step6$value[1];
1857
- processed = processed.replaceAll(_value4._placeholder, "<sup><strong>".concat(_value4._displayIndex, "</strong></sup>"));
1858
- }
1859
- } catch (err) {
1860
- _iterator6.e(err);
1861
- } finally {
1862
- _iterator6.f();
1863
- }
1864
- processed = processed.replaceAll('</strong></sup><sup>', '</strong></sup><sup>,</sup><sup>');
1865
- processed += '<br><br>';
1866
- var _iterator7 = _createForOfIteratorHelper(citedRefs),
1867
- _step7;
1868
- try {
1869
- for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
1870
- var _step7$value = _slicedToArray(_step7.value, 2),
1871
- _key3 = _step7$value[0],
1872
- _value5 = _step7$value[1];
1873
- var label = _value5.text || _key3;
1874
- processed += _value5.link ? "**".concat(_value5._displayIndex, "**. [").concat(label, "](").concat(_value5.link, ")<br>") : "**".concat(_value5._displayIndex, "**. ").concat(label, "<br>");
1875
- }
1876
- } catch (err) {
1877
- _iterator7.e(err);
1878
- } finally {
1879
- _iterator7.f();
1880
- }
1881
- message.content = processed;
1882
- message.htmlContent = parseMarkdownContent(processed, message);
1722
+ message.rawContent = (_ref23 = (_message$rawContent2 = message.rawContent) !== null && _message$rawContent2 !== void 0 ? _message$rawContent2 : message.content) !== null && _ref23 !== void 0 ? _ref23 : '';
1723
+ updateMessagePresentation(message, references);
1883
1724
  this.messages[messageIndex] = message;
1884
1725
  this.$nextTick(function () {
1885
1726
  renderChartsInMessage(message);
1886
- _this0.scrollToBottom();
1727
+ _this9.scrollToBottom();
1887
1728
  });
1888
1729
  }
1889
1730
  },
@@ -1934,22 +1775,48 @@ window.openAIChatManager = function () {
1934
1775
  this.stopAudio(false);
1935
1776
  },
1936
1777
  updateTtsPlaybackButtons: function updateTtsPlaybackButtons() {
1937
- var _this1 = this;
1778
+ var _this0 = this;
1938
1779
  if (!this.chatContainer) {
1939
1780
  return;
1940
1781
  }
1941
1782
  var buttons = this.chatContainer.querySelectorAll('[data-tts-message-index]');
1942
1783
  buttons.forEach(function (button) {
1943
1784
  var buttonIndex = Number(button.getAttribute('data-tts-message-index'));
1944
- var isPlaying = buttonIndex === _this1.ttsPlayingMessageIndex;
1945
- var iconHtml = isPlaying ? '<span class="fa-solid fa-circle-pause"></span>' : '<span class="fa-solid fa-circle-play"></span>';
1785
+ var isPlaying = buttonIndex === _this0.ttsPlayingMessageIndex;
1946
1786
  button.classList.toggle('tts-playing', isPlaying);
1947
1787
  button.setAttribute('title', isPlaying ? 'Pause audio' : 'Read aloud');
1788
+ });
1789
+ },
1790
+ updateCopyButtons: function updateCopyButtons() {
1791
+ var _this1 = this;
1792
+ if (!this.chatContainer) {
1793
+ return;
1794
+ }
1795
+ var buttons = this.chatContainer.querySelectorAll('[data-copy-message-index]');
1796
+ buttons.forEach(function (button) {
1797
+ var buttonIndex = Number(button.getAttribute('data-copy-message-index'));
1798
+ var isCopied = buttonIndex === _this1.copiedMessageIndex;
1799
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1800
+ button.classList.toggle('text-success', isCopied);
1801
+ button.classList.toggle('text-secondary', !isCopied);
1802
+ button.setAttribute('title', isCopied ? _this1.copiedTitle : _this1.copyTitle);
1948
1803
  button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1949
1804
  RETURN_DOM_FRAGMENT: true
1950
1805
  }));
1951
1806
  });
1952
1807
  },
1808
+ setCopyButtonState: function setCopyButtonState(button, isCopied) {
1809
+ if (!button) {
1810
+ return;
1811
+ }
1812
+ var iconHtml = isCopied ? '<i class="fa-solid fa-check"></i>' : '<i class="fa-solid fa-copy"></i>';
1813
+ button.classList.toggle('text-success', isCopied);
1814
+ button.classList.toggle('text-secondary', !isCopied);
1815
+ button.setAttribute('title', isCopied ? this.copiedTitle : this.copyTitle);
1816
+ button.replaceChildren(DOMPurify.sanitize(iconHtml, {
1817
+ RETURN_DOM_FRAGMENT: true
1818
+ }));
1819
+ },
1953
1820
  synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1954
1821
  var _this10 = this;
1955
1822
  if (!this.textToSpeechEnabled || !text || !this.connection) {
@@ -2007,18 +1874,18 @@ window.openAIChatManager = function () {
2007
1874
  }, 0);
2008
1875
  var combined = new Uint8Array(totalLength);
2009
1876
  var offset = 0;
2010
- var _iterator8 = _createForOfIteratorHelper(this.audioChunks),
2011
- _step8;
1877
+ var _iterator6 = _createForOfIteratorHelper(this.audioChunks),
1878
+ _step6;
2012
1879
  try {
2013
- for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
2014
- var chunk = _step8.value;
1880
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
1881
+ var chunk = _step6.value;
2015
1882
  combined.set(chunk, offset);
2016
1883
  offset += chunk.length;
2017
1884
  }
2018
1885
  } catch (err) {
2019
- _iterator8.e(err);
1886
+ _iterator6.e(err);
2020
1887
  } finally {
2021
- _iterator8.f();
1888
+ _iterator6.f();
2022
1889
  }
2023
1890
  this.audioChunks = [];
2024
1891
  var blob = new Blob([combined], {
@@ -2173,15 +2040,15 @@ window.openAIChatManager = function () {
2173
2040
  // Always send audio to STT — browser echo cancellation
2174
2041
  // handles speaker echo; continuous audio avoids gaps
2175
2042
  // that increase recognition latency.
2176
- pendingChunk = pendingChunk.then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
2043
+ pendingChunk = pendingChunk.then(/*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
2177
2044
  var data, uint8Array, binaryString, base64;
2178
- return _regenerator().w(function (_context5) {
2179
- while (1) switch (_context5.n) {
2045
+ return _regenerator().w(function (_context8) {
2046
+ while (1) switch (_context8.n) {
2180
2047
  case 0:
2181
- _context5.n = 1;
2048
+ _context8.n = 1;
2182
2049
  return e.data.arrayBuffer();
2183
2050
  case 1:
2184
- data = _context5.v;
2051
+ data = _context8.v;
2185
2052
  uint8Array = new Uint8Array(data);
2186
2053
  binaryString = uint8Array.reduce(function (str, _byte2) {
2187
2054
  return str + String.fromCharCode(_byte2);
@@ -2193,9 +2060,9 @@ window.openAIChatManager = function () {
2193
2060
  // Subject may have been completed already.
2194
2061
  }
2195
2062
  case 2:
2196
- return _context5.a(2);
2063
+ return _context8.a(2);
2197
2064
  }
2198
- }, _callee5);
2065
+ }, _callee8);
2199
2066
  })));
2200
2067
  }
2201
2068
  });
@@ -2426,26 +2293,25 @@ window.openAIChatManager = function () {
2426
2293
  this.prompt = event.target.value;
2427
2294
  },
2428
2295
  getProfileId: function getProfileId() {
2429
- return this.inputElement.getAttribute('data-profile-id');
2296
+ return this.inputElement ? this.inputElement.getAttribute('data-profile-id') : null;
2430
2297
  },
2431
2298
  setSessionId: function setSessionId(sessionId) {
2299
+ if (!this.inputElement) {
2300
+ return;
2301
+ }
2432
2302
  this.inputElement.setAttribute('data-session-id', sessionId || '');
2433
2303
  },
2434
2304
  resetSession: function resetSession() {
2435
2305
  this.stopRecording();
2306
+ this.rejectPendingSessionRequest('Session was reset.');
2436
2307
  this.setSessionId('');
2437
2308
  this.isSessionStarted = false;
2438
2309
  this.sessionRating = null;
2439
- if (this.widgetIsInitialized) {
2440
- localStorage.removeItem(this.chatWidgetStateSession);
2441
- }
2442
2310
  this.messages = [];
2443
2311
  this.documents = [];
2444
- if (!config.autoCreateSession) {
2445
- this.showPlaceholder();
2446
- }
2447
- if (config.autoCreateSession) {
2448
- this.startNewSession();
2312
+ this.showPlaceholder();
2313
+ if (widgetBehavior && typeof widgetBehavior.onSessionReset === 'function') {
2314
+ widgetBehavior.onSessionReset(this, config);
2449
2315
  }
2450
2316
  },
2451
2317
  startNewSession: function startNewSession() {
@@ -2456,22 +2322,98 @@ window.openAIChatManager = function () {
2456
2322
  if (!profileId || !this.connection) {
2457
2323
  return;
2458
2324
  }
2459
- this.connection.invoke("StartSession", profileId, null)["catch"](function (err) {
2325
+ this.requestNewSession(profileId)["catch"](function (err) {
2460
2326
  return console.error(err);
2461
2327
  });
2462
2328
  },
2463
- initializeApp: function initializeApp() {
2329
+ ensureSessionForDocuments: function ensureSessionForDocuments(profileId) {
2464
2330
  var _this20 = this;
2331
+ return _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2332
+ var sessionId;
2333
+ return _regenerator().w(function (_context9) {
2334
+ while (1) switch (_context9.n) {
2335
+ case 0:
2336
+ sessionId = _this20.getSessionId();
2337
+ if (!sessionId) {
2338
+ _context9.n = 1;
2339
+ break;
2340
+ }
2341
+ return _context9.a(2, sessionId);
2342
+ case 1:
2343
+ if (!(!profileId || !_this20.connection)) {
2344
+ _context9.n = 2;
2345
+ break;
2346
+ }
2347
+ return _context9.a(2, null);
2348
+ case 2:
2349
+ _context9.n = 3;
2350
+ return _this20.requestNewSession(profileId);
2351
+ case 3:
2352
+ return _context9.a(2, _context9.v);
2353
+ }
2354
+ }, _callee9);
2355
+ }))();
2356
+ },
2357
+ requestNewSession: function requestNewSession(profileId) {
2358
+ var _this21 = this;
2359
+ if (this.pendingSessionPromise) {
2360
+ return this.pendingSessionPromise;
2361
+ }
2362
+ if (!profileId || !this.connection) {
2363
+ return Promise.resolve(null);
2364
+ }
2365
+ this.pendingSessionPromise = new Promise(function (resolve, reject) {
2366
+ _this21.pendingSessionResolver = resolve;
2367
+ _this21.pendingSessionRejector = reject;
2368
+ _this21.pendingSessionTimeoutId = window.setTimeout(function () {
2369
+ _this21.rejectPendingSessionRequest('Timed out while creating a chat session.');
2370
+ }, 15000);
2371
+ });
2372
+ this.connection.invoke("StartSession", profileId, null)["catch"](function (err) {
2373
+ _this21.rejectPendingSessionRequest(err);
2374
+ });
2375
+ return this.pendingSessionPromise;
2376
+ },
2377
+ resolvePendingSessionRequest: function resolvePendingSessionRequest(sessionId) {
2378
+ if (this.pendingSessionResolver) {
2379
+ this.pendingSessionResolver(sessionId);
2380
+ }
2381
+ this.clearPendingSessionRequest();
2382
+ },
2383
+ rejectPendingSessionRequest: function rejectPendingSessionRequest(error) {
2384
+ if (this.pendingSessionRejector) {
2385
+ this.pendingSessionRejector(error);
2386
+ }
2387
+ this.clearPendingSessionRequest();
2388
+ },
2389
+ clearPendingSessionRequest: function clearPendingSessionRequest() {
2390
+ if (this.pendingSessionTimeoutId) {
2391
+ window.clearTimeout(this.pendingSessionTimeoutId);
2392
+ }
2393
+ this.pendingSessionPromise = null;
2394
+ this.pendingSessionResolver = null;
2395
+ this.pendingSessionRejector = null;
2396
+ this.pendingSessionTimeoutId = null;
2397
+ },
2398
+ initializeApp: function initializeApp() {
2399
+ var _this22 = this;
2465
2400
  this.inputElement = document.querySelector(config.inputElementSelector);
2466
2401
  this.buttonElement = document.querySelector(config.sendButtonElementSelector);
2467
2402
  this.chatContainer = document.querySelector(config.chatContainerElementSelector);
2468
2403
  this.placeholder = document.querySelector(config.placeholderElementSelector);
2404
+ if (!this.inputElement || !this.buttonElement || !this.chatContainer || !this.placeholder) {
2405
+ console.error('AI chat app could not initialize because one or more required elements were not found.', {
2406
+ inputElementSelector: config.inputElementSelector,
2407
+ sendButtonElementSelector: config.sendButtonElementSelector,
2408
+ chatContainerElementSelector: config.chatContainerElementSelector,
2409
+ placeholderElementSelector: config.placeholderElementSelector
2410
+ });
2411
+ return false;
2412
+ }
2469
2413
  this.applyOrchestratorAvailability();
2470
2414
  var sessionId = this.getSessionId();
2471
2415
  if (!hasWidgetConfig && sessionId) {
2472
2416
  this.loadSession(sessionId);
2473
- } else if (this.isOrchestratorAvailable() && config.autoCreateSession && !hasWidgetConfig && !sessionId) {
2474
- this.startNewSession();
2475
2417
  }
2476
2418
 
2477
2419
  // Initialize document bar if enabled.
@@ -2490,7 +2432,7 @@ window.openAIChatManager = function () {
2490
2432
  fileInput.accept = config.allowedExtensions;
2491
2433
  }
2492
2434
  fileInput.addEventListener('change', function (e) {
2493
- return _this20.handleFileInputChange(e);
2435
+ return _this22.handleFileInputChange(e);
2494
2436
  });
2495
2437
  this.documentBar.parentElement.appendChild(fileInput);
2496
2438
 
@@ -2498,13 +2440,13 @@ window.openAIChatManager = function () {
2498
2440
  var inputArea = this.inputElement ? this.inputElement.closest('.ai-admin-widget-input, .text-bg-light') : null;
2499
2441
  if (inputArea) {
2500
2442
  inputArea.addEventListener('dragover', function (e) {
2501
- return _this20.handleDragOver(e);
2443
+ return _this22.handleDragOver(e);
2502
2444
  });
2503
2445
  inputArea.addEventListener('dragleave', function (e) {
2504
- return _this20.handleDragLeave(e);
2446
+ return _this22.handleDragLeave(e);
2505
2447
  });
2506
2448
  inputArea.addEventListener('drop', function (e) {
2507
- return _this20.handleDrop(e);
2449
+ return _this22.handleDrop(e);
2508
2450
  });
2509
2451
  }
2510
2452
  }
@@ -2512,55 +2454,55 @@ window.openAIChatManager = function () {
2512
2454
 
2513
2455
  // Pause auto-scroll when the user manually scrolls up during streaming.
2514
2456
  this.chatContainer.addEventListener('scroll', function () {
2515
- if (!_this20.stream) {
2457
+ if (!_this22.stream) {
2516
2458
  return;
2517
2459
  }
2518
2460
  var threshold = 30;
2519
- var atBottom = _this20.chatContainer.scrollHeight - _this20.chatContainer.clientHeight - _this20.chatContainer.scrollTop <= threshold;
2520
- _this20.autoScroll = atBottom;
2461
+ var atBottom = _this22.chatContainer.scrollHeight - _this22.chatContainer.clientHeight - _this22.chatContainer.scrollTop <= threshold;
2462
+ _this22.autoScroll = atBottom;
2521
2463
  });
2522
2464
  this.inputElement.addEventListener('keydown', function (event) {
2523
- if (_this20.stream != null) {
2465
+ if (_this22.stream != null) {
2524
2466
  return;
2525
2467
  }
2526
2468
  if (event.key === "Enter" && !event.shiftKey) {
2527
2469
  event.preventDefault();
2528
- _this20.buttonElement.click();
2470
+ _this22.buttonElement.click();
2529
2471
  }
2530
2472
  });
2531
2473
  this.inputElement.addEventListener('input', function (e) {
2532
- _this20.handleUserInput(e);
2474
+ _this22.handleUserInput(e);
2533
2475
  if (e.target.value.trim()) {
2534
- _this20.buttonElement.removeAttribute('disabled');
2476
+ _this22.buttonElement.removeAttribute('disabled');
2535
2477
  } else {
2536
- _this20.buttonElement.setAttribute('disabled', true);
2478
+ _this22.buttonElement.setAttribute('disabled', true);
2537
2479
  }
2538
2480
  });
2539
2481
  this.buttonElement.addEventListener('click', function () {
2540
- if (_this20.stream != null) {
2541
- _this20.stream.dispose();
2542
- _this20.stream = null;
2543
- _this20.streamingFinished();
2544
- _this20.hideTypingIndicator();
2482
+ if (_this22.stream != null) {
2483
+ _this22.stream.dispose();
2484
+ _this22.stream = null;
2485
+ _this22.streamingFinished();
2486
+ _this22.hideTypingIndicator();
2545
2487
 
2546
2488
  // Clean up: remove empty assistant message or stop streaming animation.
2547
- if (_this20.messages.length > 0) {
2548
- var lastMsg = _this20.messages[_this20.messages.length - 1];
2489
+ if (_this22.messages.length > 0) {
2490
+ var lastMsg = _this22.messages[_this22.messages.length - 1];
2549
2491
  if (lastMsg.role === 'assistant' && !lastMsg.content) {
2550
- _this20.messages.pop();
2492
+ _this22.messages.pop();
2551
2493
  } else if (lastMsg.isStreaming) {
2552
2494
  lastMsg.isStreaming = false;
2553
2495
  }
2554
2496
  }
2555
2497
  return;
2556
2498
  }
2557
- _this20.sendMessage();
2499
+ _this22.sendMessage();
2558
2500
  });
2559
2501
  var promptGenerators = document.getElementsByClassName('profile-generated-prompt');
2560
2502
  for (var i = 0; i < promptGenerators.length; i++) {
2561
2503
  promptGenerators[i].addEventListener('click', function (e) {
2562
2504
  e.preventDefault();
2563
- _this20.generatePrompt(e.target);
2505
+ _this22.generatePrompt(e.target);
2564
2506
  });
2565
2507
  }
2566
2508
  var chatSessions = document.getElementsByClassName('chat-session-history-item');
@@ -2572,17 +2514,18 @@ window.openAIChatManager = function () {
2572
2514
  console.error('an element with the class chat-session-history-item with no data-session-id set.');
2573
2515
  return;
2574
2516
  }
2575
- _this20.loadSession(sessionId);
2576
- _this20.showChatScreen();
2517
+ _this22.loadSession(sessionId);
2518
+ _this22.showChatScreen();
2577
2519
  });
2578
2520
  }
2579
- for (var _i5 = 0; _i5 < config.messages.length; _i5++) {
2580
- this.addMessage(config.messages[_i5]);
2521
+ var initialMessages = Array.isArray(config.messages) ? config.messages : [];
2522
+ for (var _i5 = 0; _i5 < initialMessages.length; _i5++) {
2523
+ this.addMessage(initialMessages[_i5]);
2581
2524
  }
2582
2525
 
2583
2526
  // Update feedback icons in the DOM after initial messages have rendered.
2584
2527
  this.$nextTick(function () {
2585
- _this20.refreshAllFeedbackIcons();
2528
+ _this22.refreshAllFeedbackIcons();
2586
2529
  });
2587
2530
 
2588
2531
  // Delegate click for code block copy buttons.
@@ -2600,9 +2543,9 @@ window.openAIChatManager = function () {
2600
2543
  if (codeEl) {
2601
2544
  navigator.clipboard.writeText(codeEl.textContent);
2602
2545
  var copiedText = config.codeCopiedText || 'Copied!';
2603
- btn.innerHTML = '<span class="fa-solid fa-check"></span> ' + copiedText;
2546
+ btn.innerHTML = '<i class="fa-solid fa-check"></i> ' + copiedText;
2604
2547
  setTimeout(function () {
2605
- btn.innerHTML = '<span class="fa-regular fa-copy"></span>';
2548
+ btn.innerHTML = '<i class="fa-regular fa-copy"></i>';
2606
2549
  }, 2000);
2607
2550
  }
2608
2551
  });
@@ -2614,7 +2557,7 @@ window.openAIChatManager = function () {
2614
2557
  if (this.micButton) {
2615
2558
  this.micButton.style.display = '';
2616
2559
  this.micButton.addEventListener('click', function () {
2617
- _this20.toggleRecording();
2560
+ _this22.toggleRecording();
2618
2561
  });
2619
2562
  }
2620
2563
  }
@@ -2624,10 +2567,11 @@ window.openAIChatManager = function () {
2624
2567
  this.conversationButton = document.querySelector(config.conversationButtonElementSelector);
2625
2568
  if (this.conversationButton) {
2626
2569
  this.conversationButton.addEventListener('click', function () {
2627
- _this20.toggleConversationMode();
2570
+ _this22.toggleConversationMode();
2628
2571
  });
2629
2572
  }
2630
2573
  }
2574
+ return true;
2631
2575
  },
2632
2576
  loadSession: function loadSession(sessionId) {
2633
2577
  this.connection.invoke("LoadSession", sessionId)["catch"](function (err) {
@@ -2642,130 +2586,69 @@ window.openAIChatManager = function () {
2642
2586
  },
2643
2587
  initializeSession: function initializeSession(sessionId, force) {
2644
2588
  if (this.isSessionStarted && !force) {
2589
+ if (sessionId && this.getSessionId() === sessionId) {
2590
+ this.resolvePendingSessionRequest(sessionId);
2591
+ }
2645
2592
  return;
2646
2593
  }
2647
- this.fireEvent(new CustomEvent("initializingSessionOpenAIChat", {
2594
+ this.fireEvent(new CustomEvent("initializingSessionCoreAIChat", {
2648
2595
  detail: {
2649
2596
  sessionId: sessionId
2650
2597
  }
2651
2598
  }));
2652
2599
  this.setSessionId(sessionId);
2653
2600
  this.isSessionStarted = true;
2654
- if (this.widgetIsInitialized) {
2655
- localStorage.setItem(this.chatWidgetStateSession, sessionId);
2656
- }
2657
- },
2658
- initializeWidget: function initializeWidget() {
2659
- var _this21 = this;
2660
- if (!config.widget.chatWidgetContainer) {
2661
- console.error('The widget chatWidgetContainer is required.');
2662
- return;
2663
- }
2664
- if (!config.widget.chatWidgetStateName) {
2665
- console.error('The widget chatWidgetStateName is required.');
2666
- return;
2667
- }
2668
- var chatWidgetContainer = document.querySelector(config.widget.chatWidgetContainer);
2669
- if (!chatWidgetContainer) {
2670
- return;
2671
- }
2672
- if (config.widget.chatHistorySection) {
2673
- this.chatHistorySection = document.querySelector(config.widget.chatHistorySection);
2674
- }
2675
- this.chatWidgetStateName = config.widget.chatWidgetStateName;
2676
- this.chatWidgetStateSession = config.widget.chatWidgetStateName + 'Session';
2677
- this.chatWidgetLayoutKey = config.widget.chatWidgetStateName + 'Layout';
2678
- this.widgetContainerElement = chatWidgetContainer;
2679
- this.widgetToggleButtonElement = config.widget.toggleButtonSelector ? document.querySelector(config.widget.toggleButtonSelector) : null;
2680
- this.widgetResetSizeButtonElement = config.widget.resetSizeButtonSelector ? document.querySelector(config.widget.resetSizeButtonSelector) : null;
2681
- this.widgetDefaultWidth = parsePixelValue(config.widget.defaultWidth) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).width) || 380;
2682
- this.widgetDefaultHeight = parsePixelValue(config.widget.defaultHeight) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).height) || 520;
2683
- this.widgetIsInitialized = true;
2684
- if (config.widget.enableResizing) {
2685
- chatWidgetContainer.classList.add('ai-chat-widget-resizable');
2686
- chatWidgetContainer.style.minWidth = (parsePixelValue(config.widget.minWidth) || 320) + 'px';
2687
- chatWidgetContainer.style.minHeight = (parsePixelValue(config.widget.minHeight) || 420) + 'px';
2688
- if (typeof ResizeObserver !== 'undefined') {
2689
- this.widgetResizeObserver = new ResizeObserver(function () {
2690
- _this21.persistWidgetPanelSize();
2691
- _this21.constrainWidgetElementsToViewport();
2692
- _this21.updateWidgetResetSizeButtonVisibility();
2693
- });
2694
- this.widgetResizeObserver.observe(chatWidgetContainer);
2695
- }
2696
- }
2697
- var dragHandleSelector = config.widget.dragHandleSelector || '.ai-chat-widget-header';
2698
- var dragHandle = chatWidgetContainer.querySelector(dragHandleSelector);
2699
- if (config.widget.enableDragging && dragHandle) {
2700
- dragHandle.classList.add('ai-chat-widget-drag-handle');
2701
- chatWidgetContainer.classList.add('ai-chat-widget-draggable');
2702
- this.attachDraggableWidgetElement(chatWidgetContainer, dragHandle, 'panel', false);
2703
- }
2704
- if (config.widget.enableDragging && this.widgetToggleButtonElement) {
2705
- this.widgetToggleButtonElement.classList.add('ai-chat-widget-draggable');
2706
- this.attachDraggableWidgetElement(this.widgetToggleButtonElement, this.widgetToggleButtonElement, 'toggle', true);
2707
- }
2708
- if (this.widgetResetSizeButtonElement) {
2709
- this.widgetResetSizeButtonElement.addEventListener('click', function (event) {
2710
- event.preventDefault();
2711
- _this21.resetWidgetSize();
2712
- });
2601
+ this.resolvePendingSessionRequest(sessionId);
2602
+ if (widgetBehavior && typeof widgetBehavior.onSessionInitialized === 'function') {
2603
+ widgetBehavior.onSessionInitialized(this, sessionId, config);
2713
2604
  }
2714
- this.restoreWidgetLayout();
2715
- if (!this.widgetViewportResizeHandler) {
2716
- this.widgetViewportResizeHandler = function () {
2717
- _this21.constrainWidgetElementsToViewport();
2718
- _this21.updateWidgetResetSizeButtonVisibility();
2719
- };
2720
- window.addEventListener('resize', this.widgetViewportResizeHandler);
2721
- }
2722
-
2723
- // Auto-load the last session so the user always sees previous chat history.
2724
- this.reloadCurrentSession();
2725
- if (config.autoCreateSession && !this.getSessionId()) {
2726
- this.startNewSession();
2727
- }
2728
- if (config.widget.showHistoryButton && this.chatHistorySection) {
2729
- var showHistoryButton = document.querySelector(config.widget.showHistoryButton);
2730
- if (showHistoryButton) {
2731
- showHistoryButton.addEventListener('click', function () {
2732
- _this21.chatHistorySection.classList.toggle('show');
2733
- });
2734
- }
2735
- if (config.widget.closeHistoryButton) {
2736
- var closeHistoryButton = document.querySelector(config.widget.closeHistoryButton);
2737
- if (closeHistoryButton) {
2738
- closeHistoryButton.addEventListener('click', function () {
2739
- _this21.showChatScreen();
2740
- });
2741
- }
2742
- }
2743
- }
2744
- if (config.widget.newChatButton) {
2745
- var newChatButton = document.querySelector(config.widget.newChatButton);
2746
- if (newChatButton) {
2747
- newChatButton.addEventListener('click', function () {
2748
- _this21.resetSession();
2749
- _this21.showChatScreen();
2750
- });
2751
- }
2752
- }
2753
- },
2754
- showChatScreen: function showChatScreen() {
2755
- if (!this.chatHistorySection) {
2756
- return;
2757
- }
2758
- this.chatHistorySection.classList.remove('show');
2759
2605
  },
2760
2606
  getSessionId: function getSessionId() {
2761
- var sessionId = this.inputElement.getAttribute('data-session-id');
2762
- if (!sessionId && this.widgetIsInitialized) {
2763
- sessionId = localStorage.getItem(this.chatWidgetStateSession);
2607
+ var sessionId = this.inputElement ? this.inputElement.getAttribute('data-session-id') : null;
2608
+ if (!sessionId && widgetBehavior && typeof widgetBehavior.resolveSessionId === 'function') {
2609
+ sessionId = widgetBehavior.resolveSessionId(this, config);
2764
2610
  }
2765
2611
  return sessionId;
2766
2612
  },
2767
- copyResponse: function copyResponse(message) {
2768
- navigator.clipboard.writeText(message);
2613
+ clearCopiedMessageState: function clearCopiedMessageState() {
2614
+ if (this.copyResetTimeoutId) {
2615
+ window.clearTimeout(this.copyResetTimeoutId);
2616
+ this.copyResetTimeoutId = null;
2617
+ }
2618
+ if (this.activeCopyButton) {
2619
+ this.setCopyButtonState(this.activeCopyButton, false);
2620
+ this.activeCopyButton = null;
2621
+ }
2622
+ this.copiedMessageIndex = -1;
2623
+ },
2624
+ copyResponse: function copyResponse(message, index, event) {
2625
+ var _ref25,
2626
+ _message$copyContent,
2627
+ _event$target,
2628
+ _event$target$closest,
2629
+ _this23 = this;
2630
+ 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 : '';
2631
+ 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;
2632
+ navigator.clipboard.writeText(text).then(function () {
2633
+ _this23.clearCopiedMessageState();
2634
+ _this23.copiedMessageIndex = typeof index === 'number' ? index : -1;
2635
+ _this23.activeCopyButton = button;
2636
+ if (button) {
2637
+ _this23.setCopyButtonState(button, true);
2638
+ } else {
2639
+ _this23.$nextTick(function () {
2640
+ return _this23.updateCopyButtons();
2641
+ });
2642
+ }
2643
+ _this23.copyResetTimeoutId = window.setTimeout(function () {
2644
+ _this23.clearCopiedMessageState();
2645
+ _this23.$nextTick(function () {
2646
+ return _this23.updateCopyButtons();
2647
+ });
2648
+ }, Number(config.copyResetDelayMs) || 2000);
2649
+ })["catch"](function (err) {
2650
+ return console.error('Failed to copy response:', err);
2651
+ });
2769
2652
  },
2770
2653
  updateFeedbackIcons: function updateFeedbackIcons(container, userRating) {
2771
2654
  if (!container) {
@@ -2844,16 +2727,28 @@ window.openAIChatManager = function () {
2844
2727
  documents: {
2845
2728
  handler: function handler() {
2846
2729
  this.renderDocumentBar();
2730
+ if (widgetBehavior && typeof widgetBehavior.onDocumentsChanged === 'function') {
2731
+ widgetBehavior.onDocumentsChanged(this, this.documents, config);
2732
+ }
2847
2733
  },
2848
2734
  deep: true
2849
2735
  },
2850
2736
  isUploading: function isUploading() {
2851
2737
  this.renderDocumentBar();
2852
2738
  },
2739
+ isDocumentOperationPending: function isDocumentOperationPending() {
2740
+ this.renderDocumentBar();
2741
+ },
2853
2742
  isPlayingAudio: function isPlayingAudio() {
2854
2743
  // Reserved for future use — volume-based interrupt detection
2855
2744
  // no longer mutes tracks; browser echo cancellation handles echo.
2856
2745
  },
2746
+ copiedMessageIndex: function copiedMessageIndex() {
2747
+ var _this24 = this;
2748
+ this.$nextTick(function () {
2749
+ return _this24.updateCopyButtons();
2750
+ });
2751
+ },
2857
2752
  isConversationMode: function isConversationMode(active) {
2858
2753
  // Hide/show mic button.
2859
2754
  if (this.micButton) {
@@ -2875,22 +2770,28 @@ window.openAIChatManager = function () {
2875
2770
  }
2876
2771
  },
2877
2772
  mounted: function mounted() {
2878
- var _this22 = this;
2879
- _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
2880
- return _regenerator().w(function (_context6) {
2881
- while (1) switch (_context6.n) {
2773
+ var _this25 = this;
2774
+ _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
2775
+ var isInitialized;
2776
+ return _regenerator().w(function (_context0) {
2777
+ while (1) switch (_context0.n) {
2882
2778
  case 0:
2883
- _context6.n = 1;
2884
- return _this22.startConnection();
2779
+ _context0.n = 1;
2780
+ return _this25.startConnection();
2885
2781
  case 1:
2886
- _this22.initializeApp();
2887
- if (hasWidgetConfig) {
2888
- _this22.initializeWidget();
2782
+ isInitialized = _this25.initializeApp();
2783
+ if (isInitialized && hasWidgetConfig && widgetBehavior && typeof widgetBehavior.onMounted === 'function') {
2784
+ widgetBehavior.onMounted(_this25, config);
2889
2785
  }
2786
+ _this25.$nextTick(function () {
2787
+ _this25.updateCopyButtons();
2788
+ refreshFontAwesomeIcons(_this25.$el);
2789
+ _this25.fontAwesomeObserver = observeFontAwesomeIcons(_this25.$el);
2790
+ });
2890
2791
  case 2:
2891
- return _context6.a(2);
2792
+ return _context0.a(2);
2892
2793
  }
2893
- }, _callee6);
2794
+ }, _callee0);
2894
2795
  }))();
2895
2796
  window.addEventListener('beforeunload', this.handleBeforeUnload);
2896
2797
  window.addEventListener('crestapps-ai-chat-stop-tts', this.handleExternalTtsStop);
@@ -2898,6 +2799,11 @@ window.openAIChatManager = function () {
2898
2799
  beforeUnmount: function beforeUnmount() {
2899
2800
  window.removeEventListener('beforeunload', this.handleBeforeUnload);
2900
2801
  window.removeEventListener('crestapps-ai-chat-stop-tts', this.handleExternalTtsStop);
2802
+ if (this.fontAwesomeObserver) {
2803
+ this.fontAwesomeObserver.disconnect();
2804
+ this.fontAwesomeObserver = null;
2805
+ }
2806
+ this.clearCopiedMessageState();
2901
2807
  this.stopAudio(false);
2902
2808
  if (this.stream) {
2903
2809
  this.stream.dispose();
@@ -2906,13 +2812,162 @@ window.openAIChatManager = function () {
2906
2812
  if (this.connection) {
2907
2813
  this.connection.stop();
2908
2814
  }
2815
+ if (widgetBehavior && typeof widgetBehavior.onBeforeUnmount === 'function') {
2816
+ widgetBehavior.onBeforeUnmount(this, config);
2817
+ }
2909
2818
  },
2910
2819
  template: config.messageTemplate
2911
- }).mount(config.appElementSelector);
2820
+ };
2821
+ var app = Vue.createApp(appDefinition).mount(config.appElementSelector);
2822
+ if (widgetBehavior && typeof widgetBehavior.attach === 'function') {
2823
+ widgetBehavior.attach(app, config);
2824
+ }
2912
2825
  return app;
2913
2826
  };
2827
+ var autoInitializeSelector = '[data-coreai-chat-config],[data-coreai-chat-app-element-selector]';
2828
+ function getAttributeValue(element, attributeName) {
2829
+ var value = element.getAttribute(attributeName);
2830
+ return value === null || value === '' ? null : value;
2831
+ }
2832
+ function parseBooleanAttributeValue(value) {
2833
+ if (value === null || value === undefined) {
2834
+ return null;
2835
+ }
2836
+ if (typeof value === 'boolean') {
2837
+ return value;
2838
+ }
2839
+ var normalized = String(value).trim().toLowerCase();
2840
+ if (normalized === 'true') {
2841
+ return true;
2842
+ }
2843
+ if (normalized === 'false') {
2844
+ return false;
2845
+ }
2846
+ return null;
2847
+ }
2848
+ function parseJsonAttribute(element, attributeName, description) {
2849
+ var rawValue = getAttributeValue(element, attributeName);
2850
+ if (!rawValue) {
2851
+ return null;
2852
+ }
2853
+ try {
2854
+ return JSON.parse(rawValue);
2855
+ } catch (error) {
2856
+ console.error('Failed to parse ' + description + ' JSON.', error);
2857
+ return null;
2858
+ }
2859
+ }
2860
+ function buildConfigFromDataAttributes(element, options) {
2861
+ var _parseJsonAttribute, _parseJsonAttribute2;
2862
+ var settings = options || {};
2863
+ if (element.hasAttribute('data-coreai-chat-widget-container-selector') && settings.allowWidgetHost !== true) {
2864
+ return null;
2865
+ }
2866
+ var appElementSelector = getAttributeValue(element, 'data-coreai-chat-app-element-selector');
2867
+ if (!appElementSelector) {
2868
+ return null;
2869
+ }
2870
+ var config = {
2871
+ signalRHubUrl: getAttributeValue(element, 'data-coreai-chat-signalr-hub-url'),
2872
+ appElementSelector: appElementSelector,
2873
+ chatContainerElementSelector: getAttributeValue(element, 'data-coreai-chat-container-element-selector'),
2874
+ inputElementSelector: getAttributeValue(element, 'data-coreai-chat-input-element-selector'),
2875
+ sendButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-send-button-element-selector'),
2876
+ placeholderElementSelector: getAttributeValue(element, 'data-coreai-chat-placeholder-element-selector'),
2877
+ messages: (_parseJsonAttribute = parseJsonAttribute(element, 'data-coreai-chat-messages', 'CoreAI chat messages')) !== null && _parseJsonAttribute !== void 0 ? _parseJsonAttribute : undefined,
2878
+ chatMode: getAttributeValue(element, 'data-coreai-chat-mode'),
2879
+ micButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-mic-button-element-selector'),
2880
+ conversationButtonElementSelector: getAttributeValue(element, 'data-coreai-chat-conversation-button-element-selector'),
2881
+ ttsVoiceName: getAttributeValue(element, 'data-coreai-chat-tts-voice-name'),
2882
+ documentBarSelector: getAttributeValue(element, 'data-coreai-chat-document-bar-selector'),
2883
+ uploadDocumentUrl: getAttributeValue(element, 'data-coreai-chat-upload-document-url'),
2884
+ removeDocumentUrl: getAttributeValue(element, 'data-coreai-chat-remove-document-url'),
2885
+ allowedExtensions: getAttributeValue(element, 'data-coreai-chat-allowed-extensions'),
2886
+ supportedExtensionsText: getAttributeValue(element, 'data-coreai-chat-supported-extensions-text'),
2887
+ existingDocuments: (_parseJsonAttribute2 = parseJsonAttribute(element, 'data-coreai-chat-existing-documents', 'CoreAI chat existing documents')) !== null && _parseJsonAttribute2 !== void 0 ? _parseJsonAttribute2 : undefined,
2888
+ initialPrompt: getAttributeValue(element, 'data-coreai-chat-initial-prompt'),
2889
+ userLabel: getAttributeValue(element, 'data-coreai-chat-user-label'),
2890
+ assistantLabel: getAttributeValue(element, 'data-coreai-chat-assistant-label'),
2891
+ copyTitle: getAttributeValue(element, 'data-coreai-chat-copy-title'),
2892
+ copiedTitle: getAttributeValue(element, 'data-coreai-chat-copied-title')
2893
+ };
2894
+ var booleanAttributes = {
2895
+ metricsEnabled: 'data-coreai-chat-metrics-enabled',
2896
+ textToSpeechEnabled: 'data-coreai-chat-text-to-speech-enabled',
2897
+ sessionDocumentsEnabled: 'data-coreai-chat-session-documents-enabled',
2898
+ singleResponseMode: 'data-coreai-chat-single-response-mode'
2899
+ };
2900
+ Object.keys(booleanAttributes).forEach(function (key) {
2901
+ var parsed = parseBooleanAttributeValue(getAttributeValue(element, booleanAttributes[key]));
2902
+ if (parsed !== null) {
2903
+ config[key] = parsed;
2904
+ }
2905
+ });
2906
+ return compactObject(config);
2907
+ }
2908
+ function buildConfigFromElement(element, options) {
2909
+ var jsonConfig = parseJsonAttribute(element, 'data-coreai-chat-config', 'CoreAI chat config');
2910
+ var attributeConfig = buildConfigFromDataAttributes(element, options);
2911
+ if (!jsonConfig && !attributeConfig) {
2912
+ return null;
2913
+ }
2914
+ return Object.assign({}, jsonConfig || {}, attributeConfig || {});
2915
+ }
2916
+ function initializeFromElement(element) {
2917
+ if (!element || element.dataset.coreAiChatInitialized === 'true') {
2918
+ return element ? element.__coreAIChatApp || null : null;
2919
+ }
2920
+ var config = buildConfigFromElement(element);
2921
+ if (!config) {
2922
+ return null;
2923
+ }
2924
+ var app = initialize(config);
2925
+ if (!app) {
2926
+ return null;
2927
+ }
2928
+ element.dataset.coreAiChatInitialized = 'true';
2929
+ element.__coreAIChatApp = app;
2930
+ return app;
2931
+ }
2932
+ function scanForAutoInitialization(root) {
2933
+ if (!root || typeof root.querySelectorAll !== 'function') {
2934
+ return;
2935
+ }
2936
+ if (typeof root.matches === 'function' && root.matches(autoInitializeSelector)) {
2937
+ initializeFromElement(root);
2938
+ }
2939
+ root.querySelectorAll(autoInitializeSelector).forEach(initializeFromElement);
2940
+ }
2941
+ function startAutoInitialization() {
2942
+ scanForAutoInitialization(document);
2943
+ if (typeof MutationObserver === 'undefined') {
2944
+ return;
2945
+ }
2946
+ var observer = new MutationObserver(function (mutations) {
2947
+ mutations.forEach(function (mutation) {
2948
+ mutation.addedNodes.forEach(function (node) {
2949
+ if (node && node.nodeType === 1) {
2950
+ scanForAutoInitialization(node);
2951
+ }
2952
+ });
2953
+ });
2954
+ });
2955
+ observer.observe(document.body, {
2956
+ childList: true,
2957
+ subtree: true
2958
+ });
2959
+ }
2960
+ if (document.readyState === 'loading') {
2961
+ document.addEventListener('DOMContentLoaded', startAutoInitialization, {
2962
+ once: true
2963
+ });
2964
+ } else {
2965
+ startAutoInitialization();
2966
+ }
2914
2967
  return {
2915
- initialize: initialize
2968
+ initialize: initialize,
2969
+ initializeFromElement: initializeFromElement,
2970
+ buildConfigFromElement: buildConfigFromElement
2916
2971
  };
2917
2972
  }();
2918
2973