bunnyquery 1.4.4 → 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/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) {
@@ -1090,11 +1098,17 @@ ${options.inlineContentPlaceholder}
1090
1098
  }
1091
1099
  dispatchAgentRequest(params) {
1092
1100
  var self = this;
1101
+ var dispatchItemId;
1093
1102
  var sendAndPoll = function() {
1094
1103
  return Promise.resolve(
1095
1104
  self._callProviderFor(params.aiPlatform, params.text, params.boundedMessages, params.systemPrompt, params.aiModel, params.userId, params.extractContent)
1096
1105
  ).then(function(initial) {
1097
1106
  if (initial && initial.poll && (initial.status === "pending" || initial.status === "running")) {
1107
+ if (initial.id) {
1108
+ if (dispatchItemId && dispatchItemId !== initial.id) self.historyItemPolls.delete(dispatchItemId);
1109
+ dispatchItemId = initial.id;
1110
+ self.historyItemPolls.set(initial.id, true);
1111
+ }
1098
1112
  return initial.poll({ latency: POLL_INTERVAL });
1099
1113
  }
1100
1114
  return initial;
@@ -1117,37 +1131,29 @@ ${options.inlineContentPlaceholder}
1117
1131
  return { content: getErrorMessage(err), isError: true };
1118
1132
  }).then(function(result) {
1119
1133
  delete self.pendingAgentRequests[params.key];
1134
+ if (dispatchItemId) self.historyItemPolls.delete(dispatchItemId);
1120
1135
  var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
1121
1136
  var reply = { role: "assistant", content: result.content, isError: result.isError };
1122
- var onThisVisibleChat = self.host.isViewMounted() && self.getHistoryCacheKey() === params.key;
1123
- if (onThisVisibleChat) {
1124
- self.aiChatHistoryCache[params.key] = {
1125
- messages: existing.messages.concat([reply]),
1126
- endOfList: existing.endOfList,
1127
- startKeyHistory: existing.startKeyHistory
1128
- };
1129
- } else {
1130
- var msgs = existing.messages.slice();
1131
- var idx = -1;
1132
- for (var i = msgs.length - 1; i >= 0; i--) {
1133
- var m = msgs[i];
1134
- if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
1135
- idx = i;
1136
- break;
1137
- }
1138
- }
1139
- if (idx !== -1) {
1140
- reply._serverItemId = msgs[idx]._serverItemId;
1141
- msgs[idx] = reply;
1142
- } else {
1143
- 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;
1144
1144
  }
1145
- self.aiChatHistoryCache[params.key] = {
1146
- messages: msgs,
1147
- endOfList: existing.endOfList,
1148
- startKeyHistory: existing.startKeyHistory
1149
- };
1150
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
+ };
1151
1157
  return result;
1152
1158
  });
1153
1159
  this.pendingAgentRequests[params.key] = run;
@@ -1200,6 +1206,7 @@ ${options.inlineContentPlaceholder}
1200
1206
  self.host.notify();
1201
1207
  }
