Skip to content

feat: add persistent per-app quick-jump letters - #183

Open
GeronimoCastano wants to merge 2 commits into
rokartur:mainfrom
GeronimoCastano:quick-jump-mappings
Open

GeronimoCastano wants to merge 2 commits into
rokartur:mainfrom
GeronimoCastano:quick-jump-mappings

Conversation

@GeronimoCastano

Copy link
Copy Markdown
Contributor

Fixes #181

What changed

  • Store one quick-jump letter per application bundle identifier.
  • Add an AppKit editor under Controls → Letter jump → Custom app mappings.
  • Reserve custom letters so the dynamic label generator cannot assign them to another app.
  • Give the first, most-recent window of a mapped app its stable letter. Other windows keep generated labels.
  • Let a visible custom mapping take priority over a same-key panel action. Modified actions using Option or Control remain available.
  • Add configuration-schema documentation, translations, and tests.

Behavior

These mappings apply only while the switcher is open. Direct Activation remains unchanged.

This first version keeps mappings global and limited to one ASCII letter per app. For example:

  • W selects WhatsApp
  • S selects Safari
  • V selects Visual Studio Code

Testing

  • QuickJumpMappingTests
  • RowLabelsTests
  • HotkeyTapVimNavigationTests
  • SettingsPortabilityTests
  • Localization and configuration-schema tests
  • Manual verification of the settings sheet and app picker

@GeronimoCastano
GeronimoCastano marked this pull request as ready for review August 20, 2026 11:37
@quintrino

Copy link
Copy Markdown

This might solve the feature that I want. I have system level shortcuts set up for a number of apps using Karabiner, so when I'm using bettercmdtab I want to be able to use the letter switching to select apps that aren't already covered by a system level shortcut.

Will This feature allow either

  • Assigning "nil" to a app so it doesn't get included in letter hints / Blocklisting apps just from letter hints
  • Setting multiple apps to a single letter (for example q) so that all the other letters get assigned to other apps
  • Excluding apps from letter hint selection?

My goal is to have every app selectable via a single letter.

For example

If I currently have Alfred and Alacritty both open, I have to type 2 letters to select an option, but if I can assign alacritty (and all the other system shortcut apps) either to block letter selection or to q or something similar, then I will hopefully only need to tap a single letter.

@GeronimoCastano

Copy link
Copy Markdown
Contributor Author

None of the three, sorry. A mapping takes exactly one letter, so nothing maps to nil, and saving the list keeps the first app that claims a letter and drops anything else claiming the same one. One app, one letter.

Your goal still works through a different route. Take your example with both apps open and neither mapped: both want a, they collide, and both fall back to two letters. Map Alacritty to q and it leaves the automatic pass, which gives Alfred a to itself as a single letter. Pinning one side of a collision fixes both sides. (But be careful since if you select q as your mapping then you won't be able to quit the app via BetterCmdTab using Cmd + Q.

One cost to know about. A mapped letter stays reserved even while that app is closed, and reserving a letter moves other apps to the next letter inside their own name instead of dropping them from the hints. Pin ten Karabiner apps and those ten letters leave the pool, which can push a new collision somewhere else.

Blocklisting an app from letter hints is a separate change from this PR.

The per-app letter editor could only assign a letter. An app already
reachable by a global shortcut (Karabiner, Direct Activation) still
took an in-panel jump letter, and a taken letter is reserved even
against the other apps that could use it — so two apps whose names
start with the same letter both fall back to two-letter hints.

The same editor now offers a per-row "Skip" toggle: a skipped app
drops out of hint generation entirely — no hint drawn, no letter
reserved — so its letter frees up for another app. Skipping one side
of a collision gives the other side a bare single letter. Assigning a
letter and skipping are the two answers to one per-app question, so
they share the sheet; an app is in one state or the other.

Stored under Switcher.letterHintExcludedBundleIDs (normalized, blanks
and duplicates dropped), mirrored into the label generator the same
lock-backed way as the custom letters, applied live to an open panel,
and exported / imported / config-file synced like every other key.
An explicit letter still wins over a skip in the label pass.
@GeronimoCastano

Copy link
Copy Markdown
Contributor Author

Pushed a second commit that extends this to skipping.

The letter editor could only assign a letter. But an app you already open with a global shortcut (Karabiner, a Direct Activation binding) still takes an in-panel letter, and a taken letter is reserved against the other apps too, so two apps whose names start with the same letter both drop to two-letter hints.

Each row in the same sheet now has a Skip toggle. A skipped app leaves hint generation entirely: no letter drawn, none reserved, so its letter frees up for another app. Skip one side of a collision and the other side gets a bare single letter. Assigning a letter and skipping are two answers to one per-app question, so they share the sheet; an app is in one state or the other, and an explicit letter still wins over a skip.

It stores under Switcher.letterHintExcludedBundleIDs, mirrors into the label generator the same way the letters do, applies live to an open panel, and rides export / import / config.json like the rest. Six tests cover the label behavior.

All the tests pass.

@quintrino, I'd be happy to split these into two different branches if you'd like to review them separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow persistent per-app mappings for in-panel quick-jump letters

2 participants