mirror of
https://github.com/NeuralNomadsAI/CodeNomad.git
synced 2026-10-22 06:11:33 +00:00
Shift+Up/Down already selects text in every text field, so the user-message jump could not work from the composer, where people usually are. The defaults become Alt+Up/Down, and Cmd+Option+Up/Down on macOS, where Option+Up/Down moves to a paragraph's start or end in text fields. Neither edits text, so the jump now also works from the composer and other text fields, keeping the draft, its caret and focus; Shift+Up/Down selects text again everywhere. Saved custom bindings are unchanged. The key guard allows text fields and leaves modified arrows where they already mean something: code editors (Monaco moves lines) and terminals, arrow-key widgets, selects and popup triggers (Alt+Down opens them, such as the new-instance launcher), fields that move a list's active option (aria-activedescendant, input[aria-controls], such as the mission picker search), non-text inputs and IME composition. The composer's / and @ picker moves its selection with every arrow key, so its root carries data-prompt-picker and the jump yields while it is open. Platform defaults live in defaultHotkeyBindings, like prompt-alternate-send. The help shortcuts table (10 locales) and HOTKEYS.md describe the new defaults. Validation: unit tests for the platform defaults (no conflict on either platform) and the key guard; browser regressions jump with Alt from the conversation and the composer (draft, caret and focus kept; Shift+ArrowUp still selects), keep the key for an open composer picker, and, with a real MissionPicker for Alt and emulated-macOS Cmd+Option, move the picker search's option without moving the conversation (the guard checks fail without their selectors). hotkeys-settings and composer-picker-enter pass; UI own-tsconfig typecheck, root typecheck and test:help pass. Gatekeeper review (GPT-6.1 Sol): one P2 (mission picker search) fixed, then zero actionable findings at 7eb2cb081da4ebb1a04e5a7391322deaeb154dd4. Refs #1075
3.9 KiB
3.9 KiB
Custom keyboard shortcuts
Agent conventions
- Settings → Keyboard shortcuts (
components/settings/hotkeys-settings-section.tsx, square token-based styles instyles/components/hotkeys-settings.css) manages the static renderer-only catalog inlib/hotkey-definitions.tsin both app and native Preferences renderers, without mounting the app. Keep thehotkeyssection allowlist identical in renderer, Electron and Tauri. Initially unbound palette commands appear with “No shortcut” under their existing translated names; aliases (new-session→session-new) keep one row. Native workspace actions are configurable but register for execution only in local desktop windows. - Bindings live in the UI owner's
settings.hotkeyOverrides: a missing ID uses its default,[]disables it, merge-patchnullresets one ID or the map. The savedpromptSubmitOnEntersupplies Enter defaults until an explicit composer override replaces them. - Recording is renderer-only and target-scoped: it suppresses app shortcuts (not native accelerators), ignores IME composition and bare modifiers, lets unmodified Tab leave normally, and ends on blur, cancellation, inactive settings or disposal. Conflicts warn on every affected row and never silently disable a binding.
- Digit-row and bracket keys share one physical identity for recording, dispatch and conflicts (
Digit1→1,BracketLeft→[, including AZERTY and QWERTZ); other keys stay logical. Saved layout-symbol overrides (&,ü,!) must be re-recorded to bind physical slots; never guess or silently migrate them. - Initially unbound commands dispatch the palette's real action through
use-app-lifecycle.tsand the existing keyboard registry, respecting its disabled state, active project/session and modal/selector guards, without key repeat; global commands also work on the home view. - Shortcut hints live only in tooltips and the manager, never status strips or help footers: a bound control sets
title={withHotkey(label, id)}(lib/hotkeys.ts) and menu items setdescription: hotkeyLabel(id) || undefined, leaving the accessible name unchanged, only where the shortcut performs that action in the control's current context (Ctrl+W only on the active tab). - Composer shortcuts (Clear, agent/model/thinking selectors) reach the shown composer, including the no-session draft, through the instance's
session-sidebar-eventsrequest; Clear dispatchesPROMPT_CLEAR_EVENTso the composer runs its own Clear action, never a bare textarea write. - Jump to previous/next user message (
user-message-prev/-next,components/user-message-navigation.ts) defaults to Alt+↑/↓, and to Cmd+Option+↑/↓ on macOS, where Option+↑/↓ moves within a text field's paragraphs: neither default edits text, so the jump also works from the composer and other text fields. It runs in the activeMessageSection's capture-phase handler, before the transcript reads arrows as scrolling; palette commands reach it throughJUMP_USER_MESSAGE_EVENT. Code editors and terminals (which bind modified arrows), arrow-key widgets, selects and popup triggers (Alt+↓ opens them), fields that move a list's active option (aria-activedescendant,input[aria-controls], such as the mission picker search), non-text inputs, IME composition, modal dialogs and an open composer picker ([data-prompt-picker], which moves its selection with every arrow) keep the keystroke; key repeat does not repeat the jump. Targets are the timeline's markers, including indexed history beyond the resident window (requested even while the timeline is hidden; a jump that finds nothing resident runs once it arrives). The last target stays the reference while loading or shown, until a scroll gesture or other navigation. Covered bytests/browser/history-navigation.test.ts. - Native hotkeys keep their platform defaults outside the manager; native menu keys and OS-reserved combinations may intercept assignments before the renderer.