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 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
- function sleep(ms) {
1040
- return new Promise(function(r) {
1041
- setTimeout(r, ms);
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 onThisVisibleChat = self.host.isViewMounted() && self.getHistoryCacheKey() === params.key;
1130
- if (onThisVisibleChat) {
1131
- self.aiChatHistoryCache[params.key] = {
1132
- messages: existing.messages.concat([reply]),
1133
- endOfList: existing.endOfList,
1134
- startKeyHistory: existing.startKeyHistory
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
- this.state.messages.splice(nextIdx + 1, 0, { role: "assistant", content: "", isPending: true });
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 TICK_MS = 16, charsPerTick = 3, FENCE_REVEAL_MS = 200;
1514
- var fenceTicks = Math.max(1, Math.floor(FENCE_REVEAL_MS / TICK_MS));
1515
- var fenceRegions = [], m;
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) fenceRegions.push({ start: m.index, end: m.index + m[0].length });
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 ((lm = linkRegex.exec(fullText)) !== null) linkRegions.push({ start: lm.index, end: lm.index + lm[0].length });
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
- return (function loop() {
1525
- if (self.state.typingAbort || i >= fullText.length) return Promise.resolve();
1526
- var step = charsPerTick;
1527
- var region = fenceRegions.find(function(r) {
1528
- return i >= r.start && i < r.end;
1529
- });
1530
- var linkRegion = linkRegions.find(function(r) {
1531
- return i >= r.start && i < r.end;
1532
- });
1533
- if (region) step = Math.max(charsPerTick, Math.ceil((region.end - i) / fenceTicks));
1534
- else if (linkRegion) step = Math.max(charsPerTick, linkRegion.end - i);
1535
- else {
1536
- var nextLink = linkRegions.find(function(r) {
1537
- return i < r.start && i + step > r.start;
1538
- });
1539
- if (nextLink) step = nextLink.end - i;
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
- i = Math.min(fullText.length, i + step);
1542
- var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1543
- return mm._localId === localId;
1544
- }) : idx;
1545
- if (currentIdx === -1) return Promise.resolve();
1546
- var target = self.state.messages[currentIdx];
1547
- if (!target) return Promise.resolve();
1548
- target.content = fullText.slice(0, i);
1549
- self.host.refreshMessageBubble(currentIdx);
1550
- return Promise.resolve(self.host.scrollToBottomIfSticky()).then(function() {
1551
- return sleep(TICK_MS);
1552
- }).then(loop);
1553
- })().then(function() {
1554
- if (!self.state.typingAbort) {
1555
- var fi = localId ? self.state.messages.findIndex(function(mm) {
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
- var t = fi !== -1 ? self.state.messages[fi] : self.state.messages[idx];
1559
- if (t) {
1560
- t.content = fullText;
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
- self.state.typing = false;
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 ((item.status === "running" || item.status === "pending") && self.pendingAgentRequests[self.getHistoryCacheKey()]) return;
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.4.5" ;
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
- h("span", { class: "bq-loader", text: "Loading" })
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" }, h("span", { class: "bq-loader", text: "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" + (initial ? " is-initial" : "") },
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;