@crestapps/ai-chat-ui 1.0.0-preview-22-preview.4 → 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
@@ -34,7 +34,7 @@ window.openAIChatManager = function () {
34
34
  thumbsDownTitle: 'Thumbs down',
35
35
  copyTitle: 'Click here to copy response to clipboard.',
36
36
  codeCopiedText: 'Copied!',
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 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 ",
38
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 "
39
39
  };
40
40
 
@@ -388,13 +388,16 @@ window.openAIChatManager = function () {
388
388
  preRecordingPrompt: '',
389
389
  micButton: null,
390
390
  speechToTextEnabled: config.chatMode === 'AudioInput' || config.chatMode === 'Conversation',
391
- textToSpeechEnabled: config.chatMode === 'Conversation',
391
+ textToSpeechEnabled: config.chatMode === 'Conversation' || !!config.textToSpeechEnabled,
392
392
  ttsVoiceName: config.ttsVoiceName || null,
393
393
  audioChunks: [],
394
394
  audioPlayQueue: [],
395
395
  isPlayingAudio: false,
396
396
  currentAudioElement: null,
397
397
  ttsButton: null,
398
+ ttsPlayingMessageIndex: -1,
399
+ ttsAudioCache: {},
400
+ singleResponseMode: !!config.singleResponseMode,
398
401
  conversationModeEnabled: config.chatMode === 'Conversation',
399
402
  conversationButton: null,
400
403
  isConversationMode: false,
@@ -1190,6 +1193,11 @@ window.openAIChatManager = function () {
1190
1193
 
1191
1194
  // Prevent stale ReceiveTranscript events from repopulating the prompt.
1192
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
+ }
1193
1201
  this.addMessage({
1194
1202
  role: 'user',
1195
1203
  content: trimmedPrompt
@@ -1545,18 +1553,34 @@ window.openAIChatManager = function () {
1545
1553
  }
1546
1554
  }
1547
1555
  },
1548
- synthesizeSpeech: function synthesizeSpeech(text) {
1556
+ synthesizeSpeech: function synthesizeSpeech(text, cacheIndex) {
1549
1557
  var _this0 = this;
1550
1558
  if (!this.textToSpeechEnabled || !text || !this.connection) {
1551
1559
  return;
1552
1560
  }
1553
1561
  this.audioChunks = [];
1554
1562
  this.isPlayingAudio = true;
1563
+ this._ttsCacheIndex = cacheIndex !== undefined ? cacheIndex : -1;
1555
1564
  this.connection.invoke("SynthesizeSpeech", this.getProfileId(), this.getSessionId(), text, this.ttsVoiceName)["catch"](function (err) {
1556
1565
  console.error("TTS synthesis error:", err);
1557
1566
  _this0.isPlayingAudio = false;
1567
+ _this0.ttsPlayingMessageIndex = -1;
1568
+ _this0._ttsCacheIndex = -1;
1558
1569
  });
1559
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
+ },
1560
1584
  playCollectedAudio: function playCollectedAudio() {
1561
1585
  if (this.audioChunks.length === 0) {
1562
1586
  if (!this.currentAudioElement && this.audioPlayQueue.length === 0) {
@@ -1586,6 +1610,10 @@ window.openAIChatManager = function () {
1586
1610
  var blob = new Blob([combined], {
1587
1611
  type: 'audio/mp3'
1588
1612
  });
1613
+ if (this._ttsCacheIndex >= 0) {
1614
+ this.ttsAudioCache[this._ttsCacheIndex] = blob;
1615
+ this._ttsCacheIndex = -1;
1616
+ }
1589
1617
 
1590
1618
  // If audio is already playing, queue this blob for sequential playback.
1591
1619
  if (this.currentAudioElement) {
@@ -1623,6 +1651,7 @@ window.openAIChatManager = function () {
1623
1651
  this.playAudioBlob(nextBlob);
1624
1652
  } else {
1625
1653
  this.isPlayingAudio = false;
1654
+ this.ttsPlayingMessageIndex = -1;
1626
1655
  this.conversationModeOnAudioEnded();
1627
1656
  }
1628
1657
  },
@@ -1635,6 +1664,7 @@ window.openAIChatManager = function () {
1635
1664
  this.audioChunks = [];
1636
1665
  this.audioPlayQueue = [];
1637
1666
  this.isPlayingAudio = false;
1667
+ this.ttsPlayingMessageIndex = -1;
1638
1668
  },
1639
1669
  toggleConversationMode: function toggleConversationMode() {
1640
1670
  if (this.isConversationMode) {
@@ -2461,4 +2491,5 @@ document.addEventListener('click', function (e) {
2461
2491
  })["catch"](function (err) {
2462
2492
  console.error('Failed to download image:', err);
2463
2493
  });
2464
- });
2494
+ });
2495
+ //# sourceMappingURL=ai-chat.js.map