@ryuu-64/dsh-find-all 0.1.9 → 0.1.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.10
4
+
5
+ - Keep the find bar below the conversation header and allow moving it within the conversation body with a drag handle or directional buttons.
6
+ - Preserve its manual position while the same conversation stays open, with a control to restore the default position; reset it when the bar reopens or the conversation changes.
7
+ - Constrain the bar to the available conversation area as the window, sidebar or input area changes, and reduce unused space in its default layout.
8
+ - Keep the current search match clear of the find bar, including matches in nested scrolling areas and when navigation feedback expands the bar.
9
+
10
+ The supported host-version list and return-to-reading-position behavior are unchanged.
11
+
3
12
  ## 0.1.9
4
13
 
5
14
  - Reveal host-owned `hidden="until-found"` content before scrolling to a search match, instead of treating its layout geometry as proof that the match is visible.
package/lib/client.js CHANGED
@@ -76,6 +76,7 @@ window.__ModuleLoader__.load({
76
76
  prev: "上一个匹配",
77
77
  next: "下一个匹配",
78
78
  close: "关闭查找",
79
+ move: "拖动查找栏;点击展开移动按钮", moveLeft: "向左移动", moveRight: "向右移动", moveUp: "向上移动", moveDown: "向下移动", resetPosition: "恢复默认位置",
79
80
  cap: "仅显示前 {n} 处匹配",
80
81
  resultCount: "{n} 个结果",
81
82
  scopeWhole: "包含更早内容",
@@ -106,6 +107,7 @@ window.__ModuleLoader__.load({
106
107
  prev: "Previous match",
107
108
  next: "Next match",
108
109
  close: "Close find",
110
+ move: "Drag find bar; click for move controls", moveLeft: "Move left", moveRight: "Move right", moveUp: "Move up", moveDown: "Move down", resetPosition: "Reset position",
109
111
  cap: "Showing only the first {n} matches",
110
112
  resultCount: "{n} results",
111
113
  scopeWhole: "Include earlier content",
@@ -234,14 +236,19 @@ window.__ModuleLoader__.load({
234
236
 
235
237
  function cssText() {
236
238
  return "" +
237
- "#" + BAR_ID + "{--dsh-find-all-anchor-gap:4px;--dsh-find-all-panel-inset:16px;--dsh-find-all-max-width:760px;position:fixed;top:calc(var(--dsh-find-all-header-bottom) + var(--dsh-find-all-anchor-gap));right:calc(var(--dsh-find-all-panel-right-space) + var(--dsh-find-all-panel-inset));z-index:2147483000;visibility:hidden;isolation:isolate;box-sizing:border-box;align-items:stretch;gap:4px;padding:6px 8px;display:flex;flex-direction:column;width:min(var(--dsh-find-all-max-width),calc(var(--dsh-find-all-panel-width) - var(--dsh-find-all-panel-inset) - var(--dsh-find-all-panel-inset)));max-width:calc(100vw - var(--dsh-find-all-panel-inset) - var(--dsh-find-all-panel-inset));background:transparent;--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);border:0;border-radius:var(--dsw-radius-md,8px);box-shadow:var(--dsw-elevation-panel,var(--dsw-shadow-lv2))}" +
239
+ "#" + BAR_ID + "{--dsh-find-all-max-width:600px;position:fixed;top:var(--dsh-find-all-bar-top);right:var(--dsh-find-all-bar-right);z-index:2147483000;visibility:hidden;isolation:isolate;box-sizing:border-box;align-items:stretch;gap:4px;padding:6px 8px;display:flex;flex-direction:column;width:min(var(--dsh-find-all-max-width),var(--dsh-find-all-panel-width));max-width:calc(100vw - 16px);max-height:var(--dsh-find-all-max-height);overflow:auto;background:transparent;--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);border:0;border-radius:var(--dsw-radius-md,8px);box-shadow:var(--dsw-elevation-panel,var(--dsw-shadow-lv2))}" +
238
240
  "#" + BAR_ID + "[data-positioned]{visibility:visible}" +
239
241
  "#" + BAR_ID + ":before{content:\"\";position:absolute;inset:0;z-index:-1;pointer-events:none;border-radius:inherit;background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2));-webkit-backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%));backdrop-filter:var(--dsw-menu-backdrop-filter,blur(40px) saturate(150%))}" +
240
242
  "#" + BAR_ID + " .controls{display:flex;flex-wrap:wrap;align-items:center;gap:6px;min-width:0}" +
241
- "#" + BAR_ID + " input{flex:1 1 210px;min-width:120px;width:auto;height:28px;color:var(--dsw-alias-label-primary);font:13px/1 var(--dsw-font-family);background:var(--dsw-alias-bg-base);border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm,6px);outline:none;padding:0 8px}" +
243
+ "#" + BAR_ID + " input{box-sizing:border-box;flex:1 1 160px;min-width:0;max-width:100%;width:auto;height:28px;color:var(--dsw-alias-label-primary);font:13px/1 var(--dsw-font-family);background:var(--dsw-alias-bg-base);border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-sm,6px);outline:none;padding:0 8px}" +
242
244
  "#" + BAR_ID + " input:focus-visible{border-color:var(--dsw-alias-state-business-primary)}" +
243
245
  "#" + BAR_ID + " .count{min-width:52px;color:var(--dsw-alias-label-tertiary);font:12px/1 var(--dsw-font-family);font-variant-numeric:tabular-nums;text-align:center}" +
244
- "#" + BAR_ID + " .status{width:100%;min-height:15px;color:var(--dsw-alias-label-tertiary);font:12px/1.25 var(--dsw-font-family);text-align:left;white-space:normal;overflow-wrap:anywhere}" +
246
+ "#" + BAR_ID + " .status{width:100%;color:var(--dsw-alias-label-tertiary);font:12px/1.25 var(--dsw-font-family);text-align:left;white-space:normal;overflow-wrap:anywhere}" +
247
+ "#" + BAR_ID + " .status:empty{display:none}" +
248
+ "#" + BAR_ID + " .position-controls{display:flex;flex-wrap:wrap;align-items:center;gap:4px}" +
249
+ "#" + BAR_ID + " .position-controls[hidden]{display:none}" +
250
+ "#" + BAR_ID + " button.drag-handle{touch-action:none;user-select:none;cursor:grab;flex:none}" +
251
+ "#" + BAR_ID + "[data-dragging] button.drag-handle{cursor:grabbing}" +
245
252
  "#" + BAR_ID + " .status[data-busy]{cursor:pointer;color:var(--dsw-alias-state-business-primary)}" +
246
253
  "#" + BAR_ID + " .scope{width:auto;padding:0 9px;color:var(--dsw-alias-label-secondary);font:12px/1 var(--dsw-font-family);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:999px}" +
247
254
  "#" + BAR_ID + " .scope[data-whole]{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-state-business-primary)}" +
@@ -423,13 +430,78 @@ window.__ModuleLoader__.load({
423
430
  return highlight;
424
431
  }
425
432
 
433
+ function matchBarRect() {
434
+ return isOpen() && state.bar.hasAttribute("data-positioned") ? state.bar.getBoundingClientRect() : null;
435
+ }
436
+
437
+ // Match visibility is deliberately independent of reading-origin capture
438
+ // and return. Range geometry alone does not account for floating UI.
439
+ function visibleMatchRect(range, viewport) {
440
+ if (!viewport || !rangeRect(range, viewport)) return null;
441
+ var clip = clippedViewport(range.startContainer.parentElement, viewport), bar = matchBarRect();
442
+ var rects = range.getClientRects();
443
+ for (var i = 0; i < rects.length; i++) {
444
+ var rect = rects[i], left = Math.max(rect.left, clip.left), right = Math.min(rect.right, clip.right);
445
+ var top = Math.max(rect.top, clip.top), bottom = Math.min(rect.bottom, clip.bottom);
446
+ if (right <= left || bottom <= top) continue;
447
+ if (!bar || right <= bar.left || left >= bar.right || bottom <= bar.top || top >= bar.bottom) return rect;
448
+ }
449
+ return null;
450
+ }
451
+
452
+ function scrollRangeWithin(range, scroll, viewport, bar) {
453
+ var rect = rangeRect(range);
454
+ if (!rect) return;
455
+ var current = scroll.scrollTop, maximum = Math.max(0, scroll.scrollHeight - scroll.clientHeight);
456
+ var bands = [[viewport.top, viewport.bottom]];
457
+ if (bar && rect.right > bar.left && rect.left < bar.right && bar.bottom > viewport.top && bar.top < viewport.bottom) {
458
+ bands = [[viewport.top, Math.min(viewport.bottom, bar.top - 8)],
459
+ [Math.max(viewport.top, bar.bottom + 8), viewport.bottom]];
460
+ }
461
+ var best = null;
462
+ bands.forEach(function (band) {
463
+ if (band[1] <= band[0]) return;
464
+ var delta = rect.top < band[0] ? rect.top - band[0] :
465
+ rect.bottom > band[1] ? Math.min(rect.top - band[0], rect.bottom - band[1]) : 0;
466
+ delta = Math.max(0, Math.min(maximum, current + delta)) - current;
467
+ var shown = Math.max(0, Math.min(rect.bottom - delta, band[1]) - Math.max(rect.top - delta, band[0]));
468
+ // At either history boundary, prefer the region actually reachable
469
+ // after clamping rather than assuming the larger region is reachable.
470
+ if (!best || shown > best.shown || (shown === best.shown && Math.abs(delta) < Math.abs(best.delta))) best = { delta: delta, shown: shown };
471
+ });
472
+ var x = rect.left < viewport.left ? rect.left - viewport.left :
473
+ rect.right > viewport.right ? Math.min(rect.left - viewport.left, rect.right - viewport.right) : 0;
474
+ if ((best && best.delta) || x) {
475
+ try { scroll.scrollBy({ top: best ? best.delta : 0, left: x, behavior: "instant" }); } catch (error) {}
476
+ }
477
+ }
478
+
426
479
  function scrollToRange(range) {
427
480
  var viewport = readingViewport(state.target);
428
- if (viewport && rangeRect(range, viewport)) return;
429
- var el = range.startContainer.parentElement;
430
- if (el && el.scrollIntoView) {
431
- try { el.scrollIntoView({ block: "center", behavior: "instant" }); } catch (error) {}
481
+ if (!viewport || visibleMatchRect(range, viewport)) return;
482
+ // Reveal the exact Range, not its potentially very tall <pre>/<p>.
483
+ // Work from inner scroll clips outwards, without scrolling the window
484
+ // or unrelated panels. Re-read geometry after every scroll.
485
+ for (var node = range.startContainer.parentElement; node && node !== viewport.scroll; node = node.parentElement) {
486
+ var style = window.getComputedStyle(node);
487
+ var clipsY = /^(auto|scroll|hidden)$/.test(style.overflowY) && node.scrollHeight > node.clientHeight;
488
+ var clipsX = /^(auto|scroll|hidden)$/.test(style.overflowX) && node.scrollWidth > node.clientWidth;
489
+ if (!clipsX && !clipsY) continue;
490
+ var box = node.getBoundingClientRect(), top = box.top + node.clientTop, left = box.left + node.clientLeft;
491
+ var rect = rangeRect(range);
492
+ if (!rect) return;
493
+ var clip = clippedViewport(node, viewport);
494
+ var safeTop = Math.max(top, clip.top), safeBottom = Math.min(top + node.clientHeight, clip.bottom);
495
+ var shown = safeBottom > safeTop;
496
+ // A code scroller may still have room when the conversation is at
497
+ // its boundary. Let it avoid the overlay within its visible clip.
498
+ scrollRangeWithin(range, node, { top: clipsY ? (shown ? safeTop : top) : rect.top,
499
+ bottom: clipsY ? (shown ? safeBottom : top + node.clientHeight) : rect.bottom,
500
+ left: clipsX ? left : rect.left, right: clipsX ? left + node.clientWidth : rect.right },
501
+ clipsY && shown ? matchBarRect() : null);
432
502
  }
503
+ viewport = readingViewport(state.target);
504
+ if (viewport && !visibleMatchRect(range, viewport)) scrollRangeWithin(range, viewport.scroll, viewport, matchBarRect());
433
505
  }
434
506
 
435
507
  //#region host adapter
@@ -854,10 +926,11 @@ window.__ModuleLoader__.load({
854
926
  if (state.target !== target || !state.adapter.valid(target) || state.adapter.face(target) !== face) return;
855
927
  }
856
928
  var origin = state.origin, token = state.navigationToken, started = Date.now();
857
- var activated = new Set(), scrolledRange = null;
929
+ var activated = new Set(), announceReturn = false;
858
930
  function check() {
859
931
  state.navigationTimer = 0;
860
932
  if (token !== state.navigationToken || !originValid(origin)) return;
933
+ positionBar();
861
934
  var viewport = readingViewport(origin.target);
862
935
  var current = state.ranges[state.index];
863
936
  var attached = current && origin.target.root.contains(current.startContainer);
@@ -867,11 +940,8 @@ window.__ModuleLoader__.load({
867
940
  var concealed = attached && hiddenUntilFoundAncestor(current.startContainer, origin.target.root);
868
941
  var geometry = attached && !concealed && rangeRect(current);
869
942
  if (current && (!geometry || concealed)) revealMatchRange(origin.target, current, activated);
870
- if (geometry && scrolledRange !== current) {
871
- scrollToRange(current);
872
- scrolledRange = current;
873
- }
874
- if (viewport && attached && !concealed && rangeRect(current, viewport)) {
943
+ if (geometry && !visibleMatchRect(current, viewport)) scrollToRange(current);
944
+ if (viewport && attached && !concealed && visibleMatchRect(current, viewport)) {
875
945
  var firstNavigation = !origin.navigated;
876
946
  origin.navigated = true;
877
947
  updateReturnButton();
@@ -881,8 +951,13 @@ window.__ModuleLoader__.load({
881
951
  var priorStatus = state.jumpStatus;
882
952
  setStatus(priorStatus, false);
883
953
  } else setStatus(L.returnAvailable, false);
884
- if (state.announcement) state.announcement.textContent = L.returnAvailable;
954
+ announceReturn = true;
955
+ // Return/status controls can wrap and grow the overlay. Verify
956
+ // the resulting layout before declaring the match visible.
957
+ state.navigationTimer = setTimeout(check, 0);
958
+ return;
885
959
  }
960
+ if (announceReturn && state.announcement) state.announcement.textContent = L.returnAvailable;
886
961
  return;
887
962
  }
888
963
  if (Date.now() - started < 1500) {
@@ -1113,7 +1188,8 @@ window.__ModuleLoader__.load({
1113
1188
  origin: null, restoring: null, returnBtn: null, announcement: null, navigationTimer: 0, navigationToken: 0,
1114
1189
  jumpStatus: null,
1115
1190
  positionObserver: null,
1116
- positionFrame: 0
1191
+ positionFrame: null,
1192
+ positionOwner: null, manualPosition: null, drag: null, dragHandle: null, positionControls: null, suppressMoveClick: false
1117
1193
  };
1118
1194
 
1119
1195
  function barAnchor(target) {
@@ -1129,39 +1205,115 @@ window.__ModuleLoader__.load({
1129
1205
 
1130
1206
  function positionBar() {
1131
1207
  if (!isOpen() || !state.bar) return false;
1132
- if (!state.target) { state.bar.removeAttribute("data-positioned"); return false; }
1133
- var header = barAnchor(state.target);
1134
- if (!header || !header.isConnected || !state.target.panel.isConnected) {
1208
+ var target = state.target, header = barAnchor(target), viewport = readingViewport(target);
1209
+ if (!target || !header || !header.isConnected || !target.panel.isConnected || !viewport) {
1210
+ state.bar.removeAttribute("data-positioned");
1211
+ return false;
1212
+ }
1213
+ var panel = target.panel.getBoundingClientRect(), headerRect = header.getBoundingClientRect();
1214
+ var leftEdge = Math.max(panel.left, viewport.left) + 8;
1215
+ var rightEdge = Math.min(panel.right, viewport.right) - 8;
1216
+ var topEdge = Math.max(panel.top, viewport.top, headerRect.bottom) + 8;
1217
+ var bottomEdge = Math.min(panel.bottom, viewport.bottom) - 8;
1218
+ if (rightEdge <= leftEdge || bottomEdge <= topEdge) {
1135
1219
  state.bar.removeAttribute("data-positioned");
1136
1220
  return false;
1137
1221
  }
1138
- var headerRect = header.getBoundingClientRect();
1139
- var panelRect = state.target.panel.getBoundingClientRect();
1140
- var viewportWidth = document.documentElement.clientWidth || window.innerWidth;
1141
- var panelLeft = Math.max(0, panelRect.left);
1142
- var panelRight = Math.min(viewportWidth, panelRect.right);
1143
1222
  var dpr = window.devicePixelRatio || 1;
1144
1223
  function cssPixel(value) { return Math.round(value * dpr) / dpr + "px"; }
1145
- state.bar.style.setProperty("--dsh-find-all-header-bottom", cssPixel(headerRect.bottom));
1146
- state.bar.style.setProperty("--dsh-find-all-panel-right-space", cssPixel(Math.max(0, viewportWidth - panelRight)));
1147
- state.bar.style.setProperty("--dsh-find-all-panel-width", cssPixel(Math.max(0, panelRight - panelLeft)));
1224
+ // This is the only position writer, including pointer, resize, scroll
1225
+ // and polling updates. Manual placement never falls back to the anchor.
1226
+ state.bar.style.setProperty("--dsh-find-all-panel-width", cssPixel(rightEdge - leftEdge));
1227
+ state.bar.style.setProperty("--dsh-find-all-max-height", cssPixel(bottomEdge - topEdge));
1228
+ var box = state.bar.getBoundingClientRect(), manual = state.manualPosition;
1229
+ var left = manual ? manual.left : rightEdge - box.width;
1230
+ var top = manual ? manual.top : topEdge;
1231
+ left = Math.max(leftEdge, Math.min(left, rightEdge - box.width));
1232
+ top = Math.max(topEdge, Math.min(top, bottomEdge - box.height));
1233
+ if (manual) { manual.left = left; manual.top = top; }
1234
+ state.bar.style.setProperty("--dsh-find-all-bar-top", cssPixel(top));
1235
+ state.bar.style.setProperty("--dsh-find-all-bar-right", cssPixel(window.innerWidth - left - box.width));
1148
1236
  state.bar.setAttribute("data-positioned", "");
1149
1237
  return true;
1150
1238
  }
1151
1239
 
1240
+ function finishBarDrag(event) {
1241
+ var drag = state.drag;
1242
+ if (!drag || (event && event.pointerId !== drag.id)) return;
1243
+ state.drag = null;
1244
+ state.bar.removeAttribute("data-dragging");
1245
+ try { if (drag.handle.hasPointerCapture(drag.id)) drag.handle.releasePointerCapture(drag.id); } catch (error) {}
1246
+ }
1247
+
1248
+ function resetBarPosition() {
1249
+ finishBarDrag();
1250
+ state.manualPosition = null;
1251
+ scheduleBarPosition();
1252
+ }
1253
+
1254
+ function moveBarBy(x, y) {
1255
+ if (!positionBar()) return;
1256
+ var box = state.bar.getBoundingClientRect();
1257
+ state.manualPosition = { left: box.left + x, top: box.top + y };
1258
+ scheduleBarPosition();
1259
+ }
1260
+
1261
+ function buildPositionControls(controls, bar) {
1262
+ var moves = document.createElement("div");
1263
+ moves.className = "position-controls"; moves.id = BAR_ID + "-position"; moves.hidden = true;
1264
+ var handle = button(L.move, "M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01", function (event) {
1265
+ if (event.detail && state.suppressMoveClick) { state.suppressMoveClick = false; return; }
1266
+ moves.hidden = !moves.hidden;
1267
+ handle.setAttribute("aria-expanded", String(!moves.hidden));
1268
+ scheduleBarPosition();
1269
+ });
1270
+ handle.className = "drag-handle";
1271
+ handle.setAttribute("aria-expanded", "false"); handle.setAttribute("aria-controls", moves.id);
1272
+ handle.addEventListener("pointerdown", function (event) {
1273
+ if (!event.isPrimary || event.button !== 0 || state.drag || !positionBar()) return;
1274
+ state.suppressMoveClick = false;
1275
+ try { handle.setPointerCapture(event.pointerId); } catch (error) { return; }
1276
+ var box = state.bar.getBoundingClientRect();
1277
+ state.drag = { id: event.pointerId, handle: handle, x: event.clientX, y: event.clientY, left: box.left, top: box.top };
1278
+ // Dragging the handle must not steal focus/selection from the query.
1279
+ event.preventDefault();
1280
+ });
1281
+ handle.addEventListener("pointermove", function (event) {
1282
+ var drag = state.drag;
1283
+ if (!drag || event.pointerId !== drag.id) return;
1284
+ var x = event.clientX - drag.x, y = event.clientY - drag.y;
1285
+ if (!state.suppressMoveClick && Math.abs(x) + Math.abs(y) < 4) return;
1286
+ state.suppressMoveClick = true;
1287
+ state.bar.setAttribute("data-dragging", "");
1288
+ state.manualPosition = { left: drag.left + x, top: drag.top + y };
1289
+ scheduleBarPosition();
1290
+ });
1291
+ ["pointerup", "pointercancel", "lostpointercapture"].forEach(function (type) { handle.addEventListener(type, finishBarDrag); });
1292
+ moves.appendChild(button(L.moveLeft, "m14 6-6 6 6 6", function () { moveBarBy(-32, 0); }));
1293
+ moves.appendChild(button(L.moveRight, "m10 6 6 6-6 6", function () { moveBarBy(32, 0); }));
1294
+ moves.appendChild(button(L.moveUp, "m6 14 6-6 6 6", function () { moveBarBy(0, -32); }));
1295
+ moves.appendChild(button(L.moveDown, "m6 10 6 6 6-6", function () { moveBarBy(0, 32); }));
1296
+ var reset = button(L.resetPosition, "M3 10a9 9 0 1 1 2 8M3 4v6h6", resetBarPosition);
1297
+ moves.appendChild(reset);
1298
+ state.dragHandle = handle; state.positionControls = moves;
1299
+ controls.appendChild(handle);
1300
+ bar.appendChild(moves);
1301
+ }
1302
+
1152
1303
  function scheduleBarPosition() {
1153
- if (!isOpen() || state.positionFrame) return;
1304
+ if (!isOpen() || state.positionFrame !== null) return;
1154
1305
  if (typeof requestAnimationFrame !== "function") { positionBar(); return; }
1155
1306
  state.positionFrame = requestAnimationFrame(function () {
1156
- state.positionFrame = 0;
1307
+ state.positionFrame = null;
1157
1308
  positionBar();
1158
1309
  });
1159
1310
  }
1160
1311
 
1161
1312
  function stopBarPositioning() {
1313
+ finishBarDrag();
1162
1314
  if (state.positionObserver) { state.positionObserver.disconnect(); state.positionObserver = null; }
1163
- if (state.positionFrame && typeof cancelAnimationFrame === "function") cancelAnimationFrame(state.positionFrame);
1164
- state.positionFrame = 0;
1315
+ if (state.positionFrame !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(state.positionFrame);
1316
+ state.positionFrame = null;
1165
1317
  }
1166
1318
 
1167
1319
  function startBarPositioning() {
@@ -1171,6 +1323,13 @@ window.__ModuleLoader__.load({
1171
1323
  if (!header || typeof ResizeObserver === "undefined") return;
1172
1324
  state.positionObserver = new ResizeObserver(scheduleBarPosition);
1173
1325
  state.positionObserver.observe(header);
1326
+ var viewport = readingViewport(state.target);
1327
+ if (viewport) {
1328
+ state.positionObserver.observe(viewport.scroll);
1329
+ var composer = viewport.scroll.querySelector("[data-composer-seat]");
1330
+ if (composer) state.positionObserver.observe(composer);
1331
+ }
1332
+ state.positionObserver.observe(state.bar);
1174
1333
  if (state.target.panel !== header) state.positionObserver.observe(state.target.panel);
1175
1334
  }
1176
1335
 
@@ -1453,6 +1612,15 @@ window.__ModuleLoader__.load({
1453
1612
  function syncTarget(target, passive) {
1454
1613
  if (!state.adapter) return;
1455
1614
  var selected = passive ? state.adapter.refresh(state.target) : state.adapter.select(target || document.activeElement, state.target);
1615
+ if (selected) {
1616
+ var owner = state.positionOwner;
1617
+ if (owner && (owner.id !== selected.id || owner.panel !== selected.panel)) {
1618
+ resetBarPosition();
1619
+ if (state.positionControls) state.positionControls.hidden = true;
1620
+ if (state.dragHandle) state.dragHandle.setAttribute("aria-expanded", "false");
1621
+ }
1622
+ state.positionOwner = { id: selected.id, panel: selected.panel };
1623
+ }
1456
1624
  if (selected && state.target && selected.id === state.target.id && selected.anchor === state.target.anchor && selected.root === state.target.root && selected.panel === state.target.panel && selected.registration === state.target.registration) {
1457
1625
  if (state.origin && !originValid(state.origin)) { discardOrigin(); setStatus(returnFailure('unavailable'), false); }
1458
1626
  scheduleBarPosition();
@@ -1569,6 +1737,7 @@ window.__ModuleLoader__.load({
1569
1737
  setStatus(L.incomplete, false);
1570
1738
  });
1571
1739
 
1740
+ buildPositionControls(controls, bar);
1572
1741
  controls.appendChild(input);
1573
1742
  controls.appendChild(scopeBtn);
1574
1743
  controls.appendChild(count);
@@ -1581,7 +1750,7 @@ window.__ModuleLoader__.load({
1581
1750
  state.returnBtn = returnBtn;
1582
1751
  controls.appendChild(returnBtn);
1583
1752
  controls.appendChild(button(L.close, "M18 6 6 18M6 6l12 12", close));
1584
- bar.appendChild(controls);
1753
+ bar.insertBefore(controls, bar.firstChild);
1585
1754
  bar.appendChild(status);
1586
1755
 
1587
1756
  state.bar = bar;
@@ -1631,6 +1800,10 @@ window.__ModuleLoader__.load({
1631
1800
  cancelPageIn();
1632
1801
  stopWatchingSession();
1633
1802
  stopBarPositioning();
1803
+ state.manualPosition = state.positionOwner = null;
1804
+ state.suppressMoveClick = false;
1805
+ if (state.positionControls) state.positionControls.hidden = true;
1806
+ if (state.dragHandle) state.dragHandle.setAttribute("aria-expanded", "false");
1634
1807
  if (state.bar) { state.bar.style.display = "none"; state.bar.removeAttribute("data-positioned"); }
1635
1808
  state.ranges = [];
1636
1809
  state.index = -1;
@@ -1707,6 +1880,8 @@ window.__ModuleLoader__.load({
1707
1880
  window.addEventListener("focusin", focus, true);
1708
1881
  window.addEventListener("pointerdown", focus, true);
1709
1882
  window.addEventListener("wheel", readingFocus, true);
1883
+ function blur() { finishBarDrag(); }
1884
+ window.addEventListener("blur", blur);
1710
1885
  window.addEventListener("resize", scheduleBarPosition);
1711
1886
  window.addEventListener("scroll", scheduleBarPosition, true);
1712
1887
  return function () {
@@ -1715,13 +1890,14 @@ window.__ModuleLoader__.load({
1715
1890
  window.removeEventListener("focusin", focus, true);
1716
1891
  window.removeEventListener("pointerdown", focus, true);
1717
1892
  window.removeEventListener("wheel", readingFocus, true);
1893
+ window.removeEventListener("blur", blur);
1718
1894
  window.removeEventListener("resize", scheduleBarPosition);
1719
1895
  window.removeEventListener("scroll", scheduleBarPosition, true);
1720
1896
  close();
1721
1897
  state.query = "";
1722
1898
  if (state.bar) state.bar.remove();
1723
1899
  if (state.announcement) state.announcement.remove();
1724
- state.announcement = state.returnBtn = null;
1900
+ state.announcement = state.returnBtn = state.dragHandle = state.positionControls = null;
1725
1901
  var style = document.querySelector('style[data-plugin-css="dsh-find-all/bar.css"]');
1726
1902
  if (style) style.remove();
1727
1903
  state.bar = state.input = state.count = state.status = state.scopeBtn = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuu-64/dsh-find-all",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Ctrl+F find bar for the DSH desktop app: it searches the whole conversation, not just the messages already on screen.",
5
5
  "type": "module",
6
6
  "license": "MIT",