@crestapps/ai-chat-ui 2.0.0-preview.193 → 2.0.0-preview.197
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 +77 -20
- package/dist/ai-chat.js.map +1 -1
- package/dist/ai-chat.min.js +1 -1
- package/dist/ai-chat.min.js.map +1 -1
- package/dist/chat-interaction.js +109 -17
- package/dist/chat-interaction.js.map +1 -1
- package/dist/chat-interaction.min.js +1 -1
- package/dist/chat-interaction.min.js.map +1 -1
- package/dist/realtime-audio.js +16 -2
- package/dist/realtime-audio.js.map +1 -1
- package/dist/realtime-audio.min.js +1 -1
- package/dist/realtime-audio.min.js.map +1 -1
- package/package.json +1 -1
package/dist/chat-interaction.js
CHANGED
|
@@ -691,6 +691,11 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
691
691
|
isConversationMode: false,
|
|
692
692
|
// Realtime (speech-to-speech) controller from the shared CoreAIRealtime module.
|
|
693
693
|
realtimeController: null,
|
|
694
|
+
realtimeButton: null,
|
|
695
|
+
// Whether a realtime deployment actually resolved. The controller is attached whenever the
|
|
696
|
+
// module is loaded, so its presence says nothing about which transport carries the
|
|
697
|
+
// conversation -- only this flag does.
|
|
698
|
+
realtimeEnabled: config.realtimeEnabled === true,
|
|
694
699
|
notifications: [],
|
|
695
700
|
notificationDismissTimers: {},
|
|
696
701
|
copyTitle: config.copyTitle,
|
|
@@ -1095,6 +1100,11 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
1095
1100
|
}
|
|
1096
1101
|
return _context2.a(2);
|
|
1097
1102
|
case 1:
|
|
1103
|
+
// A live voice session and a typed turn are two writers into one interaction, and they
|
|
1104
|
+
// would interleave. End the session first; the turns it already produced are persisted, so
|
|
1105
|
+
// the typed message simply continues the same thread.
|
|
1106
|
+
_this4.endActiveVoiceSession();
|
|
1107
|
+
|
|
1098
1108
|
// Stop any active recording before sending.
|
|
1099
1109
|
if (_this4.isRecording) {
|
|
1100
1110
|
_this4.stopRecording();
|
|
@@ -1489,6 +1499,39 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
1489
1499
|
return _this12.updateTtsPlaybackButtons();
|
|
1490
1500
|
});
|
|
1491
1501
|
},
|
|
1502
|
+
// A live voice session takes the message box away and puts the audio settings in its place; ending
|
|
1503
|
+
// the session gives it back. Sending a typed message already ends the session first, so the box was
|
|
1504
|
+
// never a way to say something *during* one -- hiding it only makes that honest, and frees the row
|
|
1505
|
+
// for the gear. Both transports land here: realtime through the module's onActivate, the
|
|
1506
|
+
// speech-to-text cascade through startConversationMode.
|
|
1507
|
+
setVoiceSessionLive: function setVoiceSessionLive(live) {
|
|
1508
|
+
if (this.inputElement) {
|
|
1509
|
+
this.inputElement.hidden = live;
|
|
1510
|
+
}
|
|
1511
|
+
if (this.buttonElement) {
|
|
1512
|
+
this.buttonElement.hidden = live;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// With the message box gone the live button would otherwise sit at its natural width in an
|
|
1516
|
+
// empty row, so it takes the space the box left behind. Whichever transport is carrying the
|
|
1517
|
+
// conversation owns a different button, and the hidden one is unaffected by the class.
|
|
1518
|
+
[this.realtimeButton, this.conversationButton].forEach(function (button) {
|
|
1519
|
+
if (button) {
|
|
1520
|
+
button.classList.toggle('flex-grow-1', live);
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
// The gear is built by the realtime module directly after the button it was handed, and only
|
|
1525
|
+
// in realtime mode, so it is absent on the cascade. It belongs to a live session rather than
|
|
1526
|
+
// to the surface. The module hard-codes its element id and a page can host two chat clients
|
|
1527
|
+
// at once -- this one and the admin widget -- so it is found next to this client's own button
|
|
1528
|
+
// rather than by id.
|
|
1529
|
+
var next = this.realtimeButton && this.realtimeButton.nextElementSibling;
|
|
1530
|
+
var audioSettings = next && next.id === 'realtime-audio-settings' ? next : null;
|
|
1531
|
+
if (audioSettings) {
|
|
1532
|
+
audioSettings.hidden = !live;
|
|
1533
|
+
}
|
|
1534
|
+
},
|
|
1492
1535
|
toggleConversationMode: function toggleConversationMode() {
|
|
1493
1536
|
if (this.isConversationMode) {
|
|
1494
1537
|
this.stopConversationMode();
|
|
@@ -1496,6 +1539,17 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
1496
1539
|
this.startConversationMode();
|
|
1497
1540
|
}
|
|
1498
1541
|
},
|
|
1542
|
+
// Two writers into one interaction would interleave, so the voice session ends before the typed
|
|
1543
|
+
// turn starts rather than running alongside it.
|
|
1544
|
+
endActiveVoiceSession: function endActiveVoiceSession() {
|
|
1545
|
+
if (this.realtimeController && this.realtimeController.isActive()) {
|
|
1546
|
+
this.realtimeController.stop();
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (this.isConversationMode) {
|
|
1550
|
+
this.stopConversationMode();
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1499
1553
|
startConversationMode: function startConversationMode() {
|
|
1500
1554
|
var _this13 = this;
|
|
1501
1555
|
if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {
|
|
@@ -2130,6 +2184,8 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2130
2184
|
// module owns the mic capture, playback, echo guard, push-to-talk and audio settings; the
|
|
2131
2185
|
// callbacks below plug it into this app's connection and conversation-transcript display.
|
|
2132
2186
|
if (window.CoreAIRealtime && config.realtimeButtonElementSelector) {
|
|
2187
|
+
// Kept so the voice settings popover can be found next to this client's own button.
|
|
2188
|
+
this.realtimeButton = document.querySelector(config.realtimeButtonElementSelector);
|
|
2133
2189
|
this.realtimeController = window.CoreAIRealtime.attach({
|
|
2134
2190
|
connection: this.connection,
|
|
2135
2191
|
ensureConnected: function ensureConnected() {
|
|
@@ -2152,13 +2208,23 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2152
2208
|
},
|
|
2153
2209
|
capableDeployments: config.realtimeCapableDeployments || [],
|
|
2154
2210
|
realtimeEnabled: config.realtimeEnabled === true,
|
|
2211
|
+
// input and sendButton are deliberately not handed over. The module hides every
|
|
2212
|
+
// control it is given when realtime takes over, and typing has to stay available
|
|
2213
|
+
// during a voice conversation -- that is the whole feature.
|
|
2214
|
+
//
|
|
2215
|
+
// deploymentSelect is not handed over either: it would read the raw select value,
|
|
2216
|
+
// and an empty conversation deployment means "use the site default", which still
|
|
2217
|
+
// resolves. The host resolves it and calls applyMode itself (see below).
|
|
2155
2218
|
selectors: {
|
|
2156
2219
|
realtimeButton: config.realtimeButtonElementSelector,
|
|
2157
|
-
input: config.inputElementSelector,
|
|
2158
|
-
sendButton: config.sendButtonElementSelector,
|
|
2159
2220
|
micButton: config.micButtonElementSelector,
|
|
2160
|
-
conversationButton: config.conversationButtonElementSelector
|
|
2161
|
-
|
|
2221
|
+
conversationButton: config.conversationButtonElementSelector
|
|
2222
|
+
},
|
|
2223
|
+
// The module owns whether a session is live, so the message box follows its flag
|
|
2224
|
+
// rather than this app's copy of it. Losing one of these events used to leave the
|
|
2225
|
+
// box hidden under a button that had already gone back to "Start speaking".
|
|
2226
|
+
onSessionStateChanged: function onSessionStateChanged(active) {
|
|
2227
|
+
_this18.setVoiceSessionLive(active);
|
|
2162
2228
|
},
|
|
2163
2229
|
onActivate: function onActivate() {
|
|
2164
2230
|
_this18.isConversationMode = true;
|
|
@@ -2219,6 +2285,11 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2219
2285
|
}
|
|
2220
2286
|
});
|
|
2221
2287
|
this.setupRealtimeVoicePicker(config);
|
|
2288
|
+
|
|
2289
|
+
// The module reveals the gear as soon as the surface enters realtime mode, but it belongs
|
|
2290
|
+
// to a live session. The watcher that would hide it only runs on a change, so the idle
|
|
2291
|
+
// state has to be stated once here.
|
|
2292
|
+
this.setVoiceSessionLive(false);
|
|
2222
2293
|
}
|
|
2223
2294
|
},
|
|
2224
2295
|
// Populates the per-interaction realtime voice picker from the selected realtime deployment's
|
|
@@ -2232,14 +2303,26 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2232
2303
|
return;
|
|
2233
2304
|
}
|
|
2234
2305
|
var voiceGroup = config.realtimeVoiceGroupElementSelector ? document.querySelector(config.realtimeVoiceGroupElementSelector) : null;
|
|
2235
|
-
var
|
|
2306
|
+
var conversationSelect = config.conversationDeploymentSelectElementSelector ? document.querySelector(config.conversationDeploymentSelectElementSelector) : null;
|
|
2236
2307
|
var capable = (config.realtimeCapableDeployments || []).map(function (n) {
|
|
2237
2308
|
return (n || '').toLowerCase();
|
|
2238
2309
|
});
|
|
2239
2310
|
var savedVoiceId = config.realtimeVoiceName || '';
|
|
2311
|
+
var self = this;
|
|
2240
2312
|
function isRealtimeDeployment(name) {
|
|
2241
2313
|
return name && capable.indexOf(name.toLowerCase()) !== -1;
|
|
2242
2314
|
}
|
|
2315
|
+
|
|
2316
|
+
// Mirrors the realtime slot's chain on the server: the interaction's own choice, then the
|
|
2317
|
+
// site default, then the first realtime-capable deployment. Only conversation mode reaches
|
|
2318
|
+
// it -- an interaction that is not in conversation mode never speaks.
|
|
2319
|
+
function resolveConversationDeployment() {
|
|
2320
|
+
if (!self.conversationModeEnabled) {
|
|
2321
|
+
return '';
|
|
2322
|
+
}
|
|
2323
|
+
var name = conversationSelect && conversationSelect.value || config.defaultRealtimeDeploymentName || (capable.length ? capable[0] : '');
|
|
2324
|
+
return isRealtimeDeployment(name) ? name : '';
|
|
2325
|
+
}
|
|
2243
2326
|
function loadVoices() {
|
|
2244
2327
|
return _loadVoices.apply(this, arguments);
|
|
2245
2328
|
}
|
|
@@ -2249,11 +2332,17 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2249
2332
|
return _regenerator().w(function (_context4) {
|
|
2250
2333
|
while (1) switch (_context4.p = _context4.n) {
|
|
2251
2334
|
case 0:
|
|
2252
|
-
deploymentName =
|
|
2253
|
-
realtime =
|
|
2335
|
+
deploymentName = resolveConversationDeployment();
|
|
2336
|
+
realtime = !!deploymentName;
|
|
2254
2337
|
if (voiceGroup) {
|
|
2255
2338
|
voiceGroup.classList.toggle('d-none', !realtime);
|
|
2256
2339
|
}
|
|
2340
|
+
|
|
2341
|
+
// Changing the conversation deployment switches the voice toggle on or off without a
|
|
2342
|
+
// reload.
|
|
2343
|
+
if (self.realtimeController) {
|
|
2344
|
+
self.realtimeController.applyMode(realtime);
|
|
2345
|
+
}
|
|
2257
2346
|
if (realtime) {
|
|
2258
2347
|
_context4.n = 1;
|
|
2259
2348
|
break;
|
|
@@ -2328,8 +2417,8 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2328
2417
|
}));
|
|
2329
2418
|
return _loadVoices.apply(this, arguments);
|
|
2330
2419
|
}
|
|
2331
|
-
if (
|
|
2332
|
-
|
|
2420
|
+
if (conversationSelect) {
|
|
2421
|
+
conversationSelect.addEventListener('change', loadVoices);
|
|
2333
2422
|
}
|
|
2334
2423
|
loadVoices();
|
|
2335
2424
|
},
|
|
@@ -2621,17 +2710,20 @@ window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIC
|
|
|
2621
2710
|
// no longer mutes tracks; browser echo cancellation handles echo.
|
|
2622
2711
|
},
|
|
2623
2712
|
isConversationMode: function isConversationMode(active) {
|
|
2713
|
+
// The dictation microphone is hidden while a voice conversation runs: the conversation
|
|
2714
|
+
// already owns the microphone, and a second one would mean something different.
|
|
2624
2715
|
if (this.micButton) {
|
|
2625
2716
|
this.micButton.style.display = active ? 'none' : this.speechToTextEnabled ? '' : 'none';
|
|
2626
2717
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2718
|
+
|
|
2719
|
+
// The message box and the send button give way to the voice settings while the conversation
|
|
2720
|
+
// runs, and come back when it ends. Both kinds of turn still land in the same thread; what
|
|
2721
|
+
// they cannot do is overlap.
|
|
2722
|
+
//
|
|
2723
|
+
// Only the cascade is driven from here. Realtime follows the module's own session flag
|
|
2724
|
+
// (onSessionStateChanged), so the two never disagree about whether a session is live.
|
|
2725
|
+
if (!this.realtimeEnabled) {
|
|
2726
|
+
this.setVoiceSessionLive(active);
|
|
2635
2727
|
}
|
|
2636
2728
|
},
|
|
2637
2729
|
copiedMessageIndex: function copiedMessageIndex() {
|