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.
- package/DEVELOPERS.md +6 -6
- package/README.md +17 -13
- package/apps/README.md +3 -3
- package/apps/sticky-notes/README.md +2 -2
- package/apps/sticky-notes/{onewhack.manifest.json → clankerbend.manifest.json} +1 -1
- package/apps/sticky-notes/package.json +3 -3
- package/apps/sticky-notes/public/app.js +8 -8
- package/apps/sticky-notes/src/sticky-notes-app.js +17 -21
- package/apps/vim-nav/README.md +22 -22
- package/apps/vim-nav/{onewhack.manifest.json → clankerbend.manifest.json} +4 -4
- package/apps/vim-nav/package.json +3 -3
- package/apps/vim-nav/public/app.js +10 -10
- package/apps/vim-nav/public/index.html +2 -2
- package/apps/vim-nav/server.mjs +2 -2
- package/apps/vim-nav/src/vim-nav-app.js +5 -5
- package/assets/clankerbend-banner.webp +0 -0
- package/assets/clankerbend-demo-thumbnail.webp +0 -0
- package/assets/clankerbend.svg +26 -0
- package/cli.mjs +17 -9
- package/docs/app-lifecycle.md +9 -9
- package/docs/app-manifest.md +4 -4
- package/docs/author-guide.md +5 -5
- package/docs/launcher-profiles.md +13 -10
- package/docs/protocol.md +234 -234
- package/host/README.md +4 -4
- package/host/src/app-registry.js +6 -6
- package/host/src/codex-desktop-cdp-adapter.js +62 -64
- package/host/src/codex-desktop-renderer-bridge.js +207 -270
- package/host/src/index.js +30 -30
- package/launch/profiles.mjs +13 -13
- package/launch/runtime-paths.mjs +6 -6
- package/package.json +12 -10
- package/scripts/release-npm.mjs +1 -2
- package/server.mjs +7 -7
- package/assets/onewhack.jpg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
(function installCodexVimNavigator() {
|
|
2
2
|
const VIM_NAV_APP_ID = "onewill.vim-nav";
|
|
3
|
-
const BRIDGE_VERSION =
|
|
4
|
-
const RUNTIME_KEY = "
|
|
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 = "
|
|
13
|
-
const SELECTION_MENU_ID = "
|
|
14
|
-
const OVERLAY_ID = "
|
|
15
|
-
const COMPOSER_CHIPS_ID = "
|
|
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 =
|
|
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
|
-
|
|
39
|
+
metaDown: false,
|
|
40
40
|
altDown: false,
|
|
41
|
-
|
|
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} .
|
|
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} .
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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} .
|
|
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} .
|
|
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} .
|
|
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 = "
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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.
|
|
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
|
-
? `
|
|
1322
|
-
: `
|
|
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
|
-
|
|
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
|
-
|
|
1333
|
-
document.querySelectorAll(`.${ANNOTATION_CLASS}[data-
|
|
1334
|
-
if (!ids.has(node.dataset.anchorId) && !node.closest(".
|
|
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
|
-
|
|
1795
|
-
|
|
1796
|
-
const
|
|
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
|
|
1799
|
-
|
|
1800
|
-
|
|
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
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
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 (
|
|
1828
|
-
|
|
1829
|
-
|
|
1727
|
+
if (!target || !findAnchor(target.anchorId)) {
|
|
1728
|
+
return jumpToIndex(targetIndex, {
|
|
1729
|
+
block: "start",
|
|
1730
|
+
direction: delta,
|
|
1830
1731
|
behavior: "auto",
|
|
1831
|
-
|
|
1832
|
-
|
|
1732
|
+
approximate: true,
|
|
1733
|
+
preferExact: true,
|
|
1734
|
+
resolveMissing: "floor-on-overshoot",
|
|
1735
|
+
skipScrollIfResolvedFloor: true
|
|
1833
1736
|
});
|
|
1834
1737
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
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
|
|
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("
|
|
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("
|
|
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-
|
|
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.
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
2567
|
-
.map((input) => [input.dataset.
|
|
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?.
|
|
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.
|
|
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 = "
|
|
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
|
-
|
|
2637
|
-
|
|
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-
|
|
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 = "
|
|
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
|
|
2725
|
+
function clankerbendSubmitPrompt(submission) {
|
|
2821
2726
|
const lines = [
|
|
2822
|
-
`[[
|
|
2823
|
-
"Use these
|
|
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
|
-
`[[/
|
|
2749
|
+
`[[/CLANKERBEND_CONTEXT:${submission.submissionId}]]`,
|
|
2845
2750
|
"",
|
|
2846
|
-
submission.userText || "Use the
|
|
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 = "
|
|
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 = "
|
|
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 = `[[
|
|
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.
|
|
2881
|
-
target.dataset.
|
|
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 =
|
|
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.
|
|
2945
|
-
window.
|
|
2946
|
-
window.
|
|
2849
|
+
if (window.__clankerbendComposerSubmitInterceptorInstalled) return;
|
|
2850
|
+
window.__clankerbendComposerSubmitInterceptorInstalled = true;
|
|
2851
|
+
window.__clankerbendComposerSubmitClickHandler = (event) => {
|
|
2947
2852
|
if (isComposerSubmitButton(event.target)) prepareComposerContextForSubmit();
|
|
2948
2853
|
};
|
|
2949
|
-
window.
|
|
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.
|
|
2955
|
-
document.addEventListener("keydown", window.
|
|
2859
|
+
document.addEventListener("click", window.__clankerbendComposerSubmitClickHandler, true);
|
|
2860
|
+
document.addEventListener("keydown", window.__clankerbendComposerSubmitKeyHandler, true);
|
|
2956
2861
|
}
|
|
2957
2862
|
|
|
2958
|
-
function
|
|
2959
|
-
return String(
|
|
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 =
|
|
3014
|
-
if (!serverUrl) return { ok: false, error: "
|
|
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("
|
|
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 = "
|
|
3017
|
+
frame.id = "clankerbend-vim-nav-side-panel-frame";
|
|
3113
3018
|
frame.src = panelUrl;
|
|
3114
|
-
frame.title = "
|
|
3115
|
-
frame.setAttribute("data-
|
|
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
|
|
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 === "
|
|
3078
|
+
return event.key === "Meta" ||
|
|
3174
3079
|
event.key === "Alt" ||
|
|
3175
|
-
event.code === "
|
|
3176
|
-
event.code === "
|
|
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 === "
|
|
3183
|
-
state.
|
|
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
|
|
3191
|
-
return Boolean((event.
|
|
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
|
-
|
|
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.
|
|
3236
|
-
state.
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
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 === "
|
|
3395
|
-
state.
|
|
3396
|
-
!
|
|
3397
|
-
state.
|
|
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.
|
|
3349
|
+
state.metaDown = false;
|
|
3413
3350
|
state.altDown = false;
|
|
3414
|
-
state.
|
|
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.
|
|
3404
|
+
document.removeEventListener("mouseup", window.__clankerbendTextSelectionHandler, true);
|
|
3468
3405
|
window.__codexVimNavKeyHandler = handleKey;
|
|
3469
3406
|
window.__codexVimNavKeyUpHandler = handleKeyUp;
|
|
3470
|
-
window.
|
|
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.
|
|
3493
|
-
document.removeEventListener("keydown", window.
|
|
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.
|
|
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) =>
|
|
3512
|
-
?
|
|
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
|
-
|
|
3455
|
+
jumpRelativeFast,
|
|
3519
3456
|
__testUpdateTextSelectionFromDom: updateTextSelectionFromDom
|
|
3520
3457
|
};
|
|
3521
3458
|
|
|
3522
|
-
|
|
3459
|
+
clankerbendRuntime().registerApp({
|
|
3523
3460
|
appId: VIM_NAV_APP_ID,
|
|
3524
3461
|
capabilities: {
|
|
3525
3462
|
transcriptRead: true,
|