From 932bbf2bb501d9b451f6827aaad84eada15d762b Mon Sep 17 00:00:00 2001 From: Jiayuan Zhang Date: Fri, 10 Jul 2026 22:44:23 +0800 Subject: [PATCH] fix(editor): guard Mod-Enter submit against open IME composition (#5231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare-Enter submit path already refuses to fire while view.composing is true, but Mod-Enter had no such guard. Pressing ⌘↵ while a pinyin/kana composition is still open submits the document WITHOUT the composed text — e.g. paste a screenshot, type a Chinese sentence, hit ⌘↵ before the buffer commits, and the submission carries only the screenshot. Apply the same composing guard to Mod-Enter. Co-authored-by: Lambda Co-authored-by: multica-agent --- .../editor/extensions/submit-shortcut.test.ts | 18 ++++++++++++++++-- .../views/editor/extensions/submit-shortcut.ts | 10 +++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/views/editor/extensions/submit-shortcut.test.ts b/packages/views/editor/extensions/submit-shortcut.test.ts index 4044214473..65c4dafb30 100644 --- a/packages/views/editor/extensions/submit-shortcut.test.ts +++ b/packages/views/editor/extensions/submit-shortcut.test.ts @@ -25,7 +25,7 @@ describe("createSubmitExtension", () => { isActive: () => false, } as Partial; - it("Mod-Enter always submits", () => { + it("Mod-Enter submits when no composition is open", () => { const onSubmit = vi.fn(() => true); const shortcuts = getShortcuts( createSubmitExtension(onSubmit, { submitOnEnter: false }), @@ -33,10 +33,24 @@ describe("createSubmitExtension", () => { ); expect(shortcuts["Mod-Enter"]).toBeDefined(); - shortcuts["Mod-Enter"]!(); + expect(shortcuts["Mod-Enter"]!()).toBe(true); expect(onSubmit).toHaveBeenCalledTimes(1); }); + it("Mod-Enter is suppressed during IME composition", () => { + const onSubmit = vi.fn(() => true); + const shortcuts = getShortcuts( + createSubmitExtension(onSubmit, { submitOnEnter: false }), + { + view: { composing: true } as unknown as Editor["view"], + isActive: () => false, + }, + ); + + expect(shortcuts["Mod-Enter"]!()).toBe(false); + expect(onSubmit).not.toHaveBeenCalled(); + }); + it("bare Enter is not bound when submitOnEnter is false", () => { const onSubmit = vi.fn(() => true); const shortcuts = getShortcuts( diff --git a/packages/views/editor/extensions/submit-shortcut.ts b/packages/views/editor/extensions/submit-shortcut.ts index aea7b1904b..94654e2f98 100644 --- a/packages/views/editor/extensions/submit-shortcut.ts +++ b/packages/views/editor/extensions/submit-shortcut.ts @@ -16,7 +16,15 @@ export function createSubmitExtension( name: "submitShortcut", addKeyboardShortcuts() { const shortcuts: Record boolean> = { - "Mod-Enter": () => onSubmit(), + "Mod-Enter": () => { + // IME guard — same as Enter below. While a composition is open the + // composed text is not in the document yet, so submitting here + // would send the doc WITHOUT what the user just typed (e.g. paste + // a screenshot, type a pinyin sentence, hit ⌘↵ before the buffer + // commits — the submission carries only the screenshot). + if (this.editor.view.composing) return false; + return onSubmit(); + }, }; if (submitOnEnter) { shortcuts.Enter = () => {