From ef529de0ac6e7e2777bee567112f0ff69f5beeb6 Mon Sep 17 00:00:00 2001 From: Hypo <74409933+Theproudcold@users.noreply.github.com> Date: Sun, 8 Feb 2026 14:39:09 +0800 Subject: [PATCH] Prevent navigation keys from triggering actions during IME composition (#36540) Fixes #36532 Refined the Enter key trigger logic in the repository filter to prevent actions during IME composition. By checking the e.isComposing property, the filter now correctly distinguishes between "confirming an IME candidate" and "submitting the search." This prevents premature search triggers when users press Enter to select Chinese/Japanese characters. --------- Co-authored-by: wxiaoguang --- web_src/js/components/DashboardRepoList.vue | 1 + web_src/js/components/RepoBranchTagSelector.vue | 1 + web_src/js/components/RepoFileSearch.vue | 2 ++ web_src/js/features/common-form.ts | 1 + web_src/js/features/comp/EditorMarkdown.ts | 1 + web_src/js/features/repo-issue.ts | 1 + web_src/js/modules/fomantic/dropdown.ts | 1 + web_src/js/webcomponents/overflow-menu.ts | 1 + 8 files changed, 9 insertions(+) diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index e1f8475ea8..82e972ceaf 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -307,6 +307,7 @@ export default defineComponent({ }, async reposFilterKeyControl(e: KeyboardEvent) { + if (e.isComposing) return; switch (e.key) { case 'Enter': document.querySelector('.repo-owner-name-list li.active a')?.click(); diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index a83227d43b..ab32baed37 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -159,6 +159,7 @@ export default defineComponent({ return el?.length ? el[0] : null; }, keydown(e: KeyboardEvent) { + if (e.isComposing) return; if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { e.preventDefault(); diff --git a/web_src/js/components/RepoFileSearch.vue b/web_src/js/components/RepoFileSearch.vue index dd93de9850..f0c63267bc 100644 --- a/web_src/js/components/RepoFileSearch.vue +++ b/web_src/js/components/RepoFileSearch.vue @@ -42,6 +42,8 @@ const handleSearchInput = () => { }; const handleKeyDown = (e: KeyboardEvent) => { + if (e.isComposing) return; + if (e.key === 'Escape') { e.preventDefault(); clearSearch(); diff --git a/web_src/js/features/common-form.ts b/web_src/js/features/common-form.ts index fe201e2a01..67bcd55f52 100644 --- a/web_src/js/features/common-form.ts +++ b/web_src/js/features/common-form.ts @@ -14,6 +14,7 @@ export function initGlobalFormDirtyLeaveConfirm() { export function initGlobalEnterQuickSubmit() { document.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (e.key !== 'Enter') return; const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey); if (hasCtrlOrMeta && (e.target as HTMLElement).matches('textarea')) { diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index cdc9bdaf9a..d95760e841 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -223,6 +223,7 @@ function isTextExpanderShown(textarea: HTMLElement): boolean { export function initTextareaMarkdown(textarea: HTMLTextAreaElement) { textarea.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (isTextExpanderShown(textarea)) return; if (e.key === 'Tab' && !e.ctrlKey && !e.metaKey && !e.altKey) { // use Tab/Shift-Tab to indent/unindent the selected lines diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 7145936ed1..4b04ed4ccf 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -82,6 +82,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) { }); // alt(or option) + enter to exclude selected label elDropdown.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.isComposing) return; if (e.altKey && e.key === 'Enter') { const selectedItem = elDropdown.querySelector('.label-filter-query-item.selected'); if (selectedItem) excludeLabel(e, selectedItem); diff --git a/web_src/js/modules/fomantic/dropdown.ts b/web_src/js/modules/fomantic/dropdown.ts index 79ca60d347..7f7f3611be 100644 --- a/web_src/js/modules/fomantic/dropdown.ts +++ b/web_src/js/modules/fomantic/dropdown.ts @@ -225,6 +225,7 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT }; dropdown.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.isComposing) return; // here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler if (e.key === 'Enter') { const elItem = menu.querySelector(':scope > .item.selected, .menu > .item.selected'); diff --git a/web_src/js/webcomponents/overflow-menu.ts b/web_src/js/webcomponents/overflow-menu.ts index b1294eae3a..1ddd984c12 100644 --- a/web_src/js/webcomponents/overflow-menu.ts +++ b/web_src/js/webcomponents/overflow-menu.ts @@ -22,6 +22,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement { const div = document.createElement('div'); div.tabIndex = -1; // for initial focus, programmatic focus only div.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (e.key === 'Tab') { const items = this.tippyContent.querySelectorAll('[role="menuitem"]'); if (e.shiftKey) {