bunnyquery 1.8.14 → 1.8.15

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
@@ -2438,6 +2438,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2438
2438
  // src/engine/scroll_anchor.ts
2439
2439
  var ROW_KEY_ATTR = "data-row-key";
2440
2440
  var ROW_POS_ATTR = "data-row-pos";
2441
+ var MAX_ALTS = 2;
2442
+ var ALT_SCAN_LIMIT = 64;
2441
2443
  var UNKNOWN_ROW_POS = "\0?";
2442
2444
  function createScrollAnchor(options) {
2443
2445
  var held = null;
@@ -2448,6 +2450,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2448
2450
  var boxTop = box.getBoundingClientRect().top;
2449
2451
  var kids = box.children;
2450
2452
  var fallback = null;
2453
+ var fallbackAt = -1;
2451
2454
  for (var i = 0; i < kids.length; i++) {
2452
2455
  var el = kids[i];
2453
2456
  if (!el || typeof el.getAttribute !== "function") continue;
@@ -2466,10 +2469,20 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2466
2469
  scrollHeight: box.scrollHeight,
2467
2470
  el
2468
2471
  };
2469
- if (rawPos === null) return cand;
2470
- if (!fallback) fallback = cand;
2472
+ if (rawPos === null) {
2473
+ cand.alts = collectAlts(box, boxTop, i + 1);
2474
+ return cand;
2475
+ }
2476
+ if (!fallback) {
2477
+ fallback = cand;
2478
+ fallbackAt = i;
2479
+ }
2480
+ }
2481
+ if (fallback) {
2482
+ fallback.alts = collectAlts(box, boxTop, fallbackAt + 1, true);
2483
+ return fallback;
2471
2484
  }
2472
- return fallback || {
2485
+ return {
2473
2486
  key: null,
2474
2487
  top: 0,
2475
2488
  pos: null,
@@ -2478,6 +2491,26 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2478
2491
  el: null
2479
2492
  };
2480
2493
  }
2494
+ function collectAlts(box, boxTop, from, ordinaryOnly) {
2495
+ var out = [];
2496
+ var kids = box.children;
2497
+ var stop = Math.min(kids.length, from + ALT_SCAN_LIMIT);
2498
+ for (var i = from; i < stop && out.length < MAX_ALTS; i++) {
2499
+ var el = kids[i];
2500
+ if (!el || typeof el.getAttribute !== "function") continue;
2501
+ var key = el.getAttribute(ROW_KEY_ATTR);
2502
+ if (!key) continue;
2503
+ var rawPos = el.getAttribute(ROW_POS_ATTR);
2504
+ if (ordinaryOnly && rawPos !== null) continue;
2505
+ out.push({
2506
+ key,
2507
+ top: el.getBoundingClientRect().top - boxTop,
2508
+ pos: rawPos === null ? null : rawPos || UNKNOWN_ROW_POS,
2509
+ el
2510
+ });
2511
+ }
2512
+ return out.length ? out : void 0;
2513
+ }
2481
2514
  function findRow(box, anchor) {
2482
2515
  var el = anchor.el;
2483
2516
  if (el && el.parentNode === box) return el;
@@ -2490,31 +2523,79 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2490
2523
  }
2491
2524
  return null;
2492
2525
  }
