bunnyquery 1.4.5 → 1.4.6

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/engine.mjs CHANGED
@@ -1068,10 +1068,18 @@ function mapHistoryListToMessages(list, platform, opts) {
1068
1068
  }
1069
1069
 
1070
1070
  // src/engine/session.ts
1071
- function sleep(ms) {
1072
- return new Promise(function(r) {
1073
- setTimeout(r, ms);
1074
- });
1071
+ var _g = typeof globalThis !== "undefined" ? globalThis : {};
1072
+ function nowMs() {
1073
+ return _g.performance && typeof _g.performance.now === "function" ? _g.performance.now() : Date.now();
1074
+ }
1075
+ function nextFrame(cb) {
1076
+ if (typeof _g.requestAnimationFrame === "function") {
1077
+ _g.requestAnimationFrame(cb);
1078
+ return;
1079
+ }
1080
+ setTimeout(function() {
1081
+ cb(nowMs());
1082
+ }, 16);
1075
1083
  }
1076
1084
  var ChatSession = class {
1077
1085
  constructor(host) {
@@ -1158,35 +1166,26 @@ var ChatSession = class {
1158
1166
  if (dispatchItemId) self.historyItemPolls.delete(dispatchItemId);
1159
1167
  var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
1160
1168
  var reply = { role: "assistant", content: result.content, isError: result.isError };
1161
- var onThisVisibleChat = self.host.isViewMounted() && self.getHistoryCacheKey() === params.key;
1162
- if (onThisVisibleChat) {
1163
- self.aiChatHistoryCache[params.key] = {
1164
- messages: existing.messages.concat([reply]),
1165
- endOfList: existing.endOfList,
1166
- startKeyHistory: existing.startKeyHistory
1167
- };
1168
- } else {
1169
- var msgs = existing.messages.slice();
1170
- var idx = -1;
1171
- for (var i = msgs.length - 1; i >= 0; i--) {
1172
- var m = msgs[i];
1173
- if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
1174
- idx = i;
1175
- break;
1176
- }
1177
- }
1178
- if (idx !== -1) {
1179
- reply._serverItemId = msgs[idx]._serverItemId;
1180
- msgs[idx] = reply;
1181
- } else {
1182
- msgs.push(reply);
1169
+ var msgs = existing.messages.slice();
1170
+ var idx = -1;
1171
+ for (var i = msgs.length - 1; i >= 0; i--) {
1172
+ var m = msgs[i];
1173
+ if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
1174
+ idx = i;
1175
+ break;
1183
1176
  }
1184
- self.aiChatHistoryCache[params.key] = {
1185
- messages: msgs,
1186
- endOfList: existing.endOfList,
1187
- startKeyHistory: existing.startKeyHistory
1188
- };
1189
1177
  }
1178
+ if (idx !== -1) {
1179
+ reply._serverItemId = msgs[idx]._serverItemId;
1180
+ msgs[idx] = reply;
1181
+ } else {
1182
+ msgs.push(reply);
1183
+ }
1184
+ self.aiChatHistoryCache[params.key] = {
1185
+ messages: msgs,
1186
+ endOfList: existing.endOfList,
1187
+ startKeyHistory: existing.startKeyHistory
1188
+ };
1190
1189
  return result;
1191
1190
  });
1192
1191
  this.pendingAgentRequests[params.key] = run;
@@ -1329,7 +1328,9 @@ var ChatSession = class {
1329
1328
  if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
1330
1329
  if (existing.isSendingToServer) promoted.isSendingToServer = true;
1331
1330
  this.state.messages[nextIdx] = promoted;
1332
- this.state.messages.splice(nextIdx + 1, 0, { role: "assistant", content: "", isPending: true });
1331
+ var placeholder = { role: "assistant", content: "", isPending: true };
1332
+ if (existing._serverItemId !== void 0) placeholder._serverItemId = existing._serverItemId;
1333
+ this.state.messages.splice(nextIdx + 1, 0, placeholder);
1333
1334
  this.host.notify();
1334
1335
  }
1335
1336
  resolveQueuedUserBubble(serverId) {
@@ -1539,61 +1540,118 @@ var ChatSession = class {
1539
1540
  });
1540
1541
  }
1541
1542
  // --- typewriter -------------------------------------------------------
1543
+ // Reveal `fullText` into a message bubble at a constant wall-clock RATE
1544
+ // (chars/second) driven by requestAnimationFrame, rather than a fixed number
1545
+ // of characters per fixed-delay tick. This is what keeps typing smooth and
1546
+ // cheap on slow machines:
1547
+ //
1548
+ // * Each frame reveals `elapsed_ms * CHARS_PER_SEC` characters, so the
1549
+ // visual speed is the same regardless of how long a frame actually took.
1550
+ // * As the bubble's markdown grows, each re-render gets more expensive, so
1551
+ // frames get longer — which makes each frame reveal MORE characters and
1552
+ // therefore do FEWER, larger renders. That converts the old O(n^2)
1553
+ // "re-render the whole growing string once per 3 characters" (which got
1554
+ // slower and slower and pegged the CPU) into roughly O(n): the number of
1555
+ // renders self-throttles to what the machine can actually paint.
1556
+ // * rAF paces us to the browser's paint cycle and pauses in background
1557
+ // tabs, so we never queue work faster than it can be drawn.
1542
1558
  typewriteIntoIndex(idx, fullText, localId) {
1543
1559
  var self = this;
1544
1560
  if (!fullText) return Promise.resolve();
1545
- var TICK_MS = 16, charsPerTick = 3, FENCE_REVEAL_MS = 200;
1546
- var fenceTicks = Math.max(1, Math.floor(FENCE_REVEAL_MS / TICK_MS));
1547
- var fenceRegions = [], m;
1561
+ var CHARS_PER_SEC = 300;
1562
+ var MIN_STEP = 1;
1563
+ var MAX_FRAME_MS = 1e3;
1564
+ var regions = [], m;
1548
1565
  var fenceRegex = /```[\w.-]+\.[a-zA-Z0-9]+\n[\s\S]*?```/g;
1549
- while ((m = fenceRegex.exec(fullText)) !== null) fenceRegions.push({ start: m.index, end: m.index + m[0].length });
1550
- var linkRegions = [], lm;
1566
+ while ((m = fenceRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
1551
1567
  var linkRegex = createInlineLinkRegex();
1552
- while ((lm = linkRegex.exec(fullText)) !== null) linkRegions.push({ start: lm.index, end: lm.index + lm[0].length });
1568
+ while ((m = linkRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
1569
+ regions.sort(function(a, b) {
1570
+ return a.start - b.start;
1571
+ });
1553
1572
  this.state.typing = true;
1554
1573
  this.state.typingAbort = false;
1555
1574
  var i = 0;
1556
- return (function loop() {
1557
- if (self.state.typingAbort || i >= fullText.length) return Promise.resolve();
1558
- var step = charsPerTick;
1559
- var region = fenceRegions.find(function(r) {
1560
- return i >= r.start && i < r.end;
1561
- });
1562
- var linkRegion = linkRegions.find(function(r) {
1563
- return i >= r.start && i < r.end;
1564
- });
1565
- if (region) step = Math.max(charsPerTick, Math.ceil((region.end - i) / fenceTicks));
1566
- else if (linkRegion) step = Math.max(charsPerTick, linkRegion.end - i);
1567
- else {
1568
- var nextLink = linkRegions.find(function(r) {
1569
- return i < r.start && i + step > r.start;
1570
- });
1571
- if (nextLink) step = nextLink.end - i;
1575
+ var last = nowMs();
1576
+ return new Promise(function(resolve) {
1577
+ var done = false;
1578
+ var doc = _g.document;
1579
+ function isHidden() {
1580
+ return !!(doc && doc.hidden);
1581
+ }
1582
+ function cleanup() {
1583
+ if (doc && doc.removeEventListener) doc.removeEventListener("visibilitychange", onVisibility);
1584
+ }
1585
+ function finish() {
1586
+ if (done) return;
1587
+ done = true;
1588
+ cleanup();
1589
+ if (!self.state.typingAbort) {
1590
+ var fi = localId ? self.state.messages.findIndex(function(mm) {
1591
+ return mm._localId === localId;
1592
+ }) : idx;
1593
+ if (fi !== -1) {
1594
+ var t = self.state.messages[fi];
1595
+ if (t) {
1596
+ t.content = fullText;
1597
+ self.host.refreshMessageBubble(fi);
1598
+ }
1599
+ }
1600
+ }
1601
+ self.state.typing = false;
1602
+ resolve();
1603
+ }
1604
+ function onVisibility() {
1605
+ if (isHidden()) finish();
1572
1606
  }
1573
- i = Math.min(fullText.length, i + step);
1574
- var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1575
- return mm._localId === localId;
1576
- }) : idx;
1577
- if (currentIdx === -1) return Promise.resolve();
1578
- var target = self.state.messages[currentIdx];
1579
- if (!target) return Promise.resolve();
1580
- target.content = fullText.slice(0, i);
1581
- self.host.refreshMessageBubble(currentIdx);
1582
- return Promise.resolve(self.host.scrollToBottomIfSticky()).then(function() {
1583
- return sleep(TICK_MS);
1584
- }).then(loop);
1585
- })().then(function() {
1586
- if (!self.state.typingAbort) {
1587
- var fi = localId ? self.state.messages.findIndex(function(mm) {
1607
+ if (doc && doc.addEventListener) doc.addEventListener("visibilitychange", onVisibility);
1608
+ function frame(t) {
1609
+ if (done) return;
1610
+ if (self.state.typingAbort || i >= fullText.length || isHidden()) {
1611
+ finish();
1612
+ return;
1613
+ }
1614
+ var dt = t - last;
1615
+ last = t;
1616
+ if (!(dt > 0)) dt = 16;
1617
+ if (dt > MAX_FRAME_MS) dt = MAX_FRAME_MS;
1618
+ var step = Math.round(dt * CHARS_PER_SEC / 1e3);
1619
+ if (step < MIN_STEP) step = MIN_STEP;
1620
+ var next = Math.min(fullText.length, i + step);
1621
+ for (var changed = true; changed; ) {
1622
+ changed = false;
1623
+ for (var k = 0; k < regions.length; k++) {
1624
+ var r = regions[k];
1625
+ if (next > r.start && i < r.end && r.end > next) {
1626
+ next = r.end;
1627
+ changed = true;
1628
+ }
1629
+ }
1630
+ }
1631
+ if (next > fullText.length) next = fullText.length;
1632
+ i = next;
1633
+ var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1588
1634
  return mm._localId === localId;
1589
1635
  }) : idx;
1590
- var t = fi !== -1 ? self.state.messages[fi] : self.state.messages[idx];
1591
- if (t) {
1592
- t.content = fullText;
1593
- self.host.refreshMessageBubble(fi !== -1 ? fi : idx);
1636
+ if (currentIdx === -1) {
1637
+ finish();
1638
+ return;
1594
1639
  }
1640
+ var target = self.state.messages[currentIdx];
1641
+ if (!target) {
1642
+ finish();
1643
+ return;
1644
+ }
1645
+ target.content = fullText.slice(0, i);
1646
+ self.host.refreshMessageBubble(currentIdx);
1647
+ self.host.scrollToBottomIfSticky();
1648
+ nextFrame(frame);
1649
+ }
1650
+ if (isHidden()) {
1651
+ finish();
1652
+ return;
1595
1653
  }
1596
- self.state.typing = false;
1654
+ nextFrame(frame);
1597
1655
  });
1598
1656
  }
1599
1657
  enqueueTypewrite(idx, fullText, localId) {
@@ -1907,7 +1965,7 @@ var ChatSession = class {
1907
1965
  if (item.status !== "running" && item.status !== "pending") return;
1908
1966
  if (!item.poll || !item.id) return;
1909
1967
  if (self.historyItemPolls.has(item.id)) return;
1910
- if ((item.status === "running" || item.status === "pending") && self.pendingAgentRequests[self.getHistoryCacheKey()]) return;
1968
+ if (self.pendingAgentRequests[self.getHistoryCacheKey()] && !item._isBgTask && !item._isOnBgQueue) return;
1911
1969
  self.historyItemPolls.set(item.id, true);
1912
1970
  var capturedId = item.id;
1913
1971
  var pp = item.poll({