@crestapps/ai-chat-ui 2.0.0-preview.191 → 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.css +65 -0
- package/dist/ai-chat.css.map +1 -1
- package/dist/ai-chat.js +387 -147
- package/dist/ai-chat.js.map +1 -1
- package/dist/ai-chat.min.css +1 -1
- package/dist/ai-chat.min.js +1 -1
- package/dist/ai-chat.min.js.map +1 -1
- package/dist/chat-interaction.js +337 -134
- 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/chat-markers.js +439 -0
- package/dist/chat-markers.js.map +1 -0
- package/dist/chat-markers.min.js +2 -0
- package/dist/chat-markers.min.js.map +1 -0
- package/dist/chat-widget.css +50 -0
- package/dist/chat-widget.css.map +1 -1
- package/dist/chat-widget.min.css +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
|
@@ -22,7 +22,25 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
22
22
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
23
23
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
24
24
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
25
|
-
|
|
25
|
+
/*
|
|
26
|
+
* Figure markers: the short labels the model writes, turned into pictures by the host.
|
|
27
|
+
*
|
|
28
|
+
* Retrieval hands the model a label -- [fig:1] -- and keeps the picture's real link in the message's reference
|
|
29
|
+
* map, the same way a text citation keeps its source behind "[doc:1]". A model asked to reproduce a long opaque
|
|
30
|
+
* identifier does not reproduce it: it copies the shape and substitutes ordinals, so links to figures that were
|
|
31
|
+
* never in the results came back as 404s. A label short enough to be written exactly, with the link put back
|
|
32
|
+
* here where the model cannot reach it, is what stops that.
|
|
33
|
+
*
|
|
34
|
+
* It sits on window rather than inside the manager below so the host can reach it, and so its rules can be
|
|
35
|
+
* exercised in a bare sandbox with no browser and no DOM -- see tests/realtime-client/figure-markers.test.js --
|
|
36
|
+
* exactly as window.CoreAIRealtime exposes the microphone gate's rules.
|
|
37
|
+
*
|
|
38
|
+
* The same block sits at the top of ai-chat.js: the two chat clients are separate bundles that share no
|
|
39
|
+
* module of their own, and both render the same markdown. Change one, change the other.
|
|
40
|
+
*/
|
|
41
|
+
// CoreAIChatMarkers lives in chat-markers.js, which every chat surface loads before this file.
|
|
42
|
+
|
|
43
|
+
window.chatInteractionManager = function (_window$CoreAIChatMar, _window$CoreAIChatMar2, _window$CoreAIChatMar3, _window$CoreAIChatMar4, _window$CoreAIChatMar5, _window$CoreAIChatMar6, _window$CoreAIChatMar7, _window$CoreAIChatMar8, _window$CoreAIChatMar9, _window$CoreAIChatMar0) {
|
|
26
44
|
// Defaults (can be overridden by instanceConfig)
|
|
27
45
|
var defaultConfig = {
|
|
28
46
|
// UI defaults for generated media
|
|
@@ -99,7 +117,7 @@ window.chatInteractionManager = function () {
|
|
|
99
117
|
return observer;
|
|
100
118
|
}
|
|
101
119
|
function normalizeReference(reference) {
|
|
102
|
-
var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$title, _ref4, _normalized$link, _ref5, _normalized$reference, _normalized$isGenerat;
|
|
120
|
+
var _ref, _normalized$index, _ref2, _normalized$text, _ref3, _normalized$title, _ref4, _normalized$link, _ref5, _normalized$reference, _normalized$isGenerat, _normalized$isImage;
|
|
103
121
|
if (!reference || _typeof(reference) !== 'object') {
|
|
104
122
|
return null;
|
|
105
123
|
}
|
|
@@ -110,6 +128,7 @@ window.chatInteractionManager = function () {
|
|
|
110
128
|
normalized.link = sanitizeUrl((_ref4 = (_normalized$link = normalized.link) !== null && _normalized$link !== void 0 ? _normalized$link : normalized.Link) !== null && _ref4 !== void 0 ? _ref4 : null);
|
|
111
129
|
normalized.referenceType = (_ref5 = (_normalized$reference = normalized.referenceType) !== null && _normalized$reference !== void 0 ? _normalized$reference : normalized.ReferenceType) !== null && _ref5 !== void 0 ? _ref5 : null;
|
|
112
130
|
normalized.isGenerated = ((_normalized$isGenerat = normalized.isGenerated) !== null && _normalized$isGenerat !== void 0 ? _normalized$isGenerat : normalized.IsGenerated) === true;
|
|
131
|
+
normalized.isImage = ((_normalized$isImage = normalized.isImage) !== null && _normalized$isImage !== void 0 ? _normalized$isImage : normalized.IsImage) === true;
|
|
113
132
|
return normalized;
|
|
114
133
|
}
|
|
115
134
|
function isDownloadCitationReference(reference) {
|
|
@@ -138,11 +157,38 @@ window.chatInteractionManager = function () {
|
|
|
138
157
|
}
|
|
139
158
|
return normalized;
|
|
140
159
|
}
|
|
160
|
+
|
|
161
|
+
// Installed at the top of this file so it can be unit tested without a browser; see the comment there.
|
|
162
|
+
var expandImageMarkers = window.CoreAIChatMarkers.expandImageMarkers;
|
|
141
163
|
function getCitationLabel(reference, key) {
|
|
142
164
|
return reference.title || reference.text || key;
|
|
143
165
|
}
|
|
166
|
+
|
|
167
|
+
// Which references are one citation, and how a repeated marker reads, are statements about strings and
|
|
168
|
+
// live in chat-markers.js so every chat surface numbers them the same way. Without the module each
|
|
169
|
+
// reference keeps an identity of its own and nothing merges, which is the numbering this script had
|
|
170
|
+
// before it.
|
|
171
|
+
var unmergedCitationCount = 0;
|
|
172
|
+
var citationIdentity = (_window$CoreAIChatMar = (_window$CoreAIChatMar2 = window.CoreAIChatMarkers) === null || _window$CoreAIChatMar2 === void 0 ? void 0 : _window$CoreAIChatMar2.citationIdentity) !== null && _window$CoreAIChatMar !== void 0 ? _window$CoreAIChatMar : function () {
|
|
173
|
+
return "unmerged-".concat(++unmergedCitationCount);
|
|
174
|
+
};
|
|
175
|
+
var splitCombinedCitations = (_window$CoreAIChatMar3 = (_window$CoreAIChatMar4 = window.CoreAIChatMarkers) === null || _window$CoreAIChatMar4 === void 0 ? void 0 : _window$CoreAIChatMar4.splitCombinedCitations) !== null && _window$CoreAIChatMar3 !== void 0 ? _window$CoreAIChatMar3 : function (content) {
|
|
176
|
+
return content;
|
|
177
|
+
};
|
|
178
|
+
var collapseRepeatedCitations = (_window$CoreAIChatMar5 = (_window$CoreAIChatMar6 = window.CoreAIChatMarkers) === null || _window$CoreAIChatMar6 === void 0 ? void 0 : _window$CoreAIChatMar6.collapseRepeatedCitations) !== null && _window$CoreAIChatMar5 !== void 0 ? _window$CoreAIChatMar5 : function (html) {
|
|
179
|
+
return html;
|
|
180
|
+
};
|
|
181
|
+
var separateAdjacentCitations = (_window$CoreAIChatMar7 = (_window$CoreAIChatMar8 = window.CoreAIChatMarkers) === null || _window$CoreAIChatMar8 === void 0 ? void 0 : _window$CoreAIChatMar8.separateAdjacentCitations) !== null && _window$CoreAIChatMar7 !== void 0 ? _window$CoreAIChatMar7 : function (html) {
|
|
182
|
+
return html;
|
|
183
|
+
};
|
|
184
|
+
var citationMarkerHtml = (_window$CoreAIChatMar9 = (_window$CoreAIChatMar0 = window.CoreAIChatMarkers) === null || _window$CoreAIChatMar0 === void 0 ? void 0 : _window$CoreAIChatMar0.citationMarkerHtml) !== null && _window$CoreAIChatMar9 !== void 0 ? _window$CoreAIChatMar9 : function (displayIndex) {
|
|
185
|
+
return "<sup>".concat(displayIndex, "</sup>");
|
|
186
|
+
};
|
|
144
187
|
function buildCitationDisplay(content, references) {
|
|
145
|
-
|
|
188
|
+
// Before anything looks for a reference key, the combined form the model tends to write is split into
|
|
189
|
+
// the keys it means. A key that is not found is not replaced, and an unreplaced key reaches the reader
|
|
190
|
+
// as a raw marker mid-sentence.
|
|
191
|
+
var processedContent = splitCombinedCitations((content || '').trim());
|
|
146
192
|
var messageReferences = normalizeReferences(references);
|
|
147
193
|
var referenceEntries = Object.entries(messageReferences);
|
|
148
194
|
if (!referenceEntries.length) {
|
|
@@ -183,6 +229,8 @@ window.chatInteractionManager = function () {
|
|
|
183
229
|
return a.index - b.index;
|
|
184
230
|
});
|
|
185
231
|
var citations = [];
|
|
232
|
+
var citationsByIdentity = new Map();
|
|
233
|
+
var placeholders = [];
|
|
186
234
|
var displayIndex = 1;
|
|
187
235
|
var _iterator = _createForOfIteratorHelper(citedRefs),
|
|
188
236
|
_step;
|
|
@@ -191,28 +239,48 @@ window.chatInteractionManager = function () {
|
|
|
191
239
|
var _step$value = _slicedToArray(_step.value, 2),
|
|
192
240
|
key = _step$value[0],
|
|
193
241
|
value = _step$value[1];
|
|
194
|
-
var
|
|
242
|
+
var label = getCitationLabel(value, key);
|
|
243
|
+
var link = value.link || null;
|
|
244
|
+
var identity = citationIdentity(label, link);
|
|
245
|
+
var citation = citationsByIdentity.get(identity);
|
|
246
|
+
|
|
247
|
+
// Every reference still has its key replaced, so the text never keeps a raw key; what the merge
|
|
248
|
+
// changes is only how many numbers the reader is given for them.
|
|
249
|
+
if (!citation) {
|
|
250
|
+
citation = {
|
|
251
|
+
referenceKey: key,
|
|
252
|
+
referenceKeys: [],
|
|
253
|
+
displayIndex: displayIndex++,
|
|
254
|
+
label: label,
|
|
255
|
+
link: link,
|
|
256
|
+
isDownload: isDownloadCitationReference(value)
|
|
257
|
+
};
|
|
258
|
+
citationsByIdentity.set(identity, citation);
|
|
259
|
+
citations.push(citation);
|
|
260
|
+
}
|
|
261
|
+
citation.referenceKeys.push(key);
|
|
262
|
+
var placeholder = "__CITE_".concat(citation.displayIndex, "_").concat(value.index || citation.displayIndex, "__");
|
|
195
263
|
processedContent = processedContent.replaceAll(key, placeholder);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
displayIndex: displayIndex,
|
|
199
|
-
label:
|
|
200
|
-
link: value.link || null,
|
|
201
|
-
isDownload: isDownloadCitationReference(value),
|
|
202
|
-
placeholder: placeholder
|
|
264
|
+
placeholders.push({
|
|
265
|
+
placeholder: placeholder,
|
|
266
|
+
displayIndex: citation.displayIndex,
|
|
267
|
+
label: citation.label
|
|
203
268
|
});
|
|
204
|
-
displayIndex++;
|
|
205
269
|
}
|
|
206
270
|
} catch (err) {
|
|
207
271
|
_iterator.e(err);
|
|
208
272
|
} finally {
|
|
209
273
|
_iterator.f();
|
|
210
274
|
}
|
|
211
|
-
for (var _i2 = 0,
|
|
212
|
-
var
|
|
213
|
-
processedContent = processedContent.replaceAll(
|
|
275
|
+
for (var _i2 = 0, _placeholders = placeholders; _i2 < _placeholders.length; _i2++) {
|
|
276
|
+
var entry = _placeholders[_i2];
|
|
277
|
+
processedContent = processedContent.replaceAll(entry.placeholder, citationMarkerHtml(entry.displayIndex, entry.label));
|
|
214
278
|
}
|
|
215
|
-
|
|
279
|
+
|
|
280
|
+
// Merged citations can leave the same number twice over a sentence; that is collapsed before the
|
|
281
|
+
// commas go in, or the reader is given "1,1".
|
|
282
|
+
processedContent = collapseRepeatedCitations(processedContent);
|
|
283
|
+
processedContent = separateAdjacentCitations(processedContent);
|
|
216
284
|
|
|
217
285
|
// Generated files (such as exported tabular data) are always offered as a download even when
|
|
218
286
|
// the model does not cite them inline, so the user never loses access to the produced file.
|
|
@@ -225,6 +293,7 @@ window.chatInteractionManager = function () {
|
|
|
225
293
|
_value = _step2$value[1];
|
|
226
294
|
citations.push({
|
|
227
295
|
referenceKey: _key,
|
|
296
|
+
referenceKeys: [_key],
|
|
228
297
|
displayIndex: displayIndex,
|
|
229
298
|
label: getCitationLabel(_value, _key),
|
|
230
299
|
link: _value.link || null,
|
|
@@ -252,12 +321,27 @@ window.chatInteractionManager = function () {
|
|
|
252
321
|
if (!copyContent || !Array.isArray(citations) || citations.length === 0) {
|
|
253
322
|
return copyContent;
|
|
254
323
|
}
|
|
324
|
+
|
|
325
|
+
// Every key the citation absorbed is replaced, not just the first: a merged key left behind would
|
|
326
|
+
// reach the clipboard as the raw reference token the reader never saw on the page.
|
|
255
327
|
var _iterator3 = _createForOfIteratorHelper(citations),
|
|
256
328
|
_step3;
|
|
257
329
|
try {
|
|
258
330
|
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
331
|
+
var _citation$referenceKe;
|
|
259
332
|
var citation = _step3.value;
|
|
260
|
-
|
|
333
|
+
var _iterator5 = _createForOfIteratorHelper((_citation$referenceKe = citation.referenceKeys) !== null && _citation$referenceKe !== void 0 && _citation$referenceKe.length ? citation.referenceKeys : [citation.referenceKey]),
|
|
334
|
+
_step5;
|
|
335
|
+
try {
|
|
336
|
+
for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
|
|
337
|
+
var key = _step5.value;
|
|
338
|
+
copyContent = copyContent.replaceAll(key, "[".concat(citation.displayIndex, "]"));
|
|
339
|
+
}
|
|
340
|
+
} catch (err) {
|
|
341
|
+
_iterator5.e(err);
|
|
342
|
+
} finally {
|
|
343
|
+
_iterator5.f();
|
|
344
|
+
}
|
|
261
345
|
}
|
|
262
346
|
} catch (err) {
|
|
263
347
|
_iterator3.e(err);
|
|
@@ -286,13 +370,20 @@ window.chatInteractionManager = function () {
|
|
|
286
370
|
function updateMessagePresentation(message, references) {
|
|
287
371
|
var messageReferences = normalizeReferences(references !== null && references !== void 0 ? references : message.references);
|
|
288
372
|
var rawContent = typeof message.rawContent === 'string' ? message.rawContent : typeof message.content === 'string' ? message.content : '';
|
|
289
|
-
|
|
373
|
+
// Figure markers become images before anything else reads the content: the citation pass looks for its
|
|
374
|
+
// own keys in what is left, and the markdown parser then sees an ordinary image.
|
|
375
|
+
var displayContent = expandImageMarkers(rawContent, messageReferences);
|
|
376
|
+
var citationDisplay = buildCitationDisplay(displayContent, messageReferences);
|
|
290
377
|
message.rawContent = rawContent;
|
|
291
378
|
message.content = rawContent;
|
|
292
379
|
message.displayContent = citationDisplay.content;
|
|
293
380
|
message.references = messageReferences;
|
|
294
381
|
message.citationReferences = citationDisplay.citations;
|
|
295
|
-
|
|
382
|
+
// Copied from the expanded content, so a copied answer carries the picture's link rather than a label
|
|
383
|
+
// that means nothing outside this page.
|
|
384
|
+
// Split here too: the copy is built from the expanded content rather than the display path's output,
|
|
385
|
+
// so a combined marker would otherwise survive into the clipboard as a raw key.
|
|
386
|
+
message.copyContent = buildCopyContent(splitCombinedCitations(displayContent), citationDisplay.citations);
|
|
296
387
|
message.htmlContent = parseMarkdownContent(citationDisplay.content, message);
|
|
297
388
|
return message;
|
|
298
389
|
}
|
|
@@ -336,7 +427,12 @@ window.chatInteractionManager = function () {
|
|
|
336
427
|
if (!src) return '';
|
|
337
428
|
var alt = data.text || defaultConfig.generatedImageAltText;
|
|
338
429
|
var maxWidth = defaultConfig.generatedImageMaxWidth;
|
|
339
|
-
|
|
430
|
+
// Spans, not divs. A figure marker is often written mid-sentence -- "Figures [fig:1], [fig:2] and
|
|
431
|
+
// [fig:3] show..." -- and a block element cannot sit inside a paragraph: the browser closes the <p>
|
|
432
|
+
// at the first one, so the sentence is torn into fragments and the commas between the markers are
|
|
433
|
+
// left stranded on their own lines. Inline-block keeps the figure looking exactly the same while
|
|
434
|
+
// remaining something a paragraph can legally contain.
|
|
435
|
+
return "<span class=\"generated-image-container\">\n <img src=\"".concat(src, "\" alt=\"").concat(alt, "\" class=\"img-thumbnail\" style=\"max-width: ").concat(maxWidth, "px; height: auto;\" />\n <span class=\"mt-2 d-block\">\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 </span>\n </span>");
|
|
340
436
|
};
|
|
341
437
|
|
|
342
438
|
// Chart counter for unique IDs
|
|
@@ -351,6 +447,11 @@ window.chatInteractionManager = function () {
|
|
|
351
447
|
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>";
|
|
352
448
|
}
|
|
353
449
|
|
|
450
|
+
// The marker itself is read by chat-markers.js, shared with every other chat surface, so the three of them
|
|
451
|
+
// cannot disagree about what a marker is. Everything below -- the container, the id scheme, the Chart.js
|
|
452
|
+
// call -- is this surface's own and legitimately differs from the others.
|
|
453
|
+
var findChartMarker = window.CoreAIChatMarkers.findChartMarker;
|
|
454
|
+
|
|
354
455
|
// Register [chart:{...json...}] as a native marked block extension so the
|
|
355
456
|
// markdown parser handles chart markers inline with surrounding text.
|
|
356
457
|
marked.use({
|
|
@@ -362,7 +463,7 @@ window.chatInteractionManager = function () {
|
|
|
362
463
|
return idx >= 0 ? idx : undefined;
|
|
363
464
|
},
|
|
364
465
|
tokenizer: function tokenizer(src) {
|
|
365
|
-
var extracted =
|
|
466
|
+
var extracted = findChartMarker(src);
|
|
366
467
|
if (!extracted || extracted.startIndex !== 0) {
|
|
367
468
|
return undefined;
|
|
368
469
|
}
|
|
@@ -385,69 +486,60 @@ window.chatInteractionManager = function () {
|
|
|
385
486
|
}]
|
|
386
487
|
});
|
|
387
488
|
|
|
388
|
-
//
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
489
|
+
// An image the assistant named but the server will not serve. The address is written by a language
|
|
490
|
+
// model from what retrieval handed it, so a mistyped or invented one is possible, and the browser's
|
|
491
|
+
// default for that is a broken-image icon captioned with the alt text -- which reads as a real picture
|
|
492
|
+
// that failed to arrive. Saying plainly that it could not be loaded is both truer and quieter.
|
|
493
|
+
//
|
|
494
|
+
// Wired here rather than as an inline onerror attribute because message bodies go through DOMPurify,
|
|
495
|
+
// which strips event-handler attributes.
|
|
496
|
+
function markBrokenImagesInMessage() {
|
|
497
|
+
// Scanning the document rather than one message, because the value threaded through the render
|
|
498
|
+
// path is the message DATA object -- the thing carrying content, references and pending charts --
|
|
499
|
+
// and not the element it was rendered into. Wiring is idempotent via the data-broken-wired mark,
|
|
500
|
+
// so re-scanning after each render costs a query over the handful of images already handled.
|
|
501
|
+
if (typeof document === 'undefined' || typeof requestAnimationFrame !== 'function') {
|
|
502
|
+
return;
|
|
394
503
|
}
|
|
395
504
|
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
escape = false;
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
415
|
-
if (ch === '\\') {
|
|
416
|
-
escape = true;
|
|
417
|
-
continue;
|
|
418
|
-
}
|
|
419
|
-
if (ch === '"') {
|
|
420
|
-
inString = false;
|
|
421
|
-
}
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
if (ch === '"') {
|
|
425
|
-
inString = true;
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
if (ch === '{') {
|
|
429
|
-
depth++;
|
|
430
|
-
} else if (ch === '}') {
|
|
431
|
-
depth--;
|
|
432
|
-
if (depth === 0) {
|
|
433
|
-
var jsonEnd = i;
|
|
434
|
-
// Expect closing bracket after JSON
|
|
435
|
-
var closeBracketIndex = text.indexOf(']', jsonEnd + 1);
|
|
436
|
-
if (closeBracketIndex < 0) {
|
|
437
|
-
return null;
|
|
505
|
+
// Deferred for the same reason the charts below are: the render that produced these elements has
|
|
506
|
+
// not necessarily been flushed to the DOM when this runs.
|
|
507
|
+
requestAnimationFrame(wireBrokenImageHandlers);
|
|
508
|
+
}
|
|
509
|
+
function wireBrokenImageHandlers() {
|
|
510
|
+
var images = document.querySelectorAll('.generated-image-container img:not([data-broken-wired])');
|
|
511
|
+
var _iterator6 = _createForOfIteratorHelper(images),
|
|
512
|
+
_step6;
|
|
513
|
+
try {
|
|
514
|
+
var _loop = function _loop() {
|
|
515
|
+
var image = _step6.value;
|
|
516
|
+
image.setAttribute('data-broken-wired', 'true');
|
|
517
|
+
image.addEventListener('error', function () {
|
|
518
|
+
var container = image.closest('.generated-image-container');
|
|
519
|
+
if (!container || container.dataset.brokenHandled) {
|
|
520
|
+
return;
|
|
438
521
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
}
|
|
522
|
+
container.dataset.brokenHandled = 'true';
|
|
523
|
+
container.hidden = true;
|
|
524
|
+
var note = document.createElement('p');
|
|
525
|
+
note.className = 'text-muted small fst-italic mb-2';
|
|
526
|
+
note.textContent = image.alt ? "(the picture \"".concat(image.alt, "\" could not be loaded)") : '(that picture could not be loaded)';
|
|
527
|
+
container.insertAdjacentElement('afterend', note);
|
|
528
|
+
}, {
|
|
529
|
+
once: true
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
|
|
533
|
+
_loop();
|
|
446
534
|
}
|
|
535
|
+
} catch (err) {
|
|
536
|
+
_iterator6.e(err);
|
|
537
|
+
} finally {
|
|
538
|
+
_iterator6.f();
|
|
447
539
|
}
|
|
448
|
-
return null;
|
|
449
540
|
}
|
|
450
|
-
function
|
|
541
|
+
function renderMessageMedia(message) {
|
|
542
|
+
markBrokenImagesInMessage();
|
|
451
543
|
if (!message || !message._pendingCharts || !message._pendingCharts.length) {
|
|
452
544
|
return;
|
|
453
545
|
}
|
|
@@ -459,11 +551,11 @@ window.chatInteractionManager = function () {
|
|
|
459
551
|
// Defer to requestAnimationFrame so the browser has fully laid out the
|
|
460
552
|
// canvas elements before Chart.js reads their dimensions.
|
|
461
553
|
requestAnimationFrame(function () {
|
|
462
|
-
var
|
|
463
|
-
|
|
554
|
+
var _iterator7 = _createForOfIteratorHelper(charts),
|
|
555
|
+
_step7;
|
|
464
556
|
try {
|
|
465
|
-
for (
|
|
466
|
-
var c =
|
|
557
|
+
for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
|
|
558
|
+
var c = _step7.value;
|
|
467
559
|
var canvas = document.getElementById(c.chartId);
|
|
468
560
|
if (!canvas) {
|
|
469
561
|
continue;
|
|
@@ -496,9 +588,9 @@ window.chatInteractionManager = function () {
|
|
|
496
588
|
}
|
|
497
589
|
}
|
|
498
590
|
} catch (err) {
|
|
499
|
-
|
|
591
|
+
_iterator7.e(err);
|
|
500
592
|
} finally {
|
|
501
|
-
|
|
593
|
+
_iterator7.f();
|
|
502
594
|
}
|
|
503
595
|
});
|
|
504
596
|
}
|
|
@@ -599,6 +691,11 @@ window.chatInteractionManager = function () {
|
|
|
599
691
|
isConversationMode: false,
|
|
600
692
|
// Realtime (speech-to-speech) controller from the shared CoreAIRealtime module.
|
|
601
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,
|
|
602
699
|
notifications: [],
|
|
603
700
|
notificationDismissTimers: {},
|
|
604
701
|
copyTitle: config.copyTitle,
|
|
@@ -646,7 +743,7 @@ window.chatInteractionManager = function () {
|
|
|
646
743
|
((_data$messages = data.messages) !== null && _data$messages !== void 0 ? _data$messages : []).forEach(function (msg) {
|
|
647
744
|
_this.addMessage(msg);
|
|
648
745
|
_this.$nextTick(function () {
|
|
649
|
-
|
|
746
|
+
renderMessageMedia(msg);
|
|
650
747
|
});
|
|
651
748
|
});
|
|
652
749
|
});
|
|
@@ -804,7 +901,7 @@ window.chatInteractionManager = function () {
|
|
|
804
901
|
msg.references = normalizeReferences(Object.assign({}, msg.references || {}, references || {}));
|
|
805
902
|
updateMessagePresentation(msg, msg.references);
|
|
806
903
|
_this.$nextTick(function () {
|
|
807
|
-
|
|
904
|
+
renderMessageMedia(msg);
|
|
808
905
|
_this.scrollToBottom();
|
|
809
906
|
});
|
|
810
907
|
}
|
|
@@ -923,7 +1020,7 @@ window.chatInteractionManager = function () {
|
|
|
923
1020
|
this.hidePlaceholder();
|
|
924
1021
|
this.$nextTick(function () {
|
|
925
1022
|
// Render any pending charts once the DOM is updated
|
|
926
|
-
|
|
1023
|
+
renderMessageMedia(message);
|
|
927
1024
|
_this3.scrollToBottom();
|
|
928
1025
|
});
|
|
929
1026
|
},
|
|
@@ -1003,6 +1100,11 @@ window.chatInteractionManager = function () {
|
|
|
1003
1100
|
}
|
|
1004
1101
|
return _context2.a(2);
|
|
1005
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
|
+
|
|
1006
1108
|
// Stop any active recording before sending.
|
|
1007
1109
|
if (_this4.isRecording) {
|
|
1008
1110
|
_this4.stopRecording();
|
|
@@ -1091,7 +1193,7 @@ window.chatInteractionManager = function () {
|
|
|
1091
1193
|
updateMessagePresentation(message, references);
|
|
1092
1194
|
_this5.messages[messageIndex] = message;
|
|
1093
1195
|
_this5.$nextTick(function () {
|
|
1094
|
-
|
|
1196
|
+
renderMessageMedia(message);
|
|
1095
1197
|
_this5.scrollToBottom();
|
|
1096
1198
|
});
|
|
1097
1199
|
},
|
|
@@ -1150,7 +1252,7 @@ window.chatInteractionManager = function () {
|
|
|
1150
1252
|
updateMessagePresentation(message, references);
|
|
1151
1253
|
this.messages[messageIndex] = message;
|
|
1152
1254
|
this.$nextTick(function () {
|
|
1153
|
-
|
|
1255
|
+
renderMessageMedia(message);
|
|
1154
1256
|
_this6.scrollToBottom();
|
|
1155
1257
|
});
|
|
1156
1258
|
}
|
|
@@ -1303,18 +1405,18 @@ window.chatInteractionManager = function () {
|
|
|
1303
1405
|
}, 0);
|
|
1304
1406
|
var combined = new Uint8Array(totalLength);
|
|
1305
1407
|
var offset = 0;
|
|
1306
|
-
var
|
|
1307
|
-
|
|
1408
|
+
var _iterator8 = _createForOfIteratorHelper(this.audioChunks),
|
|
1409
|
+
_step8;
|
|
1308
1410
|
try {
|
|
1309
|
-
for (
|
|
1310
|
-
var chunk =
|
|
1411
|
+
for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
|
|
1412
|
+
var chunk = _step8.value;
|
|
1311
1413
|
combined.set(chunk, offset);
|
|
1312
1414
|
offset += chunk.length;
|
|
1313
1415
|
}
|
|
1314
1416
|
} catch (err) {
|
|
1315
|
-
|
|
1417
|
+
_iterator8.e(err);
|
|
1316
1418
|
} finally {
|
|
1317
|
-
|
|
1419
|
+
_iterator8.f();
|
|
1318
1420
|
}
|
|
1319
1421
|
this.audioChunks = [];
|
|
1320
1422
|
var blob = new Blob([combined], {
|
|
@@ -1397,6 +1499,39 @@ window.chatInteractionManager = function () {
|
|
|
1397
1499
|
return _this12.updateTtsPlaybackButtons();
|
|
1398
1500
|
});
|
|
1399
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
|
+
},
|
|
1400
1535
|
toggleConversationMode: function toggleConversationMode() {
|
|
1401
1536
|
if (this.isConversationMode) {
|
|
1402
1537
|
this.stopConversationMode();
|
|
@@ -1404,6 +1539,17 @@ window.chatInteractionManager = function () {
|
|
|
1404
1539
|
this.startConversationMode();
|
|
1405
1540
|
}
|
|
1406
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
|
+
},
|
|
1407
1553
|
startConversationMode: function startConversationMode() {
|
|
1408
1554
|
var _this13 = this;
|
|
1409
1555
|
if (!this.conversationModeEnabled || this.isConversationMode || !this.connection) {
|
|
@@ -2038,6 +2184,8 @@ window.chatInteractionManager = function () {
|
|
|
2038
2184
|
// module owns the mic capture, playback, echo guard, push-to-talk and audio settings; the
|
|
2039
2185
|
// callbacks below plug it into this app's connection and conversation-transcript display.
|
|
2040
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);
|
|
2041
2189
|
this.realtimeController = window.CoreAIRealtime.attach({
|
|
2042
2190
|
connection: this.connection,
|
|
2043
2191
|
ensureConnected: function ensureConnected() {
|
|
@@ -2060,13 +2208,23 @@ window.chatInteractionManager = function () {
|
|
|
2060
2208
|
},
|
|
2061
2209
|
capableDeployments: config.realtimeCapableDeployments || [],
|
|
2062
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).
|
|
2063
2218
|
selectors: {
|
|
2064
2219
|
realtimeButton: config.realtimeButtonElementSelector,
|
|
2065
|
-
input: config.inputElementSelector,
|
|
2066
|
-
sendButton: config.sendButtonElementSelector,
|
|
2067
2220
|
micButton: config.micButtonElementSelector,
|
|
2068
|
-
conversationButton: config.conversationButtonElementSelector
|
|
2069
|
-
|
|
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);
|
|
2070
2228
|
},
|
|
2071
2229
|
onActivate: function onActivate() {
|
|
2072
2230
|
_this18.isConversationMode = true;
|
|
@@ -2127,6 +2285,11 @@ window.chatInteractionManager = function () {
|
|
|
2127
2285
|
}
|
|
2128
2286
|
});
|
|
2129
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);
|
|
2130
2293
|
}
|
|
2131
2294
|
},
|
|
2132
2295
|
// Populates the per-interaction realtime voice picker from the selected realtime deployment's
|
|
@@ -2140,28 +2303,46 @@ window.chatInteractionManager = function () {
|
|
|
2140
2303
|
return;
|
|
2141
2304
|
}
|
|
2142
2305
|
var voiceGroup = config.realtimeVoiceGroupElementSelector ? document.querySelector(config.realtimeVoiceGroupElementSelector) : null;
|
|
2143
|
-
var
|
|
2306
|
+
var conversationSelect = config.conversationDeploymentSelectElementSelector ? document.querySelector(config.conversationDeploymentSelectElementSelector) : null;
|
|
2144
2307
|
var capable = (config.realtimeCapableDeployments || []).map(function (n) {
|
|
2145
2308
|
return (n || '').toLowerCase();
|
|
2146
2309
|
});
|
|
2147
2310
|
var savedVoiceId = config.realtimeVoiceName || '';
|
|
2311
|
+
var self = this;
|
|
2148
2312
|
function isRealtimeDeployment(name) {
|
|
2149
2313
|
return name && capable.indexOf(name.toLowerCase()) !== -1;
|
|
2150
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
|
+
}
|
|
2151
2326
|
function loadVoices() {
|
|
2152
2327
|
return _loadVoices.apply(this, arguments);
|
|
2153
2328
|
}
|
|
2154
2329
|
function _loadVoices() {
|
|
2155
2330
|
_loadVoices = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
|
|
2156
|
-
var deploymentName, realtime, response, result, grouped,
|
|
2331
|
+
var deploymentName, realtime, response, contentType, result, grouped, _iterator9, _step9, voice, groupName, _t2;
|
|
2157
2332
|
return _regenerator().w(function (_context4) {
|
|
2158
2333
|
while (1) switch (_context4.p = _context4.n) {
|
|
2159
2334
|
case 0:
|
|
2160
|
-
deploymentName =
|
|
2161
|
-
realtime =
|
|
2335
|
+
deploymentName = resolveConversationDeployment();
|
|
2336
|
+
realtime = !!deploymentName;
|
|
2162
2337
|
if (voiceGroup) {
|
|
2163
2338
|
voiceGroup.classList.toggle('d-none', !realtime);
|
|
2164
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
|
+
}
|
|
2165
2346
|
if (realtime) {
|
|
2166
2347
|
_context4.n = 1;
|
|
2167
2348
|
break;
|
|
@@ -2176,21 +2357,29 @@ window.chatInteractionManager = function () {
|
|
|
2176
2357
|
});
|
|
2177
2358
|
case 3:
|
|
2178
2359
|
response = _context4.v;
|
|
2179
|
-
|
|
2180
|
-
|
|
2360
|
+
contentType = (response.headers.get('content-type') || '').toLowerCase(); // A sign-in redirect answers with HTML and response.ok, so this has to be checked
|
|
2361
|
+
// rather than parsed: the default voice is a fine outcome, a parser error is not.
|
|
2362
|
+
if (!(!response.ok || contentType.indexOf('json') < 0)) {
|
|
2363
|
+
_context4.n = 4;
|
|
2364
|
+
break;
|
|
2365
|
+
}
|
|
2366
|
+
return _context4.a(2);
|
|
2181
2367
|
case 4:
|
|
2368
|
+
_context4.n = 5;
|
|
2369
|
+
return response.json();
|
|
2370
|
+
case 5:
|
|
2182
2371
|
result = _context4.v;
|
|
2183
2372
|
if (!(!result || !Array.isArray(result.voices))) {
|
|
2184
|
-
_context4.n =
|
|
2373
|
+
_context4.n = 6;
|
|
2185
2374
|
break;
|
|
2186
2375
|
}
|
|
2187
2376
|
return _context4.a(2);
|
|
2188
|
-
case
|
|
2377
|
+
case 6:
|
|
2189
2378
|
grouped = new Map();
|
|
2190
|
-
|
|
2379
|
+
_iterator9 = _createForOfIteratorHelper(result.voices);
|
|
2191
2380
|
try {
|
|
2192
|
-
for (
|
|
2193
|
-
voice =
|
|
2381
|
+
for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) {
|
|
2382
|
+
voice = _step9.value;
|
|
2194
2383
|
groupName = voice.gender || 'Voices';
|
|
2195
2384
|
if (!grouped.has(groupName)) {
|
|
2196
2385
|
grouped.set(groupName, []);
|
|
@@ -2198,9 +2387,9 @@ window.chatInteractionManager = function () {
|
|
|
2198
2387
|
grouped.get(groupName).push(voice);
|
|
2199
2388
|
}
|
|
2200
2389
|
} catch (err) {
|
|
2201
|
-
|
|
2390
|
+
_iterator9.e(err);
|
|
2202
2391
|
} finally {
|
|
2203
|
-
|
|
2392
|
+
_iterator9.f();
|
|
2204
2393
|
}
|
|
2205
2394
|
Array.from(grouped.keys()).sort(function (a, b) {
|
|
2206
2395
|
return a.localeCompare(b);
|
|
@@ -2216,20 +2405,20 @@ window.chatInteractionManager = function () {
|
|
|
2216
2405
|
});
|
|
2217
2406
|
voiceSelect.appendChild(optgroup);
|
|
2218
2407
|
});
|
|
2219
|
-
_context4.n =
|
|
2408
|
+
_context4.n = 8;
|
|
2220
2409
|
break;
|
|
2221
|
-
case 6:
|
|
2222
|
-
_context4.p = 6;
|
|
2223
|
-
_t2 = _context4.v;
|
|
2224
2410
|
case 7:
|
|
2411
|
+
_context4.p = 7;
|
|
2412
|
+
_t2 = _context4.v;
|
|
2413
|
+
case 8:
|
|
2225
2414
|
return _context4.a(2);
|
|
2226
2415
|
}
|
|
2227
|
-
}, _callee4, null, [[2,
|
|
2416
|
+
}, _callee4, null, [[2, 7]]);
|
|
2228
2417
|
}));
|
|
2229
2418
|
return _loadVoices.apply(this, arguments);
|
|
2230
2419
|
}
|
|
2231
|
-
if (
|
|
2232
|
-
|
|
2420
|
+
if (conversationSelect) {
|
|
2421
|
+
conversationSelect.addEventListener('change', loadVoices);
|
|
2233
2422
|
}
|
|
2234
2423
|
loadVoices();
|
|
2235
2424
|
},
|
|
@@ -2521,17 +2710,20 @@ window.chatInteractionManager = function () {
|
|
|
2521
2710
|
// no longer mutes tracks; browser echo cancellation handles echo.
|
|
2522
2711
|
},
|
|
2523
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.
|
|
2524
2715
|
if (this.micButton) {
|
|
2525
2716
|
this.micButton.style.display = active ? 'none' : this.speechToTextEnabled ? '' : 'none';
|
|
2526
2717
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
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);
|
|
2535
2727
|
}
|
|
2536
2728
|
},
|
|
2537
2729
|
copiedMessageIndex: function copiedMessageIndex() {
|
|
@@ -3150,9 +3342,20 @@ window.chatInteractionDocumentManager = function () {
|
|
|
3150
3342
|
showUploadStatus('Failed to remove document.', 'text-danger');
|
|
3151
3343
|
return _context8.a(2);
|
|
3152
3344
|
case 4:
|
|
3153
|
-
|
|
3154
|
-
|
|
3345
|
+
if (!((response.headers.get('content-type') || '').toLowerCase().indexOf('json') < 0)) {
|
|
3346
|
+
_context8.n = 5;
|
|
3347
|
+
break;
|
|
3348
|
+
}
|
|
3349
|
+
console.error('Remove document: response was not JSON.', {
|
|
3350
|
+
status: response.status,
|
|
3351
|
+
redirected: response.redirected
|
|
3352
|
+
});
|
|
3353
|
+
showUploadStatus(response.redirected ? 'You may not be signed in, or you do not have permission for this. Please reload and try again.' : 'Failed to remove document.', 'text-danger');
|
|
3354
|
+
return _context8.a(2);
|
|
3155
3355
|
case 5:
|
|
3356
|
+
_context8.n = 6;
|
|
3357
|
+
return response.json();
|
|
3358
|
+
case 6:
|
|
3156
3359
|
result = _context8.v;
|
|
3157
3360
|
serverDocuments = (Array.isArray(result.documents) ? result.documents : []).map(normalizeDocumentInfo).filter(function (document) {
|
|
3158
3361
|
return document && document.documentId;
|
|
@@ -3162,17 +3365,17 @@ window.chatInteractionDocumentManager = function () {
|
|
|
3162
3365
|
});
|
|
3163
3366
|
renderDocuments();
|
|
3164
3367
|
showUploadStatus('Document removed.', 'text-success');
|
|
3165
|
-
_context8.n =
|
|
3368
|
+
_context8.n = 8;
|
|
3166
3369
|
break;
|
|
3167
|
-
case
|
|
3168
|
-
_context8.p =
|
|
3370
|
+
case 7:
|
|
3371
|
+
_context8.p = 7;
|
|
3169
3372
|
_t6 = _context8.v;
|
|
3170
3373
|
console.error('Remove failed:', _t6);
|
|
3171
3374
|
showUploadStatus('Failed to remove document.', 'text-danger');
|
|
3172
|
-
case
|
|
3375
|
+
case 8:
|
|
3173
3376
|
return _context8.a(2);
|
|
3174
3377
|
}
|
|
3175
|
-
}, _callee8, null, [[1,
|
|
3378
|
+
}, _callee8, null, [[1, 7]]);
|
|
3176
3379
|
}));
|
|
3177
3380
|
return _removeDocument.apply(this, arguments);
|
|
3178
3381
|
}
|