@crestapps/ai-chat-ui 2.0.0-preview.215 → 2.0.0-preview.223

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 (49) hide show
  1. package/dist/ai-chat-widget.js +762 -964
  2. package/dist/ai-chat-widget.js.map +1 -1
  3. package/dist/ai-chat-widget.min.js +1 -1
  4. package/dist/ai-chat-widget.min.js.map +1 -1
  5. package/dist/ai-chat.css +0 -2
  6. package/dist/ai-chat.js +2380 -3698
  7. package/dist/ai-chat.js.map +1 -1
  8. package/dist/ai-chat.min.css +1 -1
  9. package/dist/ai-chat.min.js +78 -1
  10. package/dist/ai-chat.min.js.map +1 -1
  11. package/dist/chat-image-carousel.css +0 -2
  12. package/dist/chat-image-carousel.js +306 -482
  13. package/dist/chat-image-carousel.js.map +1 -1
  14. package/dist/chat-image-carousel.min.css +1 -1
  15. package/dist/chat-image-carousel.min.js +1 -1
  16. package/dist/chat-image-carousel.min.js.map +1 -1
  17. package/dist/chat-interaction-settings.js +221 -293
  18. package/dist/chat-interaction-settings.js.map +1 -1
  19. package/dist/chat-interaction-settings.min.js +1 -1
  20. package/dist/chat-interaction-settings.min.js.map +1 -1
  21. package/dist/chat-interaction.js +2330 -3557
  22. package/dist/chat-interaction.js.map +1 -1
  23. package/dist/chat-interaction.min.js +62 -1
  24. package/dist/chat-interaction.min.js.map +1 -1
  25. package/dist/chat-markers.js +193 -485
  26. package/dist/chat-markers.js.map +1 -1
  27. package/dist/chat-markers.min.js +2 -1
  28. package/dist/chat-markers.min.js.map +1 -1
  29. package/dist/chat-widget.css +0 -2
  30. package/dist/chat-widget.min.css +1 -1
  31. package/dist/document-drop-zone.css +0 -2
  32. package/dist/document-drop-zone.js +127 -153
  33. package/dist/document-drop-zone.js.map +1 -1
  34. package/dist/document-drop-zone.min.css +1 -1
  35. package/dist/document-drop-zone.min.js +1 -1
  36. package/dist/document-drop-zone.min.js.map +1 -1
  37. package/dist/realtime-audio.js +1639 -2504
  38. package/dist/realtime-audio.js.map +1 -1
  39. package/dist/realtime-audio.min.js +3 -1
  40. package/dist/realtime-audio.min.js.map +1 -1
  41. package/dist/technical-name-generator.js +58 -80
  42. package/dist/technical-name-generator.js.map +1 -1
  43. package/dist/technical-name-generator.min.js +1 -1
  44. package/dist/technical-name-generator.min.js.map +1 -1
  45. package/package.json +1 -5
  46. package/dist/ai-chat.css.map +0 -1
  47. package/dist/chat-image-carousel.css.map +0 -1
  48. package/dist/chat-widget.css.map +0 -1
  49. package/dist/document-drop-zone.css.map +0 -1
@@ -1,485 +1,309 @@
1
1
  /*
2
- ** NOTE: This file is generated by Gulp and should not be edited directly!
3
- ** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp.
2
+ ** NOTE: This file is generated by the assets build (npm run build) and should not be edited directly!
3
+ ** Edit its sources under Assets/ and rebuild; see Assets.json for the files it is built from.
4
4
  */