2493
- function restore(anchor) {
2526
+ var sawFrozen = false;
2527
+ var parked = null;
2528
+ var parkedStuck = false;
2529
+ var returning = false;
2530
+ var wroteTop = -1;
2531
+ var restoreExact = true;
2532
+ var restorePinned = false;
2533
+ function frozen() {
2534
+ var f = !!options.isFrozen && options.isFrozen();
2535
+ if (f) sawFrozen = true;
2536
+ return f;
2537
+ }
2538
+ function restore(anchor, unbounded) {
2539
+ if (frozen()) return;
2494
2540
  var box = options.getBox();
2495
2541
  if (!box || !anchor || options.isStuck()) return;
2496
2542
  var el = findRow(box, anchor);
2497
2543
  if (el) {
2498
2544
  var livePos = el.getAttribute(ROW_POS_ATTR) || UNKNOWN_ROW_POS;
2499
- if (anchor.pos !== null && anchor.pos !== UNKNOWN_ROW_POS && livePos !== UNKNOWN_ROW_POS && livePos !== anchor.pos) {
2500
- lost(box, anchor);
2501
- return;
2502
- }
2545
+ if (anchor.pos !== null && anchor.pos !== UNKNOWN_ROW_POS && livePos !== UNKNOWN_ROW_POS && livePos !== anchor.pos) el = null;
2546
+ }
2547
+ if (el) {
2503
2548
  var boxTop = box.getBoundingClientRect().top;
2504
2549
  var delta = el.getBoundingClientRect().top - boxTop - anchor.top;
2505
2550
  var slack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
2506
- if (delta > slack || delta < -slack) {
2551
+ if (!unbounded && (delta > slack || delta < -slack)) {
2507
2552
  lost(box, anchor);
2508
2553
  return;
2509
2554
  }
2555
+ var want = box.scrollTop + delta;
2510
2556
  if (delta >= 1 || delta <= -1) box.scrollTop += delta;
2557
+ wroteTop = box.scrollTop;
2558
+ restorePinned = true;
2559
+ restoreExact = box.scrollTop >= want - 1 && box.scrollTop <= want + 1;
2511
2560
  held = {
2512
2561
  key: anchor.key,
2513
2562
  top: anchor.top,
2514
2563
  pos: anchor.pos,
2515
2564
  scrollTop: box.scrollTop,
2516
2565
  scrollHeight: box.scrollHeight,
2517
- el
2566
+ el,
2567
+ // Carried, not dropped: one successful restore used to disarm the
2568
+ // standbys for every later hold.
2569
+ alts: anchor.alts
2570
+ };
2571
+ return;
2572
+ }
2573
+ var alts = anchor.alts;
2574
+ for (var ai = 0; alts && ai < alts.length; ai++) {
2575
+ var alt = alts[ai];
2576
+ var ael = findRow(box, { key: alt.key, top: alt.top, pos: alt.pos, scrollTop: anchor.scrollTop, scrollHeight: anchor.scrollHeight, el: alt.el });
2577
+ if (!ael) continue;
2578
+ if (alt.pos !== null && alt.pos !== UNKNOWN_ROW_POS) {
2579
+ var altLive = ael.getAttribute(ROW_POS_ATTR) || UNKNOWN_ROW_POS;
2580
+ if (altLive !== UNKNOWN_ROW_POS && altLive !== alt.pos) continue;
2581
+ }
2582
+ var aboxTop = box.getBoundingClientRect().top;
2583
+ var adelta = ael.getBoundingClientRect().top - aboxTop - alt.top;
2584
+ var aslack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
2585
+ if (!unbounded && (adelta > aslack || adelta < -aslack)) continue;
2586
+ var awant = box.scrollTop + adelta;
2587
+ if (adelta >= 1 || adelta <= -1) box.scrollTop += adelta;
2588
+ wroteTop = box.scrollTop;
2589
+ restorePinned = true;
2590
+ restoreExact = box.scrollTop >= awant - 1 && box.scrollTop <= awant + 1;
2591
+ held = {
2592
+ key: alt.key,
2593
+ top: alt.top,
2594
+ pos: alt.pos,
2595
+ scrollTop: box.scrollTop,
2596
+ scrollHeight: box.scrollHeight,
2597
+ el: ael,
2598
+ alts: alts.slice(ai + 1)
2518
2599
  };
2519
2600
  return;
2520
2601
  }
@@ -2525,9 +2606,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2525
2606
  var grew = box.scrollHeight - anchor.scrollHeight;
2526
2607
  if (grew > 0) {
2527
2608
  box.scrollTop = anchor.scrollTop + grew;
2609
+ wroteTop = box.scrollTop;
2528
2610
  return;
2529
2611
  }
2530
- if (options.rawFallback) box.scrollTop = anchor.scrollTop;
2612
+ if (options.rawFallback) {
2613
+ box.scrollTop = anchor.scrollTop;
2614
+ wroteTop = box.scrollTop;
2615
+ }
2531
2616
  }
2532
2617
  function preserve(mutate) {
2533
2618
  var anchor = capture();
@@ -2536,9 +2621,21 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2536
2621
  return result;
2537
2622
  }
2538
2623
  function remember() {
2624
+ var b0 = options.getBox();
2625
+ if (returning && b0 && b0.scrollTop !== wroteTop) {
2626
+ parked = null;
2627
+ parkedStuck = false;
2628
+ returning = false;
2629
+ }
2630
+ if (frozen()) return;
2539
2631
  held = capture();
2540
2632
  }
2541
2633
  function hold() {
2634
+ if (frozen()) return;
2635
+ if (sawFrozen || returning) {
2636
+ settleReturn();
2637
+ return;
2638
+ }
2542
2639
  var box = options.getBox();
2543
2640
  if (!box || options.isStuck()) {
2544
2641
  held = null;
@@ -2565,12 +2662,67 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2565
2662
  if (prev === void 0) prev = 0;
2566
2663
  var delta = h - prev;
2567
2664
  if (delta === 0) return;
2665
+ if (frozen()) {
2666
+ foldFrozenGrowth(box, el, delta);
2667
+ return;
2668
+ }
2568
2669
  if (el.getBoundingClientRect().top >= box.getBoundingClientRect().top) return;
2569
2670
  box.scrollTop += delta;
2671
+ wroteTop = box.scrollTop;
2570
2672
  if (held) held = capture();
2571
2673
  }
2674
+ function park() {
2675
+ parked = capture();
2676
+ parkedStuck = !!options.isStuck();
2677
+ returning = true;
2678
+ }
2679
+ function pinBottom() {
2680
+ var box = options.getBox();
2681
+ if (!box) return;
2682
+ box.scrollTop = box.scrollHeight;
2683
+ wroteTop = box.scrollTop;
2684
+ }
2685
+ function settleReturn() {
2686
+ if (frozen()) return false;
2687
+ sawFrozen = false;
2688
+ if (parkedStuck) {
2689
+ pinBottom();
2690
+ return true;
2691
+ }
2692
+ var target = parked || held;
2693
+ restoreExact = true;
2694
+ restorePinned = false;
2695
+ restore(target, true);
2696
+ parked = !restorePinned || !restoreExact ? target : null;
2697
+ returning = !!parked;
2698
+ return false;
2699
+ }
2700
+ function isReturning() {
2701
+ return returning;
2702
+ }
2703
+ function thaw() {
2704
+ settleReturn();
2705
+ }
2706
+ function foldFrozenGrowth(box, el, delta) {
2707
+ var elTop = el.getBoundingClientRect().top;
2708
+ foldInto(box, held, elTop, delta);
2709
+ foldInto(box, parked, elTop, delta);
2710
+ }
2711
+ function foldInto(box, a, elTop, delta) {
2712
+ if (!a || a.top >= 0) return;
2713
+ var rowEl = findRow(box, a);
2714
+ if (!rowEl) return;
2715
+ var within = elTop - rowEl.getBoundingClientRect().top;
2716
+ if (within < 0 || within >= rowEl.offsetHeight) return;
2717
+ if (within >= -a.top) return;
2718
+ a.top -= delta;
2719
+ a.scrollHeight += delta;
2720
+ }
2572
2721
  function forget() {
2573
2722
  held = null;
2723
+ parked = null;
2724
+ parkedStuck = false;
2725
+ returning = false;
2574
2726
  }
2575
2727
  return {
2576
2728
  capture,
@@ -2578,6 +2730,12 @@ Index the REMAINING windows - one record per row/item, looking at any page image
2578
2730
  preserve,
2579
2731
  remember,
2580
2732
  hold,
2733
+ park,
2734
+ settleReturn,
2735
+ isReturning,
2736
+ pinBottom,
2737
+ isFrozen: frozen,
2738
+ thaw,
2581
2739
  absorb,
2582
2740
  forget
2583
2741
  };
@@ -5680,6 +5838,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
5680
5838
  releaseBgFlag();
5681
5839
  self.updateHistoryCache();
5682
5840
  self.host.notify();
5841
+ if (self.host.settleScroll) self.host.settleScroll();
5683
5842
  }, function() {
5684
5843
  releaseBgFlag();
5685
5844
  self.host.notify();
@@ -5761,7 +5920,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
5761
5920
  self.drainBgTaskQueue();
5762
5921
  }
5763
5922
  if (!fetchMore) self.refreshLiveIndexState();
5764
- if (!fetchMore) return self.host.scrollToBottomIfSticky();
5923
+ if (!fetchMore) return self.host.settleScroll ? self.host.settleScroll() : self.host.scrollToBottomIfSticky();
5765
5924
  }).catch(function(err) {
5766
5925
  console.warn("[chat-engine] getChatHistory failed", err);
5767
5926
  }).then(function() {
@@ -8068,10 +8227,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
8068
8227
  refreshMessageBubble(i);
8069
8228
  },
8070
8229
  scrollToBottom: function(smooth) {
8071
- return scrollToBottom(smooth);
8230
+ return scrollToBottom();
8072
8231
  },
8073
8232
  scrollToBottomIfSticky: function(smooth) {
8074
- return scrollToBottomIfSticky(smooth);
8233
+ return scrollToBottomIfSticky();
8075
8234
  },
8076
8235
  // A first page can render shorter than the box (a file's every indexing
8077
8236
  // pass folds into ONE row), and a box that cannot scroll never fires the
@@ -8080,6 +8239,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
8080
8239
  onHistoryLoaded: function(fetchMore, token) {
8081
8240
  if (!fetchMore) ensureHistoryFillsViewport(token);
8082
8241
  },
8242
+ settleScroll: function() {
8243
+ settleScrollAfterRefresh();
8244
+ },
8083
8245
  cancelRequest: function(opts) {
8084
8246
  return S.skapi.cancelClientSecretRequest(opts);
8085
8247
  },
@@ -8271,7 +8433,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
8271
8433
  if (job.stageId) session.settleStagedMessage(job.stageId);
8272
8434
  CS.messages.push({ role: "assistant", content: "Something went wrong while uploading attachments. " + (err && err.message || ""), isError: true });
8273
8435
  renderMessages();
8274
- scrollToBottom(true);
8436
+ scrollToBottomIfSticky();
8275
8437
  return null;
8276
8438
  });
