From d7d3078de5a25a20c8938818dd5c4b675c71a3d4 Mon Sep 17 00:00:00 2001 From: Weijie Zhao Date: Tue, 7 Oct 2025 00:17:52 +0800 Subject: [PATCH] fix: prevent keyboard events during IME composition (#1535) --- .../src/components/gantt/primitives/GanttChartPrimitive.vue | 3 +++ .../src/components/gantt/primitives/GanttRowPrimitive.vue | 3 +++ frontend/src/components/input/editor/CommandsList.vue | 3 +++ frontend/src/components/input/editor/suggestion.ts | 4 ++++ frontend/src/components/input/filter/FilterCommandsList.vue | 3 +++ frontend/src/components/misc/Modal.vue | 3 +++ frontend/src/components/project/views/ProjectKanban.vue | 4 ++-- frontend/src/components/project/views/ProjectList.vue | 3 +++ frontend/src/components/tasks/AddTask.vue | 4 ++++ frontend/src/components/tasks/partials/Heading.vue | 4 ++-- frontend/src/helpers/inputPrompt.ts | 4 ++++ 11 files changed, 34 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/gantt/primitives/GanttChartPrimitive.vue b/frontend/src/components/gantt/primitives/GanttChartPrimitive.vue index 6b751a618..514648361 100644 --- a/frontend/src/components/gantt/primitives/GanttChartPrimitive.vue +++ b/frontend/src/components/gantt/primitives/GanttChartPrimitive.vue @@ -49,6 +49,9 @@ function onKeyDown(e: KeyboardEvent) { if (focusedRowIndex.value === null || focusedCellIndex.value === null) return if (e.key === 'Enter') { + if (e.isComposing) { + return + } e.preventDefault() emit('enterPressed', { row: focusedRow.value!, cell: focusedCellIndex.value }) return diff --git a/frontend/src/components/gantt/primitives/GanttRowPrimitive.vue b/frontend/src/components/gantt/primitives/GanttRowPrimitive.vue index 933146d0f..c97005c93 100644 --- a/frontend/src/components/gantt/primitives/GanttRowPrimitive.vue +++ b/frontend/src/components/gantt/primitives/GanttRowPrimitive.vue @@ -36,6 +36,9 @@ function onFocus() { function onKeyDown(e: KeyboardEvent) { if (e.key === 'Enter' || e.key === ' ') { + if (e.isComposing) { + return + } e.preventDefault() onSelect() } diff --git a/frontend/src/components/input/editor/CommandsList.vue b/frontend/src/components/input/editor/CommandsList.vue index 0243d0e01..8d76c0371 100644 --- a/frontend/src/components/input/editor/CommandsList.vue +++ b/frontend/src/components/input/editor/CommandsList.vue @@ -64,6 +64,9 @@ export default { } if (event.key === 'Enter') { + if (event.isComposing) { + return false + } this.enterHandler() return true } diff --git a/frontend/src/components/input/editor/suggestion.ts b/frontend/src/components/input/editor/suggestion.ts index fa2d9f65e..7d6c4a44b 100644 --- a/frontend/src/components/input/editor/suggestion.ts +++ b/frontend/src/components/input/editor/suggestion.ts @@ -233,6 +233,10 @@ export default function suggestionSetup(t) { onKeyDown(props) { if (props.event.key === 'Escape') { + if (props.event.isComposing) { + return false + } + if (popupElement) { popupElement.style.display = 'none' } diff --git a/frontend/src/components/input/filter/FilterCommandsList.vue b/frontend/src/components/input/filter/FilterCommandsList.vue index 08e9e6da9..fe1348bd4 100644 --- a/frontend/src/components/input/filter/FilterCommandsList.vue +++ b/frontend/src/components/input/filter/FilterCommandsList.vue @@ -78,6 +78,9 @@ function onKeyDown({event}: { event: KeyboardEvent }) { } if (event.key === 'Enter') { + if (event.isComposing) { + return false + } event.preventDefault() event.stopPropagation() enterHandler() diff --git a/frontend/src/components/misc/Modal.vue b/frontend/src/components/misc/Modal.vue index 165f4963e..ac71ab526 100644 --- a/frontend/src/components/misc/Modal.vue +++ b/frontend/src/components/misc/Modal.vue @@ -102,6 +102,9 @@ watchEffect(() => { function onKeydown(e: KeyboardEvent) { if (e.key === 'Escape') { + if (e.isComposing) { + return + } emit('close') } } diff --git a/frontend/src/components/project/views/ProjectKanban.vue b/frontend/src/components/project/views/ProjectKanban.vue index 961dd166a..30244fe79 100644 --- a/frontend/src/components/project/views/ProjectKanban.vue +++ b/frontend/src/components/project/views/ProjectKanban.vue @@ -56,8 +56,8 @@ class="title input" :contenteditable="(bucketTitleEditable && canWrite && !collapsedBuckets[bucket.id]) ? true : undefined" :spellcheck="false" - @keydown.enter.prevent.stop="($event.target as HTMLElement).blur()" - @keydown.esc.prevent.stop="($event.target as HTMLElement).blur()" + @keydown.enter.prevent.stop="!$event.isComposing && ($event.target as HTMLElement).blur()" + @keydown.esc.prevent.stop="!$event.isComposing && ($event.target as HTMLElement).blur()" @blur="saveBucketTitle(bucket.id, ($event.target as HTMLElement).textContent as string)" @click="focusBucketTitle" > diff --git a/frontend/src/components/project/views/ProjectList.vue b/frontend/src/components/project/views/ProjectList.vue index b3a1199f8..da0437ccd 100644 --- a/frontend/src/components/project/views/ProjectList.vue +++ b/frontend/src/components/project/views/ProjectList.vue @@ -314,6 +314,9 @@ function handleListNavigation(e: KeyboardEvent) { } if (e.key === 'Enter') { + if (e.isComposing) { + return + } e.preventDefault() taskRefs.value[focusedIndex.value]?.click(e) } diff --git a/frontend/src/components/tasks/AddTask.vue b/frontend/src/components/tasks/AddTask.vue index 0a293906f..1ce0b1d3c 100644 --- a/frontend/src/components/tasks/AddTask.vue +++ b/frontend/src/components/tasks/AddTask.vue @@ -270,6 +270,10 @@ function handleEnter(e: KeyboardEvent) { return } + if (e.isComposing) { + return + } + e.preventDefault() addTask() } diff --git a/frontend/src/components/tasks/partials/Heading.vue b/frontend/src/components/tasks/partials/Heading.vue index b3c780ee4..9209a7714 100644 --- a/frontend/src/components/tasks/partials/Heading.vue +++ b/frontend/src/components/tasks/partials/Heading.vue @@ -29,8 +29,8 @@ :contenteditable="canWrite ? true : undefined" :spellcheck="false" @blur="save(($event.target as HTMLInputElement).textContent as string)" - @keydown.enter.prevent.stop="($event.target as HTMLInputElement).blur()" - @keydown.esc.prevent.stop="cancel($event.target as HTMLInputElement)" + @keydown.enter.prevent.stop="!$event.isComposing && ($event.target as HTMLInputElement).blur()" + @keydown.esc.prevent.stop="!$event.isComposing && cancel($event.target as HTMLInputElement)" > {{ task.title.trim() }} diff --git a/frontend/src/helpers/inputPrompt.ts b/frontend/src/helpers/inputPrompt.ts index 240e48e05..77c5ad42f 100644 --- a/frontend/src/helpers/inputPrompt.ts +++ b/frontend/src/helpers/inputPrompt.ts @@ -53,6 +53,10 @@ export default function inputPrompt(pos: ClientRect, oldValue: string = ''): Pro return } + if (event.isComposing) { + return + } + const url = (event.target as HTMLInputElement).value resolve(url)