5
-
6
- 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; } } }; }
7
- 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; } }
8
- function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
9
- /*
10
- * Stacks the pictures a single answer shows one after another into one carousel.
11
- *
12
- * A tool that previews a document -- slides, sheets, pages -- hands back one figure marker per picture, all on a
13
- * line of their own, and each one renders at full thumbnail size: eight slides is eight thumbnails running down
14
- * the conversation. Here a run of two or more pictures with nothing but whitespace between them becomes a stack
15
- * of cards instead: the current picture in front, its neighbours fanned out behind it, moved through by swiping,
16
- * clicking a neighbour, the arrows, the dots or the arrow keys. A picture written mid-sentence keeps its place.
17
- *
18
- * It works on the sanitized HTML every chat surface already produces, so the surfaces share one implementation
19
- * without sharing a renderer, and the carousel is written in the same pass as the message -- there is no frame
20
- * of full-size pictures before they are gathered up. HTML with no such run is handed back exactly as it came.
21
- *
22
- * Loaded on its own, like chat-markers.js, and optional: a surface without it renders the pictures as before.
23
- */
24
- window.CoreAIChatImageCarousel = window.CoreAIChatImageCarousel || function () {
25
- 'use strict';
26
-
27
- var imageContainerClass = 'generated-image-container';
28
-
29
- // How far a drag has to travel before it counts as a swipe rather than a click.
30
- var swipeThreshold = 40;
31
-
32
- // How far a drag has to travel before the card starts following the pointer.
33
- var dragStartThreshold = 8;
34
-
35
- // Cards further than this from the current one are stacked out of sight behind the last visible one.
36
- var furthestVisibleOffset = 2;
37
-
38
- // Anything that is content in its own right, so pictures either side of it are not one run.
39
- var contentBetweenSelector = 'img, svg, canvas, video, audio, iframe, table, hr, input';
40
- var previousIcon = '<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false"><path d="M10 3 5 8l5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
41
- var nextIcon = '<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false"><path d="m6 3 5 5-5 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>';
42
-
43
- // How large the reader wants previews drawn. It is the reader's taste rather than the answer's, so one choice
44
- // applies to every carousel on the page and is kept in the browser for the next visit.
45
- var sizes = [{
46
- name: 'small',
47
- text: 'S',
48
- label: 'Small previews'
49
- }, {
50
- name: 'medium',
51
- text: 'M',
52
- label: 'Medium previews'
53
- }, {
54
- name: 'large',
55
- text: 'L',
56
- label: 'Large previews'
57
- }];
58
- var defaultSize = 'medium';
59
- var sizeStorageKey = 'coreai-image-carousel-size';
60
-
61
- // A streamed answer is re-rendered on every chunk, and every render builds the carousel afresh. The picture
62
- // the reader moved to is remembered here so the next render opens on it rather than back on the first.
63
- var activeIndexByCarouselKey = new Map();
64
- var drag = null;
65
- var suppressNextClick = false;
66
- function stackAdjacentImages(html) {
67
- if (typeof document === 'undefined' || !html) {
68
- return html;
69
- }
70
- var firstContainerIndex = html.indexOf(imageContainerClass);
71
- if (firstContainerIndex < 0 || html.indexOf(imageContainerClass, firstContainerIndex + imageContainerClass.length) < 0) {
72
- return html;
73
- }
74
- var template = document.createElement('template');
75
- template.innerHTML = html;
76
- var runs = findImageRuns(template.content);
77
- if (runs.length === 0) {
78
- return html;
79
- }
80
- var _iterator = _createForOfIteratorHelper(runs),
81
- _step;
82
- try {
83
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
84
- var run = _step.value;
85
- buildCarousel(run);
86
- }
87
- } catch (err) {
88
- _iterator.e(err);
89
- } finally {
90
- _iterator.f();
91
- }
92
- return template.innerHTML;
93
- }
94
- function findImageRuns(root) {
95
- var runs = [];
96
- var run = [];
97
- var _iterator2 = _createForOfIteratorHelper(root.querySelectorAll('.' + imageContainerClass)),
98
- _step2;
99
- try {
100
- for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
101
- var container = _step2.value;
102
- // Only a picture standing in a paragraph is gathered. One in a list, a table cell or a link belongs
103
- // to that structure, and lifting it out would change what the answer says.
104
- if (!container.parentElement || container.parentElement.tagName !== 'P') {
105
- runs.push(run);
106
- run = [];
107
- continue;
108
- }
109
- if (run.length > 0 && !isOnlyWhitespaceBetween(run[run.length - 1], container)) {
110
- runs.push(run);
111
- run = [];
112
- }
113
- run.push(container);
114
- }
115
- } catch (err) {
116
- _iterator2.e(err);
117
- } finally {
118
- _iterator2.f();
119
- }
120
- runs.push(run);
121
- return runs.filter(function (candidate) {
122
- return candidate.length > 1;
123
- });
124
- }
125
-
126
- // Two pictures belong to one run when nothing a reader would read sits between them: spaces and line breaks
127
- // within a paragraph, or the boundary between two paragraphs that hold nothing else.
128
- function isOnlyWhitespaceBetween(previous, next) {
129
- var range = previous.ownerDocument.createRange();
130
- range.setStartAfter(previous);
131
- range.setEndBefore(next);
132
- var between = range.cloneContents();
133
- return between.textContent.trim() === '' && !between.querySelector(contentBetweenSelector);
134
- }
135
- function buildCarousel(containers) {
136
- var ownerDocument = containers[0].ownerDocument;
137
- var firstParagraph = containers[0].parentElement;
138
- var vacatedParagraphs = new Set();
139
-
140
- // Spans throughout, for the same reason the picture's own wrapper is one: the carousel sits inside the
141
- // paragraph the first picture was in, and a block element there would close that paragraph.
142
- var carousel = createElement(ownerDocument, 'span', 'ai-image-carousel');
143
- carousel.tabIndex = 0;
144
- carousel.setAttribute('role', 'region');
145
- carousel.setAttribute('aria-roledescription', 'carousel');
146
- carousel.setAttribute('aria-label', "".concat(containers.length, " images"));
147
- carousel.dataset.carouselKey = getCarouselKey(containers);
148
- carousel.dataset.carouselSize = getPreferredSize();
149
- var stage = createElement(ownerDocument, 'span', 'ai-image-carousel-stage');
150
- var controls = createElement(ownerDocument, 'span', 'ai-image-carousel-controls');
151
- var caption = createElement(ownerDocument, 'span', 'ai-image-carousel-caption');
152
- var navigation = createElement(ownerDocument, 'span', 'ai-image-carousel-navigation');
153
- var dots = createElement(ownerDocument, 'span', 'ai-image-carousel-dots');
154
- caption.setAttribute('aria-live', 'polite');
155
- containers[0].before(carousel);
156
- containers.forEach(function (container, index) {
157
- removeSeparatorsBefore(container);
158
- if (container.parentElement !== firstParagraph) {
159
- vacatedParagraphs.add(container.parentElement);
160
- }
161
- container.classList.add('ai-image-carousel-item');
162
- var image = container.querySelector('img');
163
-
164
- // The browser's own image drag would otherwise take over the pointer the moment a swipe starts.
165
- if (image) {
166
- image.setAttribute('draggable', 'false');
167
- }
168
- var dot = createElement(ownerDocument, 'button', 'ai-image-carousel-dot');
169
- dot.type = 'button';
170
- dot.dataset.carouselTarget = index;
171
- dot.setAttribute('aria-label', "Show image ".concat(index + 1));
172
- dots.appendChild(dot);
173
- stage.appendChild(container);
174
- });
175
- navigation.append(createStepButton(ownerDocument, -1, 'Previous image', previousIcon), dots, createStepButton(ownerDocument, 1, 'Next image', nextIcon));
176
- controls.append(caption, createSizePicker(ownerDocument, carousel.dataset.carouselSize), navigation);
177
- carousel.append(stage, controls);
178
- var _iterator3 = _createForOfIteratorHelper(vacatedParagraphs),
179
- _step3;
180
- try {
181
- for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
182
- var paragraph = _step3.value;
183
- if (paragraph.textContent.trim() === '' && !paragraph.querySelector(contentBetweenSelector)) {
184
- paragraph.remove();
185
- }
186
- }
187
- } catch (err) {
188
- _iterator3.e(err);
189
- } finally {
190
- _iterator3.f();
191
- }
192
- showImage(carousel, activeIndexByCarouselKey.get(carousel.dataset.carouselKey) || 0);
193
- }
194
-
195
- // Whatever separated this picture from the one before it -- a space, a line break -- is all that is left once
196
- // the pictures have moved into the carousel, and it would otherwise trail the carousel as a blank line.
197
- function removeSeparatorsBefore(container) {
198
- var sibling = container.previousSibling;
199
- while (sibling && (sibling.nodeType === Node.TEXT_NODE && sibling.textContent.trim() === '' || sibling.nodeName === 'BR')) {
200
- var previousSibling = sibling.previousSibling;
201
- sibling.remove();
202
- sibling = previousSibling;
203
- }
204
- }
205
-
206
- // Cheap rather than exact: a generated image can be a data URI hundreds of kilobytes long, so only the tail
207
- // of the first address -- where a served document's identifier sits -- and the number of pictures are used.
208
- function getCarouselKey(containers) {
209
- var firstImage = containers[0].querySelector('img');
210
- var firstSource = firstImage ? firstImage.getAttribute('src') || '' : '';
211
- return "".concat(containers.length, ":").concat(firstSource.slice(-120));
212
- }
213
- function createElement(ownerDocument, tagName, className) {
214
- var element = ownerDocument.createElement(tagName);
215
- element.className = className;
216
- return element;
217
- }
218
- function createStepButton(ownerDocument, step, label, icon) {
219
- var button = createElement(ownerDocument, 'button', 'ai-image-carousel-button');
220
- button.type = 'button';
221
- button.dataset.carouselStep = step;
222
- button.setAttribute('aria-label', label);
223
- button.innerHTML = icon;
224
- return button;
225
- }
226
-
227
- // A segmented S / M / L control.
228
- function createSizePicker(ownerDocument, selectedSize) {
229
- var picker = createElement(ownerDocument, 'span', 'ai-image-carousel-sizes');
230
- picker.setAttribute('role', 'group');
231
- picker.setAttribute('aria-label', 'Preview size');
232
- var _iterator4 = _createForOfIteratorHelper(sizes),
233
- _step4;
234
- try {
235
- for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
236
- var size = _step4.value;
237
- var button = createElement(ownerDocument, 'button', 'ai-image-carousel-size');
238
- button.type = 'button';
239
- button.title = size.label;
240
- button.textContent = size.text;
241
- button.dataset.carouselSizeOption = size.name;
242
- button.setAttribute('aria-label', size.label);
243
- button.setAttribute('aria-pressed', size.name === selectedSize ? 'true' : 'false');
244
- picker.appendChild(button);
245
- }
246
- } catch (err) {
247
- _iterator4.e(err);
248
- } finally {
249
- _iterator4.f();
250
- }
251
- return picker;
252
- }
253
-
254
- // Storage can be missing or refuse access -- a private window, blocked site data -- and the default is a fine
255
- // answer then.
256
- function getPreferredSize() {
257
- try {
258
- var storedSize = localStorage.getItem(sizeStorageKey);
259
- return sizes.some(function (size) {
260
- return size.name === storedSize;
261
- }) ? storedSize : defaultSize;
262
- } catch (_) {
263
- return defaultSize;
264
- }
265
- }
266
- function setPreferredSize(selectedSize) {
267
- try {
268
- localStorage.setItem(sizeStorageKey, selectedSize);
269
- } catch (_) {
270
- // Still applied to the page; only remembering it for the next visit is lost.
271
- }
272
- var _iterator5 = _createForOfIteratorHelper(document.querySelectorAll('.ai-image-carousel')),
273
- _step5;
274
- try {
275
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
276
- var carousel = _step5.value;
277
- carousel.dataset.carouselSize = selectedSize;
278
- }
279
- } catch (err) {
280
- _iterator5.e(err);
281
- } finally {
282
- _iterator5.f();
283
- }
284
- var _iterator6 = _createForOfIteratorHelper(document.querySelectorAll('[data-carousel-size-option]')),
285
- _step6;
286
- try {
287
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
288
- var button = _step6.value;
289
- button.setAttribute('aria-pressed', button.dataset.carouselSizeOption === selectedSize ? 'true' : 'false');
290
- }
291
- } catch (err) {
292
- _iterator6.e(err);
293
- } finally {
294
- _iterator6.f();
295
- }
296
- }
297
- function getItems(carousel) {
298
- return carousel.querySelectorAll('.ai-image-carousel-stage > .ai-image-carousel-item');
299
- }
300
- function getActiveIndex(carousel) {
301
- return Number(carousel.dataset.activeIndex) || 0;
302
- }
303
- function showImage(carousel, requestedIndex) {
304
- var items = getItems(carousel);
305
- if (items.length === 0) {
306
- carousel.remove();
307
- return;
308
- }
309
- var activeIndex = Math.max(0, Math.min(items.length - 1, requestedIndex));
310
- var dots = carousel.querySelector('.ai-image-carousel-dots');
311
- while (dots.children.length > items.length) {
312
- dots.lastElementChild.remove();
313
- }
314
- items.forEach(function (item, index) {
315
- var offset = index - activeIndex;
316
- var isActive = offset === 0;
317
- var downloadLink = item.querySelector('a');
318
- item.dataset.stackOffset = Math.max(-furthestVisibleOffset - 1, Math.min(furthestVisibleOffset + 1, offset));
319
- item.classList.toggle('is-active', isActive);
320
- item.setAttribute('aria-hidden', isActive ? 'false' : 'true');
321
-
322
- // A card behind the current one is hidden from assistive technology, so nothing in it may take focus.
323
- if (downloadLink) {
324
- downloadLink.tabIndex = isActive ? 0 : -1;
325
- }
326
- dots.children[index].classList.toggle('is-active', isActive);
327
- dots.children[index].setAttribute('aria-current', isActive ? 'true' : 'false');
328
- });
329
- var activeImage = items[activeIndex].querySelector('img');
330
- var altText = activeImage ? activeImage.getAttribute('alt') : '';
331
- carousel.dataset.activeIndex = activeIndex;
332
- carousel.dataset.imageCount = items.length;
333
- carousel.querySelector('[data-carousel-step="-1"]').disabled = activeIndex === 0;
334
- carousel.querySelector('[data-carousel-step="1"]').disabled = activeIndex === items.length - 1;
335
- carousel.querySelector('.ai-image-carousel-caption').textContent = altText ? "".concat(activeIndex + 1, " / ").concat(items.length, " \xB7 ").concat(altText) : "".concat(activeIndex + 1, " / ").concat(items.length);
336
- activeIndexByCarouselKey.set(carousel.dataset.carouselKey, activeIndex);
337
- }
338
- function stopEvent(event) {
339
- event.preventDefault();
340
- event.stopPropagation();
341
- }
342
-
343
- // Captured, so it runs before anything bound to the picture itself: a card behind the current one is brought
344
- // to the front, and is not zoomed or downloaded on the way.
345
- function onClick(event) {
346
- var carousel = event.target.closest && event.target.closest('.ai-image-carousel');
347
- if (!carousel) {
348
- return;
349
- }
350
-
351
- // A click raised from the keyboard has no pointer behind it, so a swipe that ended without one -- a touch
352
- // swipe, or a drag released outside the carousel -- must not swallow it.
353
- if (suppressNextClick && event.detail > 0) {
354
- suppressNextClick = false;
355
- stopEvent(event);
356
- return;
357
- }
358
- var stepButton = event.target.closest('[data-carousel-step]');
359
- if (stepButton) {
360
- stopEvent(event);
361
- showImage(carousel, getActiveIndex(carousel) + Number(stepButton.dataset.carouselStep));
362
- return;
363
- }
364
- var sizeButton = event.target.closest('[data-carousel-size-option]');
365
- if (sizeButton) {
366
- stopEvent(event);
367
- setPreferredSize(sizeButton.dataset.carouselSizeOption);
368
- return;
369
- }
370
- var dot = event.target.closest('[data-carousel-target]');
371
- if (dot) {
372
- stopEvent(event);
373
- showImage(carousel, Number(dot.dataset.carouselTarget));
374
- return;
375
- }
376
- var item = event.target.closest('.ai-image-carousel-item');
377
- if (item && !item.classList.contains('is-active')) {
378
- stopEvent(event);
379
- showImage(carousel, Array.prototype.indexOf.call(getItems(carousel), item));
380
- }
381
- }
382
-
383
- // Only while the carousel has focus, so the arrow keys still move the caret in the prompt.
384
- function onKeyDown(event) {
385
- if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') {
386
- return;
387
- }
388
- var carousel = event.target.closest && event.target.closest('.ai-image-carousel');
389
- if (!carousel) {
390
- return;
391
- }
392
- event.preventDefault();
393
- showImage(carousel, getActiveIndex(carousel) + (event.key === 'ArrowRight' ? 1 : -1));
394
- }
395
- function onPointerDown(event) {
396
- suppressNextClick = false;
397
- var stage = event.button === 0 && event.target.closest && event.target.closest('.ai-image-carousel-stage');
398
- if (!stage) {
399
- return;
400
- }
401
- drag = {
402
- stage: stage,
403
- pointerId: event.pointerId,
404
- startX: event.clientX,
405
- startY: event.clientY,
406
- distance: 0,
407
- isSwiping: false
408
- };
409
- }
410
-
411
- // The pointer is not captured: a captured pointer retargets the click that follows to the stage, and the click
412
- // is what brings a card forward or opens the zoom. Listening on the document follows the pointer anyway.
413
- function onPointerMove(event) {
414
- if (!drag || event.pointerId !== drag.pointerId) {
415
- return;
416
- }
417
- var distance = event.clientX - drag.startX;
418
- if (!drag.isSwiping) {
419
- if (Math.abs(distance) < dragStartThreshold || Math.abs(distance) < Math.abs(event.clientY - drag.startY)) {
420
- return;
421
- }
422
- drag.isSwiping = true;
423
- drag.stage.classList.add('is-dragging');
424
- }
425
- drag.distance = distance;
426
- drag.stage.style.setProperty('--ai-image-carousel-drag', "".concat(distance, "px"));
427
- }
428
- function onPointerUp(event) {
429
- if (!drag || event.pointerId !== drag.pointerId) {
430
- return;
431
- }
432
- var _drag = drag,
433
- stage = _drag.stage,
434
- distance = _drag.distance,
435
- isSwiping = _drag.isSwiping;
436
- endDrag();
437
- if (!isSwiping) {
438
- return;
439
- }
440
-
441
- // The click the browser fires at the end of a drag is not a click on whatever the pointer ended over.
442
- suppressNextClick = true;
443
- if (Math.abs(distance) >= swipeThreshold) {
444
- var carousel = stage.closest('.ai-image-carousel');
445
- showImage(carousel, getActiveIndex(carousel) + (distance < 0 ? 1 : -1));
446
- }
447
- }
448
- function endDrag() {
449
- if (!drag) {
450
- return;
451
- }
452
- drag.stage.classList.remove('is-dragging');
453
- drag.stage.style.removeProperty('--ai-image-carousel-drag');
454
- drag = null;
455
- }
456
-
457
- // A picture the server will not serve is taken out of the stack and put back just after it, where the
458
- // surface's own handler hides it and says it could not be loaded, exactly as it does for a lone picture.
459
- // Captured on the document because an image's error event does not bubble.
460
- function onImageError(event) {
461
- var item = event.target.tagName === 'IMG' && event.target.closest('.ai-image-carousel-item');
462
- if (!item) {
463
- return;
464
- }
465
- var carousel = item.closest('.ai-image-carousel');
466
- item.classList.remove('ai-image-carousel-item', 'is-active');
467
- item.removeAttribute('data-stack-offset');
468
- item.removeAttribute('aria-hidden');
469
- carousel.after(item);
470
- showImage(carousel, getActiveIndex(carousel));
471
- }
472
- if (typeof document !== 'undefined') {
473
- document.addEventListener('click', onClick, true);
474
- document.addEventListener('keydown', onKeyDown);
475
- document.addEventListener('pointerdown', onPointerDown);
476
- document.addEventListener('pointermove', onPointerMove);
477
- document.addEventListener('pointerup', onPointerUp);
478
- document.addEventListener('pointercancel', endDrag);
479
- document.addEventListener('error', onImageError, true);
480
- }
481
- return {
482
- stackAdjacentImages: stackAdjacentImages
483
- };
484
- }();
5
+ (function() {
6
+ //#region src/Resources/CrestApps.AI.Resources/Assets/js/chat-image-carousel.js
7
+ window.CoreAIChatImageCarousel = window.CoreAIChatImageCarousel || (function() {
8
+ "use strict";
9
+ const imageContainerClass = "generated-image-container";
10
+ const swipeThreshold = 40;
11
+ const dragStartThreshold = 8;
12
+ const contentBetweenSelector = "img, svg, canvas, video, audio, iframe, table, hr, input";
13
+ const previousIcon = "<svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M10 3 5 8l5 5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg>";
14
+ const nextIcon = "<svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m6 3 5 5-5 5\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg>";
15
+ const sizes = [
16
+ {
17
+ name: "small",
18
+ text: "S",
19
+ label: "Small previews"
20
+ },
21
+ {
22
+ name: "medium",
23
+ text: "M",
24
+ label: "Medium previews"
25
+ },
26
+ {
27
+ name: "large",
28
+ text: "L",
29
+ label: "Large previews"
30
+ }
31
+ ];
32
+ const defaultSize = "medium";
33
+ const sizeStorageKey = "coreai-image-carousel-size";
34
+ const activeIndexByCarouselKey = /* @__PURE__ */ new Map();
35
+ let drag = null;
36
+ let suppressNextClick = false;
37
+ function stackAdjacentImages(html) {
38
+ if (typeof document === "undefined" || !html) return html;
39
+ const firstContainerIndex = html.indexOf(imageContainerClass);
40
+ if (firstContainerIndex < 0 || html.indexOf(imageContainerClass, firstContainerIndex + 25) < 0) return html;
41
+ const template = document.createElement("template");
42
+ template.innerHTML = html;
43
+ const runs = findImageRuns(template.content);
44
+ if (runs.length === 0) return html;
45
+ for (const run of runs) buildCarousel(run);
46
+ return template.innerHTML;
47
+ }
48
+ function findImageRuns(root) {
49
+ const runs = [];
50
+ let run = [];
51
+ for (const container of root.querySelectorAll("." + imageContainerClass)) {
52
+ if (!container.parentElement || container.parentElement.tagName !== "P") {
53
+ runs.push(run);
54
+ run = [];
55
+ continue;
56
+ }
57
+ if (run.length > 0 && !isOnlyWhitespaceBetween(run[run.length - 1], container)) {
58
+ runs.push(run);
59
+ run = [];
60
+ }
61
+ run.push(container);
62
+ }
63
+ runs.push(run);
64
+ return runs.filter((candidate) => candidate.length > 1);
65
+ }
66
+ function isOnlyWhitespaceBetween(previous, next) {
67
+ const range = previous.ownerDocument.createRange();
68
+ range.setStartAfter(previous);
69
+ range.setEndBefore(next);
70
+ const between = range.cloneContents();
71
+ return between.textContent.trim() === "" && !between.querySelector(contentBetweenSelector);
72
+ }
73
+ function buildCarousel(containers) {
74
+ const ownerDocument = containers[0].ownerDocument;
75
+ const firstParagraph = containers[0].parentElement;
76
+ const vacatedParagraphs = /* @__PURE__ */ new Set();
77
+ const carousel = createElement(ownerDocument, "span", "ai-image-carousel");
78
+ carousel.tabIndex = 0;
79
+ carousel.setAttribute("role", "region");
80
+ carousel.setAttribute("aria-roledescription", "carousel");
81
+ carousel.setAttribute("aria-label", `${containers.length} images`);
82
+ carousel.dataset.carouselKey = getCarouselKey(containers);
83
+ carousel.dataset.carouselSize = getPreferredSize();
84
+ const stage = createElement(ownerDocument, "span", "ai-image-carousel-stage");
85
+ const controls = createElement(ownerDocument, "span", "ai-image-carousel-controls");
86
+ const caption = createElement(ownerDocument, "span", "ai-image-carousel-caption");
87
+ const navigation = createElement(ownerDocument, "span", "ai-image-carousel-navigation");
88
+ const dots = createElement(ownerDocument, "span", "ai-image-carousel-dots");
89
+ caption.setAttribute("aria-live", "polite");
90
+ containers[0].before(carousel);
91
+ containers.forEach((container, index) => {
92
+ removeSeparatorsBefore(container);
93
+ if (container.parentElement !== firstParagraph) vacatedParagraphs.add(container.parentElement);
94
+ container.classList.add("ai-image-carousel-item");
95
+ const image = container.querySelector("img");
96
+ if (image) image.setAttribute("draggable", "false");
97
+ const dot = createElement(ownerDocument, "button", "ai-image-carousel-dot");
98
+ dot.type = "button";
99
+ dot.dataset.carouselTarget = index;
100
+ dot.setAttribute("aria-label", `Show image ${index + 1}`);
101
+ dots.appendChild(dot);
102
+ stage.appendChild(container);
103
+ });
104
+ navigation.append(createStepButton(ownerDocument, -1, "Previous image", previousIcon), dots, createStepButton(ownerDocument, 1, "Next image", nextIcon));
105
+ controls.append(caption, createSizePicker(ownerDocument, carousel.dataset.carouselSize), navigation);
106
+ carousel.append(stage, controls);
107
+ for (const paragraph of vacatedParagraphs) if (paragraph.textContent.trim() === "" && !paragraph.querySelector(contentBetweenSelector)) paragraph.remove();
108
+ showImage(carousel, activeIndexByCarouselKey.get(carousel.dataset.carouselKey) || 0);
109
+ }
110
+ function removeSeparatorsBefore(container) {
111
+ let sibling = container.previousSibling;
112
+ while (sibling && (sibling.nodeType === Node.TEXT_NODE && sibling.textContent.trim() === "" || sibling.nodeName === "BR")) {
113
+ const previousSibling = sibling.previousSibling;
114
+ sibling.remove();
115
+ sibling = previousSibling;
116
+ }
117
+ }
118
+ function getCarouselKey(containers) {
119
+ const firstImage = containers[0].querySelector("img");
120
+ const firstSource = firstImage ? firstImage.getAttribute("src") || "" : "";
121
+ return `${containers.length}:${firstSource.slice(-120)}`;
122
+ }
123
+ function createElement(ownerDocument, tagName, className) {
124
+ const element = ownerDocument.createElement(tagName);
125
+ element.className = className;
126
+ return element;
127
+ }
128
+ function createStepButton(ownerDocument, step, label, icon) {
129
+ const button = createElement(ownerDocument, "button", "ai-image-carousel-button");
130
+ button.type = "button";
131
+ button.dataset.carouselStep = step;
132
+ button.setAttribute("aria-label", label);
133
+ button.innerHTML = icon;
134
+ return button;
135
+ }
136
+ function createSizePicker(ownerDocument, selectedSize) {
137
+ const picker = createElement(ownerDocument, "span", "ai-image-carousel-sizes");
138
+ picker.setAttribute("role", "group");
139
+ picker.setAttribute("aria-label", "Preview size");
140
+ for (const size of sizes) {
141
+ const button = createElement(ownerDocument, "button", "ai-image-carousel-size");
142
+ button.type = "button";
143
+ button.title = size.label;
144
+ button.textContent = size.text;
145
+ button.dataset.carouselSizeOption = size.name;
146
+ button.setAttribute("aria-label", size.label);
147
+ button.setAttribute("aria-pressed", size.name === selectedSize ? "true" : "false");
148
+ picker.appendChild(button);
149
+ }
150
+ return picker;
151
+ }
152
+ function getPreferredSize() {
153
+ try {
154
+ const storedSize = localStorage.getItem(sizeStorageKey);
155
+ return sizes.some((size) => size.name === storedSize) ? storedSize : defaultSize;
156
+ } catch (_) {
157
+ return defaultSize;
158
+ }
159
+ }
160
+ function setPreferredSize(selectedSize) {
161
+ try {
162
+ localStorage.setItem(sizeStorageKey, selectedSize);
163
+ } catch (_) {}
164
+ for (const carousel of document.querySelectorAll(".ai-image-carousel")) carousel.dataset.carouselSize = selectedSize;
165
+ for (const button of document.querySelectorAll("[data-carousel-size-option]")) button.setAttribute("aria-pressed", button.dataset.carouselSizeOption === selectedSize ? "true" : "false");
166
+ }
167
+ function getItems(carousel) {
168
+ return carousel.querySelectorAll(".ai-image-carousel-stage > .ai-image-carousel-item");
169
+ }
170
+ function getActiveIndex(carousel) {
171
+ return Number(carousel.dataset.activeIndex) || 0;
172
+ }
173
+ function showImage(carousel, requestedIndex) {
174
+ const items = getItems(carousel);
175
+ if (items.length === 0) {
176
+ carousel.remove();
177
+ return;
178
+ }
179
+ const activeIndex = Math.max(0, Math.min(items.length - 1, requestedIndex));
180
+ const dots = carousel.querySelector(".ai-image-carousel-dots");
181
+ while (dots.children.length > items.length) dots.lastElementChild.remove();
182
+ items.forEach((item, index) => {
183
+ const offset = index - activeIndex;
184
+ const isActive = offset === 0;
185
+ const downloadLink = item.querySelector("a");
186
+ item.dataset.stackOffset = Math.max(-3, Math.min(3, offset));
187
+ item.classList.toggle("is-active", isActive);
188
+ item.setAttribute("aria-hidden", isActive ? "false" : "true");
189
+ if (downloadLink) downloadLink.tabIndex = isActive ? 0 : -1;
190
+ dots.children[index].classList.toggle("is-active", isActive);
191
+ dots.children[index].setAttribute("aria-current", isActive ? "true" : "false");
192
+ });
193
+ const activeImage = items[activeIndex].querySelector("img");
194
+ const altText = activeImage ? activeImage.getAttribute("alt") : "";
195
+ carousel.dataset.activeIndex = activeIndex;
196
+ carousel.dataset.imageCount = items.length;
197
+ carousel.querySelector("[data-carousel-step=\"-1\"]").disabled = activeIndex === 0;
198
+ carousel.querySelector("[data-carousel-step=\"1\"]").disabled = activeIndex === items.length - 1;
199
+ carousel.querySelector(".ai-image-carousel-caption").textContent = altText ? `${activeIndex + 1} / ${items.length} · ${altText}` : `${activeIndex + 1} / ${items.length}`;
200
+ activeIndexByCarouselKey.set(carousel.dataset.carouselKey, activeIndex);
201
+ }
202
+ function stopEvent(event) {
203
+ event.preventDefault();
204
+ event.stopPropagation();
205
+ }
206
+ function onClick(event) {
207
+ const carousel = event.target.closest && event.target.closest(".ai-image-carousel");
208
+ if (!carousel) return;
209
+ if (suppressNextClick && event.detail > 0) {
210
+ suppressNextClick = false;
211
+ stopEvent(event);
212
+ return;
213
+ }
214
+ const stepButton = event.target.closest("[data-carousel-step]");
215
+ if (stepButton) {
216
+ stopEvent(event);
217
+ showImage(carousel, getActiveIndex(carousel) + Number(stepButton.dataset.carouselStep));
218
+ return;
219
+ }
220
+ const sizeButton = event.target.closest("[data-carousel-size-option]");
221
+ if (sizeButton) {
222
+ stopEvent(event);
223
+ setPreferredSize(sizeButton.dataset.carouselSizeOption);
224
+ return;
225
+ }
226
+ const dot = event.target.closest("[data-carousel-target]");
227
+ if (dot) {
228
+ stopEvent(event);
229
+ showImage(carousel, Number(dot.dataset.carouselTarget));
230
+ return;
231
+ }
232
+ const item = event.target.closest(".ai-image-carousel-item");
233
+ if (item && !item.classList.contains("is-active")) {
234
+ stopEvent(event);
235
+ showImage(carousel, Array.prototype.indexOf.call(getItems(carousel), item));
236
+ }
237
+ }
238
+ function onKeyDown(event) {
239
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
240
+ const carousel = event.target.closest && event.target.closest(".ai-image-carousel");
241
+ if (!carousel) return;
242
+ event.preventDefault();
243
+ showImage(carousel, getActiveIndex(carousel) + (event.key === "ArrowRight" ? 1 : -1));
244
+ }
245
+ function onPointerDown(event) {
246
+ suppressNextClick = false;
247
+ const stage = event.button === 0 && event.target.closest && event.target.closest(".ai-image-carousel-stage");
248
+ if (!stage) return;
249
+ drag = {
250
+ stage,
251
+ pointerId: event.pointerId,
252
+ startX: event.clientX,
253
+ startY: event.clientY,
254
+ distance: 0,
255
+ isSwiping: false
256
+ };
257
+ }
258
+ function onPointerMove(event) {
259
+ if (!drag || event.pointerId !== drag.pointerId) return;
260
+ const distance = event.clientX - drag.startX;
261
+ if (!drag.isSwiping) {
262
+ if (Math.abs(distance) < dragStartThreshold || Math.abs(distance) < Math.abs(event.clientY - drag.startY)) return;
263
+ drag.isSwiping = true;
264
+ drag.stage.classList.add("is-dragging");
265
+ }
266
+ drag.distance = distance;
267
+ drag.stage.style.setProperty("--ai-image-carousel-drag", `${distance}px`);
268
+ }
269
+ function onPointerUp(event) {
270
+ if (!drag || event.pointerId !== drag.pointerId) return;
271
+ const { stage, distance, isSwiping } = drag;
272
+ endDrag();
273
+ if (!isSwiping) return;
274
+ suppressNextClick = true;
275
+ if (Math.abs(distance) >= swipeThreshold) {
276
+ const carousel = stage.closest(".ai-image-carousel");
277
+ showImage(carousel, getActiveIndex(carousel) + (distance < 0 ? 1 : -1));
278
+ }
279
+ }
280
+ function endDrag() {
281
+ if (!drag) return;
282
+ drag.stage.classList.remove("is-dragging");
283
+ drag.stage.style.removeProperty("--ai-image-carousel-drag");
284
+ drag = null;
285
+ }
286
+ function onImageError(event) {
287
+ const item = event.target.tagName === "IMG" && event.target.closest(".ai-image-carousel-item");
288
+ if (!item) return;
289
+ const carousel = item.closest(".ai-image-carousel");
290
+ item.classList.remove("ai-image-carousel-item", "is-active");
291
+ item.removeAttribute("data-stack-offset");
292
+ item.removeAttribute("aria-hidden");
293
+ carousel.after(item);
294
+ showImage(carousel, getActiveIndex(carousel));
295
+ }
296
+ if (typeof document !== "undefined") {
297
+ document.addEventListener("click", onClick, true);
298
+ document.addEventListener("keydown", onKeyDown);
299
+ document.addEventListener("pointerdown", onPointerDown);
300
+ document.addEventListener("pointermove", onPointerMove);
301
+ document.addEventListener("pointerup", onPointerUp);
302
+ document.addEventListener("pointercancel", endDrag);
303
+ document.addEventListener("error", onImageError, true);
304
+ }
305
+ return { stackAdjacentImages };
306
+ })();
307
+ //#endregion
308
+ })();
485
309
  //# sourceMappingURL=chat-image-carousel.js.map