8277
8439
  }
@@ -8324,26 +8486,45 @@ Index the REMAINING windows - one record per row/item, looking at any page image
8324
8486
  };
8325
8487
  enqueueAttachmentSend({ text, batchId, stageId, pinned });
8326
8488
  }
8327
- function scrollToBottom(smooth) {
8489
+ function scrollToBottom() {
8490
+ if (chatScrollAnchor.isFrozen()) {
8491
+ CS.stickToBottom = true;
8492
+ chatScrollAnchor.pinBottom();
8493
+ return Promise.resolve();
8494
+ }
8328
8495
  return raf2().then(function() {
8329
8496
  if (!CS.messagesBox) return;
8330
8497
  CS.stickToBottom = true;
8331
- if (smooth) CS.messagesBox.scrollTo({ top: CS.messagesBox.scrollHeight, behavior: "smooth" });
8332
- else CS.messagesBox.scrollTop = CS.messagesBox.scrollHeight;
8498
+ chatScrollAnchor.pinBottom();
8333
8499
  });
8334
8500
  }
8335
- function scrollToBottomIfSticky(smooth) {
8501
+ function scrollToBottomIfSticky() {
8336
8502
  if (!CS.stickToBottom) return Promise.resolve();
8503
+ if (chatScrollAnchor.isFrozen()) {
8504
+ chatScrollAnchor.pinBottom();
8505
+ return Promise.resolve();
8506
+ }
8337
8507
  return raf2().then(function() {
8338
8508
  if (!CS.stickToBottom || !CS.messagesBox) return;
8339
- if (smooth) CS.messagesBox.scrollTo({ top: CS.messagesBox.scrollHeight, behavior: "smooth" });
8340
- else CS.messagesBox.scrollTop = CS.messagesBox.scrollHeight;
8509
+ chatScrollAnchor.pinBottom();
8341
8510
  });
8342
8511
  }
8512
+ function settleScrollAfterRefresh(afterAbsence) {
8513
+ if (afterAbsence || chatScrollAnchor.isReturning()) {
8514
+ if (chatScrollAnchor.settleReturn()) CS.stickToBottom = true;
8515
+ return;
8516
+ }
8517
+ if (CS.stickToBottom) scrollToBottomIfSticky();
8518
+ else chatScrollAnchor.hold();
8519
+ }
8343
8520
  var lastHistoryScrollTop = 0;
8344
8521
  function onHistoryScroll() {
8345
8522
  if (!CS.messagesBox || CS.chatSettingsOpen) return;
8346
8523
  var el = CS.messagesBox;
8524
+ if (chatScrollAnchor.isFrozen()) {
8525
+ lastHistoryScrollTop = el.scrollTop;
8526
+ return;
8527
+ }
8347
8528
  var atBottom = el.scrollHeight - el.scrollTop - el.clientHeight <= 16;
8348
8529
  if (!atBottom) CS.stickToBottom = false;
8349
8530
  else if (el.scrollTop >= lastHistoryScrollTop) CS.stickToBottom = true;
@@ -9245,7 +9426,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
9245
9426
  // earlier failure left on this file's chips.
9246
9427
  onLoad: function(path) {
9247
9428
  clearLinkUnavailable(linkUnavailableKeysForPath(path));
9248
- scrollToBottomIfSticky(false);
9429
+ scrollToBottomIfSticky();
9249
9430
  },
9250
9431
  // The resizes an <img> makes with no event of its own to announce
9251
9432
  // them: the src landing, and the src being dropped for a retry (which
@@ -10034,6 +10215,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
10034
10215
  isStuck: function() {
10035
10216
  return !!CS.stickToBottom;
10036
10217
  },
10218
+ // A hidden tab keeps mutating this list - a resumed poll, the head refresh
10219
+ // and its deferred background batch, a settling request - and every one of
10220
+ // those would otherwise move a scroll position nobody is looking at. Freeze
10221
+ // instead, and put the reader back once they can see it (settleScroll).
10222
+ isFrozen: function() {
10223
+ return typeof document !== "undefined" && !!document.hidden;
10224
+ },
10037
10225
  rawFallback: true
10038
10226
  });
10039
10227
  function captureScrollAnchor() {
@@ -10043,7 +10231,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
10043
10231
  var box = CS.messagesBox;
10044
10232
  if (!box) return;
10045
10233
  if (CS.stickToBottom) {
10046
- box.scrollTop = box.scrollHeight;
10234
+ chatScrollAnchor.pinBottom();
10047
10235
  return;
10048
10236
  }
10049
10237
  chatScrollAnchor.restore(anchor);
@@ -10285,7 +10473,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
10285
10473
  if (drafting !== CS.drafting) {
10286
10474
  CS.drafting = drafting;
10287
10475
  syncDraftingIndicator();
10288
- scrollToBottomIfSticky(false);
10476
+ scrollToBottomIfSticky();
10289
10477
  }
10290
10478
  });
