@crestapps/ai-chat-ui 1.0.0-preview-22-preview.2 → 1.0.0-preview-22.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai-chat.js CHANGED
@@ -25,7 +25,6 @@ window.openAIChatManager = function () {
25
25
  // UI defaults for generated media
26
26
  generatedImageAltText: 'Generated Image',
27
27
  generatedImageMaxWidth: 400,
28
- generatedChartMaxWidth: 900,
29
28
  downloadImageTitle: 'Download image',
30
29
  downloadChartTitle: 'Download chart as image',
31
30
  downloadChartButtonText: 'Download',
@@ -35,7 +34,7 @@ window.openAIChatManager = function () {
35
34
  thumbsDownTitle: 'Thumbs down',
36
35
  copyTitle: 'Click here to copy response to clipboard.',
37
36
  codeCopiedText: 'Copied!',
38
- messageTemplate: "\n <div class=\"ai-chat-messages\">\n <div v-for=\"(message, index) in messages\" :key=\"'msg-' + index\" class=\"ai-chat-message-item\">\n <div>\n <div v-if=\"message.role === 'user'\" class=\"ai-chat-msg-role ai-chat-msg-role-user\">{{ userLabel }}</div>\n <div v-else-if=\"message.role !== 'indicator'\" :class=\"getAssistantRoleClasses(message)\">\n <span :class=\"getAssistantIconClasses(message, index)\"><i :class=\"getAssistantIcon(message)\"></i></span>\n {{ getAssistantLabel(message) }}\n </div>\n <div class=\"ai-chat-message-body lh-base\">\n <h4 v-if=\"message.title\">{{ message.title }}</h4>\n <div v-html=\"message.htmlContent\"></div>\n <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 <i class=\"fa-regular fa-thumbs-up\"></i>\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 <i class=\"fa-regular fa-thumbs-down\"></i>\n </button>\n </span>\n </template>\n <button class=\"btn btn-sm btn-link text-secondary p-0 button-message-toolbox\" @click=\"copyResponse(message.content)\" :title=\"copyTitle\">\n <i class=\"fa-solid fa-copy\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div v-for=\"notification in notifications\" :key=\"'notif-' + notification.type\" class=\"ai-chat-notification\" :class=\"'ai-chat-notification-' + (notification.type || 'info') + ' ' + (notification.cssClass || '')\">\n <div class=\"ai-chat-notification-content\">\n <i v-if=\"notification.icon\" :class=\"notification.icon\" class=\"ai-chat-notification-icon\"></i>\n <span class=\"ai-chat-notification-text\">{{ notification.content }}</span>\n <button 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 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 ",
37
+ messageTemplate: "\n <div class=\"ai-chat-messages\">\n <div v-for=\"(message, index) in messages\" :key=\"'msg-' + index\" class=\"ai-chat-message-item\">\n <div>\n <div v-if=\"message.role === 'user'\" class=\"ai-chat-msg-role ai-chat-msg-role-user\">{{ userLabel }}</div>\n <div v-else-if=\"message.role !== 'indicator'\" :class=\"getAssistantRoleClasses(message)\">\n <span :class=\"getAssistantIconClasses(message, index)\"><i :class=\"getAssistantIcon(message)\"></i></span>\n {{ getAssistantLabel(message) }}\n </div>\n <div class=\"ai-chat-message-body lh-base\">\n <h4 v-if=\"message.title\">{{ message.title }}</h4>\n <div v-html=\"message.htmlContent\"></div>\n <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 <i class=\"fa-regular fa-thumbs-up\"></i>\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 <i class=\"fa-regular fa-thumbs-down\"></i>\n </button>\n </span>\n </template>\n <button v-if=\"textToSpeechEnabled && 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 }\" @click=\"toggleMessageTts(message, index)\" :title=\"ttsPlayingMessageIndex === index ? 'Pause audio' : 'Read aloud'\">\n <i :class=\"ttsPlayingMessageIndex === index ? 'fa-solid fa-circle-pause' : 'fa-solid fa-circle-play'\"></i>\n </button>\n <button class=\"btn btn-sm btn-link text-secondary p-0 button-message-toolbox\" @click=\"copyResponse(message.content)\" :title=\"copyTitle\">\n <i class=\"fa-solid fa-copy\"></i>\n </button>\n </span>\n </div>\n </div>\n </div>\n <div v-for=\"notification in notifications\" :key=\"'notif-' + notification.type\" class=\"ai-chat-notification\" :class=\"'ai-chat-notification-' + (notification.type || 'info') + ' ' + (notification.cssClass || '')\">\n <div class=\"ai-chat-notification-content\">\n <i v-if=\"notification.icon\" :class=\"notification.icon\" class=\"ai-chat-notification-icon\"></i>\n <span class=\"ai-chat-notification-text\">{{ notification.content }}</span>\n <button 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 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 ",
39
38
  indicatorTemplate: "\n <div class=\"ai-chat-msg-role ai-chat-msg-role-assistant\">\n <span class=\"ai-streaming-icon\"><i class=\"fa fa-robot\" style=\"display: inline-block;\"></i></span>\n Assistant\n </div>\n "
40
39
  };
41
40
 
@@ -134,8 +133,7 @@ window.openAIChatManager = function () {
134
133
  // its DOM update to render charts it didn't create itself.
135
134
  window.__chartConfigs = window.__chartConfigs || {};
136
135
  function createChartHtml(chartId) {
137
- var chartMaxWidth = defaultConfig.generatedChartMaxWidth;
138
- return "<div class=\"chart-container\" style=\"position: relative; width: 100%; max-width: ".concat(chartMaxWidth, "px;\">") + "<canvas id=\"".concat(chartId, "\" class=\"img-thumbnail\"></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>";
136
+ 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>";
139
137
  }
140
138
 
141
139
  // Register [chart:{...json...}] as a native marked block extension so the
@@ -263,25 +261,37 @@ window.openAIChatManager = function () {
263
261
  function renderChartOnCanvas(chartId, config) {
264
262
  var canvas = document.getElementById(chartId);
265
263
  if (!canvas) {
266
- return;
264
+ return false;
267
265
  }
268
266
  if (typeof Chart === 'undefined') {
269
267
  console.warn('Chart.js is not loaded. To render interactive charts, include the Chart.js library on the page (e.g., <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>).');
270
- return;
268
+ return false;
269
+ }
270
+
271
+ // When the canvas is inside a hidden container (e.g., a widget panel with
272
+ // display:none), it has zero dimensions and Chart.js cannot render correctly.
273
+ // Keep the config in __chartConfigs so renderPendingCharts() can retry later
274
+ // once the container becomes visible.
275
+ if (canvas.offsetParent === null) {
276
+ window.__chartConfigs[chartId] = config;
277
+ return false;
271
278
  }
272
279
  try {
273
- var _cfg$options;
280
+ var _cfg$options, _cfg$options2, _cfg$options2$aspectR;
274
281
  if (canvas._chartInstance) {
275
282
  canvas._chartInstance.destroy();
276
283
  }
277
284
  var cfg = typeof config === 'string' ? JSON.parse(config) : config;
278
285
  (_cfg$options = cfg.options) !== null && _cfg$options !== void 0 ? _cfg$options : cfg.options = {};
279
286
  cfg.options.responsive = true;
280
- cfg.options.maintainAspectRatio = false;
287
+ cfg.options.maintainAspectRatio = true;
288
+ (_cfg$options2$aspectR = (_cfg$options2 = cfg.options).aspectRatio) !== null && _cfg$options2$aspectR !== void 0 ? _cfg$options2$aspectR : _cfg$options2.aspectRatio = 4 / 3;
281
289
  canvas._chartInstance = new Chart(canvas, cfg);
282
290
  delete window.__chartConfigs[chartId];
291
+ return true;
283
292
  } catch (e) {
284
293
  console.error('Error creating chart:', e);
294
+ return false;
285
295
  }
286
296
  }
287
297
 
@@ -378,13 +388,16 @@ window.openAIChatManager = function () {
378
388
  preRecordingPrompt: '',
379
389
  micButton: null,
380
390
  speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',
381
- textToSpeechEnabled: config.chatMode === 'Conversation',
391
+ textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,
382
392
  ttsVoiceName: config.ttsVoiceName || null,
383
393
  audioChunks: [],
384
394
  audioPlayQueue: [],
385
395
  isPlayingAudio: false,
386
396
  currentAudioElement: null,
387
397
  ttsButton: null,
398
+ ttsPlayingMessageIndex: -1,
399
+ ttsAudioCache: {},
400
+ singleResponseMode: !!config.singleResponseMode,
388
401
  conversationModeEnabled: config.chatMode === 'Conversation',
389
402
  conversationButton: null,
390
403
  isConversationMode: false,
@@ -1180,6 +1193,11 @@ window.openAIChatManager = function () {
1180
1193
 
1181
1194
  // Prevent stale ReceiveTranscript events from repopulating the prompt.
1182
1195
  this._audioInputSent = true;
1196
+
1197
+ // In single-response mode, clear all previous messages.
1198
+ if (this.singleResponseMode) {
1199
+ this.messages.splice(0, this.messages.length);
1200
+ }
1183
1201
  this.addMessage({
1184
1202
  role: 'user',
1185
1203
  content: trimmedPrompt
@@ -1535,18 +1553,34 @@ window.openAIChatManager = function () {
1535
1553
  }
1536
1554
  }
1537
1555
  },
1538
- synthesizeSpeech: function synthesizeSpeech(text) {
1556
+ synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1539
1557
  var _this0 = this;
1540
1558
  if (!this.textToSpeechEnabled || !text || !this.connection) {
1541
1559
  return;
1542
1560
  }
1543
1561
  this.audioChunks = [];
1544
1562
  this.isPlayingAudio = true;
1563
+ this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;
1545
1564
  this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)["catch"](function (err) {
1546
1565
  console.error("TTS synthesis error:", err);
1547
1566
  _this0.isPlayingAudio = false;
1567
+ _this0.ttsPlayingMessageIndex = -1;
1568
+ _this0._ttsCacheIndex = -1;
1548
1569
  });
1549
1570
  },
1571
+ toggleMessageTts: function toggleMessageTts(message, index) {
1572
+ if (this.ttsPlayingMessageIndex === index) {
1573
+ this.stopAudio();
1574
+ return;
1575
+ }
1576
+ this.stopAudio();
1577
+ this.ttsPlayingMessageIndex = index;
1578
+ if (this.ttsAudioCache[index]) {
1579
+ this.playAudioBlob(this.ttsAudioCache[index]);
1580
+ return;
1581
+ }
1582
+ this.synthesizeSpeech(message.content, index);
1583
+ },
1550
1584
  playCollectedAudio: function playCollectedAudio() {
1551
1585
  if (this.audioChunks.length === 0) {
1552
1586
  if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {
@@ -1576,6 +1610,10 @@ window.openAIChatManager = function () {
1576
1610
  var blob = new Blob([combined], {
1577
1611
  type: 'audio/mp3'
1578
1612
  });
1613
+ if (this._ttsCacheIndex >= 0) {
1614
+ this.ttsAudioCache[this._ttsCacheIndex] = blob;
1615
+ this._ttsCacheIndex = -1;
1616
+ }
1579
1617
 
1580
1618
  // If audio is already playing, queue this blob for sequential playback.
1581
1619
  if (this.currentAudioElement) {
@@ -1613,6 +1651,7 @@ window.openAIChatManager = function () {
1613
1651
  this.playAudioBlob(nextBlob);
1614
1652
  } else {
1615
1653
  this.isPlayingAudio = false;
1654
+ this.ttsPlayingMessageIndex = -1;
1616
1655
  this.conversationModeOnAudioEnded();
1617
1656
  }
1618
1657
  },
@@ -1625,6 +1664,7 @@ window.openAIChatManager = function () {
1625
1664
  this.audioChunks = [];
1626
1665
  this.audioPlayQueue = [];
1627
1666
  this.isPlayingAudio = false;
1667
+ this.ttsPlayingMessageIndex = -1;
1628
1668
  },
1629
1669
  toggleConversationMode: function toggleConversationMode() {
1630
1670
  if (this.isConversationMode) {
@@ -2451,4 +2491,5 @@ document.addEventListener('click', function (e) {
2451
2491
  })["catch"](function (err) {
2452
2492
  console.error('Failed to download image:', err);
2453
2493
  });
2454
- });
2494
+ });
2495
+ //# sourceMappingURL=ai-chat.js.map