Skip to content

feat: add Ctrl+Enter / Cmd+Enter as send shortcut#698

Open
hankkyy wants to merge 1 commit into
fathah:mainfrom
hankkyy:feat/ctrl-enter-send
Open

feat: add Ctrl+Enter / Cmd+Enter as send shortcut#698
hankkyy wants to merge 1 commit into
fathah:mainfrom
hankkyy:feat/ctrl-enter-send

Conversation

@hankkyy

@hankkyy hankkyy commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Feature

Add Ctrl+Enter (Windows/Linux) and Cmd+Enter (macOS) as send shortcuts alongside the existing plain Enter.

Changes

  • ChatInput.tsx: expand the Enter key check from !e.shiftKey to !e.shiftKey || e.ctrlKey || e.metaKey, so Ctrl/Cmd+Enter also triggers send
  • ChatInput.test.tsx: add 4 new test cases covering plain Enter, Shift+Enter (should not send), Ctrl+Enter, and Cmd+Enter

Behavior

Shortcut Action
Enter Send (unchanged)
Shift+Enter Newline (unchanged)
Ctrl+Enter Send (new)
Cmd+Enter Send on macOS (new)

The CJK IME composition guard is unaffected — Enter is still swallowed while an IME is composing.

Support Ctrl+Enter (Windows/Linux) and Cmd+Enter (macOS) as
additional send shortcuts alongside plain Enter. Shift+Enter
continues to insert a newline.

Changes:
- ChatInput: expand Enter check to accept Ctrl/Cmd+Enter
- ChatInput.test: add 4 tests covering plain Enter, Shift+Enter,
  Ctrl+Enter, and Cmd+Enter
@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Ctrl+Enter (Windows/Linux) and Cmd+Enter (macOS) as send shortcuts in the chat input, alongside an accompanying test suite. The boolean expression used to implement this has a subtle flaw that causes Shift+Ctrl+Enter and Shift+Cmd+Enter to unexpectedly submit instead of inserting a newline.

  • ChatInput.tsx: The original !e.shiftKey already handled Ctrl+Enter and Cmd+Enter correctly (those combos have shiftKey=false). Changing to (!e.shiftKey || e.ctrlKey || e.metaKey) makes ctrlKey/metaKey override the Shift guard, so Shift+Ctrl+Enter now sends — contradicting the stated Shift-always-means-newline invariant.
  • ChatInput.test.tsx: Four new test cases are added, but the suite omits Shift+Ctrl+Enter and Shift+Cmd+Enter, so the unintended behavior change is not caught.

Confidence Score: 3/5

Not safe to merge as-is — the condition change introduces an unintended send trigger on Shift+Ctrl+Enter that contradicts the Shift-always-means-newline design.

The original !e.shiftKey guard already made Ctrl+Enter and Cmd+Enter send messages, so the change is narrower than advertised. The new expression (!e.shiftKey || e.ctrlKey || e.metaKey) causes Shift+Ctrl+Enter and Shift+Cmd+Enter to submit instead of inserting a newline, a regression not covered by the new tests.

The single changed line in ChatInput.tsx (line 356) needs a corrected boolean expression, and ChatInput.test.tsx needs a Shift+Ctrl+Enter test case to guard against this regression.

Important Files Changed

