From 439d5b083b9a32f921f925b47ec442ca7ee46854 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Tue, 20 Oct 2026 15:42:12 +0200 Subject: [PATCH] board: F jumps to a card by letter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `F` on the board puts a letter or two over every card in view, as it does over links in the reader, and typing one puts the cursor on that card; Enter then opens its notes as before. Hints are pinned where their targets are when they appear, so `F` pressed while a pane was still sliding in — the board just opened, a space just switched to — put them where the targets were going to be. They now wait for the pane to settle. --- README.md | 1 + web/src/App.svelte | 25 +++++++++++++++++++++++-- web/src/lib/Help.svelte | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b90da9..19cbba7 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,7 @@ you were. On the board, `o` or `c` puts a card on the column under the cursor. | `D` | due date — `fri`, `tm`, `+3d`, `+2w`, `10-31`, `2026-10-31` | | `R` | rename the card, or the column when it has none | | `z` | show done cards older than a week | +| `F` | a letter over every card in view; typing it puts the cursor on that card | | `x`, `dd` | delete the card | | `q`, `Esc` | back to the tree | diff --git a/web/src/App.svelte b/web/src/App.svelte index 9701830..985766c 100644 --- a/web/src/App.svelte +++ b/web/src/App.svelte @@ -45,6 +45,7 @@ let importRoot = false let shell = $state() let overlayEl = $state() + let boardEl = $state() /** The first half of `gg`, `dd`, `]]` …, shown until the second comes. */ let pending = $state('') /** `F`: the pane whose links and buttons are wearing hints right now. */ @@ -390,6 +391,22 @@ } } + /* + * `F`: the hints are pinned where the targets are, so a pane still sliding + * in — a space switch, the board just opened — is let settle first. + */ + async function showHints(root: HTMLElement) { + // What moves the root may be an animation on a pane around it. A blinking + // cursor never finishes, so only what ends is waited for. + const settling = document.getAnimations().filter((animation) => { + const target = (animation.effect as KeyframeEffect | null)?.target + if (!target || animation.effect?.getComputedTiming().endTime === Infinity) return false + return target.contains(root) || root.contains(target) + }) + await Promise.all(settling.map((animation) => animation.finished.catch(() => {}))) + if (root.isConnected) hintRoot = root + } + /** `gx`: the file as the browser shows it, in a tab of its own. */ function openInTab() { const file = workspace.target?.file @@ -487,6 +504,10 @@ case 'z': workspace.showOldDone = !workspace.showOldDone return true + case 'F': + // A letter over every card in view: typing it puts the cursor there. + if (boardEl) void showHints(boardEl) + return true case ' ': void workspace.guard(() => workspace.toggleDone()) return true @@ -772,7 +793,7 @@ // on its other key. event.preventDefault() const root = reading ? hooks.hintRoot() : null - if (root) hintRoot = root + if (root) void showHints(root) break } case 'm': @@ -906,7 +927,7 @@ {#if workspace.view === 'board'}
-
+
{:else}