clankerbend 0.1.0 → 0.1.1

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.
@@ -1,7 +1,7 @@
1
1
  (function installCodexVimNavigator() {
2
2
  const VIM_NAV_APP_ID = "onewill.vim-nav";
3
- const BRIDGE_VERSION = 112;
4
- const RUNTIME_KEY = "__oneWhackRuntime";
3
+ const BRIDGE_VERSION = 114;
4
+ const RUNTIME_KEY = "__clankerbendRuntime";
5
5
  const STYLE_ID = "codex-vim-nav-style";
6
6
  const ANNOTATION_CLASS = "codex-vim-nav-annotation";
7
7
  const ANCHOR_CLASS = "codex-vim-nav-anchor";
@@ -9,10 +9,10 @@
9
9
  const HIGHLIGHT_CLASS = "codex-vim-nav-highlight";
10
10
  const STATUS_CHROME_CLASS = "codex-vim-nav-status-chrome";
11
11
  const MODE_BADGE_ID = "codex-vim-nav-mode-badge";
12
- const HOST_UI_CLASS = "onewhack-host-ui";
13
- const SELECTION_MENU_ID = "onewhack-selection-menu";
14
- const OVERLAY_ID = "onewhack-anchored-overlay";
15
- const COMPOSER_CHIPS_ID = "onewhack-composer-chips";
12
+ const HOST_UI_CLASS = "clankerbend-host-ui";
13
+ const SELECTION_MENU_ID = "clankerbend-selection-menu";
14
+ const OVERLAY_ID = "clankerbend-anchored-overlay";
15
+ const COMPOSER_CHIPS_ID = "clankerbend-composer-chips";
16
16
  const SELECTOR = [
17
17
  "[data-content-search-unit-key]",
18
18
  "[data-turn-key]",
@@ -20,7 +20,7 @@
20
20
  "[data-thread-user-message-navigation-item-id]"
21
21
  ].join(",");
22
22
 
23
- const existingBridge = oneWhackRuntime().getBridge(VIM_NAV_APP_ID);
23
+ const existingBridge = clankerbendRuntime().getBridge(VIM_NAV_APP_ID);
24
24
  if (existingBridge?.name === "vim-nav" && existingBridge?.version === BRIDGE_VERSION) {
25
25
  return existingBridge.snapshot();
26
26
  }
@@ -36,9 +36,9 @@
36
36
  vimMode: false,
37
37
  helpOpen: false,
38
38
  badgeHidden: readBadgeHidden(),
39
- ctrlDown: false,
39
+ metaDown: false,
40
40
  altDown: false,
41
- ctrlAltToggleDown: false,
41
+ metaAltToggleDown: false,
42
42
  pendingKeys: "",
43
43
  countPrefix: "",
44
44
  anchorOrder: [],
@@ -60,6 +60,8 @@
60
60
  lastBottomJump: null,
61
61
  lastOrderFallback: null,
62
62
  commandChain: Promise.resolve(),
63
+ relativeMoveInFlight: false,
64
+ pendingRelativeDelta: 0,
63
65
  hostState: null,
64
66
  hostEvents: [],
65
67
  activeTextSelection: null,
@@ -159,7 +161,7 @@
159
161
  font: 13px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
160
162
  padding: 9px !important;
161
163
  }
162
- #${OVERLAY_ID} .onewhack-overlay-actions {
164
+ #${OVERLAY_ID} .clankerbend-overlay-actions {
163
165
  display: flex !important;
164
166
  justify-content: flex-end !important;
165
167
  gap: 6px !important;
@@ -168,6 +170,10 @@
168
170
  background: rgba(80, 65, 20, .78) !important;
169
171
  color: #fff7d7 !important;
170
172
  }
173
+ #${OVERLAY_ID} button.clankerbend-overlay-secondary {
174
+ background: transparent !important;
175
+ color: rgba(58, 45, 12, .82) !important;
176
+ }
171
177
  #${COMPOSER_CHIPS_ID} {
172
178
  position: absolute !important;
173
179
  z-index: 2147483002 !important;
@@ -180,7 +186,7 @@
180
186
  #${COMPOSER_CHIPS_ID}.is-visible {
181
187
  display: flex !important;
182
188
  }
183
- #${COMPOSER_CHIPS_ID} .onewhack-context-chip {
189
+ #${COMPOSER_CHIPS_ID} .clankerbend-context-chip {
184
190
  display: inline-flex !important;
185
191
  align-items: center !important;
186
192
  gap: 5px !important;
@@ -194,7 +200,7 @@
194
200
  padding: 5px 8px !important;
195
201
  pointer-events: auto !important;
196
202
  }
197
- .onewhack-submitted-context-row .onewhack-context-chip {
203
+ .clankerbend-submitted-context-row .clankerbend-context-chip {
198
204
  display: inline-flex !important;
199
205
  align-items: center !important;
200
206
  gap: 5px !important;
@@ -206,24 +212,24 @@
206
212
  font: 600 12px/1.2 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
207
213
  padding: 5px 8px !important;
208
214
  }
209
- .onewhack-submitted-context-row .onewhack-context-chip::before {
215
+ .clankerbend-submitted-context-row .clankerbend-context-chip::before {
210
216
  content: "□" !important;
211
217
  color: #8a7327 !important;
212
218
  font-size: 10px !important;
213
219
  line-height: 1 !important;
214
220
  }
215
- #${COMPOSER_CHIPS_ID} .onewhack-context-chip::before {
221
+ #${COMPOSER_CHIPS_ID} .clankerbend-context-chip::before {
216
222
  content: "□" !important;
217
223
  color: #8a7327 !important;
218
224
  font-size: 10px !important;
219
225
  line-height: 1 !important;
220
226
  }
221
- #${COMPOSER_CHIPS_ID} .onewhack-context-chip span {
227
+ #${COMPOSER_CHIPS_ID} .clankerbend-context-chip span {
222
228
  overflow: hidden !important;
223
229
  text-overflow: ellipsis !important;
224
230
  white-space: nowrap !important;
225
231
  }
226
- #${COMPOSER_CHIPS_ID} .onewhack-context-chip button {
232
+ #${COMPOSER_CHIPS_ID} .clankerbend-context-chip button {
227
233
  padding: 0 2px !important;
228
234
  color: #725f20 !important;
229
235
  }
@@ -380,7 +386,7 @@
380
386
  const titleRow = document.createElement("div");
381
387
  titleRow.className = "codex-vim-nav-title-row";
382
388
  const title = document.createElement("strong");
383
- title.textContent = "OneWill VimNav";
389
+ title.textContent = "VimNav";
384
390
  const mode = document.createElement("span");
385
391
  mode.className = "codex-vim-nav-state";
386
392
  mode.textContent = `[${state.vimMode ? "ON" : "OFF"}]`;
@@ -388,7 +394,7 @@
388
394
  dismiss.type = "button";
