diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 654f2e1011..be308c5131 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -495,13 +495,14 @@ export const ChatTextArea = forwardRef( // Handle Enter key based on enterBehavior setting if (event.key === "Enter" && !isComposing) { if (enterBehavior === "newline") { - // New behavior: Enter = newline, Shift+Enter or Ctrl+Enter = send - if (event.shiftKey || event.ctrlKey || event.metaKey) { + // New behavior: Enter = newline, Ctrl/Cmd+Enter = send + // Shift+Enter also inserts newline (consistent with standard text editing) + if (event.ctrlKey || event.metaKey) { event.preventDefault() resetHistoryNavigation() onSend() } - // Otherwise, let Enter create newline (don't preventDefault) + // Otherwise, let Enter/Shift+Enter create newline (don't preventDefault) } else { // Default behavior: Enter = send, Shift+Enter = newline if (!event.shiftKey) { diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx index 0b63a68f4e..a621f14e3e 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx @@ -1081,7 +1081,7 @@ describe("ChatTextArea", () => { expect(shiftEnterEvent.defaultPrevented).toBe(false) }) - it("should treat Ctrl/Cmd/Shift+Enter as send and plain Enter as newline in newline mode", () => { + it("should treat Ctrl/Cmd+Enter as send and plain Enter/Shift+Enter as newline in newline mode", () => { const onSend = vi.fn() ;(useExtensionState as ReturnType).mockReturnValue({ @@ -1096,11 +1096,13 @@ describe("ChatTextArea", () => { const textarea = container.querySelector("textarea")! + // Plain Enter should NOT send (allows newline) const plainEnterEvent = new KeyboardEvent("keydown", { key: "Enter", bubbles: true, cancelable: true }) fireEvent(textarea, plainEnterEvent) expect(onSend).not.toHaveBeenCalled() expect(plainEnterEvent.defaultPrevented).toBe(false) + // Ctrl+Enter SHOULD send const ctrlEnterEvent = new KeyboardEvent("keydown", { key: "Enter", ctrlKey: true, @@ -1111,6 +1113,7 @@ describe("ChatTextArea", () => { expect(onSend).toHaveBeenCalledTimes(1) expect(ctrlEnterEvent.defaultPrevented).toBe(true) + // Shift+Enter should NOT send (allows newline, consistent with standard text editing) const shiftEnterEvent = new KeyboardEvent("keydown", { key: "Enter", shiftKey: true, @@ -1118,8 +1121,19 @@ describe("ChatTextArea", () => { cancelable: true, }) fireEvent(textarea, shiftEnterEvent) + expect(onSend).toHaveBeenCalledTimes(1) // Still 1, not incremented + expect(shiftEnterEvent.defaultPrevented).toBe(false) // Should not prevent default + + // Cmd+Enter (metaKey) SHOULD send + const metaEnterEvent = new KeyboardEvent("keydown", { + key: "Enter", + metaKey: true, + bubbles: true, + cancelable: true, + }) + fireEvent(textarea, metaEnterEvent) expect(onSend).toHaveBeenCalledTimes(2) - expect(shiftEnterEvent.defaultPrevented).toBe(true) + expect(metaEnterEvent.defaultPrevented).toBe(true) }) }) })