feat(app): shortcuts show your keyboard's keys, ⌘Å and ⌘¨ for Back and Forward on Norwegian
Summary
Back and Forward (⌘[ / ⌘]) are matched by the key's place (e.code), so on a layout where [ and ] need ⌥ they are ⌘ and the keys right of P. The UI never said so. Now every place that shows a shortcut's keys shows the key in that place on the layout in use: ⌘Å / ⌘¨ on Norwegian, ⌘Ü / ⌘+ on German, still ⌘[ / ⌘] on US. No key binding changes.
shortcuts.json back: keys "⌘[", code "BracketLeft" (code = matched by place)
lib/key-layout.ts layoutKeys("⌘[", "BracketLeft", {BracketLeft: "Å"}) → "⌘Å" (pure, tested)
lib/keyboard-layout.ts labels ← navigator.keyboard.getLayoutMap() (Chromium)
← invoke("key_layout") (Mac app: keys.rs, UCKeyTranslate on the input source in use)
← a bare key pressed in one of the places
asked again on focus, on layoutchange, and as a shortcut list opens
lib/shortcuts.ts keysOf / keysFor apply it → tooltip, sheet, Settings → Shortcuts, ⌘K, the ⌘⌥ menu hints
Unknown layout → a US keyboard's keys, as before. The ⌘⌥ letter shortcuts (⌘⌥D/C/S/I) go by place too, so they follow the layout as well (⌘⌥E on Dvorak).
Also:
- Shorter descriptions. Each shortcut's description is one short clause that fits its line in the sheet (17 were cut off with "…"). The details they held (mouse buttons, editor outdent, ↵/⌥↵/⌘↵ in Go to file, …) are in a new guide,
docs/guides/keyboard-shortcuts.mdx. Settings → Shortcuts also searches by the menu name and by the keys either way (⌘åand⌘[both find Back). - Tooltips on Back/Forward show on hover.
Tipwraps a disabled child in a span; when the arrow became enabled the trigger element changed and hover never opened it (keyboard focus did).Tipnow remounts its tooltip whendisabledflips. - Verify skill:
drive.mjstakes a scenarioinit(an init script, here a stubbed layout); newkeyboard-layoutandkeyboard-layout-usscenarios;navigation-history.mdhashistory-layout.
Evidence
Driven with the verify skill (keyboard-layout, keyboard-layout-us): the production build on the mock fixtures in Chromium, with getLayoutMap() stubbed, no console errors.
| Before (origin/main): hover Back, no tooltip | After, Norwegian: "Back (⌘Å)" | After, US: "Back (⌘[)" |
|---|---|---|
![]() | ![]() | ![Back ⌘ |
| Before: the sheet, 17 descriptions cut off | After, Norwegian: ⌘Å / ⌘¨, none cut off | After, US |
|---|---|---|
![]() | ![]() | ![]() |
| Forward (⌘¨) | ⌘K "back" | Settings: "⌘å" finds Back | Settings: "go to file" by its menu name | Linux app keys, none cut off |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
- open http://localhost:1435/?mock=1
- Norwegian layout, ⌘J to the dashboard: Back's tooltip reads "Back (⌘Å)"
- the keys are as they were: ⌘ and the key right of P goes back, and Forward's tooltip reads its key
- ⌘/ opens Keyboard shortcuts: Back is ⌘Å, Forward ⌘¨, and no description is cut off
- 39 descriptions, none cut off; the longest: "Open a sidebar row's or a tab's menu"
- Search them in Settings: "⌘å" finds Back, and "go to file" finds Go to file… by its menu name
- ⌘K, "back": its Back item shows ⌘Å
OK
The Mac side, on a real Mac (macOS 27, the Mac mini): cargo test --lib passes all 7, including the new keys::tests::keys_in_their_place_on_each_layout, which reads the installed layouts through the same UCKeyTranslate path without switching to them:
US BracketLeft "[" BracketRight "]" KeyD "d"
Norwegian BracketLeft "å" BracketRight "¨" KeyD "d"
German BracketLeft "ü" BracketRight "+" KeyD "d"
Dvorak BracketLeft "/" BracketRight "=" KeyD "e"
Checks:
pnpm typecheck:plugins,check:titles,check:type,check:grid,check:cspandcheck:themespass. So dopnpm test(580, with the newkey-layout.test.tsand a 40-character cap on descriptions),pnpm build,npx tsc -p e2eand(cd docs-site && pnpm build).- In the browser,
e2e/back-forward.spec.tspasses, with a new Norwegian test. That test switches to US with the window focused the whole time and checks the sheet follows.keyboard.spec.ts,compare.spec.tsandnavigation-historypass too. - Three tests already fail on origin/main and fail the same way here:
keyboard.spec.ts:173and two insidebar-fleet.spec.ts.navigation-historyfailed once on a 5px dashboard scroll difference, then passed 3 more times. - Linux
cargo checkcouldn't run on the devbox (no GTK dev libraries).keys.rscompiles for Linux on its own with-D warnings, and its non-macOS path returns an empty map.
Review: Codex review and adversarial review found two things, both fixed:
- ⌘K didn't follow layout changes.
- Switching input source while the window stayed focused left the old labels.
A simplify pass replaced the hard-coded ⌘⌥D/C/S hints with keysFor. It also added a test tying each table code to the key handler, and limits the native layout call to the Mac app.
A person checks the Mac app (the mock stubs the layout):
- On a Norwegian input source, hover the ← arrow after moving somewhere: it reads "Back (⌘Å)", and → reads "Forward (⌘¨)".
- ⌘/: the sheet shows ⌘Å and ⌘¨.
- Go menu: macOS 12+ localizes menu key equivalents for the layout (
NSMenuItem.allowsAutomaticKeyEquivalentLocalization, on by default, and muda leaves it on). The items still hand mudaCmdOrCtrl+[, so what it reads (⌘Å or ⌘[) is up to macOS. muda can't take a layout key like Å as an accelerator. - ⌘Å goes back and ⌘¨ forward. Then switch the input source to ABC/US and reopen ⌘/: it reads ⌘[ / ⌘].
Merge Danger
Door: two-way
Display, copy and docs. No key bindings, no stored data. Reverting the PR undoes it.
Blast Radius: small
- What a shortcut list shows on non-US layouts. On German, Forward reads ⌘+, the same as Zoom in. That's accurate: the place-matched handler already takes that key for Forward, a pre-existing overlap this PR only makes visible.
Tipremounts its tooltip when its child'sdisabledflips, so a tooltip open at that moment closes.- A new Tauri command,
key_layout(read-only, sync, main thread).
🤖 Generated with Claude Code









