Remap any shortcut in Settings → Keyboard; the macOS menu bar follows
A Keyboard section in the Settings window edits the shared keymap: rows by group with a search field, a chip per key (x removes), + to record, reset per row and Reset all. Recording takes the keydown before every other key in the window (Cmd+W records instead of closing), refuses reserved combos without preventDefault (Cmd+Q still quits), requires a chord or F-key for editor commands, and offers Replace when another command in an overlapping scope owns the key. Rows flag conflicts and plain character keys that would stop starting a type-to-filter query. Fixed keys are listed read-only, in the platform's spelling. Remaps are ordinary settings (only rows that differ from the defaults), so every window, the menu hints and the tooltips follow live — and the macOS menu bar: its accelerators are built from the keymap (only the combos Electrobun's parser expresses faithfully) and rebuilt on a remap. A main-window menu action fired from the editor or Settings window brings the main window forward first. Also: the "Enter / double-click on a file" setting under File list (do nothing / open local files in their default app), arrow keys in the Settings sidebar with focus on open and a hand-walked Tab, translations for all seven languages (the spec now checks every language labels every command), and a README shortcut table generated from the keymap. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+35
-7
@@ -131,10 +131,27 @@ main) and, on macOS, the app menu's "Settings…". Its RPC serves only
|
||||
`getSettings`/`saveSettings`/`pickEditorApp`/`showAbout`/`settingsClose`. Its
|
||||
page claims its own close-window key (`settingsClose`); a ⌘W it doesn't claim
|
||||
falls through to the app menu's Close Tab, and bun checks the Settings
|
||||
window's focus FIRST and closes it (see gotchas). Sidebar sections: Appearance,
|
||||
Transfers, Editor, File list (visible columns — the column-header right-click
|
||||
menu toggles the same setting), About (opens the native About box). The
|
||||
native title is `options.title`, retitled on a locale switch.
|
||||
window's focus FIRST and closes it (see gotchas). Sidebar sections (↑ / ↓ move
|
||||
between them, focus starts on the current one, Tab is walked by `cycleFocus`):
|
||||
Appearance, Transfers, Editor, File list (visible columns — the column-header
|
||||
right-click menu toggles the same setting — and "Enter / double-click on a
|
||||
file": nothing | open local files in their default app), Keyboard, About
|
||||
(opens the native About box). The native title is `options.title`, retitled on
|
||||
a locale switch.
|
||||
|
||||
**Keyboard** is the remap editor over the shared keymap: rows by group with a
|
||||
search field (label or key text), a chip per combo (× removes), **+** records
|
||||
(the next non-modifier keydown, taken before every other key in the window so
|
||||
⌘W records instead of closing; Esc cancels; a click elsewhere or a blur ends
|
||||
it), ↺ resets a row, Reset all (inline confirm). Reserved combos are refused
|
||||
without `preventDefault` (⌘Q still quits); editor-scope combos must be chords
|
||||
or F-keys (the editor's text area always has focus); a combo owned by an
|
||||
overlapping-scope command offers **Replace** (moves it). Rows flag conflicts
|
||||
and plain character keys on file-list rows (they no longer start a filter).
|
||||
Fixed keys are listed read-only below. Every edit is `ShortcutsService.rebind`
|
||||
→ `settings.keybindings` (only rows that differ from the defaults) → the
|
||||
ordinary save + broadcast, so menus, tooltips and the macOS menu bar follow
|
||||
live.
|
||||
|
||||
## Bun modules
|
||||
|
||||
@@ -185,9 +202,16 @@ native title is `options.title`, retitled on a locale switch.
|
||||
atomic tmp+rename, 0600 + win32 `icacls` ACL. `store/settings.ts` — defaults
|
||||
spread-merge on load, so NEW SETTINGS KEYS need no backend change.
|
||||
- `menu.ts` — macOS-only app menu, rebuilt on every connections change (the
|
||||
Connect submenu mirrors the store; "Settings…" carries ⌘,). ⌘W is intercepted
|
||||
by the menu, so bun routes `close-tab` to a focused Settings or editor window
|
||||
first. Labels go through
|
||||
Connect submenu mirrors the store), language switch and keyboard remap.
|
||||
**Accelerators come from the shared keymap** (`effectiveKeymap` +
|
||||
`toAccelerator`, the first combo the native parser can express faithfully:
|
||||
⌘/⌃ chords only — a bare key means ⌘+key to it — no ⌫ / ⌦ / Shift+punctuation);
|
||||
⌘Q stays fixed. The keybindings are read through a getter AFTER the
|
||||
connections load, so a remap saved meanwhile isn't lost. The page claims the
|
||||
keys it handles first, so the menu only runs what falls through; a
|
||||
`close-tab` that still arrives while a Settings or editor window is focused
|
||||
closes THAT window, and other main-targeted actions bring the main window
|
||||
forward first. Labels go through
|
||||
backend `i18n.t` and the menu is rebuilt on a locale change too.
|
||||
- `i18n.ts` — backend translator (native menus + thrown error messages, which
|
||||
no Angular service can reach). `setLocale(setting)` caches the resolved
|
||||
@@ -402,6 +426,10 @@ ends with `blockReloadKeys`. Keys that land on `<body>` (a click dropped focus)
|
||||
are forwarded to the active list (`PanelsService.forwardListKey`), and `App`'s
|
||||
`(mousedown)` keeps focus on the list after chrome-button clicks.
|
||||
|
||||
**Remaps** live in `AppSettings.keybindings` (sanitized on load), edited in
|
||||
Settings → Keyboard (see the Settings window). `ShortcutsService.rebind`
|
||||
stores only rows that differ from their defaults.
|
||||
|
||||
**macOS key equivalents are page-first.** WKWebView hands ⌘-keys to the page
|
||||
before the app menu (WebKit `WebViewImpl::performKeyEquivalent`); only keys the
|
||||
page doesn't `preventDefault` reach the menu bar. So the page owns every app
|
||||
|
||||
@@ -311,6 +311,14 @@ touching the adjacent code.
|
||||
path bar) hides keys from it — don't add one.
|
||||
- **Hold-to-repeat** fired ⌘W once per repeat — every tab closed. `claim()`
|
||||
repeats only movement-like commands (`isRepeatable`).
|
||||
- **Electrobun's native menu accelerator turns a combo with no modifier token
|
||||
into ⌘+key** ("f7" shows and fires ⌘F7), its `backspace` / `delete` tokens
|
||||
are characters AppKit matches unreliably, and AppKit matches Shift+punctuation
|
||||
by the shifted character ("}" for ⇧]). `toAccelerator` returns null for all of
|
||||
those; the menu item then shows no key and the page still handles it.
|
||||
- **Recording a key must take the keydown before every window handler** — and
|
||||
must NOT take reserved ones: preventDefault on ⌘Q while recording would make
|
||||
the app unquittable from the keyboard.
|
||||
- **Focus after a state change needs `afterNextRender({ injector })`.** Under
|
||||
zoneless CD `queueMicrotask` runs before the render that creates an `@if`
|
||||
element (the editor's find box and textarea), so `.focus()` hit nothing.
|
||||
|
||||
@@ -23,7 +23,7 @@ Every platform seam carries a `PORTABILITY:` comment at the branch site.
|
||||
| Tab focus | `core/focus-cycle.ts cycleFocus` | WKWebView skips buttons/checkboxes without Keyboard navigation → walked by hand | native order (same walk) | native order (same walk) |
|
||||
| Reload keys | `platform.ts blockReloadKeys` | — | WebView2 reloads on F5 / Ctrl+R → swallowed when unclaimed | — |
|
||||
| Tray | `src/bun/tray.ts` | 22 pt template PNG | 16 px colored PNG | colored (needs AppIndicator, see below) |
|
||||
| App menu | `src/bun/menu.ts` | full menu incl. About | none (early return) — Settings opens from the tab-bar gear (Ctrl+,), About from its About section | none (unsupported upstream) |
|
||||
| App menu | `src/bun/menu.ts` | full menu incl. About; accelerators follow the keymap (remaps included), ⌘Q fixed | none (early return) — Settings opens from the tab-bar gear (Ctrl+,), About from its About section | none (unsupported upstream) |
|
||||
| connections.json protection | `store/connections.ts` | chmod 0600 | `icacls` current-user ACL (0600 is meaningless) | 0600 |
|
||||
| App data dir | `store/paths.ts` | ~/Library/Application Support | %APPDATA% | $XDG_CONFIG_HOME/.config |
|
||||
| ssh-agent | `fs/sftp.ts` | $SSH_AUTH_SOCK | `\\.\pipe\openssh-ssh-agent` fallback | $SSH_AUTH_SOCK |
|
||||
|
||||
Reference in New Issue
Block a user