389
395
  dismiss.className = "codex-vim-nav-dismiss";
390
396
  dismiss.textContent = "x";
391
- dismiss.title = "Hide VimNav badge until Ctrl-Alt";
397
+ dismiss.title = "Hide VimNav badge until Cmd-Option";
392
398
  dismiss.addEventListener("click", (event) => {
393
399
  event.preventDefault();
394
400
  event.stopPropagation();
@@ -403,12 +409,23 @@
403
409
  helpButton.className = "codex-vim-nav-help-toggle";
404
410
  helpButton.textContent = "?";
405
411
  helpButton.title = "Toggle VimNav help";
406
- helpButton.addEventListener("click", (event) => {
412
+ const activateHelp = (event) => {
407
413
  event.preventDefault();
408
414
  event.stopPropagation();
415
+ event.stopImmediatePropagation?.();
416
+ if (event.type === "click" && helpButton.dataset.clankerbendPressed === "true") {
417
+ helpButton.dataset.clankerbendPressed = "";
418
+ return;
419
+ }
420
+ if (event.type !== "click" && helpButton.dataset.clankerbendPressed === "true") return;
421
+ if (event.type !== "click") helpButton.dataset.clankerbendPressed = "true";
409
422
  toggleHelp();
410
- });
411
- toggleRow.append("(Ctrl-Alt toggles, ", helpButton, " for help)");
423
+ };
424
+ helpButton.addEventListener("pointerdown", activateHelp);
425
+ helpButton.addEventListener("mousedown", activateHelp);
426
+ helpButton.addEventListener("touchstart", activateHelp, { passive: false });
427
+ helpButton.addEventListener("click", activateHelp);
428
+ toggleRow.append("(Cmd-Option toggles, ", helpButton, " for help)");
412
429
  badge.appendChild(toggleRow);
413
430
  if (buffer) {
414
431
  const row = document.createElement("div");
@@ -1297,13 +1314,13 @@
1297
1314
  if (!(el instanceof HTMLElement)) continue;
1298
1315
  el.classList.add(ANCHOR_CLASS);
1299
1316
  el.classList.toggle(CURRENT_CLASS, item.anchorId === state.currentAnchorId);
1300
- let label = el.querySelector(`.${ANNOTATION_CLASS}[data-anchor-id="${cssEscape(item.anchorId)}"][data-onewhack-app-id="${VIM_NAV_APP_ID}"]`);
1317
+ let label = el.querySelector(`.${ANNOTATION_CLASS}[data-anchor-id="${cssEscape(item.anchorId)}"][data-clankerbend-app-id="${VIM_NAV_APP_ID}"]`);
1301
1318
  if (!label) {
1302
1319
  label = document.createElement("button");
1303
1320
  label.type = "button";
1304
1321
  label.className = ANNOTATION_CLASS;
1305
1322
  label.dataset.anchorId = item.anchorId;
1306
- label.dataset.onewhackAppId = VIM_NAV_APP_ID;
1323
+ label.dataset.clankerbendAppId = VIM_NAV_APP_ID;
1307
1324
  label.addEventListener("click", (event) => {
1308
1325
  event.preventDefault();
1309
1326
  event.stopImmediatePropagation();
@@ -1318,10 +1335,10 @@
1318
1335
  }
1319
1336
  label.textContent = markerGlyph(item);
1320
1337
  label.title = item.indexed
1321
- ? `Vim Nav ${item.order}: ${item.inferredRole || item.kind}`
1322
- : `Vim Nav index pending: ${item.inferredRole || item.kind}`;
1338
+ ? `VimNav ${item.order}: ${item.inferredRole || item.kind}`
1339
+ : `VimNav index pending: ${item.inferredRole || item.kind}`;
1323
1340
  label.setAttribute("aria-label", label.title);
1324
- oneWhackRuntime().placeAnnotation?.(el, {
1341
+ clankerbendRuntime().placeAnnotation?.(el, {
1325
1342
  appId: VIM_NAV_APP_ID,
1326
1343
  anchorId: item.anchorId,
1327
1344
  markerId: `vim-nav:${item.anchorId}`,
@@ -1329,9 +1346,9 @@
1329
1346
  element: label
1330
1347
  }) || el.insertAdjacentElement("afterbegin", label);
1331
1348
  }
1332
- oneWhackRuntime().removeAnnotations?.(VIM_NAV_APP_ID, ids);
1333
- document.querySelectorAll(`.${ANNOTATION_CLASS}[data-onewhack-app-id="${VIM_NAV_APP_ID}"]`).forEach((node) => {
1334
- if (!ids.has(node.dataset.anchorId) && !node.closest(".onewhack-transcript-annotation-slot")) node.remove();
1349
+ clankerbendRuntime().removeAnnotations?.(VIM_NAV_APP_ID, ids);
1350
+ document.querySelectorAll(`.${ANNOTATION_CLASS}[data-clankerbend-app-id="${VIM_NAV_APP_ID}"]`).forEach((node) => {
1351
+ if (!ids.has(node.dataset.anchorId) && !node.closest(".clankerbend-transcript-annotation-slot")) node.remove();
1335
1352
  });
1336
1353
  }
1337
1354
 
@@ -1429,123 +1446,6 @@
1429
1446
  });
1430
1447
  }
1431
1448
 
1432
- async function jumpRelativeMountedAcrossGap(delta, currentOrder, beforeAnchorId) {
1433
- const targetIndex = currentOrder - 1 + delta;
1434
- const indexedTarget = await mountTargetForIndex(targetIndex, delta, {
1435
- approximate: true,
1436
- preferExact: true
1437
- });
1438
- if (indexedTarget && indexedTarget.anchorId !== beforeAnchorId) {
1439
- setCurrent(indexedTarget.anchorId, "adapter", indexedTarget.order - 1);
1440
- return scrollToAnchor(indexedTarget.anchorId, {
1441
- behavior: "auto",
1442
- block: "start",
1443
- indexHint: indexedTarget.order - 1
1444
- });
1445
- }
1446
-
1447
- for (let attempt = 0; attempt < 5; attempt += 1) {
1448
- const mounted = anchors().sort((a, b) => a.order - b.order);
1449
- const adjacent = delta < 0
1450
- ? [...mounted].reverse().find((item) => item.order < currentOrder && item.anchorId !== beforeAnchorId)
1451
- : mounted.find((item) => item.order > currentOrder && item.anchorId !== beforeAnchorId);
1452
- if (adjacent) {
1453
- setCurrent(adjacent.anchorId, "adapter", adjacent.order - 1);
1454
- return scrollToAnchor(adjacent.anchorId, {
1455
- behavior: "auto",
1456
- block: blockForAnchorVisibility(adjacent.anchorId, "start"),
1457
- indexHint: adjacent.order - 1
1458
- });
1459
- }
1460
- await delay(90);
1461
- }
1462
-
1463
- const searchRoot = () => findScrollContainer() || nearestScrollContainer(anchorElements()[0] || findAnchor(state.currentAnchorId) || document.body);
1464
- let root = searchRoot();
1465
- const viewport = Math.min(root.clientHeight || window.innerHeight || 720, 1200);
1466
- const step = delta < 0
1467
- ? Math.max(520, viewport * 1.45)
1468
- : Math.max(520, Math.min(viewport, 900) * 1.35);
1469
- const attempts = [];
1470
- for (let attempt = 0; attempt < 18; attempt += 1) {
1471
- root = searchRoot();
1472
- const beforeTop = scrollTopOf(root);
1473
- scrollRootTo(root, scrollTopForViewportDelta(root, delta < 0 ? -step : step));
1474
- await delay(80);
1475
- const mounted = anchors().sort((a, b) => a.order - b.order);
1476
- const target = delta < 0
1477
- ? [...mounted].reverse().find((item) =>
1478
- item.order < currentOrder && item.anchorId !== beforeAnchorId
1479
- )
1480
- : mounted.find((item) =>
1481
- item.order > currentOrder && item.anchorId !== beforeAnchorId
1482
- );
1483
- attempts.push({
1484
- attempt,
1485
- beforeTop: Math.round(beforeTop),
1486
- afterTop: Math.round(scrollTopOf(root)),
1487
- mounted: mounted.map((item) => item.order)
1488
- });
1489
- state.lastMountSearch = { mode: "relative-mounted-gap", direction: delta, currentOrder, attempts: attempts.slice(-5) };
1490
- if (target) {
1491
- setCurrent(target.anchorId, "adapter", target.order - 1);
1492
- return scrollToAnchor(target.anchorId, {
1493
- behavior: "auto",
1494
- block: "start",
1495
- indexHint: target.order - 1
1496
- });
1497
- }
1498
- if (Math.abs(scrollTopOf(root) - beforeTop) <= 1) break;
1499
- }
1500
- if (delta > 0) {
1501
- root = searchRoot();
1502
- scrollRootTo(root, scrollRootRange(root).max);
1503
- await delay(120);
1504
- const mounted = anchors().sort((a, b) => a.order - b.order);
1505
- const target = mounted.find((item) =>
1506
- item.order > currentOrder && item.anchorId !== beforeAnchorId
1507
- ) || mounted[mounted.length - 1];
1508
- if (target && target.anchorId !== beforeAnchorId && target.order > currentOrder) {
1509
- setCurrent(target.anchorId, "adapter", target.order - 1);
1510
- return scrollToAnchor(target.anchorId, {
1511
- behavior: "auto",
1512
- block: "start",
1513
- indexHint: target.order - 1
1514
- });
1515
- }
1516
- }
1517
- if (delta > 0 && currentOrder < lastKnownIndex() + 1) {
1518
- return jumpToBottom();
1519
- }
1520
- if (delta < 0 && currentOrder > 1) {
1521
- return jumpToIndex(0, { block: "start", direction: -1, behavior: "auto" });
1522
- }
1523
- return jumpRelativeMounted(delta);
1524
- }
1525
-
1526
- function renderedCurrentOrder() {
1527
- const desiredTop = topVisibleInset() + 8;
1528
- const visibleMarker = Array.from(document.querySelectorAll(`.${ANNOTATION_CLASS}`))
1529
- .map((marker) => {
1530
- const rect = marker.getBoundingClientRect();
1531
- return {
1532
- order: Number(marker.textContent.trim()),
1533
- top: rect.top,
1534
- bottom: rect.bottom
1535
- };
1536
- })
1537
- .filter((item) =>
1538
- Number.isSafeInteger(item.order) &&
1539
- item.bottom >= topVisibleInset() &&
1540
- item.top <= viewportSafeBottom()
1541
- )
1542
- .sort((a, b) => Math.abs(a.top - desiredTop) - Math.abs(b.top - desiredTop))[0];
1543
- if (visibleMarker) return visibleMarker.order;
1544
- const currentMarker = document.querySelector(`.${CURRENT_CLASS} .${ANNOTATION_CLASS}`);
1545
- const currentValue = Number(currentMarker?.textContent?.trim());
1546
- return Number.isSafeInteger(currentValue) ? currentValue : null;
1547
- }
1548
-
1549
1449
  function mountedFloorTargetForIndex(index) {
1550
1450
  const visible = viewportVisibleAnchors(anchors()).sort((a, b) => a.order - b.order);
1551
1451
  const visibleExact = visible.find((item) => item.order - 1 === index);
@@ -1791,59 +1691,56 @@
1791
1691
  });
1792
1692
  }
1793
1693
 
1794
- async function jumpRelative(delta) {
1795
- const visible = viewportVisibleAnchors(anchors());
1796
- const currentVisible = state.currentAnchorId ? isAnchorViewportVisible(state.currentAnchorId) : false;
1694
+ function jumpRelativeFast(delta) {
1695
+ if (!hasAppServerOrder()) return jumpRelativeMounted(delta);
1696
+ const startIndex = currentIndex();
1697
+ const targetIndex = startIndex + delta;
1698
+ const lastIndex = lastKnownIndex();
1699
+ if (targetIndex < 0 || targetIndex > lastIndex) {
1700
+ state.lastRelativeDebug = {
1701
+ delta,
1702
+ path: "edge",
1703
+ startIndex,
1704
+ targetIndex,
1705
+ lastIndex
1706
+ };
1707
+ return {
1708
+ ok: true,
1709
+ noop: true,
1710
+ edge: delta < 0 ? "top" : "bottom",
1711
+ anchorId: state.currentAnchorId
1712
+ };
1713
+ }
1714
+ const targetId = state.anchorOrder[targetIndex] || state.anchorIdByIndex[targetIndex] || null;
1797
1715
  const mounted = anchors().sort((a, b) => a.order - b.order);
1798
- const selectedIsMounted = Boolean(state.currentAnchorId && mounted.some((item) => item.anchorId === state.currentAnchorId));
1799
- if (!visible.length && !currentVisible) {
1800
- return jumpRelativeMounted(delta);
1801
- }
1802
- if (!currentVisible && visible.length && !selectedIsMounted) {
1803
- const target = delta < 0
1804
- ? visible.sort((a, b) => a.viewportTop - b.viewportTop)[0]
1805
- : visible.sort((a, b) => b.viewportBottom - a.viewportBottom)[0];
1806
- if (target) setCurrent(target.anchorId, "adapter", target.order - 1);
1807
- }
1808
- const renderedOrder = renderedCurrentOrder();
1809
- const selectedOrder = state.currentAnchorId ? anchorNumber(state.currentAnchorId) : null;
1810
- const currentAnchorOrder = selectedIsMounted ? selectedOrder : renderedOrder || selectedOrder;
1811
- const currentOrder = currentAnchorOrder != null && currentAnchorOrder <= state.anchorOrder.length
1812
- ? currentAnchorOrder
1813
- : Number.isInteger(state.currentIndexValue) ? state.currentIndexValue + 1 : null;
1814
- const adjacentMounted = delta < 0
1815
- ? [...mounted].reverse().find((item) => item.order < currentOrder && item.anchorId !== state.currentAnchorId)
1816
- : mounted.find((item) => item.order > currentOrder && item.anchorId !== state.currentAnchorId);
1716
+ const target = targetId
1717
+ ? mounted.find((item) => item.anchorId === targetId)
1718
+ : mounted.find((item) => item.order - 1 === targetIndex);
1817
1719
  state.lastRelativeDebug = {
1818
1720
  delta,
1819
- renderedOrder,
1820
- currentAnchorOrder,
1821
- currentOrder,
1822
- currentAnchorId: state.currentAnchorId,
1823
- selectedIsMounted,
1824
- adjacent: adjacentMounted ? { order: adjacentMounted.order, anchorId: adjacentMounted.anchorId } : null,
1721
+ path: "fast",
1722
+ startIndex,
1723
+ targetIndex,
1724
+ target: target ? { order: target.order, anchorId: target.anchorId } : null,
1825
1725
  mounted: mounted.map((item) => item.order)
1826
1726
  };
1827
- if (adjacentMounted) {
1828
- setCurrent(adjacentMounted.anchorId, "adapter", adjacentMounted.order - 1);
1829
- return scrollToAnchor(adjacentMounted.anchorId, {
1727
+ if (!target || !findAnchor(target.anchorId)) {
1728
+ return jumpToIndex(targetIndex, {
1729
+ block: "start",
1730
+ direction: delta,
1830
1731
  behavior: "auto",
1831
- block: blockForAnchorVisibility(adjacentMounted.anchorId, "start"),
1832
- indexHint: adjacentMounted.order - 1
1732
+ approximate: true,
1733
+ preferExact: true,
1734
+ resolveMissing: "floor-on-overshoot",
1735
+ skipScrollIfResolvedFloor: true
1833
1736
  });
1834
1737
  }
1835
- const beforeAnchorId = state.currentAnchorId;
1836
- if (delta < 0) {
1837
- return jumpRelativeMountedAcrossGap(delta, currentOrder, beforeAnchorId);
1838
- }
1839
- const beforeIndex = currentIndex();
1840
- const result = await jumpToIndex(beforeIndex + delta, { block: "start", direction: delta, behavior: "auto" });
1841
- const afterIndex = state.anchorOrder.indexOf(state.currentAnchorId);
1842
- const moved = result?.ok &&
1843
- state.currentAnchorId !== beforeAnchorId &&
1844
- (delta < 0 ? afterIndex < beforeIndex : afterIndex > beforeIndex);
1845
- if (moved) return result;
1846
- return jumpRelativeMountedAcrossGap(delta, currentOrder, beforeAnchorId);
1738
+ setCurrent(target.anchorId, "adapter", target.order - 1);
1739
+ return scrollToAnchor(target.anchorId, {
1740
+ behavior: "auto",
1741
+ block: "start",
1742
+ indexHint: target.order - 1
1743
+ });
1847
1744
  }
1848
1745
 
1849
1746
  function jumpToNumberIndex(requested) {
@@ -2173,14 +2070,14 @@
2173
2070
  return new Promise((resolve) => setTimeout(resolve, ms));
2174
2071
  }
2175
2072
 
2176
- function oneWhackRuntime() {
2073
+ function clankerbendRuntime() {
2177
2074
  if (window[RUNTIME_KEY]?.registerApp) return window[RUNTIME_KEY];
2178
2075
  const runtime = {
2179
2076
  protocolVersion: null,
2180
2077
  hostUrl: null,
2181
2078
  apps: {},
2182
2079
  registerApp(app) {
2183
- if (!app?.appId) throw new Error("OneWhack appId is required");
2080
+ if (!app?.appId) throw new Error("ClankerBend appId is required");
2184
2081
  const current = this.apps[app.appId] || {};
2185
2082
  const currentVersion = Number(current.bridge?.version);
2186
2083
  const nextVersion = Number(app.bridge?.version);
@@ -2191,7 +2088,7 @@
2191
2088
  Number.isFinite(nextVersion) &&
2192
2089
  nextVersion < currentVersion
2193
2090
  ) {
2194
- throw new Error("OneWhack app bridge version regressed for " + app.appId);
2091
+ throw new Error("ClankerBend app bridge version regressed for " + app.appId);
2195
2092
  }
2196
2093
  const slot = {
2197
2094
  ...current,
@@ -2332,7 +2229,7 @@
2332
2229
  function hideSelectionMenu() {
2333
2230
  const menu = document.getElementById(SELECTION_MENU_ID);
2334
2231
  menu?.remove();
2335
- document.querySelectorAll("[data-onewhack-native-selection-action='true']").forEach((node) => node.remove());
2232
+ document.querySelectorAll("[data-clankerbend-native-selection-action='true']").forEach((node) => node.remove());
2336
2233
  }
2337
2234
 
2338
2235
  function selectionPayloadForAction(action, selection) {
@@ -2419,7 +2316,7 @@
2419
2316
 
2420
2317
  function extendNativeSelectionToolbar(toolbar, target) {
2421
2318
  if (!(toolbar instanceof HTMLElement)) return;
2422
- toolbar.dataset.onewhackNativeSelectionToolbar = "true";
2319
+ toolbar.dataset.clankerbendNativeSelectionToolbar = "true";
2423
2320
  toolbar.style.setProperty("display", "inline-flex", "important");
2424
2321
  toolbar.style.setProperty("align-items", "center", "important");
2425
2322
  toolbar.style.setProperty("flex-wrap", "nowrap", "important");
@@ -2465,7 +2362,7 @@
2465
2362
  triggerSelectionAction(action, selection);
2466
2363
  };
2467
2364
  button.type = "button";
2468
- button.dataset.onewhackNativeSelectionAction = "true";
2365
+ button.dataset.clankerbendNativeSelectionAction = "true";
2469
2366
  button.textContent = nativeSelectionButtonLabel(action);
2470
2367
  button.title = action.label || action.type;
2471
2368
  button.style.cssText = [
@@ -2503,7 +2400,7 @@
2503
2400
  function injectNativeSelectionActions(selection, actions) {
2504
2401
  const toolbar = nativeSelectionToolbar();
2505
2402
  if (!toolbar) return false;
2506
- document.querySelectorAll("[data-onewhack-native-selection-action='true']").forEach((node) => node.remove());
2403
+ document.querySelectorAll("[data-clankerbend-native-selection-action='true']").forEach((node) => node.remove());
2507
2404
  const target = nativeSelectionInsertionTarget(toolbar);
2508
2405
  extendNativeSelectionToolbar(toolbar, target);
2509
2406
  const referenceButton = nativeSelectionReferenceButton(toolbar);
@@ -2525,7 +2422,7 @@
2525
2422
  const actions = hostSelectionActions();
2526
2423
  if (!selection || !actions.length) {
2527
2424
  menu?.remove();
2528
- document.querySelectorAll("[data-onewhack-native-selection-action='true']").forEach((node) => node.remove());
2425
+ document.querySelectorAll("[data-clankerbend-native-selection-action='true']").forEach((node) => node.remove());
2529
2426
  return;
2530
2427
  }
2531
2428
  if (injectNativeSelectionActions(selection, actions)) {
@@ -2563,8 +2460,8 @@
2563
2460
  }
2564
2461
 
2565
2462
  function overlayFieldValues(el) {
2566
- return Object.fromEntries([...el.querySelectorAll("[data-onewhack-field-id]")]
2567
- .map((input) => [input.dataset.onewhackFieldId, input.value || ""]));
2463
+ return Object.fromEntries([...el.querySelectorAll("[data-clankerbend-field-id]")]
2464
+ .map((input) => [input.dataset.clankerbendFieldId, input.value || ""]));
2568
2465
  }
2569
2466
 
2570
2467
  function positionOverlay(el, overlay) {
@@ -2613,13 +2510,13 @@
2613
2510
  return;
2614
2511
  }
2615
2512
  const currentValues = overlayFieldValues(el);
2616
- const activeFieldId = document.activeElement?.dataset?.onewhackFieldId || null;
2513
+ const activeFieldId = document.activeElement?.dataset?.clankerbendFieldId || null;
2617
2514
  const fields = (overlay.fields || []).map((field) => {
2618
2515
  const input = field.kind === "textarea" ? document.createElement("textarea") : document.createElement("input");
2619
2516
  input.name = field.fieldId;
2620
2517
  input.placeholder = field.label || field.fieldId;
2621
2518
  input.value = currentValues[field.fieldId] ?? field.value ?? "";
2622
- input.dataset.onewhackFieldId = field.fieldId;
2519
+ input.dataset.clankerbendFieldId = field.fieldId;
2623
2520
  return input;
2624
2521
  });
2625
2522
  const titleText = String(overlay.title || "").trim();
@@ -2627,14 +2524,22 @@
2627
2524
  const title = titleText && titleText !== firstFieldLabel ? document.createElement("strong") : null;
2628
2525
  if (title) title.textContent = titleText;
2629
2526
  const actions = document.createElement("div");
2630
- actions.className = "onewhack-overlay-actions";
2527
+ actions.className = "clankerbend-overlay-actions";
2631
2528
  for (const action of overlay.actions || []) {
2632
2529
  const button = document.createElement("button");
2633
2530
  button.type = "button";
2634
2531
  button.textContent = action.label || "Apply";
2532
+ button.className = action.type === "overlay.close" ? "clankerbend-overlay-secondary" : "clankerbend-overlay-primary";
2635
2533
  button.addEventListener("click", () => {
2636
- const values = Object.fromEntries([...el.querySelectorAll("[data-onewhack-field-id]")]
2637
- .map((input) => [input.dataset.onewhackFieldId, input.value || ""]));
2534
+ if (action.type === "overlay.close") {
2535
+ enqueueHostEvent({
2536
+ kind: "overlayClose",
2537
+ overlayId: overlay.overlayId
2538
+ });
2539
+ return;
2540
+ }
2541
+ const values = Object.fromEntries([...el.querySelectorAll("[data-clankerbend-field-id]")]
2542
+ .map((input) => [input.dataset.clankerbendFieldId, input.value || ""]));
2638
2543
  enqueueHostEvent({
2639
2544
  kind: "appAction",
2640
2545
  appId: overlay.appId,
@@ -2653,7 +2558,7 @@
2653
2558
  el.classList.add("is-visible");
2654
2559
  positionOverlay(el, overlay);
2655
2560
  const activeField = activeFieldId
2656
- ? [...el.querySelectorAll("[data-onewhack-field-id]")].find((field) => field.dataset.onewhackFieldId === activeFieldId)
2561
+ ? [...el.querySelectorAll("[data-clankerbend-field-id]")].find((field) => field.dataset.clankerbendFieldId === activeFieldId)
2657
2562
  : null;
2658
2563
  const firstField = fields.find((field) => field instanceof HTMLTextAreaElement || field instanceof HTMLInputElement);
2659
2564
  setTimeout(() => focusOverlayField(activeField || firstField), 0);
@@ -2738,7 +2643,7 @@
2738
2643
  }
2739
2644
  el.replaceChildren(...items.map((item) => {
2740
2645
  const chip = document.createElement("div");
2741
- chip.className = "onewhack-context-chip";
2646
+ chip.className = "clankerbend-context-chip";
2742
2647
  const label = document.createElement("span");
2743
2648
  label.textContent = item.label || item.body || item.itemId;
2744
2649
  chip.title = item.body || item.label || item.itemId;
@@ -2817,10 +2722,10 @@
2817
2722
  return true;
2818
2723
  }
2819
2724
 
2820
- function oneWhackSubmitPrompt(submission) {
2725
+ function clankerbendSubmitPrompt(submission) {
2821
2726
  const lines = [
2822
- `[[ONEWHACK_CONTEXT:${submission.submissionId}]]`,
2823
- "Use these OneWhack sticky notes as context. Do not mention the marker."
2727
+ `[[CLANKERBEND_CONTEXT:${submission.submissionId}]]`,
2728
+ "Use these ClankerBend sticky notes as context. Do not mention the marker."
2824
2729
  ];
2825
2730
  for (const [index, item] of submission.items.entries()) {
2826
2731
  const selected = item.range?.quote || item.range?.text || item.label || "";
@@ -2841,16 +2746,16 @@
2841
2746
  }
2842
2747
  }
2843
2748
  lines.push(
2844
- `[[/ONEWHACK_CONTEXT:${submission.submissionId}]]`,
2749
+ `[[/CLANKERBEND_CONTEXT:${submission.submissionId}]]`,
2845
2750
  "",
2846
- submission.userText || "Use the OneWhack context above."
2751
+ submission.userText || "Use the ClankerBend context above."
2847
2752
  );
2848
2753
  return lines.join("\n");
2849
2754
  }
2850
2755
 
2851
2756
  function submittedChipRow(submission) {
2852
2757
  const row = document.createElement("div");
2853
- row.className = "onewhack-submitted-context-row";
2758
+ row.className = "clankerbend-submitted-context-row";
2854
2759
  row.style.cssText = [
2855
2760
  "display:flex",
2856
2761
  "flex-wrap:wrap",
@@ -2859,7 +2764,7 @@
2859
2764
  ].join(";");
2860
2765
  for (const item of submission.items) {
2861
2766
  const chip = document.createElement("span");
2862
- chip.className = "onewhack-context-chip";
2767
+ chip.className = "clankerbend-context-chip";
2863
2768
  chip.textContent = item.label || item.body || item.itemId;
2864
2769
  chip.title = item.body || item.label || item.itemId;
2865
2770
  row.appendChild(chip);
@@ -2870,15 +2775,15 @@
2870
2775
  function maskSubmittedComposerContexts() {
2871
2776
  if (!state.submittedComposerSubmissions.length) return;
2872
2777
  for (const submission of state.submittedComposerSubmissions) {
2873
- const marker = `[[ONEWHACK_CONTEXT:${submission.submissionId}]]`;
2778
+ const marker = `[[CLANKERBEND_CONTEXT:${submission.submissionId}]]`;
2874
2779
  const candidates = Array.from(document.querySelectorAll("main *, [role='main'] *, body *"))
2875
2780
  .filter((el) => el instanceof HTMLElement && !isHostUiElement(el))
2876
2781
  .filter((el) => (el.innerText || el.textContent || "").includes(marker))
2877
2782
  .filter((el) => !Array.from(el.children || []).some((child) => (child.innerText || child.textContent || "").includes(marker)))
2878
2783
  .sort((a, b) => (a.textContent || "").length - (b.textContent || "").length);
2879
2784
  const target = candidates[0];
2880
- if (!target || target.dataset.onewhackMaskedSubmission === submission.submissionId) continue;
2881
- target.dataset.onewhackMaskedSubmission = submission.submissionId;
2785
+ if (!target || target.dataset.clankerbendMaskedSubmission === submission.submissionId) continue;
2786
+ target.dataset.clankerbendMaskedSubmission = submission.submissionId;
2882
2787
  const prompt = document.createElement("span");
2883
2788
  prompt.textContent = submission.userText || "";
2884
2789
  target.replaceChildren(submittedChipRow(submission), prompt);
@@ -2904,7 +2809,7 @@
2904
2809
  items: items.map((item) => ({ ...item })),
2905
2810
  submittedAt: Date.now()
2906
2811
  };
2907
- const nextText = oneWhackSubmitPrompt(submission);
2812
+ const nextText = clankerbendSubmitPrompt(submission);
2908
2813
  if (!setComposerText(input, nextText)) return false;
2909
2814
  state.pendingComposerSubmission = submission;
2910
2815
  state.submittedComposerSubmissions = [...state.submittedComposerSubmissions, submission].slice(-8);
@@ -2941,22 +2846,22 @@
2941
2846
  }
2942
2847
 
2943
2848
  function installComposerSubmitInterceptor() {
2944
- if (window.__oneWhackComposerSubmitInterceptorInstalled) return;
2945
- window.__oneWhackComposerSubmitInterceptorInstalled = true;
2946
- window.__oneWhackComposerSubmitClickHandler = (event) => {
2849
+ if (window.__clankerbendComposerSubmitInterceptorInstalled) return;
2850
+ window.__clankerbendComposerSubmitInterceptorInstalled = true;
2851
+ window.__clankerbendComposerSubmitClickHandler = (event) => {
2947
2852
  if (isComposerSubmitButton(event.target)) prepareComposerContextForSubmit();
2948
2853
  };
2949
- window.__oneWhackComposerSubmitKeyHandler = (event) => {
2854
+ window.__clankerbendComposerSubmitKeyHandler = (event) => {
2950
2855
  if (event.key !== "Enter" || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
2951
2856
  const input = composerInputElement();
2952
2857
  if (input && (event.target === input || input.contains?.(event.target))) prepareComposerContextForSubmit();
2953
2858
  };
2954
- document.addEventListener("click", window.__oneWhackComposerSubmitClickHandler, true);
2955
- document.addEventListener("keydown", window.__oneWhackComposerSubmitKeyHandler, true);
2859
+ document.addEventListener("click", window.__clankerbendComposerSubmitClickHandler, true);
2860
+ document.addEventListener("keydown", window.__clankerbendComposerSubmitKeyHandler, true);
2956
2861
  }
2957
2862
 
2958
- function oneWhackAppUrl() {
2959
- return String(oneWhackRuntime().getEntryUrl(VIM_NAV_APP_ID) || "").trim();
2863
+ function clankerbendAppUrl() {
2864
+ return String(clankerbendRuntime().getEntryUrl(VIM_NAV_APP_ID) || "").trim();
2960
2865
  }
2961
2866
 
2962
2867
  async function ensureNativeBrowserPanel() {
@@ -3010,8 +2915,8 @@
3010
2915
  }
3011
2916
 
3012
2917
  async function doOpenPanel() {
3013
- const serverUrl = oneWhackAppUrl().replace(/\/$/, "");
3014
- if (!serverUrl) return { ok: false, error: "OneWhack host URL missing" };
2918
+ const serverUrl = clankerbendAppUrl().replace(/\/$/, "");
2919
+ if (!serverUrl) return { ok: false, error: "ClankerBend host URL missing" };
3015
2920
  const panelUrl = `${serverUrl}/`;
3016
2921
  const webviews = () => Array.from(document.querySelectorAll("webview, iframe"));
3017
2922
  const normalizedSrc = (el) => String(el.getAttribute("src") || "")
@@ -3076,7 +2981,7 @@
3076
2981
  : null;
3077
2982
  };
3078
2983
  const installPanelIframe = async () => {
3079
- const existing = document.getElementById("onewhack-vim-nav-side-panel-frame");
2984
+ const existing = document.getElementById("clankerbend-vim-nav-side-panel-frame");
3080
2985
  if (existing) {
3081
2986
  existing.setAttribute("src", panelUrl);
3082
2987
  await delay(300);
@@ -3109,10 +3014,10 @@
3109
3014
  if (!input && !/new tab|start browsing|enter a url|browser/i.test(panelText)) return null;
3110
3015
  if (getComputedStyle(panel).position === "static") panel.style.position = "relative";
3111
3016
  const frame = document.createElement("iframe");
3112
- frame.id = "onewhack-vim-nav-side-panel-frame";
3017
+ frame.id = "clankerbend-vim-nav-side-panel-frame";
3113
3018
  frame.src = panelUrl;
3114
- frame.title = "Vim Nav";
3115
- frame.setAttribute("data-onewhack-panel-frame", VIM_NAV_APP_ID);
3019
+ frame.title = "VimNav";
3020
+ frame.setAttribute("data-clankerbend-panel-frame", VIM_NAV_APP_ID);
3116
3021
  frame.style.cssText = [
3117
3022
  "position:absolute",
3118
3023
  "left:0",
@@ -3154,12 +3059,12 @@
3154
3059
  if (!browserUrlInput() && !localServerButton()) {
3155
3060
  return { ok: false, error: "native Browser panel controls not found", debug: panelDebug({ stage: "no-controls" }) };
3156
3061
  }
3157
- return { ok: false, error: "native Browser panel did not navigate to Vim Nav", debug: panelDebug({ stage: "not-navigated" }) };
3062
+ return { ok: false, error: "native Browser panel did not navigate to VimNav", debug: panelDebug({ stage: "not-navigated" }) };
3158
3063
  }
3159
3064
 
3160
3065
  function shouldIgnoreKey(event) {
3066
+ if (effectiveCommandOption(event)) return false;
3161
3067
  if (event.metaKey) return true;
3162
- if (effectiveCtrlAlt(event)) return false;
3163
3068
  if (state.vimMode) return false;
3164
3069
  if (event.defaultPrevented) return true;
3165
3070
  if (event.ctrlKey || event.altKey) return true;
@@ -3170,25 +3075,25 @@
3170
3075
  }
3171
3076
 
3172
3077
  function isModifierOnlyKey(event) {
3173
- return event.key === "Control" ||
3078
+ return event.key === "Meta" ||
3174
3079
  event.key === "Alt" ||
3175
- event.code === "ControlLeft" ||
3176
- event.code === "ControlRight" ||
3080
+ event.code === "MetaLeft" ||
3081
+ event.code === "MetaRight" ||
3177
3082
  event.code === "AltLeft" ||
3178
3083
  event.code === "AltRight";
3179
3084
  }
3180
3085
 
3181
3086
  function updateModifierState(event, isDown) {
3182
- if (event.key === "Control" || event.code === "ControlLeft" || event.code === "ControlRight") {
3183
- state.ctrlDown = Boolean(isDown);
3087
+ if (event.key === "Meta" || event.code === "MetaLeft" || event.code === "MetaRight") {
3088
+ state.metaDown = Boolean(isDown);
3184
3089
  }
3185
3090
  if (event.key === "Alt" || event.code === "AltLeft" || event.code === "AltRight") {
3186
3091
  state.altDown = Boolean(isDown);
3187
3092
  }
3188
3093
  }
3189
3094
 
3190
- function effectiveCtrlAlt(event) {
3191
- return Boolean((event.ctrlKey && event.altKey) || (state.ctrlDown && state.altDown));
3095
+ function effectiveCommandOption(event) {
3096
+ return Boolean((event.metaKey && event.altKey) || (state.metaDown && state.altDown));
3192
3097
  }
3193
3098
 
3194
3099
  function digitFromKeyEvent(event) {
@@ -3228,12 +3133,44 @@
3228
3133
  return null;
3229
3134
  });
3230
3135
  };
3231
- if (effectiveCtrlAlt(event)) {
3136
+ const runRelativeCommand = (name, delta, commandForDelta) => {
3137
+ if (state.relativeMoveInFlight) {
3138
+ state.pendingRelativeDelta = Math.max(-1, Math.min(1, state.pendingRelativeDelta + Math.sign(delta)));
3139
+ state.lastCommandResult = { name, pending: true, pendingRelativeDelta: state.pendingRelativeDelta };
3140
+ ensureModeBadge();
3141
+ return;
3142
+ }
3143
+ const prefixBefore = state.countPrefix;
3144
+ state.relativeMoveInFlight = true;
3145
+ state.commandChain = state.commandChain
3146
+ .catch(() => {})
3147
+ .then(async () => {
3148
+ let step = Math.sign(delta);
3149
+ let result = null;
3150
+ try {
3151
+ while (step) {
3152
+ result = await Promise.resolve().then(() => commandForDelta(step));
3153
+ state.lastCommandResult = { name, prefixBefore, result };
3154
+ ensureModeBadge();
3155
+ step = state.pendingRelativeDelta;
3156
+ state.pendingRelativeDelta = 0;
3157
+ }
3158
+ return result;
3159
+ } catch (err) {
3160
+ state.lastCommandResult = { name, prefixBefore, ok: false, error: err?.message || String(err) };
3161
+ ensureModeBadge();
3162
+ return null;
3163
+ } finally {
3164
+ state.relativeMoveInFlight = false;
3165
+ }
3166
+ });
3167
+ };
3168
+ if (effectiveCommandOption(event)) {
3232
3169
  if (isModifierOnlyKey(event)) {
3233
3170
  event.preventDefault();
3234
3171
  event.stopImmediatePropagation();
3235
- if (!event.repeat && !state.ctrlAltToggleDown) {
3236
- state.ctrlAltToggleDown = true;
3172
+ if (!event.repeat && !state.metaAltToggleDown) {
3173
+ state.metaAltToggleDown = true;
3237
3174
  setVimMode(!state.vimMode);
3238
3175
  }
3239
3176
  return;
@@ -3249,35 +3186,35 @@
3249
3186
  if (code === "KeyJ" || key === "j" || key === "ArrowDown") {
3250
3187
  event.preventDefault();
3251
3188
  event.stopImmediatePropagation();
3252
- runCommand("ctrl-alt-j", () => withVisibleMountedIndex(() => hasAppServerOrder() ? jumpRelative(1) : jumpRelativeMounted(1)));
3189
+ runRelativeCommand("cmd-option-j", 1, (step) => withVisibleMountedIndex(() => jumpRelativeFast(step)));
3253
3190
  } else if (code === "KeyK" || key === "k" || key === "ArrowUp") {
3254
3191
  event.preventDefault();
3255
3192
  event.stopImmediatePropagation();
3256
- runCommand("ctrl-alt-k", () => withVisibleMountedIndex(() => hasAppServerOrder() ? jumpRelative(-1) : jumpRelativeMounted(-1)));
3193
+ runRelativeCommand("cmd-option-k", -1, (step) => withVisibleMountedIndex(() => jumpRelativeFast(step)));
3257
3194
  } else if ((code === "KeyG" && event.shiftKey) || key === "G" || key === "End") {
3258
3195
  event.preventDefault();
3259
3196
  event.stopImmediatePropagation();
3260
- runCommand("ctrl-alt-G", () => afterIndexing(() => jumpToBottom()));
3197
+ runCommand("cmd-option-G", () => afterIndexing(() => jumpToBottom()));
3261
3198
  } else if (code === "KeyG" || key === "g" || key === "Home") {
3262
3199
  event.preventDefault();
3263
3200
  event.stopImmediatePropagation();
3264
- runCommand("ctrl-alt-g", () => afterIndexing(() => jumpToIndex(0, { block: "start", behavior: "auto" })));
3201
+ runCommand("cmd-option-g", () => afterIndexing(() => jumpToIndex(0, { block: "start", behavior: "auto" })));
3265
3202
  } else if (key === "[" || (code === "BracketLeft" && !event.shiftKey)) {
3266
3203
  event.preventDefault();
3267
3204
  event.stopImmediatePropagation();
3268
- runCommand("ctrl-alt-[", () => alignCurrent("start"));
3205
+ runCommand("cmd-option-[", () => alignCurrent("start"));
3269
3206
  } else if (key === "]" || (code === "BracketRight" && !event.shiftKey)) {
3270
3207
  event.preventDefault();
3271
3208
  event.stopImmediatePropagation();
3272
- runCommand("ctrl-alt-]", () => alignCurrent("end"));
3209
+ runCommand("cmd-option-]", () => alignCurrent("end"));
3273
3210
  } else if (key === "{" || (code === "BracketLeft" && event.shiftKey) || key === "PageUp") {
3274
3211
  event.preventDefault();
3275
3212
  event.stopImmediatePropagation();
3276
- runCommand("ctrl-alt-{", () => jumpRole("user", -1));
3213
+ runCommand("cmd-option-{", () => jumpRole("user", -1));
3277
3214
  } else if (key === "}" || (code === "BracketRight" && event.shiftKey) || key === "PageDown") {
3278
3215
  event.preventDefault();
3279
3216
  event.stopImmediatePropagation();
3280
- runCommand("ctrl-alt-}", () => jumpRole("user", 1));
3217
+ runCommand("cmd-option-}", () => jumpRole("user", 1));
3281
3218
  } else if (key === "?" || (code === "Slash" && event.shiftKey)) {
3282
3219
  event.preventDefault();
3283
3220
  event.stopImmediatePropagation();
@@ -3349,12 +3286,12 @@
3349
3286
  event.preventDefault();
3350
3287
  state.countPrefix = "";
3351
3288
  ensureModeBadge();
3352
- runCommand("j", () => withVisibleMountedIndex(() => hasAppServerOrder() ? jumpRelative(1) : jumpRelativeMounted(1)));
3289
+ runRelativeCommand("j", 1, (step) => withVisibleMountedIndex(() => jumpRelativeFast(step)));
3353
3290
  } else if (code === "KeyK" || key === "k") {
3354
3291
  event.preventDefault();
3355
3292
  state.countPrefix = "";
3356
3293
  ensureModeBadge();
3357
- runCommand("k", () => withVisibleMountedIndex(() => hasAppServerOrder() ? jumpRelative(-1) : jumpRelativeMounted(-1)));
3294
+ runRelativeCommand("k", -1, (step) => withVisibleMountedIndex(() => jumpRelativeFast(step)));
3358
3295
  } else if ((code === "KeyG" && event.shiftKey) || key === "G") {
3359
3296
  event.preventDefault();
3360
3297
  const prefix = state.countPrefix;
@@ -3391,10 +3328,10 @@
3391
3328
 
3392
3329
  function handleKeyUp(event) {
3393
3330
  updateModifierState(event, false);
3394
- if ((event.key === "Control" || event.key === "Alt" || !event.ctrlKey || !event.altKey) &&
3395
- state.ctrlAltToggleDown &&
3396
- !effectiveCtrlAlt(event)) {
3397
- state.ctrlAltToggleDown = false;
3331
+ if ((event.key === "Meta" || event.key === "Alt" || !event.metaKey || !event.altKey) &&
3332
+ state.metaAltToggleDown &&
3333
+ !effectiveCommandOption(event)) {
3334
+ state.metaAltToggleDown = false;
3398
3335
  }
3399
3336
  }
3400
3337
 
@@ -3409,9 +3346,9 @@
3409
3346
  }
3410
3347
 
3411
3348
  function resetModifierState() {
3412
- state.ctrlDown = false;
3349
+ state.metaDown = false;
3413
3350
  state.altDown = false;
3414
- state.ctrlAltToggleDown = false;
3351
+ state.metaAltToggleDown = false;
3415
3352
  }
3416
3353
 
3417
3354
  function snapshot() {
@@ -3464,10 +3401,10 @@
3464
3401
  document.removeEventListener("keydown", window.__codexVimNavKeyHandler, true);
3465
3402
  window.removeEventListener("keyup", window.__codexVimNavKeyUpHandler, true);
3466
3403
  document.removeEventListener("keyup", window.__codexVimNavKeyUpHandler, true);
3467
- document.removeEventListener("mouseup", window.__oneWhackTextSelectionHandler, true);
3404
+ document.removeEventListener("mouseup", window.__clankerbendTextSelectionHandler, true);
3468
3405
  window.__codexVimNavKeyHandler = handleKey;
3469
3406
  window.__codexVimNavKeyUpHandler = handleKeyUp;
3470
- window.__oneWhackTextSelectionHandler = handlePossibleTextSelection;
3407
+ window.__clankerbendTextSelectionHandler = handlePossibleTextSelection;
3471
3408
  window.addEventListener("keydown", handleKey, true);
3472
3409
  document.addEventListener("keydown", handleKey, true);
3473
3410
  window.addEventListener("keyup", handleKeyUp, true);
@@ -3489,14 +3426,14 @@
3489
3426
  window.removeEventListener("keyup", handleKeyUp, true);
3490
3427
  document.removeEventListener("keyup", handleKeyUp, true);
3491
3428
  document.removeEventListener("mouseup", handlePossibleTextSelection, true);
3492
- document.removeEventListener("click", window.__oneWhackComposerSubmitClickHandler, true);
3493
- document.removeEventListener("keydown", window.__oneWhackComposerSubmitKeyHandler, true);
3429
+ document.removeEventListener("click", window.__clankerbendComposerSubmitClickHandler, true);
3430
+ document.removeEventListener("keydown", window.__clankerbendComposerSubmitKeyHandler, true);
3494
3431
  window.removeEventListener("resize", resizeHandler);
3495
3432
  window.removeEventListener("blur", blurHandler);
3496
3433
  mutationObserver.disconnect();
3497
3434
  if (annotationRefreshTimer) clearTimeout(annotationRefreshTimer);
3498
3435
  annotationRefreshTimer = null;
3499
- window.__oneWhackComposerSubmitInterceptorInstalled = false;
3436
+ window.__clankerbendComposerSubmitInterceptorInstalled = false;
3500
3437
  };
3501
3438
 
3502
3439
  const bridge = {
@@ -3508,18 +3445,18 @@
3508
3445
  openPanel,
3509
3446
  scrollToAnchor,
3510
3447
  highlightAnchor,
3511
- highlightRange: (range, options) => oneWhackRuntime().highlightRange
3512
- ? oneWhackRuntime().highlightRange(range, options)
3448
+ highlightRange: (range, options) => clankerbendRuntime().highlightRange
3449
+ ? clankerbendRuntime().highlightRange(range, options)
3513
3450
  : highlightAnchor(range?.anchorId, options),
3514
3451
  setAnnotations,
3515
3452
  setTranscriptOrder,
3516
3453
  primeAnchorOrder,
3517
3454
  jumpToIndex,
3518
- jumpRelative,
3455
+ jumpRelativeFast,
3519
3456
  __testUpdateTextSelectionFromDom: updateTextSelectionFromDom
3520
3457
  };
3521
3458
 
3522
- oneWhackRuntime().registerApp({
3459
+ clankerbendRuntime().registerApp({
3523
3460
  appId: VIM_NAV_APP_ID,
3524
3461
  capabilities: {
3525
3462
  transcriptRead: true,