Conversation
A new Commands/ namespace centralises a registry of invokable actions, a chord-based KeyMap with VS Code-flavoured parsing (cmd → Meta on macOS, Ctrl elsewhere), and a window-level KeyRouter that grabs modifier-bearing chords on the tunnel phase so they win over a focused TextBox. The palette modal is mouse-optional: subsequence fuzzy match, ListBox selection, auto-focused query, Up/Down/Enter/Esc. Initial bindings ship just Cmd-K → palette.open. The catalog wires up ten existing ShellVm actions (new session, cancel turn, view switch, prefs, about, ...) plus dynamic "Switch to <session>" entries the palette materialises on demand. User overrides are read from a new keybindings.json settings key via JsonDocument so we stay AOT-clean.
Greptile SummaryAdds a Four P2 findings worth addressing before the UX stabilises:
Confidence Score: 4/5Safe to merge; all findings are P2 quality-of-life issues, none are blocking correctness or security. No P0 or P1 issues found. The four P2 findings (palette.open self-appearance, missing focus restore, possible LeftMeta gap, non-deterministic empty-query order) are real usability rough edges but none cause data loss, crashes, or broken core behaviour. P2-only ceiling is 4/5. src/Conclave.App/ViewModels/ShellVm.cs (palette.open CanExecute), src/Conclave.App/Commands/KeyRouter.cs (IsModifierKey completeness), src/Conclave.App/Views/Shell/CommandPaletteModal.axaml.cs (focus restore) Important Files Changed
|
| private void OnBackdropPressed(object? sender, PointerPressedEventArgs e) | ||
| { | ||
| if (DataContext is ShellVm shell) shell.CloseCommandPalette(); | ||
| } |
There was a problem hiding this comment.
Focus not restored after palette dismissal
CloseCommandPalette() (invoked by backdrop press, Escape, or ExecuteSelected) removes focus from QueryInput but doesn't return it to whatever was focused before the palette opened. After dismissal the user must click or Tab to re-engage the text composer or any other input, which interrupts keyboard-driven workflows. Consider saving the previously focused element before FocusOnOpen() posts the focus change and restoring it in each close path.
| private static bool IsModifierKey(Key k) => k | ||
| is Key.LeftCtrl or Key.RightCtrl | ||
| or Key.LeftShift or Key.RightShift | ||
| or Key.LeftAlt or Key.RightAlt | ||
| or Key.LWin or Key.RWin; |
There was a problem hiding this comment.
Key.LeftMeta / Key.RightMeta missing from IsModifierKey
Avalonia exposes Key.LeftMeta and Key.RightMeta for the macOS Command key (in addition to Key.LWin / Key.RWin). If either value is what Avalonia actually emits when the user holds Command alone on macOS, IsModifierKey returns false, KeyChord.FromEvent builds a chord whose Key is LeftMeta/RightMeta, map.Lookup finds nothing, and the event falls through harmlessly — but the guard's intent (skip bare-modifier keystrokes entirely) is violated. Adding both values keeps the intent sound across platform variations:
private static bool IsModifierKey(Key k) => k
is Key.LeftCtrl or Key.RightCtrl
or Key.LeftShift or Key.RightShift
or Key.LeftAlt or Key.RightAlt
or Key.LWin or Key.RWin
or Key.LeftMeta or Key.RightMeta;
Summary
Commands/namespace:AppCommandrecords, aCommandRegistry, a chord-basedKeyMapwith VS Code-flavoured parsing (cmd→ Meta on macOS, Ctrl elsewhere), and a window-levelKeyRouterthat grabs modifier-bearing chords on the tunnel phase so they win over a focusedTextBox.CommandPaletteModalfollows the existing modal pattern (lazy-instantiated byMainWindow, backdrop dismiss,Escapeto close). Subsequence fuzzy match, ListBox-driven selection, auto-focused query input, Up/Down/Enter/Esc.ShellVmgains a small initial command catalog (10 actions: new session, cancel turn, view switch, prefs, about, ...) plus dynamic "Switch to " entries materialised on demand by the palette VM.Cmd-K→palette.open. Designing the rest of the shortcut catalog is intentionally deferred until we can see the palette in action and decide what's worth a hotkey.keybindings.jsonsettings key viaJsonDocument(manual walk, AOT-clean — no reflection-based deserialiser).Out of scope (future work)
KeyChordis a single chord today; the structure leaves room for chord sequences without forcing them now.@mentions, vim mode toggle) — separate stage.