1202
1208
  if (result && result.poll && (result.status === "pending" || result.status === "running")) {
1209
+ if (serverId) self.historyItemPolls.set(serverId, true);
1203
1210
  return result.poll({ latency: POLL_INTERVAL }).then(function(res) {
1204
1211
  return self.onQueuedSendResponse(capturedComposed, res, capturedPlatform, serverId);
1205
1212
  }).catch(function(err) {
@@ -1289,7 +1296,9 @@ ${options.inlineContentPlaceholder}
1289
1296
  if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
1290
1297
  if (existing.isSendingToServer) promoted.isSendingToServer = true;
1291
1298
  this.state.messages[nextIdx] = promoted;
1292
- 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);
1293
1302
  this.host.notify();
1294
1303
  }
1295
1304
  resolveQueuedUserBubble(serverId) {
@@ -1339,6 +1348,7 @@ ${options.inlineContentPlaceholder}
1339
1348
  else this.state.messages.push(msg);
1340
1349
  }
1341
1350
  onQueuedSendResponse(_composed, response, platform, serverId) {
1351
+ if (serverId) this.historyItemPolls.delete(serverId);
1342
1352
  var targetIdx = this.resolveQueuedUserBubble(serverId);
1343
1353
  if (targetIdx === void 0) {
1344
1354
  this.host.notify();
@@ -1373,6 +1383,7 @@ ${options.inlineContentPlaceholder}
1373
1383
  this.host.scrollToBottom(true);
1374
1384
  }
1375
1385
  onQueuedSendError(_composed, err, serverId) {
1386
+ if (serverId) this.historyItemPolls.delete(serverId);
1376
1387
  var isNotExists = err && (err.code === "NOT_EXISTS" || err.body && err.body.code === "NOT_EXISTS");
1377
1388
  if (isNotExists) {
1378
1389
  var userIdx = serverId ? this.state.messages.findIndex(function(m) {
@@ -1497,61 +1508,118 @@ ${options.inlineContentPlaceholder}
1497
1508
  });
1498
1509
  }
1499
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.
1500
1526
  typewriteIntoIndex(idx, fullText, localId) {
1501
1527
  var self = this;
1502
1528
  if (!fullText) return Promise.resolve();
1503
- var TICK_MS = 16, charsPerTick = 3, FENCE_REVEAL_MS = 200;
1504
- var fenceTicks = Math.max(1, Math.floor(FENCE_REVEAL_MS / TICK_MS));
1505
- var fenceRegions = [], m;
1529
+ var CHARS_PER_SEC = 300;
1530
+ var MIN_STEP = 1;
1531
+ var MAX_FRAME_MS = 1e3;
1532
+ var regions = [], m;
1506
1533
  var fenceRegex = /```[\w.-]+\.[a-zA-Z0-9]+\n[\s\S]*?```/g;
1507
- while ((m = fenceRegex.exec(fullText)) !== null) fenceRegions.push({ start: m.index, end: m.index + m[0].length });
1508
- var linkRegions = [], lm;
1534
+ while ((m = fenceRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
1509
1535
  var linkRegex = createInlineLinkRegex();
1510
- 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
+ });
1511
1540
  this.state.typing = true;
1512
1541
  this.state.typingAbort = false;
1513
1542
  var i = 0;
1514
- return (function loop() {
1515
- if (self.state.typingAbort || i >= fullText.length) return Promise.resolve();
1516
- var step = charsPerTick;
1517
- var region = fenceRegions.find(function(r) {
1518
- return i >= r.start && i < r.end;
1519
- });
1520
- var linkRegion = linkRegions.find(function(r) {
1521
- return i >= r.start && i < r.end;
1522
- });
1523
- if (region) step = Math.max(charsPerTick, Math.ceil((region.end - i) / fenceTicks));
1524
- else if (linkRegion) step = Math.max(charsPerTick, linkRegion.end - i);
1525
- else {
1526
- var nextLink = linkRegions.find(function(r) {
1527
- return i < r.start && i + step > r.start;
1528
- });
1529
- 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);
1530
1552
  }
1531
- i = Math.min(fullText.length, i + step);
1532
- var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1533
- return mm._localId === localId;
1534
- }) : idx;
1535
- if (currentIdx === -1) return Promise.resolve();
1536
- var target = self.state.messages[currentIdx];
1537
- if (!target) return Promise.resolve();
1538
- target.content = fullText.slice(0, i);
1539
- self.host.refreshMessageBubble(currentIdx);
1540
- return Promise.resolve(self.host.scrollToBottomIfSticky()).then(function() {
1541
- return sleep(TICK_MS);
1542
- }).then(loop);
1543
- })().then(function() {
1544
- if (!self.state.typingAbort) {
1545
- var fi = localId ? self.state.messages.findIndex(function(mm) {
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();
1571
+ }
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) {
1546
1602
  return mm._localId === localId;
1547
1603
  }) : idx;
1548
- var t = fi !== -1 ? self.state.messages[fi] : self.state.messages[idx];
1549
- if (t) {
1550
- t.content = fullText;
1551
- self.host.refreshMessageBubble(fi !== -1 ? fi : idx);
1604
+ if (currentIdx === -1) {
1605
+ finish();
1606
+ return;
1552
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;
1553
1621
  }
1554
- self.state.typing = false;
1622
+ nextFrame(frame);
1555
1623
  });
1556
1624
  }
1557
1625
  enqueueTypewrite(idx, fullText, localId) {
@@ -1865,7 +1933,7 @@ ${options.inlineContentPlaceholder}
1865
1933
  if (item.status !== "running" && item.status !== "pending") return;
1866
1934
  if (!item.poll || !item.id) return;
1867
1935
  if (self.historyItemPolls.has(item.id)) return;
1868
- if ((item.status === "running" || item.status === "pending") && self.pendingAgentRequests[self.getHistoryCacheKey()]) return;
1936
+ if (self.pendingAgentRequests[self.getHistoryCacheKey()] && !item._isBgTask && !item._isOnBgQueue) return;
1869
1937
  self.historyItemPolls.set(item.id, true);
1870
1938
  var capturedId = item.id;
1871
1939
  var pp = item.poll({
@@ -2108,7 +2176,7 @@ ${options.inlineContentPlaceholder}
2108
2176
  (function() {
2109
2177
  var MCP_PROD = "https://mcp.broadwayinc.computer";
2110
2178
  var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
2111
- var BQ_VERSION = "1.4.4" ;
2179
+ var BQ_VERSION = "1.4.6" ;
2112
2180
  var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
2113
2181
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
2114
2182
  var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
@@ -4841,7 +4909,6 @@ ${options.inlineContentPlaceholder}
4841
4909
  CS.settingsBtnEl = null;
4842
4910
  CS.composerEl = null;
4843
4911
  CS.gateRefreshToken += 1;
4844
- historyItemPolls.clear();
4845
4912
  if (CS.pollTimer) {
4846
4913
  clearInterval(CS.pollTimer);
4847
4914
  CS.pollTimer = null;
package/dist/engine.cjs CHANGED
@@ -1070,10 +1070,18 @@ function mapHistoryListToMessages(list, platform, opts) {
1070
1070
  }
1071
1071
 
1072
1072
  // src/engine/session.ts
1073
- function sleep(ms) {
1074
- return new Promise(function(r) {
1075
- setTimeout(r, ms);
1076
- });
1073
+ var _g = typeof globalThis !== "undefined" ? globalThis : {};
1074
+ function nowMs() {
1075
+ return _g.performance && typeof _g.performance.now === "function" ? _g.performance.now() : Date.now();
1076
+ }
1077
+ function nextFrame(cb) {
1078
+ if (typeof _g.requestAnimationFrame === "function") {
1079
+ _g.requestAnimationFrame(cb);
1080
+ return;
1081
+ }
1082
+ setTimeout(function() {
1083
+ cb(nowMs());
1084
+ }, 16);
1077
1085
  }
1078
1086
  var ChatSession = class {
1079
1087
  constructor(host) {
@@ -1124,11 +1132,17 @@ var ChatSession = class {
1124
1132
  }
1125
1133
  dispatchAgentRequest(params) {
1126
1134
  var self = this;
1135
+ var dispatchItemId;
1127
1136
  var sendAndPoll = function() {
1128
1137
  return Promise.resolve(
1129
1138
  self._callProviderFor(params.aiPlatform, params.text, params.boundedMessages, params.systemPrompt, params.aiModel, params.userId, params.extractContent)
1130
1139
  ).then(function(initial) {
1131
1140
  if (initial && initial.poll && (initial.status === "pending" || initial.status === "running")) {
1141
+ if (initial.id) {
1142
+ if (dispatchItemId && dispatchItemId !== initial.id) self.historyItemPolls.delete(dispatchItemId);
1143
+ dispatchItemId = initial.id;
1144
+ self.historyItemPolls.set(initial.id, true);
1145
+ }
1132
1146
  return initial.poll({ latency: POLL_INTERVAL });
1133
1147
  }
1134
1148
  return initial;
@@ -1151,37 +1165,29 @@ var ChatSession = class {
1151
1165
  return { content: getErrorMessage(err), isError: true };
1152
1166
  }).then(function(result) {
1153
1167
  delete self.pendingAgentRequests[params.key];
1168
+ if (dispatchItemId) self.historyItemPolls.delete(dispatchItemId);
1154
1169
  var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
1155
1170
  var reply = { role: "assistant", content: result.content, isError: result.isError };
1156
- var onThisVisibleChat = self.host.isViewMounted() && self.getHistoryCacheKey() === params.key;
1157
- if (onThisVisibleChat) {
1158
- self.aiChatHistoryCache[params.key] = {
1159
- messages: existing.messages.concat([reply]),
1160
- endOfList: existing.endOfList,
1161
- startKeyHistory: existing.startKeyHistory
1162
- };
1163
- } else {
1164
- var msgs = existing.messages.slice();
1165
- var idx = -1;
1166
- for (var i = msgs.length - 1; i >= 0; i--) {
1167
- var m = msgs[i];
1168
- if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
1169
- idx = i;
1170
- break;
1171
- }
1172
- }
1173
- if (idx !== -1) {
1174
- reply._serverItemId = msgs[idx]._serverItemId;
1175
- msgs[idx] = reply;
1176
- } else {
1177
- msgs.push(reply);
1171
+ var msgs = existing.messages.slice();
1172
+ var idx = -1;
1173
+ for (var i = msgs.length - 1; i >= 0; i--) {
1174
+ var m = msgs[i];
1175
+ if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
1176
+ idx = i;
1177
+ break;
1178
1178
  }
1179
- self.aiChatHistoryCache[params.key] = {
1180
- messages: msgs,
1181
- endOfList: existing.endOfList,
1182
- startKeyHistory: existing.startKeyHistory
1183
- };
1184
1179
  }
1180
+ if (idx !== -1) {
1181
+ reply._serverItemId = msgs[idx]._serverItemId;
1182
+ msgs[idx] = reply;
1183
+ } else {
1184
+ msgs.push(reply);
1185
+ }
1186
+ self.aiChatHistoryCache[params.key] = {
1187
+ messages: msgs,
1188
+ endOfList: existing.endOfList,
1189
+ startKeyHistory: existing.startKeyHistory
1190
+ };
1185
1191
  return result;
1186
1192
  });
1187
1193
  this.pendingAgentRequests[params.key] = run;
@@ -1234,6 +1240,7 @@ var ChatSession = class {
1234
1240
  self.host.notify();
1235
1241
  }
1236
1242
  if (result && result.poll && (result.status === "pending" || result.status === "running")) {
1243
+ if (serverId) self.historyItemPolls.set(serverId, true);
1237
1244
  return result.poll({ latency: POLL_INTERVAL }).then(function(res) {
1238
1245
  return self.onQueuedSendResponse(capturedComposed, res, capturedPlatform, serverId);
1239
1246
  }).catch(function(err) {
@@ -1323,7 +1330,9 @@ var ChatSession = class {
1323
1330
  if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
1324
1331
  if (existing.isSendingToServer) promoted.isSendingToServer = true;
1325
1332
  this.state.messages[nextIdx] = promoted;
1326
- this.state.messages.splice(nextIdx + 1, 0, { role: "assistant", content: "", isPending: true });
1333
+ var placeholder = { role: "assistant", content: "", isPending: true };
1334
+ if (existing._serverItemId !== void 0) placeholder._serverItemId = existing._serverItemId;
1335
+ this.state.messages.splice(nextIdx + 1, 0, placeholder);
1327
1336
  this.host.notify();
1328
1337
  }
1329
1338
  resolveQueuedUserBubble(serverId) {
@@ -1373,6 +1382,7 @@ var ChatSession = class {
1373
1382
  else this.state.messages.push(msg);
1374
1383
  }
1375
1384
  onQueuedSendResponse(_composed, response, platform, serverId) {
1385
+ if (serverId) this.historyItemPolls.delete(serverId);
1376
1386
  var targetIdx = this.resolveQueuedUserBubble(serverId);
1377
1387
  if (targetIdx === void 0) {
1378
1388
  this.host.notify();
@@ -1407,6 +1417,7 @@ var ChatSession = class {
1407
1417
  this.host.scrollToBottom(true);
1408
1418
  }
1409
1419
  onQueuedSendError(_composed, err, serverId) {
1420
+ if (serverId) this.historyItemPolls.delete(serverId);
1410
1421
  var isNotExists = err && (err.code === "NOT_EXISTS" || err.body && err.body.code === "NOT_EXISTS");
1411
1422
  if (isNotExists) {
1412
1423
  var userIdx = serverId ? this.state.messages.findIndex(function(m) {
@@ -1531,61 +1542,118 @@ var ChatSession = class {
1531
1542
  });
1532
1543
  }
1533
1544
  // --- typewriter -------------------------------------------------------
1545
+ // Reveal `fullText` into a message bubble at a constant wall-clock RATE
1546
+ // (chars/second) driven by requestAnimationFrame, rather than a fixed number
1547
+ // of characters per fixed-delay tick. This is what keeps typing smooth and
1548
+ // cheap on slow machines:
1549
+ //
1550
+ // * Each frame reveals `elapsed_ms * CHARS_PER_SEC` characters, so the
1551
+ // visual speed is the same regardless of how long a frame actually took.
1552
+ // * As the bubble's markdown grows, each re-render gets more expensive, so
1553
+ // frames get longer — which makes each frame reveal MORE characters and
1554
+ // therefore do FEWER, larger renders. That converts the old O(n^2)
1555
+ // "re-render the whole growing string once per 3 characters" (which got
1556
+ // slower and slower and pegged the CPU) into roughly O(n): the number of
1557
+ // renders self-throttles to what the machine can actually paint.
1558
+ // * rAF paces us to the browser's paint cycle and pauses in background
1559
+ // tabs, so we never queue work faster than it can be drawn.
1534
1560
  typewriteIntoIndex(idx, fullText, localId) {
1535
1561
  var self = this;
1536
1562
  if (!fullText) return Promise.resolve();
1537
- var TICK_MS = 16, charsPerTick = 3, FENCE_REVEAL_MS = 200;
1538
- var fenceTicks = Math.max(1, Math.floor(FENCE_REVEAL_MS / TICK_MS));
1539
- var fenceRegions = [], m;
1563
+ var CHARS_PER_SEC = 300;
1564
+ var MIN_STEP = 1;
1565
+ var MAX_FRAME_MS = 1e3;
1566
+ var regions = [], m;
1540
1567
  var fenceRegex = /```[\w.-]+\.[a-zA-Z0-9]+\n[\s\S]*?```/g;
1541
- while ((m = fenceRegex.exec(fullText)) !== null) fenceRegions.push({ start: m.index, end: m.index + m[0].length });
1542
- var linkRegions = [], lm;
1568
+ while ((m = fenceRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
1543
1569
  var linkRegex = createInlineLinkRegex();
1544
- while ((lm = linkRegex.exec(fullText)) !== null) linkRegions.push({ start: lm.index, end: lm.index + lm[0].length });
1570
+ while ((m = linkRegex.exec(fullText)) !== null) regions.push({ start: m.index, end: m.index + m[0].length });
1571
+ regions.sort(function(a, b) {
1572
+ return a.start - b.start;
1573
+ });
1545
1574
  this.state.typing = true;
1546
1575
  this.state.typingAbort = false;
1547
1576
  var i = 0;
1548
- return (function loop() {
1549
- if (self.state.typingAbort || i >= fullText.length) return Promise.resolve();
1550
- var step = charsPerTick;
1551
- var region = fenceRegions.find(function(r) {
1552
- return i >= r.start && i < r.end;
1553
- });
1554
- var linkRegion = linkRegions.find(function(r) {
1555
- return i >= r.start && i < r.end;
1556
- });
1557
- if (region) step = Math.max(charsPerTick, Math.ceil((region.end - i) / fenceTicks));
1558
- else if (linkRegion) step = Math.max(charsPerTick, linkRegion.end - i);
1559
- else {
1560
- var nextLink = linkRegions.find(function(r) {
1561
- return i < r.start && i + step > r.start;
1562
- });
1563
- if (nextLink) step = nextLink.end - i;
1577
+ var last = nowMs();
1578
+ return new Promise(function(resolve) {
1579
+ var done = false;
1580
+ var doc = _g.document;
1581
+ function isHidden() {
1582
+ return !!(doc && doc.hidden);
1583
+ }
1584
+ function cleanup() {
1585
+ if (doc && doc.removeEventListener) doc.removeEventListener("visibilitychange", onVisibility);
1586
+ }
1587
+ function finish() {
1588
+ if (done) return;
1589
+ done = true;
1590
+ cleanup();
1591
+ if (!self.state.typingAbort) {
1592
+ var fi = localId ? self.state.messages.findIndex(function(mm) {
1593
+ return mm._localId === localId;
1594
+ }) : idx;
1595
+ if (fi !== -1) {
1596
+ var t = self.state.messages[fi];
1597
+ if (t) {
1598
+ t.content = fullText;
1599
+ self.host.refreshMessageBubble(fi);
1600
+ }
1601
+ }
1602
+ }
1603
+ self.state.typing = false;
1604
+ resolve();
1605
+ }
1606
+ function onVisibility() {
1607
+ if (isHidden()) finish();
1564
1608
  }
1565
- i = Math.min(fullText.length, i + step);
1566
- var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1567
- return mm._localId === localId;
1568
- }) : idx;
1569
- if (currentIdx === -1) return Promise.resolve();
1570
- var target = self.state.messages[currentIdx];
1571
- if (!target) return Promise.resolve();
1572
- target.content = fullText.slice(0, i);
1573
- self.host.refreshMessageBubble(currentIdx);
1574
- return Promise.resolve(self.host.scrollToBottomIfSticky()).then(function() {
1575
- return sleep(TICK_MS);
1576
- }).then(loop);
1577
- })().then(function() {
1578
- if (!self.state.typingAbort) {
1579
- var fi = localId ? self.state.messages.findIndex(function(mm) {
1609
+ if (doc && doc.addEventListener) doc.addEventListener("visibilitychange", onVisibility);
1610
+ function frame(t) {
1611
+ if (done) return;
1612
+ if (self.state.typingAbort || i >= fullText.length || isHidden()) {
1613
+ finish();
1614
+ return;
1615
+ }
1616
+ var dt = t - last;
1617
+ last = t;
1618
+ if (!(dt > 0)) dt = 16;
1619
+ if (dt > MAX_FRAME_MS) dt = MAX_FRAME_MS;
1620
+ var step = Math.round(dt * CHARS_PER_SEC / 1e3);
1621
+ if (step < MIN_STEP) step = MIN_STEP;
1622
+ var next = Math.min(fullText.length, i + step);
1623
+ for (var changed = true; changed; ) {
1624
+ changed = false;
1625
+ for (var k = 0; k < regions.length; k++) {
1626
+ var r = regions[k];
1627
+ if (next > r.start && i < r.end && r.end > next) {
1628
+ next = r.end;
1629
+ changed = true;
1630
+ }
1631
+ }
1632
+ }
1633
+ if (next > fullText.length) next = fullText.length;
1634
+ i = next;
1635
+ var currentIdx = localId ? self.state.messages.findIndex(function(mm) {
1580
1636
  return mm._localId === localId;
1581
1637
  }) : idx;
1582
- var t = fi !== -1 ? self.state.messages[fi] : self.state.messages[idx];
1583
- if (t) {
1584
- t.content = fullText;
1585
- self.host.refreshMessageBubble(fi !== -1 ? fi : idx);
1638
+ if (currentIdx === -1) {
1639
+ finish();
1640
+ return;
1586
1641
  }
1642
+ var target = self.state.messages[currentIdx];
1643
+ if (!target) {
1644
+ finish();
1645
+ return;
1646
+ }
1647
+ target.content = fullText.slice(0, i);
1648
+ self.host.refreshMessageBubble(currentIdx);
1649
+ self.host.scrollToBottomIfSticky();
1650
+ nextFrame(frame);
1651
+ }
1652
+ if (isHidden()) {
1653
+ finish();
1654
+ return;
1587
1655
  }
1588
- self.state.typing = false;
1656
+ nextFrame(frame);
1589
1657
  });
1590
1658
  }
1591
1659
  enqueueTypewrite(idx, fullText, localId) {
@@ -1899,7 +1967,7 @@ var ChatSession = class {
1899
1967
  if (item.status !== "running" && item.status !== "pending") return;
1900
1968
  if (!item.poll || !item.id) return;
1901
1969
  if (self.historyItemPolls.has(item.id)) return;
1902
- if ((item.status === "running" || item.status === "pending") && self.pendingAgentRequests[self.getHistoryCacheKey()]) return;
1970
+ if (self.pendingAgentRequests[self.getHistoryCacheKey()] && !item._isBgTask && !item._isOnBgQueue) return;
1903
1971
  self.historyItemPolls.set(item.id, true);
1904
1972
  var capturedId = item.id;
1905
1973
  var pp = item.poll({