feat(ui): add "Enter sends" composer keymap setting - #1128
Conversation
|
Thanks, this is well built. The IME handling, the registry usage, and the fallback design are all right. Three things before merge:
Also heads up: this will need a rebase once #1127 lands, it touches the same two files. If you are short on time, say the word and we are happy to push these changes to your branch ourselves. |
66caaa4 to
edb9454
Compare
@backnotprop: Thanks for the feedback, good catch on the expanded comment dialog - fixed this. Also updated the PR desc and rebased on main, would be great to see this as part of the next release! Just saw https://github.com/backnotprop/plannotator/releases/tag/v0.25.0 my day's ruined. 😆 |
feat(ui): "Enter sends" composer keymap setting
Adds an opt-in setting that makes Enter submit comments and AI messages, Shift+Enter insert a newline, and Mod+Enter ask AI. Default is unchanged (
Mod+Entersubmits), so nobody's muscle memory moves without opting in.Toggle lives in Settings → Shortcuts → "Enter sends", directly above the shortcut reference table, which rerenders from the same value.
Behavior
EnterShift+EnterMod+EnterApplies to every text composer, so the two halves of the app stay consistent: plan-review comment popover (dialog + inline), annotation panel cards, code-file popout, document AI chat; code-review annotation toolbar, expanded comment dialog, file comment banner, Ask AI input, AI tab.
Two intentional changes that apply with the setting off
Routing every composer through one resolver tightened two loose behaviors. Both are deliberate, and both ship in the default keymap:
Mod+Enteron a cleared textarea, saving an empty comment and closing the editor. The shared handler gates submit on non-empty trimmed text, so that keystroke is now a no-op. Clearing a comment to delete it was never the intended gesture.Enter && (metaKey || ctrlKey), which also fired onCmd+Shift+EnterandCmd+Alt+Enter. The registry matcher requires exact modifiers, so those combinations do nothing in a composer now. (The review toolbar's suggested-code textarea kept its hardcoded check and still accepts them.)Implementation
packages/core/config-types.ts-ComposerSubmitKey = 'mod-enter' | 'enter'.packages/ui/utils/composerKeymap.ts- keymap table plus a pureresolveComposerIntent(event, keymap, caps)resolver. Bindings are written in the shortcut-registry language and matched withmatchesShortcutBinding, so one place decides what "Mod+Enter" means. Returningnull(rather than a "newline" intent) is what lets Shift+Enter and Enter-while-empty fall through to the textarea: the caller must notpreventDefault.packages/ui/hooks/useComposerKeys.ts- the React handler each composer wires up, plususeComposerSubmitHint/useComposerAskAIHintkeycaps. A local handler rather than auseShortcutScopewindow listener because composers are portaled and nested and the shortcut engine has no cross-scope arbitration.packages/ui/config/settings.ts-composerSubmitKey, cookie-only, default'mod-enter'.KeyboardShortcuts.tsx- reference sections became functions of the active submit key, so the help table tells the truth in both modes.Notes
isComposing/keyCode === 229decides whether CJK input works at all. Guarded in the resolver and covered by tests.Mod+Enterkeeps working with focus anywhere in the dialog, as before. BareEnteronly drives the keymap from the textarea, otherwise it wouldpreventDefaultthe activation of a focused Collapse/Close/Ask AI button.Mod+Enteris also stopped from propagating, so it can't reach the window listener that submits the whole review while the dialog is open.Mod+Enter- Enter is a newline in a code block.ToggleSwitchgainedshrink-0+gap-4: a long description used to squeeze the track below its width and push the thumb outside it.Testing
canSubmitgating, IME guards).bun testand all 7typecheckprojects match the pre-change baseline.