Filename Overview
src/renderer/src/screens/Chat/ChatInput.tsx Single-line condition change to add Ctrl/Cmd+Enter as send shortcuts, but the new boolean expression unintentionally allows Shift+Ctrl+Enter and Shift+Cmd+Enter to send — the original !e.shiftKey already handled the plain Ctrl+Enter/Cmd+Enter cases.
src/renderer/src/screens/Chat/ChatInput.test.tsx Four new test cases covering plain Enter, Shift+Enter, Ctrl+Enter, and Cmd+Enter. Missing a test for Shift+Ctrl+Enter / Shift+Cmd+Enter which are the only combos whose behavior actually changed.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[keyDown: Enter] --> B{isComposing?}
    B -- yes --> C[Swallow — IME guard]
    B -- no --> D{"(!shiftKey || ctrlKey || metaKey)\n[NEW condition]"}
    D -- false --> E[Default: insert newline]
    D -- true --> F[preventDefault + handleSend]

    style D fill:#f9a,stroke:#c33
    G[Problem path] --> H["Shift+Ctrl+Enter\nshiftKey=true, ctrlKey=true"]
    H --> I{"(!true || true || false) = true"}
    I --> F
    style G fill:#fdd,stroke:#c33
    style H fill:#fdd,stroke:#c33
    style I fill:#fdd,stroke:#c33
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[keyDown: Enter] --> B{isComposing?}
    B -- yes --> C[Swallow — IME guard]
    B -- no --> D{"(!shiftKey || ctrlKey || metaKey)\n[NEW condition]"}
    D -- false --> E[Default: insert newline]
    D -- true --> F[preventDefault + handleSend]

    style D fill:#f9a,stroke:#c33
    G[Problem path] --> H["Shift+Ctrl+Enter\nshiftKey=true, ctrlKey=true"]
    H --> I{"(!true || true || false) = true"}
    I --> F
    style G fill:#fdd,stroke:#c33
    style H fill:#fdd,stroke:#c33
    style I fill:#fdd,stroke:#c33
Loading

Reviews (1): Last reviewed commit: "feat: add Ctrl+Enter / Cmd+Enter as send..." | Re-trigger Greptile


if (e.key === "Enter" && !e.shiftKey) {
// Send: plain Enter (without Shift) or Ctrl/Cmd+Enter
if (e.key === "Enter" && (!e.shiftKey || e.ctrlKey || e.metaKey)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Logic bug: Shift+Ctrl+Enter and Shift+Cmd+Enter now unexpectedly send

The new condition (!e.shiftKey || e.ctrlKey || e.metaKey) evaluates to true when ctrlKey or metaKey is set, regardless of shiftKey. So holding Shift+Ctrl+Enter (or Shift+Cmd+Enter) now submits the message instead of inserting a newline — contradicting the stated invariant that Shift always produces a newline.

Also note: the original !e.shiftKey condition already handled Ctrl+Enter and Cmd+Enter correctly (since those combos have shiftKey=false). The net observable change here is only for the Shift+modifier combinations, which aren't mentioned in the PR description or covered by the new tests.

A missing test case Shift+Ctrl+Enter (expecting onSubmit NOT to have been called) would surface this regression.

Comment on lines +81 to +86
it("does not submit on Shift+Enter (newline)", () => {
const { onSubmit, textarea } = renderInput();
fireEvent.change(textarea, { target: { value: "hello" } });
fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true });
expect(onSubmit).not.toHaveBeenCalled();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 A test for Shift+Ctrl+Enter is missing. With the new condition this combo now submits (because ctrlKey short-circuits !shiftKey), but the PR description says Shift+Enter should always produce a newline. Adding this case would immediately surface the regression.

Suggested change
it("does not submit on Shift+Enter (newline)", () => {
const { onSubmit, textarea } = renderInput();
fireEvent.change(textarea, { target: { value: "hello" } });
fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true });
expect(onSubmit).not.toHaveBeenCalled();
});
it("does not submit on Shift+Enter (newline)", () => {
const { onSubmit, textarea } = renderInput();
fireEvent.change(textarea, { target: { value: "hello" } });
fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true });
expect(onSubmit).not.toHaveBeenCalled();
});
it("does not submit on Shift+Ctrl+Enter (newline takes precedence)", () => {
const { onSubmit, textarea } = renderInput();
fireEvent.change(textarea, { target: { value: "hello" } });
fireEvent.keyDown(textarea, { key: "Enter", shiftKey: true, ctrlKey: true });
expect(onSubmit).not.toHaveBeenCalled();
});

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant