authorgravatar for maxtheyoung@gmail.comMaxwell Young <maxtheyoung@gmail.com> 2026-07-18 09:14:45+12:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-17 23:14:45+02:00
log6a17629670f31a00719fb92c7c9413661cd6def5
tree017840fdba9d35d859d31a9010771bcba2265921
parent7b8d12618b3d857874a1cc709d11c5df087b6390
signature Signed by PGP key B5690EEEBB952194

[codex] fix mobile composer Enter behavior (#3930)

Co-authored-by: Julius Marminge <julius0216@outlook.com> Co-authored-by: codex <codex@users.noreply.github.com>

3 files changed, 27 insertions(+), 1 deletions(-)

apps/web/src/components/chat/ChatComposer.tsx+5-1
...@@ -41,6 +41,7 @@ import {...@@ -41,6 +41,7 @@ import {
41 detectComposerTrigger,41 detectComposerTrigger,
42 expandCollapsedComposerCursor,42 expandCollapsedComposerCursor,
43 replaceTextRange,43 replaceTextRange,
44 shouldSubmitComposerOnEnter,
44} from "../../composer-logic";45} from "../../composer-logic";
45import { deriveComposerSendState, readFileAsDataUrl } from "../ChatView.logic";46import { deriveComposerSendState, readFileAsDataUrl } from "../ChatView.logic";
46import {47import {
...@@ -1750,7 +1751,10 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -1750,7 +1751,10 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
1750 return true;1751 return true;
1751 }1752 }
1752 }1753 }
1753 if (key === "Enter" && !event.shiftKey) {1754 if (
1755 key === "Enter" &&
1756 shouldSubmitComposerOnEnter({ isMobileViewport, shiftKey: event.shiftKey })
1757 ) {
1754 submitComposer();1758 submitComposer();
1755 return true;1759 return true;
1756 }1760 }
apps/web/src/composer-logic.test.ts+15
...@@ -8,9 +8,24 @@ import {...@@ -8,9 +8,24 @@ import {
8 isCollapsedCursorAdjacentToInlineToken,8 isCollapsedCursorAdjacentToInlineToken,
9 parseStandaloneComposerSlashCommand,9 parseStandaloneComposerSlashCommand,
10 replaceTextRange,10 replaceTextRange,
11 shouldSubmitComposerOnEnter,
11} from "./composer-logic";12} from "./composer-logic";
12import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER } from "./lib/terminalContext";13import { INLINE_TERMINAL_CONTEXT_PLACEHOLDER } from "./lib/terminalContext";
1314
15describe("shouldSubmitComposerOnEnter", () => {
16 it("submits plain Enter on desktop", () => {
17 expect(shouldSubmitComposerOnEnter({ isMobileViewport: false, shiftKey: false })).toBe(true);
18 });
19
20 it("inserts a newline for plain Enter on mobile", () => {
21 expect(shouldSubmitComposerOnEnter({ isMobileViewport: true, shiftKey: false })).toBe(false);
22 });
23
24 it("inserts a newline for Shift+Enter", () => {
25 expect(shouldSubmitComposerOnEnter({ isMobileViewport: false, shiftKey: true })).toBe(false);
26 });
27});
28
14describe("detectComposerTrigger", () => {29describe("detectComposerTrigger", () => {
15 it("detects @path trigger at cursor", () => {30 it("detects @path trigger at cursor", () => {
16 const text = "Please check @src/com";31 const text = "Please check @src/com";
apps/web/src/composer-logic.ts+7
...@@ -11,6 +11,13 @@ export interface ComposerTrigger {...@@ -11,6 +11,13 @@ export interface ComposerTrigger {
11 rangeEnd: number;11 rangeEnd: number;
12}12}
1313
14export function shouldSubmitComposerOnEnter(input: {
15 isMobileViewport: boolean;
16 shiftKey: boolean;
17}): boolean {
18 return !input.isMobileViewport && !input.shiftKey;
19}
20
14const isInlineTokenSegment = (21const isInlineTokenSegment = (
15 segment:22 segment:
16 | { type: "text"; text: string }23 | { type: "text"; text: string }