10291
10479
  input.addEventListener("keydown", function(e) {
@@ -10639,18 +10827,26 @@ Index the REMAINING windows - one record per row/item, looking at any page image
10639
10827
  S._resizeBound = true;
10640
10828
  window.addEventListener("resize", function() {
10641
10829
  scheduleAttachmentOverflowRecompute();
10642
- scrollToBottomIfSticky(false);
10830
+ scrollToBottomIfSticky();
10643
10831
  ensureHistoryFillsViewport();
10644
10832
  });
10645
10833
  }
10646
10834
  if (!S._visBound && typeof document !== "undefined" && document.addEventListener) {
10647
10835
  S._visBound = true;
10836
+ window.addEventListener("blur", function() {
10837
+ chatScrollAnchor.park();
10838
+ });
10839
+ window.addEventListener("focus", function() {
10840
+ settleScrollAfterRefresh(true);
10841
+ });
10648
10842
  document.addEventListener("visibilitychange", function() {
10649
10843
  if (document.visibilityState === "hidden") {
10844
+ chatScrollAnchor.park();
10650
10845
  if (session && session.pausePolling) session.pausePolling("hidden");
10651
10846
  return;
10652
10847
  }
10653
10848
  if (document.visibilityState === "visible") {
10849
+ settleScrollAfterRefresh(true);
10654
10850
  var refreshed = S.user ? ensureMcpGrantFresh() : null;
10655
10851
  Promise.resolve(refreshed).catch(function() {
10656
10852
  }).then(function() {