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"