docs: design proposal for more wt switch picker shortcuts (built-in + custom template-bound)#3209
docs: design proposal for more wt switch picker shortcuts (built-in + custom template-bound)#3209max-sixty wants to merge 4 commits into
Conversation
Proposal-only (no production code) covering two questions about the interactive picker: which built-in shortcuts to add, and whether to let users bind custom keys to row-expanded templates. Recommends user-config-only custom shortcuts under [switch.picker.shortcuts], executed via the existing alt-r reload machinery, and a small built-in set (reload, copy branch, open URL). Lives in design/ so it stays outside the docs/content sync test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
worktrunk-bot
left a comment
There was a problem hiding this comment.
Reviewed mainly for citation accuracy, since this is a code-grounded proposal — every anchor I spot-checked is correct (bind block at mod.rs:844, alt-r:reload(remove {}) at :934, ValidationScope's Hook/SwitchExecute/Alias, SwitchPickerConfig's pager, expand_template(..., ShellEscapeMode::Posix, ...), ACTIVE_VARS/REPO_VARS membership incl. pr_number/pr_url, ListItem.head/short_sha/branch, SemiStrict, multi(false), sanitize_for_filename, and PR #3199 merged with the cited reposition behavior). One design-completeness note inline on Key validation.
| (`enter`, `alt-c`, `alt-r`, `alt-p`, `alt-1`…`alt-7`, `tab`, `shift-tab`, | ||
| `ctrl-u`, `ctrl-d`) or with a bare digit (which must reach the query). A | ||
| collision is a config error naming the conflict, not a last-writer-wins | ||
| surprise. |
There was a problem hiding this comment.
Key validation here covers collisions and bare digits, but — unlike the careful typo story you build for template variables (a {{ brnach }} typo becomes a config error listing the valid names) — it doesn't address validating the key name itself. A typo'd or unrecognized skim key (clt-y, meta-q) passes the collision check and is only discovered when the assembled format!("{key}:reload(...)") bind reaches skim's parser. Worth deciding whether load-time validation should also reject keys that aren't recognized skim bind names, so the failure surfaces as a config error at the same gate as the variable-typo case rather than at picker launch. The asymmetry (validated variables, unvalidated keys) is the thing to resolve, not necessarily the exact failure mode.
Reload, copy, and open move to alt-l / alt-y / alt-o so all discrete actions share the alt+ family (alt-c/alt-r/alt-p/alt-1..7). Enter (accept), tab cycling, and ctrl-u/d preview scroll stay as navigation conventions. Adds a "Key convention" section stating the split. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
worktrunk-bot
left a comment
There was a problem hiding this comment.
Since this proposal was last revised, the picker's keybindings were reworked on main (the branch is now rebased onto it), and that rework already ships Part 1's three built-ins — with different keys than this revision proposes. In src/commands/picker/mod.rs, the .bind(vec![...]) block now has "alt-r:reload(refresh)" (the reload built-in, on alt-r), "alt-l:ignore" (alt-l suppresses skim's hscroll), and alt-x for removal via install_remove_keybinding (reload(remove {})). Copy and open are live too: install_shortcut_keybindings binds alt-y→os::copy_to_clipboard and alt-o→os::open_url, backed by the new src/commands/picker/os.rs (arboard + the open crate).
So a few of the doc's load-bearing claims have inverted against the shipped code:
- The "Key convention" reasons that reload should be
alt-l"becausealt-ris taken (remove)" — but onmainalt-ris the reload, removal moved toalt-x, andalt-lis now taken (ignore). The proposedalt-lreload would collide. - "The cost: worktrunk has no clipboard or open abstraction" no longer holds —
os::copy_to_clipboard/os::open_urlare exactly that helper, already cross-platform.
The doc is also now internally split on alt-r: the Key convention and the alt-r:reload(remove {}) discussion treat it as remove, while the appendix's implementation map already lists "alt-r reload". Inline notes on the two spots. (Custom shortcuts in Part 2 still read as net-new — this is specifically about Part 1.) Flagging as a comment since design/ is review-only; no verdict needed.
| |--------|-----|-------| | ||
| | Copy branch name | `alt-y` | "yank" | | ||
| | Open PR/MR URL | `alt-o` | no-op when the row has no URL | | ||
| | Reload list | `alt-l` | "reload"; `alt-r` is taken (remove) | |
There was a problem hiding this comment.
On current main this is inverted: the .bind(vec![...]) block in src/commands/picker/mod.rs now has "alt-r:reload(refresh)" — reload is already a shipped built-in on alt-r — while removal moved to alt-x (install_remove_keybinding). And alt-l isn't free: it's bound to "alt-l:ignore" to suppress skim's hscroll. So alt-l for reload would collide, and the "alt-r is taken (remove)" premise no longer matches the code. Worth reconciling the whole Key-convention table against the reworked binds (copy alt-y / open alt-o do still match).
| | Open PR/MR URL | `alt-o` | no-op when the row has no URL | | ||
| | Reload list | `alt-l` | "reload"; `alt-r` is taken (remove) | | ||
|
|
||
| ### The cost: worktrunk has no clipboard or open abstraction |
There was a problem hiding this comment.
This is no longer true — main now has src/commands/picker/os.rs with copy_to_clipboard (arboard) and open_url (the open crate), wired up by install_shortcut_keybindings (alt-y→copy, alt-o→open). The "build a ~30-line cross-platform helper" cost in this section has effectively already been paid; the section reads as describing a pre-rework state.
Design proposal only — no production code. The full document is
design/picker-shortcuts.md; this summarizes the recommendation and the decisions a reviewer needs to weigh.What it answers
Two questions about the
wt switchinteractive picker: (1) which built-in keyboard shortcuts are worth adding, and (2) whether to let users bind a key to a template that expands against the selected row, the way[aliases]and hooks already do.Recommendations
Built-in shortcuts.
ctrl-rto reload the worktree list (pure worktrunk, no OS dependency, and it exercises the samereload-verb plumbing custom shortcuts reuse).alt-y(copy branch) andctrl-o(open the row's PR/MR URL) once a small cross-platform clipboard/open helper exists — worktrunk has none today, and that helper is the real cost, not the binds.Custom shortcuts. Worth building, tightly scoped: user-config only, under
[switch.picker.shortcuts], one key to one template, expanded against the selected row and run in the background while the picker stays open. They fill a gap[aliases]can't: side-effects on a worktree you haven't switched to (copy, open, kick a build), since aliases always run against the current worktree.Execution. Reuse the
alt-rmachinery — a newrunverb on the existingPickerCollector, expanding viaexpand_template(..., Posix, ...)and spawning on a background thread, with PR #3199's cursor reposition. Anexecute-silentplus a hiddenwthelper is the lighter-touch alternative; the doc weighs both.Decisions a reviewer should weigh
[switch.picker],worktree-path,[list]custom-columns are all user-only). Because the picker never loads project shortcuts, the "Project Commands Run Only After Approval" invariant holds structurally.reloadround-trip and a brief cursor flash per key-press; (B) avoids the reload but pays a subprocess per press and token-only context. (A) is recommended; the cursor flash should be confirmed on a real picker before committing. This is the one call most likely to flip.ctrl-r) rather than being guarded, consistent with how[aliases]runs user commands unguarded.Source grounding
The doc cites the picker bind block,
PickerCollector, the approval model (HookSource,approve_readonly), the template engine (expand_template,validate_template,ValidationScope), the row model, andSwitchPickerConfig, with an implementation map in the appendix. The technical claims and the design reasoning were each run through an adversarial review pass against the source; corrections from it are folded in (notably reframing project-config shortcuts as feasible-but-inert rather than impossible).No code is changed, so there is nothing to test; the doc lives in
design/to stay outside thedocs/contentsync test.🤖 Generated with Claude Code