diff --git a/ui/src/components/task-chat/TaskChatComposer.test.tsx b/ui/src/components/task-chat/TaskChatComposer.test.tsx index fc031eebb76..90b57b909f2 100644 --- a/ui/src/components/task-chat/TaskChatComposer.test.tsx +++ b/ui/src/components/task-chat/TaskChatComposer.test.tsx @@ -36,16 +36,19 @@ function setTextareaValue(textarea: HTMLTextAreaElement, value: string) { }); } -function pressEnter(textarea: HTMLTextAreaElement, shiftKey = false) { +function pressEnter( + textarea: HTMLTextAreaElement, + modifiers: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean } = {}, +) { flushSync(() => { textarea.dispatchEvent( - new KeyboardEvent("keydown", { key: "Enter", shiftKey, bubbles: true, cancelable: true }), + new KeyboardEvent("keydown", { key: "Enter", ...modifiers, bubbles: true, cancelable: true }), ); }); } -function pressShiftEnter(textarea: HTMLTextAreaElement) { - pressEnter(textarea, true); +function pressCmdEnter(textarea: HTMLTextAreaElement) { + pressEnter(textarea, { metaKey: true }); } async function flushAsync() { @@ -61,7 +64,7 @@ function sendButton() { } describe("TaskChatComposer", () => { - it("submits the trimmed body on Shift+Enter and clears the draft", async () => { + it("submits the trimmed body on Cmd+Enter and clears the draft", async () => { const onAdd = vi.fn().mockResolvedValue(undefined); render(); @@ -69,19 +72,31 @@ describe("TaskChatComposer", () => { setTextareaValue(input(), " hello there "); expect(sendButton().disabled).toBe(false); - pressShiftEnter(input()); + pressCmdEnter(input()); await flushAsync(); expect(onAdd).toHaveBeenCalledWith("hello there", undefined, undefined); expect(input().value).toBe(""); }); - it("does not submit on plain Enter (newline stays in the draft)", async () => { + it("submits on Ctrl+Enter", async () => { + const onAdd = vi.fn().mockResolvedValue(undefined); + render(); + + setTextareaValue(input(), "hello"); + pressEnter(input(), { ctrlKey: true }); + await flushAsync(); + + expect(onAdd).toHaveBeenCalledWith("hello", undefined, undefined); + }); + + it("does not submit on plain Enter or Shift+Enter (newline stays in the draft)", async () => { const onAdd = vi.fn().mockResolvedValue(undefined); render(); setTextareaValue(input(), "line one"); pressEnter(input()); + pressEnter(input(), { shiftKey: true }); await flushAsync(); expect(onAdd).not.toHaveBeenCalled(); @@ -108,7 +123,7 @@ describe("TaskChatComposer", () => { expect(chip.textContent).toContain("Plan"); setTextareaValue(input(), "do the plan"); - pressShiftEnter(input()); + pressCmdEnter(input()); await flushAsync(); expect(onWorkModeChange).toHaveBeenCalledWith("planning"); @@ -127,7 +142,7 @@ describe("TaskChatComposer", () => { ); setTextareaValue(input(), "wake up"); - pressShiftEnter(input()); + pressCmdEnter(input()); await flushAsync(); expect(onAdd).toHaveBeenCalledWith("wake up", true, undefined); diff --git a/ui/src/components/task-chat/TaskChatComposer.tsx b/ui/src/components/task-chat/TaskChatComposer.tsx index d509ce053b1..79b8acf47ff 100644 --- a/ui/src/components/task-chat/TaskChatComposer.tsx +++ b/ui/src/components/task-chat/TaskChatComposer.tsx @@ -272,9 +272,9 @@ export function TaskChatComposer({ value={body} onChange={(e) => setBody(e.target.value)} onKeyDown={(e) => { - // Shift+Enter posts; plain Enter inserts a newline (Enter alone was - // too easy to trip while drafting). - if (e.key === "Enter" && e.shiftKey) { + // Cmd/Ctrl+Enter posts; plain Enter inserts a newline (Enter alone + // was too easy to trip while drafting). + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); void submit(); return; @@ -419,7 +419,7 @@ export function TaskChatComposer({ type="button" onClick={() => void submit()} disabled={disabled || submitting || body.trim().length === 0} - title="Send (Shift+Enter)" + title="Send (⌘+Enter)" aria-label="Send" className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground transition-transform hover:scale-105 disabled:scale-100 disabled:bg-muted disabled:text-muted-foreground" data-testid="task-chat-composer-send"