Repository navigation
Add a setting to send messages with Command+Enter - #881
mfieldingsquare wants to merge 2 commits into
Conversation
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes’s GitHub account.
Requesting changes for one correctness issue, explained in the inline thread: with the setting on, ⌘/Ctrl+Enter sends a draft while a mention, emoji, or command completion is still showing.
Validation at 017ba7af: tsc --noEmit passes, and the MessageComposer, ShortcutSettings, AgentSettings and Settings test files pass locally (324 tests). Main CI is waiting for maintainer approval on this fork PR, so it hasn't run yet. The PR notes that desktop and WebKit testing are still to do.
To merge: ⌘/Ctrl+Enter must not send while a completion menu is showing, with a test that covers it, and CI must pass.
| !event.ctrlKey && | ||
| !event.metaKey | ||
| ) { | ||
| const sendKeystroke = readSendKeystroke(event.nativeEvent); |
There was a problem hiding this comment.
🤖 ⌘/Ctrl+Enter sends the draft while a completion menu is open.
ComposerCompletions (src/features/conversation/ComposerCompletions.tsx:289) returns false straight away for any key pressed with Alt, Ctrl or Meta. So its "a displayed choice owns acceptance, never turn it into an unintended send" guard only covers plain Enter. Once ⌘/Ctrl+Enter is the send key, a half-typed @Ali, :smi or !search gets sent as plain text, and the person you meant to mention isn't notified. That breaks the rule in docs/plugin-architecture.md: "A rejected displayed choice must not fall through to sending."
I reproduced it at 017ba7af with a throwaway test placed next to accepts a completion before handling list Enter:
sendWithModEnterPreference.setEnabled(true);
const h = mount();
await h.user.type(h.input(), "!search");
expect(h.publish(h.completionRequests.length - 1)).not.toBe(false);
await h.user.keyboard("{Control>}{Enter}{/Control}");
expect(h.messages.send).not.toHaveBeenCalled();
// fails: send called with ("channel", "!search", [], [])Fix: when a completion is showing, the active send keystroke should either accept the choice or be swallowed, the same as plain Enter is today. That could mean the completion handler checks the send keystroke, or the composer refuses to send while a choice is showing. Please add this case as a test.
There was a problem hiding this comment.
🤖 Fixed in e5f3b81f. While a completion is showing, the completion key handler now ignores the active send keystroke, so the draft is not sent. Only plain Enter and Tab accept a choice, as docs/plugin-architecture.md describes, and that doc now mentions this case. Default mode is unchanged. The new test, does not send on Mod+Enter while a completion is showing, fails before the fix and passes after: Mod+Enter with !search showing does not send, Enter accepts the choice, and Mod+Enter then sends. The branch is rebased on main, and the affected tests (1703), typecheck and biome pass.
017ba7a to
e5f3b81
Compare
e5f3b81 to
27c0abe
Compare
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes’s GitHub account.
No blocking findings at 27c0abe. Three optional suggestions are inline.
What was checked, by reading the code plus a few throwaway local tests:
- With the setting off, Enter behaves exactly as before.
- With it on, plain Enter goes down the same line-break path as Shift+Enter, so leaving an empty list item still works.
- Holding ⌘Enter, IME composition, and the zero-results retry row are handled.
- Every composer (thread, edit, DM, solo, media review, forum) goes through
MessageComposer. - No global or native shortcut uses Mod+Enter.
- The
device-toggleextraction keeps "Remember mentioned agents" behaving the same. - The new Messages group follows the
DESIGN.mdsettings layout.
Not checked here: the setting in the desktop app, WebKit, and on-screen keyboards on touch devices. The PR description already lists these as open, and the send button covers touch.
| if (event.altKey || event.ctrlKey || event.metaKey) { | ||
| // When ⌘/Ctrl+Enter is the send key, a displayed choice must still not | ||
| // fall through to sending. Only plain Enter and Tab accept a choice. | ||
| if (count && readSendKeystroke(event.nativeEvent).sends) { |
There was a problem hiding this comment.
Optional (low). This branch swallows ⌘/Ctrl+Enter without calling invalidate(), even when !active(). The plain-Enter branch below (:303-308) does call it.
When this bites: a provider's evidence is revoked while its menu is still on screen. Plain Enter dismisses the menu, and a second Enter sends. ⌘Enter does nothing and the stale menu stays until the user types or presses Escape.
Suggested fix: call current.current.editor.invalidate() here when !active(), matching the plain branch.
Related product question, not a defect: with a valid menu open, ⌘Enter neither accepts nor sends, with no feedback. That matches the updated plugin-architecture doc. Someone who types @ or : mid-sentence may still read it as ⌘Enter "not working".
| <InlineHeader title="Messages" /> | ||
| <SettingsGroup> | ||
| <SwitchPreferenceRow | ||
| label={`Send with ${modEnter}`} |
There was a problem hiding this comment.
Optional (a11y nit). formatBinding returns .label ("Command Enter" / "Control Enter") specifically for assistive tech. Keep .text as the visible label and pass .label through the row's existing aria-label prop, e.g. aria-label={Send with ${formatBinding(MOD_ENTER, apple).label}}. The getByRole("switch", { name }) queries in ShortcutSettings.test.tsx would need to follow.
| ); | ||
| }); | ||
|
|
||
| it("sends from inside a list on Mod+Enter when that preference is on", async () => { |
There was a problem hiding this comment.
Optional (coverage). The likeliest regression here is plain Enter inside a list once the setting is on. It now falls through to EditableInput → insertLineBreak rather than this handler's list branch. A throwaway local test (- first{Enter}{Enter}outside with the setting on: one <li>, then a paragraph, and nothing sent) passed. Adding that case to this test would pin it, if you think it's worth a test.
Enter adds a new line when the setting is on. The on/off storage code for device-local choices is now shared with "Remember mentioned agents". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Fielding <mfielding@squareup.com>
When Command/Ctrl+Enter is the send key, pressing it while a mention, emoji or command completion is showing sent the unfinished text. It is now ignored until the choice is accepted or dismissed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Matt Fielding <mfielding@squareup.com>
27c0abe to
aa311dd
Compare
Why?
Pressing Enter sends a message straight away, so people who write multi-line messages can send a draft before it is finished.
Changes
Validation
🤖 Generated with Claude Code