bunnyquery 1.4.5 → 1.5.0
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 +81 -0
- package/bunnyquery.js +171 -80
- package/dist/engine.cjs +133 -75
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.mjs +133 -75
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/session.ts +167 -70
- package/src/widget.css +81 -0
package/bunnyquery.css
CHANGED
|
@@ -162,6 +162,87 @@
|
|
|
162
162
|
to { transform: rotate(360deg); }
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/* jumping ASCII bunny — full-area "loading/fetching" indicator (page/gate loads,
|
|
166
|
+
initial history fetch, settings panel). Ported from www.bunnyquery.com
|
|
167
|
+
bunnyLoader.vue. Two <pre> frames toggle + hop across the track and flip. */
|
|
168
|
+
.bq-bunny-loader {
|
|
169
|
+
display: flex;
|
|
170
|
+
flex-direction: column;
|
|
171
|
+
align-items: center;
|
|
172
|
+
justify-content: center;
|
|
173
|
+
gap: 0.75rem;
|
|
174
|
+
}
|
|
175
|
+
.bq-bunny-loader--overlay {
|
|
176
|
+
position: absolute;
|
|
177
|
+
inset: 0;
|
|
178
|
+
pointer-events: none;
|
|
179
|
+
z-index: 4;
|
|
180
|
+
}
|
|
181
|
+
.bq-bunny-stage {
|
|
182
|
+
display: inline-block;
|
|
183
|
+
position: relative;
|
|
184
|
+
text-align: left;
|
|
185
|
+
}
|
|
186
|
+
.bq-bunny-track {
|
|
187
|
+
position: relative;
|
|
188
|
+
height: 3.6rem;
|
|
189
|
+
width: 100%;
|
|
190
|
+
animation: bq-bunny-move 9s steps(12) infinite;
|
|
191
|
+
}
|
|
192
|
+
.bq-bunny-dir {
|
|
193
|
+
display: inline-block;
|
|
194
|
+
position: absolute;
|
|
195
|
+
bottom: 0;
|
|
196
|
+
left: 0;
|
|
197
|
+
animation: bq-bunny-flip 9s steps(1) infinite;
|
|
198
|
+
}
|
|
199
|
+
.bq-frame {
|
|
200
|
+
margin: 0;
|
|
201
|
+
/* Pin a Latin monospace font. Without it the <pre> falls back to the UA
|
|
202
|
+
`monospace` keyword, which on Korean/Japanese systems can resolve to a
|
|
203
|
+
CJK font that renders U+005C (backslash) as ₩ / ¥ — wrecking the art. */
|
|
204
|
+
font-family: var(--bq-mono);
|
|
205
|
+
font-size: 1rem;
|
|
206
|
+
line-height: 1.2;
|
|
207
|
+
white-space: pre;
|
|
208
|
+
color: var(--bq-ink);
|
|
209
|
+
}
|
|
210
|
+
.bq-frame-a {
|
|
211
|
+
animation: bq-bunny-toggle-a 1s steps(1) infinite;
|
|
212
|
+
}
|
|
213
|
+
.bq-frame-b {
|
|
214
|
+
animation: bq-bunny-toggle-b 1s steps(1) infinite;
|
|
215
|
+
opacity: 0;
|
|
216
|
+
}
|
|
217
|
+
.bq-bunny-loader__label {
|
|
218
|
+
color: var(--bq-ink);
|
|
219
|
+
font-family: var(--bq-mono);
|
|
220
|
+
font-size: 0.83rem;
|
|
221
|
+
}
|
|
222
|
+
@keyframes bq-bunny-move {
|
|
223
|
+
0% { transform: translateX(-11ch); }
|
|
224
|
+
25% { transform: translateX(4ch); }
|
|
225
|
+
37.5% { transform: translateX(4ch); }
|
|
226
|
+
50% { transform: translateX(4ch); }
|
|
227
|
+
75% { transform: translateX(-11ch); }
|
|
228
|
+
87.5% { transform: translateX(-11ch); }
|
|
229
|
+
100% { transform: translateX(-11ch); }
|
|
230
|
+
}
|
|
231
|
+
@keyframes bq-bunny-flip {
|
|
232
|
+
0% { transform: scaleX(1); }
|
|
233
|
+
50% { transform: scaleX(-1); }
|
|
234
|
+
}
|
|
235
|
+
@keyframes bq-bunny-toggle-a {
|
|
236
|
+
0% { opacity: 1; }
|
|
237
|
+
25% { opacity: 0; }
|
|
238
|
+
100% { opacity: 1; }
|
|
239
|
+
}
|
|
240
|
+
@keyframes bq-bunny-toggle-b {
|
|
241
|
+
0% { opacity: 0; }
|
|
242
|
+
25% { opacity: 1; }
|
|
243
|
+
100% { opacity: 0; }
|
|
244
|
+
}
|
|
245
|
+
|
|
165
246
|
/* ============================================================================
|
|
166
247
|
* AUTH VIEWS (login / signup / forgot / verify / settings)
|
|
167
248
|
* ==========================================================================*/
|
package/bunnyquery.js
CHANGED
|
@@ -1036,10 +1036,18 @@ ${options.inlineContentPlaceholder}
|
|
|
1036
1036
|
}
|
|
1037
1037
|
|
|
1038
1038
|
// src/engine/session.ts
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1039
|
+
var _g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
1040
|
+
function nowMs() {
|
|
1041
|
+
return _g.performance && typeof _g.performance.now === "function" ? _g.performance.now() : Date.now();
|
|
1042
|
+
}
|
|
1043
|
+
function nextFrame(cb) {
|
|
1044
|
+
if (typeof _g.requestAnimationFrame === "function") {
|
|
1045
|
+
_g.requestAnimationFrame(cb);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
setTimeout(function() {
|
|
1049
|
+
cb(nowMs());
|
|
1050
|
+
}, 16);
|
|
1043
1051
|
}
|
|
1044
1052
|
var ChatSession = class {
|
|
1045
1053
|
constructor(host) {
|
|
@@ -1126,35 +1134,26 @@ ${options.inlineContentPlaceholder}
|
|
|
1126
1134
|
if (dispatchItemId) self.historyItemPolls.delete(dispatchItemId);
|
|
1127
1135
|
var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
|
|
1128
1136
|
var reply = { role: "assistant", content: result.content, isError: result.isError };
|
|
1129
|
-
var
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
} else {
|
|
1137
|
-
var msgs = existing.messages.slice();
|
|
1138
|
-
var idx = -1;
|
|
1139
|
-
for (var i = msgs.length - 1; i >= 0; i--) {
|
|
1140
|
-
var m = msgs[i];
|
|
1141
|
-
if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
|
|
1142
|
-
idx = i;
|
|
1143
|
-
break;
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
if (idx !== -1) {
|
|
1147
|
-
reply._serverItemId = msgs[idx]._serverItemId;
|
|
1148
|
-
msgs[idx] = reply;
|
|
1149
|
-
} else {
|
|
1150
|
-
msgs.push(reply);
|
|
1137
|
+
var msgs = existing.messages.slice();
|
|
1138
|
+
var idx = -1;
|
|
1139
|
+
for (var i = msgs.length - 1; i >= 0; i--) {
|
|
1140
|
+
var m = msgs[i];
|
|
1141
|
+
if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
|
|
1142
|
+
idx = i;
|
|
1143
|
+
break;
|
|
1151
1144
|
}
|
|
1152
|
-
self.aiChatHistoryCache[params.key] = {
|
|
1153
|
-
messages: msgs,
|
|
1154
|
-
endOfList: existing.endOfList,
|
|
1155
|
-
startKeyHistory: existing.startKeyHistory
|
|
1156
|
-
};
|
|
1157
1145
|
}
|
|
1146
|
+
if (idx !== -1) {
|
|
1147
|
+
reply._serverItemId = msgs[idx]._serverItemId;
|
|
1148
|
+
msgs[idx] = reply;
|
|
1149
|
+
} else {
|
|
1150
|
+
msgs.push(reply);
|
|
1151
|
+
}
|
|
1152
|
+
self.aiChatHistoryCache[params.key] = {
|
|
1153
|
+
messages: msgs,
|
|
1154
|
+
endOfList: existing.endOfList,
|
|
1155
|
+
startKeyHistory: existing.startKeyHistory
|
|
1156
|
+
};
|
|
1158
1157
|
return result;
|
|
1159
1158
|
});
|
|
1160
1159
|
this.pendingAgentRequests[params.key] = run;
|
|
@@ -1297,7 +1296,9 @@ ${options.inlineContentPlaceholder}
|
|
|
1297
1296
|
if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
|
|
1298
1297
|
if (existing.isSendingToServer) promoted.isSendingToServer = true;
|
|
1299
1298
|
this.state.messages[nextIdx] = promoted;
|
|
1300
|
-
|
|
1299
|
+
var placeholder = { role: "assistant", content: "", isPending: true };
|
|
1300
|
+
if (existing._serverItemId !== void 0) placeholder._serverItemId = existing._serverItemId;
|
|
1301
|
+
this.state.messages.splice(nextIdx + 1, 0, placeholder);
|
|
1301
1302
|
this.host.notify();
|
|
1302
1303
|
}
|
|
1303
1304
|
resolveQueuedUserBubble(serverId) {
|
|
@@ -1507,61 +1508,118 @@ ${options.inlineContentPlaceholder}
|
|
|
1507
1508
|
});
|
|
1508
1509
|
}
|
|
1509
1510
|
// --- typewriter -------------------------------------------------------
|
|
1511
|
+
// Reveal `fullText` into a message bubble at a constant wall-clock RATE
|
|
1512
|
+
// (chars/second) driven by requestAnimationFrame, rather than a fixed number
|
|
1513
|
+
// of characters per fixed-delay tick. This is what keeps typing smooth and
|
|
1514
|
+
// cheap on slow machines:
|
|
1515
|
+
//
|
|
1516
|
+
// * Each frame reveals `elapsed_ms * CHARS_PER_SEC` characters, so the
|
|
1517
|
+
// visual speed is the same regardless of how long a frame actually took.
|
|
1518
|
+
// * As the bubble's markdown grows, each re-render gets more expensive, so
|
|
1519
|
+
// frames get longer — which makes each frame reveal MORE characters and
|
|
1520
|
+
// therefore do FEWER, larger renders. That converts the old O(n^2)
|
|
1521
|
+
// "re-render the whole growing string once per 3 characters" (which got
|
|
1522
|
+
// slower and slower and pegged the CPU) into roughly O(n): the number of
|
|
1523
|
+
// renders self-throttles to what the machine can actually paint.
|
|
1524
|
+
// * rAF paces us to the browser's paint cycle and pauses in background
|
|
1525
|
+
// tabs, so we never queue work faster than it can be drawn.
|
|
1510
1526
|
typewriteIntoIndex(idx, fullText, localId) {
|
|
1511
1527
|
var self = this;
|
|
1512
1528
|
if (!fullText) return Promise.resolve();
|
|
1513
|
-
var
|
|
1514
|
-
var
|
|
1515
|
-
var
|
|
1529
|
+
var CHARS_PER_SEC = 300;
|
|
1530
|
+
var MIN_STEP = 1;
|
|
1531
|
+
var MAX_FRAME_MS = 1e3;
|
|
1532
|
+
var regions = [], m;
|
|
1516
1533
|
var fenceRegex = /```[\w.-]+\.[a-zA-Z0-9]+\n[\s\S]*?```/g;
|
|
1517
|
-
while ((m = fenceRegex.exec(fullText)) !== null)
|
|
1518
|
-
var linkRegions = [], lm;
|
|
1534
|
+
while ((m = fenceRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
|
|
1519
1535
|
var linkRegex = createInlineLinkRegex();
|
|
1520
|
-
while ((
|
|
1536
|
+
while ((m = linkRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
|
|
1537
|
+
regions.sort(function(a, b) {
|
|
1538
|
+
return a.start - b.start;
|
|
1539
|
+
});
|
|
1521
1540
|
this.state.typing = true;
|
|
1522
1541
|
this.state.typingAbort = false;
|
|
1523
1542
|
var i = 0;
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
var
|
|
1527
|
-
var
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1543
|
+
var last = nowMs();
|
|
1544
|
+
return new Promise(function(resolve) {
|
|
1545
|
+
var done = false;
|
|
1546
|
+
var doc = _g.document;
|
|
1547
|
+
function isHidden() {
|
|
1548
|
+
return !!(doc && doc.hidden);
|
|
1549
|
+
}
|
|
1550
|
+
function cleanup() {
|
|
1551
|
+
if (doc && doc.removeEventListener) doc.removeEventListener("visibilitychange", onVisibility);
|
|
1552
|
+
}
|
|
1553
|
+
function finish() {
|
|
1554
|
+
if (done) return;
|
|
1555
|
+
done = true;
|
|
1556
|
+
cleanup();
|
|
1557
|
+
if (!self.state.typingAbort) {
|
|
1558
|
+
var fi = localId ? self.state.messages.findIndex(function(mm) {
|
|
1559
|
+
return mm._localId === localId;
|
|
1560
|
+
}) : idx;
|
|
1561
|
+
if (fi !== -1) {
|
|
1562
|
+
var t = self.state.messages[fi];
|
|
1563
|
+
if (t) {
|
|
1564
|
+
t.content = fullText;
|
|
1565
|
+
self.host.refreshMessageBubble(fi);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
self.state.typing = false;
|
|
1570
|
+
resolve();
|
|
1540
1571
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
var
|
|
1572
|
+
function onVisibility() {
|
|
1573
|
+
if (isHidden()) finish();
|
|
1574
|
+
}
|
|
1575
|
+
if (doc && doc.addEventListener) doc.addEventListener("visibilitychange", onVisibility);
|
|
1576
|
+
function frame(t) {
|
|
1577
|
+
if (done) return;
|
|
1578
|
+
if (self.state.typingAbort || i >= fullText.length || isHidden()) {
|
|
1579
|
+
finish();
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
var dt = t - last;
|
|
1583
|
+
last = t;
|
|
1584
|
+
if (!(dt > 0)) dt = 16;
|
|
1585
|
+
if (dt > MAX_FRAME_MS) dt = MAX_FRAME_MS;
|
|
1586
|
+
var step = Math.round(dt * CHARS_PER_SEC / 1e3);
|
|
1587
|
+
if (step < MIN_STEP) step = MIN_STEP;
|
|
1588
|
+
var next = Math.min(fullText.length, i + step);
|
|
1589
|
+
for (var changed = true; changed; ) {
|
|
1590
|
+
changed = false;
|
|
1591
|
+
for (var k = 0; k < regions.length; k++) {
|
|
1592
|
+
var r = regions[k];
|
|
1593
|
+
if (next > r.start && i < r.end && r.end > next) {
|
|
1594
|
+
next = r.end;
|
|
1595
|
+
changed = true;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
if (next > fullText.length) next = fullText.length;
|
|
1600
|
+
i = next;
|
|
1601
|
+
var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
|
|
1556
1602
|
return mm._localId === localId;
|
|
1557
1603
|
}) : idx;
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
self.host.refreshMessageBubble(fi !== -1 ? fi : idx);
|
|
1604
|
+
if (currentIdx === -1) {
|
|
1605
|
+
finish();
|
|
1606
|
+
return;
|
|
1562
1607
|
}
|
|
1608
|
+
var target = self.state.messages[currentIdx];
|
|
1609
|
+
if (!target) {
|
|
1610
|
+
finish();
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
target.content = fullText.slice(0, i);
|
|
1614
|
+
self.host.refreshMessageBubble(currentIdx);
|
|
1615
|
+
self.host.scrollToBottomIfSticky();
|
|
1616
|
+
nextFrame(frame);
|
|
1617
|
+
}
|
|
1618
|
+
if (isHidden()) {
|
|
1619
|
+
finish();
|
|
1620
|
+
return;
|
|
1563
1621
|
}
|
|
1564
|
-
|
|
1622
|
+
nextFrame(frame);
|
|
1565
1623
|
});
|
|
1566
1624
|
}
|
|
1567
1625
|
enqueueTypewrite(idx, fullText, localId) {
|
|
@@ -1875,7 +1933,7 @@ ${options.inlineContentPlaceholder}
|
|
|
1875
1933
|
if (item.status !== "running" && item.status !== "pending") return;
|
|
1876
1934
|
if (!item.poll || !item.id) return;
|
|
1877
1935
|
if (self.historyItemPolls.has(item.id)) return;
|
|
1878
|
-
if (
|
|
1936
|
+
if (self.pendingAgentRequests[self.getHistoryCacheKey()] && !item._isBgTask && !item._isOnBgQueue) return;
|
|
1879
1937
|
self.historyItemPolls.set(item.id, true);
|
|
1880
1938
|
var capturedId = item.id;
|
|
1881
1939
|
var pp = item.poll({
|
|
@@ -2118,7 +2176,7 @@ ${options.inlineContentPlaceholder}
|
|
|
2118
2176
|
(function() {
|
|
2119
2177
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
2120
2178
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
2121
|
-
var BQ_VERSION = "1.
|
|
2179
|
+
var BQ_VERSION = "1.5.0" ;
|
|
2122
2180
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
2123
2181
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
2124
2182
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -2373,13 +2431,40 @@ ${options.inlineContentPlaceholder}
|
|
|
2373
2431
|
})
|
|
2374
2432
|
);
|
|
2375
2433
|
}
|
|
2434
|
+
var BUNNY_FRAME_A = ' (\\(\\\n ( - -)\n c(")(")';
|
|
2435
|
+
var BUNNY_FRAME_B = ' /)/)\n ( . .)\nc(")(")';
|
|
2436
|
+
function bunnyLoader(label, overlay) {
|
|
2437
|
+
return h(
|
|
2438
|
+
"div",
|
|
2439
|
+
{
|
|
2440
|
+
class: "bq-bunny-loader" + (""),
|
|
2441
|
+
"aria-hidden": "true",
|
|
2442
|
+
translate: "no"
|
|
2443
|
+
},
|
|
2444
|
+
h(
|
|
2445
|
+
"div",
|
|
2446
|
+
{ class: "bq-bunny-stage" },
|
|
2447
|
+
h(
|
|
2448
|
+
"div",
|
|
2449
|
+
{ class: "bq-bunny-track" },
|
|
2450
|
+
h(
|
|
2451
|
+
"div",
|
|
2452
|
+
{ class: "bq-bunny-dir" },
|
|
2453
|
+
h("pre", { class: "bq-frame bq-frame-a", translate: "no", text: BUNNY_FRAME_A }),
|
|
2454
|
+
h("pre", { class: "bq-frame bq-frame-b", translate: "no", text: BUNNY_FRAME_B })
|
|
2455
|
+
)
|
|
2456
|
+
)
|
|
2457
|
+
),
|
|
2458
|
+
label ? h("div", { class: "bq-bunny-loader__label", text: label }) : null
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2376
2461
|
function showLoading(label) {
|
|
2377
2462
|
render("loading", function() {
|
|
2378
2463
|
return pageRoot(
|
|
2379
2464
|
h(
|
|
2380
2465
|
"div",
|
|
2381
2466
|
{ class: "bq-disabled-inner", style: { marginTop: "3rem" } },
|
|
2382
|
-
|
|
2467
|
+
bunnyLoader("Loading...")
|
|
2383
2468
|
)
|
|
2384
2469
|
);
|
|
2385
2470
|
});
|
|
@@ -3265,7 +3350,7 @@ ${options.inlineContentPlaceholder}
|
|
|
3265
3350
|
CS.messagesBox.appendChild(h(
|
|
3266
3351
|
"div",
|
|
3267
3352
|
{ class: "bq-chat-settings" },
|
|
3268
|
-
h("div", { class: "bq-chat-settings-loading" },
|
|
3353
|
+
h("div", { class: "bq-chat-settings-loading" }, bunnyLoader("Loading..."))
|
|
3269
3354
|
));
|
|
3270
3355
|
Promise.all([getProfile(), getNewsletterStatus()]).then(function(res) {
|
|
3271
3356
|
if (res[0]) S.user = res[0];
|
|
@@ -4785,9 +4870,16 @@ ${options.inlineContentPlaceholder}
|
|
|
4785
4870
|
return h("div", { class: cls.join(" "), dataset: { msgIndex: String(idx) } }, bubble);
|
|
4786
4871
|
}
|
|
4787
4872
|
function historyLoadingEl(initial) {
|
|
4873
|
+
if (initial) {
|
|
4874
|
+
return h(
|
|
4875
|
+
"div",
|
|
4876
|
+
{ class: "bq-history-loading is-initial" },
|
|
4877
|
+
bunnyLoader("Fetching history...")
|
|
4878
|
+
);
|
|
4879
|
+
}
|
|
4788
4880
|
return h(
|
|
4789
4881
|
"div",
|
|
4790
|
-
{ class: "bq-history-loading"
|
|
4882
|
+
{ class: "bq-history-loading" },
|
|
4791
4883
|
h("span", { text: "Fetching history" }),
|
|
4792
4884
|
h("span", { class: "bq-loader" })
|
|
4793
4885
|
);
|
|
@@ -4851,7 +4943,6 @@ ${options.inlineContentPlaceholder}
|
|
|
4851
4943
|
CS.settingsBtnEl = null;
|
|
4852
4944
|
CS.composerEl = null;
|
|
4853
4945
|
CS.gateRefreshToken += 1;
|
|
4854
|
-
historyItemPolls.clear();
|
|
4855
4946
|
if (CS.pollTimer) {
|
|
4856
4947
|
clearInterval(CS.pollTimer);
|
|
4857
4948
|
CS.pollTimer = null;
|