bunnyquery 1.1.1 → 1.1.2
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/bunnyquery.css +4 -4
- package/bunnyquery.js +29 -73
- package/package.json +1 -1
package/bunnyquery.css
CHANGED
|
@@ -353,7 +353,7 @@
|
|
|
353
353
|
font-size: 0.9em;
|
|
354
354
|
padding: 0.1em 0.35em;
|
|
355
355
|
background: rgba(0, 0, 0, 0.06);
|
|
356
|
-
border-radius:
|
|
356
|
+
border-radius: 0;
|
|
357
357
|
white-space: pre-wrap;
|
|
358
358
|
word-break: break-word;
|
|
359
359
|
}
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
padding: 0.6em 0.8em;
|
|
363
363
|
background: #1e1e1e;
|
|
364
364
|
color: #f5f5f5;
|
|
365
|
-
border-radius:
|
|
365
|
+
border-radius: 0;
|
|
366
366
|
overflow-x: auto;
|
|
367
367
|
white-space: pre;
|
|
368
368
|
}
|
|
@@ -850,6 +850,7 @@ a.bq-link-button:hover {
|
|
|
850
850
|
.bq-modal-delete-header {
|
|
851
851
|
display: flex;
|
|
852
852
|
align-items: center;
|
|
853
|
+
justify-content: space-between;
|
|
853
854
|
gap: 0.5rem;
|
|
854
855
|
font-size: 0.95rem;
|
|
855
856
|
font-weight: 600;
|
|
@@ -1144,13 +1145,12 @@ a.bq-link-button:hover {
|
|
|
1144
1145
|
|
|
1145
1146
|
@media (max-width: 800px) {
|
|
1146
1147
|
.bq-modal-backdrop {
|
|
1147
|
-
padding: 0;
|
|
1148
|
+
padding: 0.5rem;
|
|
1148
1149
|
}
|
|
1149
1150
|
}
|
|
1150
1151
|
|
|
1151
1152
|
@media (max-width: 430px) {
|
|
1152
1153
|
.bq-modal-delete {
|
|
1153
|
-
max-width: calc(100% - 1rem);
|
|
1154
1154
|
max-height: calc(100% - 1rem);
|
|
1155
1155
|
}
|
|
1156
1156
|
|
package/bunnyquery.js
CHANGED
|
@@ -320,28 +320,16 @@
|
|
|
320
320
|
};
|
|
321
321
|
|
|
322
322
|
// ----- file-fence helpers (mirrors agent.vue) ---------------------------
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
const FENCE_LANGUAGE_EXTENSIONS = {
|
|
327
|
-
html: 'html',
|
|
328
|
-
htm: 'html',
|
|
329
|
-
csv: 'csv',
|
|
330
|
-
tsv: 'tsv',
|
|
331
|
-
json: 'json',
|
|
332
|
-
xml: 'xml',
|
|
333
|
-
svg: 'svg',
|
|
334
|
-
md: 'md',
|
|
335
|
-
markdown: 'md',
|
|
336
|
-
yaml: 'yaml',
|
|
337
|
-
yml: 'yml',
|
|
338
|
-
txt: 'txt',
|
|
339
|
-
sql: 'sql',
|
|
340
|
-
css: 'css',
|
|
341
|
-
};
|
|
323
|
+
// Only `\`\`\`filename.ext\n...\n\`\`\`` fences become downloadable file
|
|
324
|
+
// anchors (matches the host system prompt in agent.vue). Bare-language
|
|
325
|
+
// fences like \`\`\`html / \`\`\`csv render as normal code blocks.
|
|
342
326
|
|
|
327
|
+
// Minimal MIME table for the file extensions the host agent is known to
|
|
328
|
+
// emit. Anything else falls back to text/plain so the browser still
|
|
329
|
+
// downloads the blob as a text file.
|
|
343
330
|
const FENCE_MIME_TYPES = {
|
|
344
331
|
html: 'text/html',
|
|
332
|
+
htm: 'text/html',
|
|
345
333
|
csv: 'text/csv',
|
|
346
334
|
tsv: 'text/tab-separated-values',
|
|
347
335
|
json: 'application/json',
|
|
@@ -355,22 +343,9 @@
|
|
|
355
343
|
css: 'text/css',
|
|
356
344
|
};
|
|
357
345
|
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
// Closed fences whose info-string is one of the known language tags
|
|
361
|
-
// above. The model often emits ```html ... ``` or ```csv ... ``` instead
|
|
362
|
-
// of a full filename — surface those as downloadable files too.
|
|
363
|
-
const FENCE_LANG_RE = new RegExp(
|
|
364
|
-
'```(' + Object.keys(FENCE_LANGUAGE_EXTENSIONS).join('|') + ')[ \\t]*\\n([\\s\\S]*?)```',
|
|
365
|
-
'gi'
|
|
366
|
-
);
|
|
367
|
-
// Open (still-streaming) fences for either pattern, used to suppress the
|
|
368
|
-
// raw fence text mid-typewriter.
|
|
346
|
+
// Open (still-streaming) filename fence, used to suppress the raw fence
|
|
347
|
+
// text mid-typewriter.
|
|
369
348
|
const OPEN_FENCE_FILENAME_RE = /```([\w.-]+\.[a-zA-Z0-9]+)\n?/;
|
|
370
|
-
const OPEN_FENCE_LANG_RE = new RegExp(
|
|
371
|
-
'```(' + Object.keys(FENCE_LANGUAGE_EXTENSIONS).join('|') + ')[ \\t]*\\n?',
|
|
372
|
-
'i'
|
|
373
|
-
);
|
|
374
349
|
|
|
375
350
|
// Cache blob URLs keyed by (filename + content) so re-renders don't leak
|
|
376
351
|
// a fresh ObjectURL on every tick of streaming text.
|
|
@@ -387,16 +362,6 @@
|
|
|
387
362
|
return href;
|
|
388
363
|
};
|
|
389
364
|
|
|
390
|
-
// Counter used to mint stable filenames for bare-language fences within
|
|
391
|
-
// a single rendered message ("download.html", "download-2.html", ...).
|
|
392
|
-
// Keyed by message identity so re-renders of the same message reuse the
|
|
393
|
-
// same names (which lets the blob cache hit).
|
|
394
|
-
const _bareFenceNameCounters = new WeakMap();
|
|
395
|
-
const _bareFilenameFor = (msgKey, ext, occurrence) => {
|
|
396
|
-
const base = 'download';
|
|
397
|
-
return occurrence === 0 ? `${base}.${ext}` : `${base}-${occurrence + 1}.${ext}`;
|
|
398
|
-
};
|
|
399
|
-
|
|
400
365
|
// Split message content into renderable parts. When `marked` is loaded,
|
|
401
366
|
// returns a single `{type:'html', html}` part containing the full
|
|
402
367
|
// markdown-rendered bubble (with file/link anchors spliced back in).
|
|
@@ -440,44 +405,19 @@
|
|
|
440
405
|
return PH(idx);
|
|
441
406
|
};
|
|
442
407
|
|
|
443
|
-
//
|
|
444
|
-
// (typewriter ticks) reuse the same synthesized names → blob cache
|
|
445
|
-
// hits instead of leaking ObjectURLs every frame.
|
|
446
|
-
let bareCounters = msgKey ? _bareFenceNameCounters.get(msgKey) : null;
|
|
447
|
-
if (!bareCounters && msgKey) {
|
|
448
|
-
bareCounters = {};
|
|
449
|
-
_bareFenceNameCounters.set(msgKey, bareCounters);
|
|
450
|
-
}
|
|
451
|
-
const bareSeen = {};
|
|
452
|
-
|
|
453
|
-
// 1a. Closed filename fences: ```filename.ext\n...\n```
|
|
408
|
+
// 1. Closed filename fences: ```filename.ext\n...\n```
|
|
454
409
|
let working = content.replace(
|
|
455
410
|
/```([\w.-]+\.[a-zA-Z0-9]+)\n([\s\S]*?)```/g,
|
|
456
411
|
(_full, filename, body) =>
|
|
457
412
|
push(fileToAnchorHtml(filename, _getOrCreateFileHref(filename, body))),
|
|
458
413
|
);
|
|
459
414
|
|
|
460
|
-
// 1b. Closed bare-language fences: ```html ... ``` etc.
|
|
461
|
-
working = working.replace(FENCE_LANG_RE, (_full, lang, body) => {
|
|
462
|
-
const ext = FENCE_LANGUAGE_EXTENSIONS[lang.toLowerCase()] || 'txt';
|
|
463
|
-
const occurrence = bareSeen[ext] || 0;
|
|
464
|
-
bareSeen[ext] = occurrence + 1;
|
|
465
|
-
const filename = _bareFilenameFor(msgKey, ext, occurrence);
|
|
466
|
-
return push(fileToAnchorHtml(filename, _getOrCreateFileHref(filename, body)));
|
|
467
|
-
});
|
|
468
|
-
|
|
469
415
|
// 2. Mid-typewriter unclosed fence → hide raw streaming source.
|
|
470
416
|
if (ctx.isTyping) {
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
const open = fnOpen && (!langOpen || fnOpen.index <= langOpen.index)
|
|
474
|
-
? { match: fnOpen, label: fnOpen[1] }
|
|
475
|
-
: langOpen
|
|
476
|
-
? { match: langOpen, label: 'download.' + (FENCE_LANGUAGE_EXTENSIONS[langOpen[1].toLowerCase()] || 'txt') }
|
|
477
|
-
: null;
|
|
478
|
-
if (open && typeof open.match.index === 'number') {
|
|
417
|
+
const open = working.match(OPEN_FENCE_FILENAME_RE);
|
|
418
|
+
if (open && typeof open.index === 'number') {
|
|
479
419
|
const dots = '.'.repeat(((ctx.dotTick || 0) % 3) + 1);
|
|
480
|
-
working = working.slice(0, open.
|
|
420
|
+
working = working.slice(0, open.index) + `\n[generating ${open[1]}${dots}]`;
|
|
481
421
|
}
|
|
482
422
|
}
|
|
483
423
|
|
|
@@ -1909,6 +1849,13 @@
|
|
|
1909
1849
|
},
|
|
1910
1850
|
}, 'Clear');
|
|
1911
1851
|
|
|
1852
|
+
const closeBtn = $('button', {
|
|
1853
|
+
type: 'button',
|
|
1854
|
+
class: 'bq-modal-close',
|
|
1855
|
+
'aria-label': 'Close',
|
|
1856
|
+
onclick: close,
|
|
1857
|
+
}, '×');
|
|
1858
|
+
|
|
1912
1859
|
const modal = $('div', {
|
|
1913
1860
|
class: 'bq-modal-backdrop',
|
|
1914
1861
|
onclick: (e) => { if (e.target === modal) close(); },
|
|
@@ -1916,6 +1863,7 @@
|
|
|
1916
1863
|
$('div', { class: 'bq-modal-delete' }, [
|
|
1917
1864
|
$('div', { class: 'bq-modal-delete-header' }, [
|
|
1918
1865
|
$('span', null, 'Clear chat history'),
|
|
1866
|
+
closeBtn,
|
|
1919
1867
|
]),
|
|
1920
1868
|
$('div', { class: 'bq-modal-delete-body' }, [
|
|
1921
1869
|
$('p', null, 'Hide all previous messages from this chat?'),
|
|
@@ -1965,6 +1913,13 @@
|
|
|
1965
1913
|
},
|
|
1966
1914
|
}, 'Logout');
|
|
1967
1915
|
|
|
1916
|
+
const closeBtn = $('button', {
|
|
1917
|
+
type: 'button',
|
|
1918
|
+
class: 'bq-modal-close',
|
|
1919
|
+
'aria-label': 'Close',
|
|
1920
|
+
onclick: close,
|
|
1921
|
+
}, '×');
|
|
1922
|
+
|
|
1968
1923
|
const modal = $('div', {
|
|
1969
1924
|
class: 'bq-modal-backdrop',
|
|
1970
1925
|
onclick: (e) => { if (e.target === modal) close(); },
|
|
@@ -1972,6 +1927,7 @@
|
|
|
1972
1927
|
$('div', { class: 'bq-modal-delete' }, [
|
|
1973
1928
|
$('div', { class: 'bq-modal-delete-header' }, [
|
|
1974
1929
|
$('span', null, 'Logout'),
|
|
1930
|
+
closeBtn,
|
|
1975
1931
|
]),
|
|
1976
1932
|
$('div', { class: 'bq-modal-delete-body' }, [
|
|
1977
1933
|
$('p', null, 'Log out of this chat?'),
|