Skip to content

feat(web): open Settings with ⌘, on macOS - #641

Draft
Andrey Markin (Mark-Life) wants to merge 18 commits into
JetBrains:mainfrom
Mark-Life:feat/settings-shortcut
Draft

Andrey Markin (Mark-Life) wants to merge 18 commits into
JetBrains:mainfrom
Mark-Life:feat/settings-shortcut

Conversation

@Mark-Life

Copy link
Copy Markdown
Contributor

Problem

On macOS, ⌘, does nothing. Every Mac app opens its settings with it, so in ThinkRail Settings opens only with the mouse. The gear also always opens on Providers, even when the user was last on another section.

Approach

⌘, opens Settings on macOS only. Windows and Linux get no Ctrl+, so terminals and the browser keep that key. Each runtime has exactly one owner for the key, the rule #609 set:

  • Desktop: a native Settings… item in the app menu, forwarded as the open-settings native command. If the window is minimized, it is restored first.
  • Browser on a Mac: the web client handles the keypress itself. Chrome does not reserve ⌘, so the page can catch it.

Both paths do nothing while a modal, popover or menu is open. Otherwise Settings would open over the menu, and the menu would still be open after Settings closed. In the browser the key is still caught there, so Chrome's own settings page never opens.

openSettings() with no section now reopens the last section shown in this session, for both the gear and ⌘,. Links to a specific section (Templates, Terminal, Updates, Providers) still go there. When Settings opens, focus goes to the active section's button. Before, it went to Providers, so two items looked selected.

Stacked on #609: the diff includes its commits until it merges. The last two commits are this change.

Changes

  • contracts: NativeCommand adds open-settings.
  • desktop: Settings… (CommandOrControl+,) in the macOS app menu; restores a minimized window before forwarding the command.
  • web: the ⌘, browser handler in useGlobalHotkeys, matched on the typed , with a physical-key fallback for non-Latin layouts, like ⌘W/⌘Q. Includes the shared gate (openSettingsUnlessLayered), last-section openSettings(), focus on the active section, and the gear tooltip Settings (⌘,) plus aria-keyshortcuts.
  • Specs: contracts, desktop, shell, store, panels, UI-TERMINOLOGY.md.

Screenshots

Browser at 1440×900, platform set to Mac. Flow: open Settings → Appearance → Esc → ⌘,.

Before After
before after
⌘, does nothing; the gear reopens Providers ⌘, reopens on Appearance, with focus on it

Packaged desktop build (bun run desktop:build), Settings opened by ⌘,:

desktop

Related issues

Closes #623. Depends on #609.

Checklist

  • Fast gates pass: bun run lint, bun run typecheck, bun run test. The only failure was a timing-sensitive server test (review resolution rejects a commit past the newest-200 listCommits cap, 5.2s against a 5s timeout), which passes when rerun alone.
  • E2E suite passes for app-affecting changes: bun run e2e 477/477 on fadaf313.
  • Before/after screenshots are included for frontend changes, or marked not applicable
  • Relevant SPEC.md / top-level specs updated to reflect any boundary, contract, or behavior change
  • I have read the Contributing guide and agree to the Code of Conduct

Checked by hand on a packaged macOS build through System Events: the app menu shows Settings… , with Command only; ⌘, and the menu item open Settings; a minimized window comes back with Settings open on the last section. Not tested: Safari and Firefox (Playwright runs Chromium only), and Windows/Linux desktop builds.

🤖 Generated with Claude Code

Cmd+Q did nothing on macOS because Electrobun role menu items get no
default accelerator. Quit now needs a 1.2s hold or a double press and
fires on release, with a centered hint; menu, Dock, and update quits
stay direct and share the existing quit coordinator.

Shortcuts follow VS Code: Cmd+Q / Linux Ctrl+Q quit (confirmed);
Cmd+W, Win/Linux Ctrl+W and Ctrl+F4 close the focused dialog or tab
through the tab X-button path and never close the window. Hide, Hide
Others, and Minimize get their macOS accelerators.

The confirmation rules live once in contracts (quitConfirmation); macOS
feeds them CoreGraphics key state because menu events repeat without a
repeat flag, Linux feeds them web key events. A web shortcut command
table serves native commands and Win/Linux keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cmd+W bumped a closeRequestKey state in Shell, re-rendering Shell,
WorkspaceWorkbench and Workbench per press. A shell-owned
closeRequestChannel now fires the request; Workbench gets it via an
injected subscribeCloseRequest prop, so layout stays props-driven and
the mount-time stale-key rule goes away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shell no longer calls useAppShortcuts. The AppShortcuts leaf runs the hook
and renders QuitHintOverlay, so quit hint changes re-render only that leaf.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
matchShortcut now takes a lazy terminal check and calls it only after a
chord matches. useAppShortcuts adds window keydown/keyup listeners only
on platforms whose table binds web chords (windows, linux), so macOS
desktop has none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
close-item dismisses layers with an untrusted Escape. Spell out what each
dismissible layer must do to keep closing on the shortcut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Decision #3 named one function and its injection details. It now states
the rule (types plus small dependency-free runtime) and points to the
contracts spec for the list. README no longer says types-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On AZERTY/Dvorak the KeyW/KeyQ positions type other letters, so Ctrl+Z
closed tabs and double Ctrl+A in a Linux terminal quit the app. Letter
chords now match event.key, falling back to code for non-Latin layouts.
The quit hold releases on the keyup of the physical key that started it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A key pressed after Command-Q no longer completes the hold. The hold
tracks the non-modifier keys down when the menu action fired, so it stays
layout-independent. Key-repeat presses never add keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tab strip shares data-group-id with its group root, so closest()
from a focused tab returned the strip and missed the overlay; close-item
then closed the chat. Match every element with the focused group id.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assert QUIT_CONFIRMATION matches the documented 1200/500/40 ms values,
and check that a hold released before the threshold never quits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shell, web, and desktop specs said contracts is types-only, but both
import createQuitConfirmation. Shell always mounts AppShortcuts; the
hook is inert without the bridge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Command up, a last key-down newer than the last modifier change
means the Quit item was picked from the menu by keyboard, not by
Command-Q. A late-handled tap still arms: its Command release is newer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The busy-close step waits for a computed marker printed by the child
shell. The terminal Ctrl+W step checks the shell erased a word. Use
pressPlatformShortcut for the new-workspace chord.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split CoreGraphics reads from a pure decodeKeyState and test the
modifier keycode range, Command flag, and event-age ordering. Test
createShortcutsBridge pub/sub directly. Type the e2e bridge stub as
NativeShortcutsBridge so drift fails typecheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The macOS app menu gains "Settings…" (⌘,), forwarded to the web client as
the `open-settings` native command. In a browser on a Mac the web client
owns ⌘, itself. Both paths open Settings unless a modal or menu is open,
and `openSettings()` with no section now reopens the last section shown.

Closes JetBrains#623

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings reopens on the last section, but focus still landed on the first
rail item, so two items looked selected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

Settings should open with ⌘+, on macOS

2 participants