You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- The four macOS-native commands (`app.quit`/`hide`/`hideOthers`/`showAll`) are AppKit `PredefinedMenuItem`s: macOS owns both the behavior and the accelerator, so editing them in Settings was a double illusion (removal didn't disable the OS accelerator; a new binding dispatched into a void).
- Registry: add a `nativeShortcut?: true` flag on the `Command` interface, set on exactly those four entries. New `NATIVE_SHORTCUT_COMMAND_IDS` export is the single source of truth; `command-handlers/types.ts` now sources its Family-1 dispatch-exempt list from it. A `command-registry.test.ts` set-equality test pins the flag to the list and proves the dispatch-exempt round-trip.
- Editor: native rows render read-only — plain static pills, a small "macOS" badge with the tooltip "macOS handles this shortcut. Cmdr can't change it.", and no `+`/`×`/reset/add-slot. `Show all` shows `(none)` unframed plus the badge. Normal rows keep every affordance.
- Honest conflict capture: when a captured combo conflicts with a native command (native wins even in a mixed set), the banner reads `⌘H is reserved by macOS (Hide Cmdr) and won't reach Cmdr. Pick a different combo.` and offers ONLY Cancel — no "Remove from other" / "Keep both" (both would be a lie). Purely non-native conflicts keep the resolvable banner. Classification extracted to the pure `keyboard-shortcuts-banner.ts`.
- Store: `initializeShortcuts` drops any persisted native customization on load (the dev `shortcuts.json` had `app.hide: []`); `setShortcut`/`addShortcut`/`removeShortcut` no-op with a `log.warn` for native commands (defense in depth — MCP shortcut edits route through these mutators too). `resetShortcut` stays permissive (delete-only). New `isNativeShortcutCommand` predicate.
- Scrollbar gutter: reserve `scrollbar-gutter: stable` on `.commands-list` so the trailing `+`/badge controls never sit under an overlay scrollbar when the list scrolls.
- Migrated palette / store tests that used native ids as plain rebindable fixtures to non-native commands; added store-guard, healing, read-only-row, reserved-banner, and conflict-detection tests. Updated colocated CLAUDE.md docs.
|`license-section-utils.ts`| Pure label/status formatters extracted from `LicenseSection` for testability |
36
36
|`ram-gauge-utils.ts`| Pure stacked-bar segment math for `AiLocalSection`'s memory gauge (used → projected → free, plus warning thresholds) |
37
37
|`keyboard-shortcuts-grouping.ts`| Pure scope→group logic for `KeyboardShortcutsSection` (one titled group per `CommandScope`, fixed order). Tested by the set-equality regression guard |
38
+
|`keyboard-shortcuts-banner.ts`| Pure conflict-banner classification for `KeyboardShortcutsSection` (`classifyConflict` → native vs normal; `reservedByMacOsMessage`). Native conflicts win even in a mixed set. Unit-tested |
38
39
39
40
Each section ships with an `*.a11y.test.ts` (axe-core tier-3). `McpServerSection`, `UpdatesSection`, `SearchSection`,
40
41
`FileSystemWatchingSection`, and `KeyboardShortcutsSection` also have functional `*.test.ts` / `*.svelte.test.ts` files;
@@ -142,6 +143,33 @@ after that lands as an overwrite of that pill instead of an append. It needs a p
142
143
command, the result is visible immediately, and any fix (re-deriving the slot on `shortcutChangeCounter` bumps) costs
143
144
more state than the race is worth. Revisit only if it shows up in practice.
144
145
146
+
## macOS-native rows are read-only
147
+
148
+
The four `nativeShortcut` commands (`app.quit`/`hide`/`hideOthers`/`showAll`) render read-only: their combos show as
149
+
plain `.shortcut-pill.static` spans (no click-to-edit), with no `+` add, no `×` remove, no reset button, and never the
150
+
add slot. Each native row also carries a small "macOS" badge (`.macos-badge`) with a tooltip: "macOS handles this
151
+
shortcut. Cmdr can't change it." (`Show all` has no default binding, so it renders its `(none)` unframed plus the
152
+
badge.) The branch is keyed off `isNativeShortcutCommand(command.id)` from `$lib/shortcuts`. This is honest: AppKit owns
153
+
both the behavior and the accelerator (see `lib/shortcuts/CLAUDE.md` § "macOS-native commands are not customizable"), so
154
+
an editable control here would be a double illusion. The store also refuses these writes as defense in depth, so the UI
155
+
and the store agree.
156
+
157
+
## Conflict banner: native conflicts are honest (reserved by macOS), others are resolvable
158
+
159
+
`handleKeyCapture` classifies the captured combo's conflicts via the pure `classifyConflict`
160
+
(`keyboard-shortcuts-banner.ts`):
161
+
162
+
- If the conflict set includes a `nativeShortcut` command (even in a MIXED set with a normal command — the native wins,
163
+
because the combo is unusable regardless), the banner reads
164
+
`⌘H is reserved by macOS (Hide Cmdr) and won't reach Cmdr. Pick a different combo.` (`reservedByMacOsMessage`) and
165
+
offers ONLY Cancel. No "Remove from other" (removing Cmdr's binding doesn't free the OS accelerator) and no "Keep
166
+
both" (the user's binding would never fire) — both would be a lie.
167
+
- A purely non-native conflict keeps the resolvable banner (Remove-from-other / Keep-both / Cancel).
168
+
169
+
`conflictWarning` carries `{ shortcut, conflict: ConflictKind }`; the template branches on `conflict.kind`. The
170
+
classification logic is extracted to the pure `keyboard-shortcuts-banner.ts` (unit-tested) to keep the section component
171
+
lean.
172
+
145
173
## Conflict banner: the editing pill reads as a pending decision
146
174
147
175
When a captured combo conflicts, `handleKeyCapture` sets `conflictWarning` and returns without saving (the banner offers
0 commit comments