@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.
- package/dist/ai-chat-widget.js +762 -964
- package/dist/ai-chat-widget.js.map +1 -1
- package/dist/ai-chat-widget.min.js +1 -1
- package/dist/ai-chat-widget.min.js.map +1 -1
- package/dist/ai-chat.css +0 -2
- package/dist/ai-chat.js +2380 -3698
- package/dist/ai-chat.js.map +1 -1
- package/dist/ai-chat.min.css +1 -1
- package/dist/ai-chat.min.js +78 -1
- package/dist/ai-chat.min.js.map +1 -1
- package/dist/chat-image-carousel.css +0 -2
- package/dist/chat-image-carousel.js +306 -482
- package/dist/chat-image-carousel.js.map +1 -1
- package/dist/chat-image-carousel.min.css +1 -1
- package/dist/chat-image-carousel.min.js +1 -1
- package/dist/chat-image-carousel.min.js.map +1 -1
- package/dist/chat-interaction-settings.js +221 -293
- package/dist/chat-interaction-settings.js.map +1 -1
- package/dist/chat-interaction-settings.min.js +1 -1
- package/dist/chat-interaction-settings.min.js.map +1 -1
- package/dist/chat-interaction.js +2330 -3557
- package/dist/chat-interaction.js.map +1 -1
- package/dist/chat-interaction.min.js +62 -1
- package/dist/chat-interaction.min.js.map +1 -1
- package/dist/chat-markers.js +193 -485
- package/dist/chat-markers.js.map +1 -1
- package/dist/chat-markers.min.js +2 -1
- package/dist/chat-markers.min.js.map +1 -1
- package/dist/chat-widget.css +0 -2
- package/dist/chat-widget.min.css +1 -1
- package/dist/document-drop-zone.css +0 -2
- package/dist/document-drop-zone.js +127 -153
- package/dist/document-drop-zone.js.map +1 -1
- package/dist/document-drop-zone.min.css +1 -1
- package/dist/document-drop-zone.min.js +1 -1
- package/dist/document-drop-zone.min.js.map +1 -1
- package/dist/realtime-audio.js +1639 -2504
- package/dist/realtime-audio.js.map +1 -1
- package/dist/realtime-audio.min.js +3 -1
- package/dist/realtime-audio.min.js.map +1 -1
- package/dist/technical-name-generator.js +58 -80
- package/dist/technical-name-generator.js.map +1 -1
- package/dist/technical-name-generator.min.js +1 -1
- package/dist/technical-name-generator.min.js.map +1 -1
- package/package.json +1 -5
- package/dist/ai-chat.css.map +0 -1
- package/dist/chat-image-carousel.css.map +0 -1
- package/dist/chat-widget.css.map +0 -1
- package/dist/document-drop-zone.css.map +0 -1
package/dist/chat-markers.js
CHANGED
|
@@ -1,488 +1,196 @@
|
|
|
1
1
|
/*
|
|
2
|
-
** NOTE: This file is generated by
|
|
3
|
-
**
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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 '&';
|
|
449
|
-
case '<':
|
|
450
|
-
return '<';
|
|
451
|
-
case '>':
|
|
452
|
-
return '>';
|
|
453
|
-
case '"':
|
|
454
|
-
return '"';
|
|
455
|
-
default:
|
|
456
|
-
return ''';
|
|
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: `})`
|
|
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 "&";
|
|
172
|
+
case "<": return "<";
|
|
173
|
+
case ">": return ">";
|
|
174
|
+
case "\"": return """;
|
|
175
|
+
default: return "'";
|
|
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
|