@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,488 +1,196 @@
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 _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
7
- function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
8
- function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
9
- function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
10
- 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; } } }; }
11
- 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; } }
12
- 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; }
13
- 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); }
14
- /*
15
- * Markers the host expands on the model's behalf.
16
- *
17
- * Retrieval hands the model a short label such as [fig:1] rather than a figure's address, because a model
18
- * asked to reproduce a long opaque identifier reproduces its shape and varies the digits instead --
19
- * producing addresses that look right and resolve to nothing. The model therefore never types an address,
20
- * and this turns the label it does type back into the picture.
21
- *
22
- * The chart tool works the same way from the other end: it hands back a [chart:{...}] marker and asks for it
23
- * verbatim, and the host turns that marker into a canvas. Reading a marker is a statement about a string, so
24
- * it lives here; drawing one is not, so each surface still does its own drawing.
25
- *
26
- * Loaded on its own so every chat surface shares one implementation: the two shared chat scripts and the
27
- * MVC chat-interaction view, which renders its markdown with its own inline marked setup.
28
- */
29
- window.CoreAIChatMarkers = window.CoreAIChatMarkers || function () {
30
- 'use strict';
31
-
32
- // Used when a figure has no caption, so the image is still announced as something.
33
- var defaultImageAltText = 'Figure';
34
-
35
- /*
36
- * Makes a caption safe to sit inside the alt text of ![alt](link).
37
- *
38
- * Captions really do contain brackets, and an unescaped one closes the alt text early: a caption of
39
- * "Figure 2 (revised) [draft]" would end the image after "Figure 2 (revised) " and leave the rest of the
40
- * syntax on the page as markup. Parentheses are harmless there, only the brackets are. A caption that spans
41
- * lines would end the paragraph the image lives in, so its whitespace is collapsed to single spaces.
42
- */
43
- function escapeImageAltText(title) {
44
- var text = typeof title === 'string' ? title.replace(/\s+/g, ' ').trim() : '';
45
- if (!text) {
46
- return defaultImageAltText;
47
- }
48
-
49
- // The backslash is escaped as well, or a caption ending in one would escape the bracket that closes
50
- // the alt text and the image would swallow the rest of the line.
51
- return text.replace(/[\\\[\]]/g, '\\$&');
52
- }
53
-
54
- // Escapes a literal for use inside a regular expression.
55
- function escapeRegExp(text) {
56
- return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
57
- }
58
-
59
- /*
60
- * Makes a link safe to sit inside the destination of ![alt](link): the few characters that would end the
61
- * destination early are percent-encoded, which leaves the URL addressing the same resource. The link is
62
- * still run through sanitizeUrl by the image renderer, so this is about parsing, not about safety.
63
- */
64
- function encodeImageLink(link) {
65
- return link.replace(/[\s()<>"\\]/g, function (character) {
66
- return '%' + character.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
67
- });
68
- }
69
-
70
- /*
71
- * Returns the content with every marker the reference map describes as a servable image replaced by
72
- * markdown image syntax, so the ordinary image renderer draws it with its thumbnail, its download button
73
- * and its size cap. Pure: nothing here touches the DOM or the message.
74
- *
75
- * A marker is replaced only when its reference is an image and carries a link. Anything else is left
76
- * exactly as written -- a marker the model invented for a figure that was never in the results, or one
77
- * whose picture the host cannot serve, reaches the reader as the few characters the model typed rather
78
- * than as a broken image.
79
- *
80
- * Each picture is drawn once per message, where it is first mentioned. Models repeat the line of markers
81
- * a tool asked them to write -- once where they introduce the preview, again where they sum up -- and
82
- * drawing every occurrence showed each page of a three-page document twice. A later mention of the same
83
- * marker, or of another marker for the same picture, is dropped rather than left as raw text.
84
- */
85
- function expandImageMarkers(content, references) {
86
- if (typeof content !== 'string' || !content) {
87
- return typeof content === 'string' ? content : '';
88
- }
89
- if (!references || _typeof(references) !== 'object') {
90
- return content;
91
- }
92
- var images = new Map();
93
- for (var _i = 0, _Object$keys = Object.keys(references); _i < _Object$keys.length; _i++) {
94
- var _reference$isImage, _reference$link, _reference$title;
95
- var marker = _Object$keys[_i];
96
- var reference = references[marker];
97
- if (!marker || !reference || _typeof(reference) !== 'object') {
98
- continue;
99
- }
100
- if (((_reference$isImage = reference.isImage) !== null && _reference$isImage !== void 0 ? _reference$isImage : reference.IsImage) !== true) {
101
- continue;
102
- }
103
- var rawLink = (_reference$link = reference.link) !== null && _reference$link !== void 0 ? _reference$link : reference.Link;
104
- var link = typeof rawLink === 'string' ? rawLink.trim() : '';
105
- if (!link) {
106
- continue;
107
- }
108
- images.set(marker, {
109
- link: link,
110
- image: "![".concat(escapeImageAltText((_reference$title = reference.title) !== null && _reference$title !== void 0 ? _reference$title : reference.Title), "](").concat(encodeImageLink(link), ")")
111
- });
112
- }
113
- if (images.size === 0) {
114
- return content;
115
- }
116
-
117
- // A smaller model dresses the marker up as the markdown it was told not to write --
118
- // "![Page 1][fig:1]", "![Page 1]([fig:1])", "![fig:1]", "[fig:1](page1.png)" -- and the reader saw
119
- // "!Page 1" as text where the picture should be. Each of those is the marker, so it is put back to the
120
- // bare marker before anything is drawn.
121
- var normalized = content;
122
- var _iterator = _createForOfIteratorHelper(images.keys()),
123
- _step;
124
- try {
125
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
126
- var _marker = _step.value;
127
- var escaped = escapeRegExp(_marker);
128
- var inner = escapeRegExp(_marker.slice(1, -1));
129
- normalized = normalized.replace(new RegExp('!\\[[^\\]\\n]*\\]\\(\\s*\\[?' + inner + '\\]?\\s*\\)', 'g'), _marker).replace(new RegExp('!\\[[^\\]\\n]*\\]' + escaped, 'g'), _marker).replace(new RegExp(escaped + '\\([^)\\s]*\\)', 'g'), _marker).replace(new RegExp('!(?=' + escaped + ')', 'g'), '');
130
- }
131
-
132
- // One pass over every marker at once, so "first mention" means first in the text rather than first in
133
- // the map. The markers are bracketed, so [fig:1] cannot match inside [fig:11]; the longer ones are
134
- // tried first all the same.
135
- } catch (err) {
136
- _iterator.e(err);
137
- } finally {
138
- _iterator.f();
139
- }
140
- var pattern = new RegExp(_toConsumableArray(images.keys()).sort(function (left, right) {
141
- return right.length - left.length;
142
- }).map(escapeRegExp).join('|'), 'g');
143
- var drawn = new Set();
144
-
145
- // Through a replacer function, so a '$' in a caption or a link is not read as a replacement pattern.
146
- return normalized.replace(pattern, function (marker) {
147
- var entry = images.get(marker);
148
- if (drawn.has(entry.link)) {
149
- return '';
150
- }
151
- drawn.add(entry.link);
152
- return entry.image;
153
- });
154
- }
155
-
156
- // The marker the chart tool emits and asks the model to repeat verbatim, braces and all.
157
- var chartMarkerPrefix = '[chart:';
158
-
159
- // What a citation the model typed opens with. The reference map is keyed by the whole marker, so this is
160
- // also how a rewritten one is put back together.
161
- var citationMarkerPrefix = '[doc:';
162
-
163
- /*
164
- * How much text may sit between "[doc:" and its "]" before it stops being a citation.
165
- *
166
- * A list of reference numbers is short: even ten of them, written the long way, is under a hundred
167
- * characters. The limit is what keeps the work per marker fixed, and so the whole pass linear -- without
168
- * it, an opening bracket whose "]" is thousands of characters away is examined in full, and a paragraph
169
- * full of such openings is examined once per opening.
170
- */
171
- var maxCitationMarkerLength = 512;
172
- function isMarkerWhitespace(character) {
173
- return character === ' ' || character === '\n' || character === '\r' || character === '\t';
174
- }
175
-
176
- /*
177
- * Reads the marker that begins at startIndex, or returns null when what begins there is not one.
178
- *
179
- * The configuration is JSON and JSON nests, so the end of the object is found by counting braces rather
180
- * than by a regular expression: an expression that stopped at the first '}' would truncate every chart
181
- * whose options carry an object of their own, which is all of them. The count has to know when it is
182
- * inside a string as well -- an axis label really does read "Q1 { Q2", and a counter blind to strings
183
- * would end the object in the middle of the label and hand the renderer a fragment. Escapes are tracked
184
- * for the same reason, so a \" inside a label does not look like the end of it.
185
- */
186
- function readChartMarkerAt(text, startIndex) {
187
- var index = startIndex + chartMarkerPrefix.length;
188
- while (index < text.length && isMarkerWhitespace(text[index])) {
189
- index++;
190
- }
191
- if (index >= text.length || text[index] !== '{') {
192
- return null;
193
- }
194
- var jsonStart = index;
195
- var depth = 0;
196
- var inString = false;
197
- var escaped = false;
198
- for (; index < text.length; index++) {
199
- var character = text[index];
200
- if (inString) {
201
- if (escaped) {
202
- escaped = false;
203
- } else if (character === '\\') {
204
- escaped = true;
205
- } else if (character === '"') {
206
- inString = false;
207
- }
208
- continue;
209
- }
210
- if (character === '"') {
211
- inString = true;
212
- continue;
213
- }
214
- if (character === '{') {
215
- depth++;
216
- continue;
217
- }
218
- if (character !== '}') {
219
- continue;
220
- }
221
- depth--;
222
- if (depth > 0) {
223
- continue;
224
- }
225
- var jsonEnd = index + 1;
226
- var closeIndex = jsonEnd;
227
- while (closeIndex < text.length && isMarkerWhitespace(text[closeIndex])) {
228
- closeIndex++;
229
- }
230
-
231
- // Only whitespace may sit between the object and the bracket that closes the marker. Hunting
232
- // further ahead for a ']' would let a marker whose bracket the model dropped reach the one
233
- // belonging to something else, and everything in between -- a paragraph of the answer, the next
234
- // marker -- would be swallowed into the span the caller removes from the text.
235
- if (closeIndex >= text.length || text[closeIndex] !== ']') {
236
- return null;
237
- }
238
- return {
239
- startIndex: startIndex,
240
- endIndex: closeIndex + 1,
241
- json: text.substring(jsonStart, jsonEnd)
242
- };
243
- }
244
- return null;
245
- }
246
-
247
- /*
248
- * Finds the first [chart:{...}] marker in the content and returns where it starts, where it ends and the
249
- * JSON between, or null when there is no marker. Pure: it knows nothing of canvases, element ids or
250
- * Chart.js, because the three chat surfaces draw a chart differently and agree only on what a marker is.
251
- *
252
- * Text that merely looks like a marker is not one, and none of these return a span: "[chart:" with no
253
- * object after it, an object whose braces never balance, an object the model never closed with ']'. Each
254
- * reaches the reader as the text the model typed, which is what the caller does with a null.
255
- */
256
- function findChartMarker(text) {
257
- if (typeof text !== 'string' || !text) {
258
- return null;
259
- }
260
- var searchFrom = 0;
261
- for (;;) {
262
- var start = text.indexOf(chartMarkerPrefix, searchFrom);
263
- if (start < 0) {
264
- return null;
265
- }
266
- var marker = readChartMarkerAt(text, start);
267
- if (marker) {
268
- return marker;
269
- }
270
-
271
- // Something that opened like a marker and was not one must not hide a real one further on, so the
272
- // search carries on past it rather than giving up at the first disappointment.
273
- searchFrom = start + 1;
274
- }
275
- }
276
-
277
- /*
278
- * Splits a citation marker the model wrote as one bracket into the separate markers it means.
279
- *
280
- * A reference is keyed by the literal string the model is asked to type -- "[doc:1]" -- and the renderer
281
- * replaces exactly that. Models routinely gather several into one bracket instead: "[doc:1, doc:2]". No
282
- * key matches that, so nothing is replaced and the reader is shown the raw marker in the middle of a
283
- * sentence, while the citations the model happened to write singly render correctly beside it.
284
- *
285
- * Rewriting the combined form into the separate markers is what makes the rest of the pipeline see them.
286
- * Both spellings are accepted, since a model that writes "[doc:1, doc:2]" will also write "[doc:1, 2]".
287
- * A lone "[doc:1]" is left exactly as it is.
288
- */
289
- function splitCombinedCitations(content) {
290
- if (typeof content !== 'string' || !content) {
291
- return '';
292
- }
293
- var result = '';
294
- var from = 0;
295
-
296
- // The closing bracket last searched for. A ']' found from an earlier position is still the next one
297
- // from here, because there was none in between -- so text like "[doc:x[doc:x[doc:x...]" is walked once
298
- // rather than rescanned from every false start, which would make the whole pass quadratic.
299
- var knownClose = -1;
300
- for (;;) {
301
- var start = content.indexOf(citationMarkerPrefix, from);
302
- if (start < 0) {
303
- return result + content.slice(from);
304
- }
305
- var end = knownClose >= start ? knownClose : content.indexOf(']', start);
306
- knownClose = end;
307
- if (end < 0) {
308
- // Nothing closes it, so nothing here is a marker. The rest is returned as it was written.
309
- return result + content.slice(from);
310
- }
311
- var numbers = end - start <= maxCitationMarkerLength ? readCitationNumbers(content.slice(start + citationMarkerPrefix.length, end)) : null;
312
- if (!numbers) {
313
- // Whatever this opened, it was not a list of references. The search carries on from just
314
- // after the prefix rather than past the bracket, because the ']' that was found may belong to
315
- // a real marker further along -- "[doc:1, and later [doc:3, doc:4]" closes the second one, and
316
- // skipping to it would hide it.
317
- result += content.slice(from, start + citationMarkerPrefix.length);
318
- from = start + citationMarkerPrefix.length;
319
- continue;
320
- }
321
-
322
- // Rewritten whenever the bracket does not already read as the canonical marker: several numbers
323
- // in one bracket, or a single number carrying a locator the model appended. A bare "[doc:1]"
324
- // rewrites to itself, so it is passed through untouched rather than rebuilt.
325
- var canonical = numbers.map(function (number) {
326
- return citationMarkerPrefix + number + ']';
327
- }).join('');
328
- var original = content.slice(start, end + 1);
329
- result += content.slice(from, start);
330
- result += canonical === original ? original : canonical;
331
- from = end + 1;
332
- }
333
- }
334
-
335
- /*
336
- * Reads the numbers out of the inside of a citation bracket, or returns null when it does not hold a list
337
- * of them.
338
- *
339
- * Deliberately not a regular expression over the whole bracket. The pattern that reads naturally --
340
- * optional spaces on both sides of each comma, repeated -- gives the engine several ways to match the same
341
- * text, and on a bracket that never closes it explores all of them: CodeQL flagged it as exponential, and
342
- * it was. A model writing "[doc:9, 9, 9, 9..." and forgetting the bracket is not a hostile act, and it is
343
- * a reader's own tab that stops responding.
344
- *
345
- * Splitting on the comma and checking each part on its own is linear, and each pattern below is anchored
346
- * over a single character class, so neither can backtrack at all.
347
- */
348
- /*
349
- * A locator a model appends to a citation instead of writing the bare marker it was asked for: "pages
350
- * 13-15", "page 4", "pp. 3-5", "pg 7". It is not a citation number and the reference map holds nothing
351
- * under it, so before this it made the whole marker unreadable and "[doc:1, pages 13-15]" reached the
352
- * reader as those characters.
353
- *
354
- * Dropped rather than rendered. The page a citation points at is already carried by the reference the
355
- * number resolves to, which is the one the host looked up rather than the one the model recalled.
356
- */
357
- var citationLocatorPattern = /^(?:pages?|pp?g?|pp)\.?\s*[\d\s‐-―,&+-]*$/i;
358
- function readCitationNumbers(interior) {
359
- var parts = interior.split(',');
360
- var numbers = [];
361
- for (var index = 0; index < parts.length; index++) {
362
- // Only the parts after the first may repeat the prefix: "[doc:1, doc:2]" and "[doc:1, 2]" are both
363
- // written by models, "[doc:doc:1]" is not.
364
- var part = index === 0 ? parts[index].trim() : parts[index].trim().replace(/^doc:\s*/, '');
365
- if (/^\d+$/.test(part)) {
366
- numbers.push(part);
367
- continue;
368
- }
369
-
370
- // A locator is allowed only after a number has been read, so "[doc:pages 3]" is still not a
371
- // citation -- something that names no document is not one whatever follows it.
372
- if (numbers.length > 0 && citationLocatorPattern.test(part)) {
373
- continue;
374
- }
375
- return null;
376
- }
377
- return numbers.length > 0 ? numbers : null;
378
- }
379
-
380
- /*
381
- * The identity of a citation as a reader meets it: the line it prints, and where it points.
382
- *
383
- * Retrieval returns one reference per chunk, so a single article that answered a question through three of
384
- * its chunks arrives as three references. Numbering those separately prints "1,2,3" over the sentence and
385
- * then lists the same title three times, which tells the reader there are three sources corroborating the
386
- * claim when there is one. Two references that would print the same line and lead to the same place are
387
- * one citation, and are numbered once.
388
- *
389
- * Case and surrounding whitespace are not part of the identity, because they are not part of what the
390
- * reader sees as different. Anything else -- a different title, a different page, a link to one figure
391
- * rather than another -- keeps the citations apart, since that is a distinction the reader can act on.
392
- */
393
- function citationIdentity(label, link) {
394
- var text = typeof label === 'string' ? label.replace(/\s+/g, ' ').trim().toLowerCase() : '';
395
- var target = typeof link === 'string' ? link.trim().toLowerCase() : '';
396
-
397
- // A null byte cannot occur in either part, so no pair of values can collide across the boundary:
398
- // without it, label "a" + link "bc" and label "ab" + link "c" would be the same identity.
399
- return text + '' + target;
400
- }
401
-
402
- /*
403
- * Collapses a run of identical citation markers into one.
404
- *
405
- * Once two references share a number, a sentence that cited both ends with that number twice over --
406
- * "1,1" -- which reads as a typo. Only an immediately repeated marker is removed: the same source cited
407
- * again later in the paragraph is a separate citation of it and keeps its marker.
408
- *
409
- * The marker is matched with whatever attributes it carries, because the number is what makes two of them
410
- * the same citation; a pattern written for a bare <sup> would silently stop collapsing the moment a
411
- * marker gained a tooltip.
412
- *
413
- * What separates the two markers is swallowed with them, because the model writes its own punctuation
414
- * between references -- "[ref1], [ref2]" -- and once both carry the same number that comma is left
415
- * between a number and itself, printing "1,1". Only a separator that was joining the two citations is
416
- * eaten: whitespace, a comma, or the marker the comma rule inserts. Anything else means the second
417
- * marker is a fresh citation later in the sentence, and it is left alone.
418
- */
419
- var citationSeparator = '(?:\\s*(?:,|<sup(?:\\s[^>]*)?>,<\\/sup>)?\\s*)';
420
- var repeatedCitationPattern = new RegExp('(<sup(?:\\s[^>]*)?>(\\d+)<\\/sup>)(?:' + citationSeparator + '<sup(?:\\s[^>]*)?>\\2<\\/sup>)+', 'g');
421
- function collapseRepeatedCitations(html) {
422
- if (typeof html !== 'string' || !html) {
423
- return '';
424
- }
425
- return html.replace(repeatedCitationPattern, '$1');
426
- }
427
-
428
- /*
429
- * Puts a comma between two citation markers that ended up side by side, so "12" reads as two sources
430
- * rather than as the twelfth.
431
- *
432
- * Only the boundary between the markers is matched, so whatever attributes either one carries are left
433
- * untouched. The inserted marker is not itself rescanned, since a replace walks the string it was given.
434
- */
435
- function separateAdjacentCitations(html) {
436
- if (typeof html !== 'string' || !html) {
437
- return '';
438
- }
439
- return html.replace(/<\/sup><sup/g, '</sup><sup>,</sup><sup');
440
- }
441
-
442
- // Escapes the few characters that would end an HTML attribute early, so a caption containing a quote
443
- // cannot introduce markup of its own.
444
- function escapeAttributeValue(text) {
445
- return text.replace(/[&<>"']/g, function (character) {
446
- switch (character) {
447
- case '&':
448
- return '&amp;';
449
- case '<':
450
- return '&lt;';
451
- case '>':
452
- return '&gt;';
453
- case '"':
454
- return '&quot;';
455
- default:
456
- return '&#39;';
457
- }
458
- });
459
- }
460
-
461
- /*
462
- * The inline marker for a citation: its number, and the source it stands for as a tooltip.
463
- *
464
- * A bare number tells the reader only that something was cited, and answers which source it was by
465
- * making them look away from the sentence and match a number against a list underneath. Carrying the
466
- * label means the answer arrives where the question is asked. The list stays as it is, since a tooltip
467
- * is not reachable by keyboard and cannot be the only place the source appears.
468
- */
469
- function citationMarkerHtml(displayIndex, label) {
470
- var text = typeof label === 'string' ? label.replace(/\s+/g, ' ').trim() : '';
471
- if (!text) {
472
- return '<sup>' + displayIndex + '</sup>';
473
- }
474
- return '<sup title="' + escapeAttributeValue(text) + '">' + displayIndex + '</sup>';
475
- }
476
-
477
- // Deliberately small: pure functions over strings, for the hosts below and for the tests.
478
- return {
479
- expandImageMarkers: expandImageMarkers,
480
- findChartMarker: findChartMarker,
481
- citationIdentity: citationIdentity,
482
- splitCombinedCitations: splitCombinedCitations,
483
- collapseRepeatedCitations: collapseRepeatedCitations,
484
- separateAdjacentCitations: separateAdjacentCitations,
485
- citationMarkerHtml: citationMarkerHtml
486
- };
487
- }();
5
+ (function() {
6
+ //#region src/Resources/CrestApps.AI.Resources/Assets/js/chat-markers.js
7
+ window.CoreAIChatMarkers = window.CoreAIChatMarkers || (function() {
8
+ "use strict";
9
+ const defaultImageAltText = "Figure";
10
+ function escapeImageAltText(title) {
11
+ const text = typeof title === "string" ? title.replace(/\s+/g, " ").trim() : "";
12
+ if (!text) return defaultImageAltText;
13
+ return text.replace(/[\\\[\]]/g, "\\$&");
14
+ }
15
+ function escapeRegExp(text) {
16
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17
+ }
18
+ function encodeImageLink(link) {
19
+ return link.replace(/[\s()<>"\\]/g, function(character) {
20
+ return "%" + character.charCodeAt(0).toString(16).toUpperCase().padStart(2, "0");
21
+ });
22
+ }
23
+ function expandImageMarkers(content, references) {
24
+ if (typeof content !== "string" || !content) return typeof content === "string" ? content : "";
25
+ if (!references || typeof references !== "object") return content;
26
+ const images = /* @__PURE__ */ new Map();
27
+ for (const marker of Object.keys(references)) {
28
+ const reference = references[marker];
29
+ if (!marker || !reference || typeof reference !== "object") continue;
30
+ if ((reference.isImage ?? reference.IsImage) !== true) continue;
31
+ const rawLink = reference.link ?? reference.Link;
32
+ const link = typeof rawLink === "string" ? rawLink.trim() : "";
33
+ if (!link) continue;
34
+ images.set(marker, {
35
+ link,
36
+ image: `![${escapeImageAltText(reference.title ?? reference.Title)}](${encodeImageLink(link)})`
37
+ });
38
+ }
39
+ if (images.size === 0) return content;
40
+ let normalized = content;
41
+ for (const marker of images.keys()) {
42
+ const escaped = escapeRegExp(marker);
43
+ const inner = escapeRegExp(marker.slice(1, -1));
44
+ normalized = normalized.replace(new RegExp("!\\[[^\\]\\n]*\\]\\(\\s*\\[?" + inner + "\\]?\\s*\\)", "g"), marker).replace(new RegExp("!\\[[^\\]\\n]*\\]" + escaped, "g"), marker).replace(new RegExp(escaped + "\\([^)\\s]*\\)", "g"), marker).replace(new RegExp("!(?=" + escaped + ")", "g"), "");
45
+ }
46
+ const pattern = new RegExp([...images.keys()].sort(function(left, right) {
47
+ return right.length - left.length;
48
+ }).map(escapeRegExp).join("|"), "g");
49
+ const drawn = /* @__PURE__ */ new Set();
50
+ return normalized.replace(pattern, function(marker) {
51
+ const entry = images.get(marker);
52
+ if (drawn.has(entry.link)) return "";
53
+ drawn.add(entry.link);
54
+ return entry.image;
55
+ });
56
+ }
57
+ const chartMarkerPrefix = "[chart:";
58
+ const citationMarkerPrefix = "[doc:";
59
+ const maxCitationMarkerLength = 512;
60
+ function isMarkerWhitespace(character) {
61
+ return character === " " || character === "\n" || character === "\r" || character === " ";
62
+ }
63
+ function readChartMarkerAt(text, startIndex) {
64
+ let index = startIndex + 7;
65
+ while (index < text.length && isMarkerWhitespace(text[index])) index++;
66
+ if (index >= text.length || text[index] !== "{") return null;
67
+ const jsonStart = index;
68
+ let depth = 0;
69
+ let inString = false;
70
+ let escaped = false;
71
+ for (; index < text.length; index++) {
72
+ const character = text[index];
73
+ if (inString) {
74
+ if (escaped) escaped = false;
75
+ else if (character === "\\") escaped = true;
76
+ else if (character === "\"") inString = false;
77
+ continue;
78
+ }
79
+ if (character === "\"") {
80
+ inString = true;
81
+ continue;
82
+ }
83
+ if (character === "{") {
84
+ depth++;
85
+ continue;
86
+ }
87
+ if (character !== "}") continue;
88
+ depth--;
89
+ if (depth > 0) continue;
90
+ const jsonEnd = index + 1;
91
+ let closeIndex = jsonEnd;
92
+ while (closeIndex < text.length && isMarkerWhitespace(text[closeIndex])) closeIndex++;
93
+ if (closeIndex >= text.length || text[closeIndex] !== "]") return null;
94
+ return {
95
+ startIndex,
96
+ endIndex: closeIndex + 1,
97
+ json: text.substring(jsonStart, jsonEnd)
98
+ };
99
+ }
100
+ return null;
101
+ }
102
+ function findChartMarker(text) {
103
+ if (typeof text !== "string" || !text) return null;
104
+ let searchFrom = 0;
105
+ for (;;) {
106
+ const start = text.indexOf(chartMarkerPrefix, searchFrom);
107
+ if (start < 0) return null;
108
+ const marker = readChartMarkerAt(text, start);
109
+ if (marker) return marker;
110
+ searchFrom = start + 1;
111
+ }
112
+ }
113
+ function splitCombinedCitations(content) {
114
+ if (typeof content !== "string" || !content) return "";
115
+ let result = "";
116
+ let from = 0;
117
+ let knownClose = -1;
118
+ for (;;) {
119
+ const start = content.indexOf(citationMarkerPrefix, from);
120
+ if (start < 0) return result + content.slice(from);
121
+ const end = knownClose >= start ? knownClose : content.indexOf("]", start);
122
+ knownClose = end;
123
+ if (end < 0) return result + content.slice(from);
124
+ const numbers = end - start <= maxCitationMarkerLength ? readCitationNumbers(content.slice(start + 5, end)) : null;
125
+ if (!numbers) {
126
+ result += content.slice(from, start + 5);
127
+ from = start + 5;
128
+ continue;
129
+ }
130
+ const canonical = numbers.map(function(number) {
131
+ return citationMarkerPrefix + number + "]";
132
+ }).join("");
133
+ const original = content.slice(start, end + 1);
134
+ result += content.slice(from, start);
135
+ result += canonical === original ? original : canonical;
136
+ from = end + 1;
137
+ }
138
+ }
139
+ const citationLocatorPattern = /^(?:pages?|pp?g?|pp)\.?\s*[\d\s‐-―,&+-]*$/i;
140
+ function readCitationNumbers(interior) {
141
+ const parts = interior.split(",");
142
+ const numbers = [];
143
+ for (let index = 0; index < parts.length; index++) {
144
+ const part = index === 0 ? parts[index].trim() : parts[index].trim().replace(/^doc:\s*/, "");
145
+ if (/^\d+$/.test(part)) {
146
+ numbers.push(part);
147
+ continue;
148
+ }
149
+ if (numbers.length > 0 && citationLocatorPattern.test(part)) continue;
150
+ return null;
151
+ }
152
+ return numbers.length > 0 ? numbers : null;
153
+ }
154
+ function citationIdentity(label, link) {
155
+ const text = typeof label === "string" ? label.replace(/\s+/g, " ").trim().toLowerCase() : "";
156
+ const target = typeof link === "string" ? link.trim().toLowerCase() : "";
157
+ return text + "\0" + target;
158
+ }
159
+ const repeatedCitationPattern = /* @__PURE__ */ new RegExp("(<sup(?:\\s[^>]*)?>(\\d+)<\\/sup>)(?:(?:\\s*(?:,|<sup(?:\\s[^>]*)?>,<\\/sup>)?\\s*)<sup(?:\\s[^>]*)?>\\2<\\/sup>)+", "g");
160
+ function collapseRepeatedCitations(html) {
161
+ if (typeof html !== "string" || !html) return "";
162
+ return html.replace(repeatedCitationPattern, "$1");
163
+ }
164
+ function separateAdjacentCitations(html) {
165
+ if (typeof html !== "string" || !html) return "";
166
+ return html.replace(/<\/sup><sup/g, "</sup><sup>,</sup><sup");
167
+ }
168
+ function escapeAttributeValue(text) {
169
+ return text.replace(/[&<>"']/g, function(character) {
170
+ switch (character) {
171
+ case "&": return "&amp;";
172
+ case "<": return "&lt;";
173
+ case ">": return "&gt;";
174
+ case "\"": return "&quot;";
175
+ default: return "&#39;";
176
+ }
177
+ });
178
+ }
179
+ function citationMarkerHtml(displayIndex, label) {
180
+ const text = typeof label === "string" ? label.replace(/\s+/g, " ").trim() : "";
181
+ if (!text) return "<sup>" + displayIndex + "</sup>";
182
+ return "<sup title=\"" + escapeAttributeValue(text) + "\">" + displayIndex + "</sup>";
183
+ }
184
+ return {
185
+ expandImageMarkers,
186
+ findChartMarker,
187
+ citationIdentity,
188
+ splitCombinedCitations,
189
+ collapseRepeatedCitations,
190
+ separateAdjacentCitations,
191
+ citationMarkerHtml
192
+ };
193
+ })();
194
+ //#endregion
195
+ })();
488
196
  //# sourceMappingURL=chat-markers.js.map