diff --git a/apps/desktop/src/plugins/kanban/board.tsx b/apps/desktop/src/plugins/kanban/board.tsx index 2124e8dcd0..b8eade333b 100644 --- a/apps/desktop/src/plugins/kanban/board.tsx +++ b/apps/desktop/src/plugins/kanban/board.tsx @@ -79,6 +79,7 @@ import { } from './api' import { BoardSwitcher } from './board-switcher' import { TaskDrawer } from './drawer' +import { shouldSubmitOnEnter } from './ime-enter' import { EMPTY_OVERRIDE, ModelOverrideField, overrideCreateFields, type TaskModelOverride } from './model-override' import { OrchestrationPanel } from './orchestration' import { columnMeta, type KanbanBoard, type KanbanTask, type TaskEstimate } from './types' @@ -683,7 +684,7 @@ function NewTaskDialog({ autoFocus onChange={event => setTitle(event.target.value)} onKeyDown={event => { - if (event.key === 'Enter') { + if (shouldSubmitOnEnter(event)) { event.preventDefault() void submit() } diff --git a/apps/desktop/src/plugins/kanban/ime-enter.test.ts b/apps/desktop/src/plugins/kanban/ime-enter.test.ts new file mode 100644 index 0000000000..b0180b69e0 --- /dev/null +++ b/apps/desktop/src/plugins/kanban/ime-enter.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' + +import { shouldSubmitOnEnter } from './ime-enter' + +describe('shouldSubmitOnEnter', () => { + it('submits a normal Enter keypress', () => { + expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: {} })).toBe(true) + }) + + it('does not submit while IME composition is active', () => { + expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: { isComposing: true } })).toBe(false) + }) + + it('does not submit Chromium composition-boundary keyCode 229', () => { + expect(shouldSubmitOnEnter({ key: 'Enter', nativeEvent: { keyCode: 229 } })).toBe(false) + }) + + it('does not submit non-Enter keys', () => { + expect(shouldSubmitOnEnter({ key: 'Escape', nativeEvent: {} })).toBe(false) + }) +}) diff --git a/apps/desktop/src/plugins/kanban/ime-enter.ts b/apps/desktop/src/plugins/kanban/ime-enter.ts new file mode 100644 index 0000000000..53941e0f02 --- /dev/null +++ b/apps/desktop/src/plugins/kanban/ime-enter.ts @@ -0,0 +1,16 @@ +export interface ImeKeyEvent { + key: string + nativeEvent: { + isComposing?: boolean + keyCode?: number + } +} + +/** + * Enter confirms an IME conversion before it should act as a submit shortcut. + * Chromium can report the legacy 229 keyCode around composition boundaries, + * so keep that fallback in addition to the standard isComposing signal. + */ +export function shouldSubmitOnEnter(event: ImeKeyEvent): boolean { + return event.key === 'Enter' && !event.nativeEvent.isComposing && event.nativeEvent.keyCode !== 229 +}