From 3503a9da8e701a5f0398fd0a2bd752a21afb21e2 Mon Sep 17 00:00:00 2001 From: Su Date: Sat, 1 Aug 2026 22:45:36 +0800 Subject: [PATCH] Fix inline rename during IME composition (#519) --- packages/ui/src/components/tree/TreeItem.tsx | 3 +++ .../ui/src/components/tree/keyboard.test.ts | 20 +++++++++++++++++++ packages/ui/src/components/tree/keyboard.ts | 7 +++++++ 3 files changed, 30 insertions(+) create mode 100644 packages/ui/src/components/tree/keyboard.test.ts create mode 100644 packages/ui/src/components/tree/keyboard.ts diff --git a/packages/ui/src/components/tree/TreeItem.tsx b/packages/ui/src/components/tree/TreeItem.tsx index 6898d114..3f14ffd7 100644 --- a/packages/ui/src/components/tree/TreeItem.tsx +++ b/packages/ui/src/components/tree/TreeItem.tsx @@ -20,6 +20,7 @@ import { } from "./context"; import type { TreeNode } from "./common"; import { getNodeKey } from "./common"; +import { isImeCompositionEvent } from "./keyboard"; import type { TreeProps } from "./Tree"; import { TreeIndentGuide } from "./TreeIndentGuide"; @@ -170,6 +171,8 @@ function TreeItem_({ const handleEditKeyDown = useCallback( async (e: ReactKeyboardEvent) => { e.stopPropagation(); // Don't trigger other tree keys (like arrows) + if (isImeCompositionEvent(e.nativeEvent)) return; + switch (e.key) { case "Enter": if (editing) { diff --git a/packages/ui/src/components/tree/keyboard.test.ts b/packages/ui/src/components/tree/keyboard.test.ts new file mode 100644 index 00000000..de7c0fe0 --- /dev/null +++ b/packages/ui/src/components/tree/keyboard.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, test } from "vite-plus/test"; +import { isImeCompositionEvent } from "./keyboard"; + +describe("isImeCompositionEvent", () => { + test("detects an active standards-based composition", () => { + expect(isImeCompositionEvent({ isComposing: true, keyCode: 13 })).toBe(true); + }); + + test("detects the Safari/WebKit key code fallback", () => { + expect(isImeCompositionEvent({ isComposing: false, keyCode: 229 })).toBe(true); + }); + + test("does not classify an ordinary Enter keydown as composition", () => { + expect(isImeCompositionEvent({ isComposing: false, keyCode: 13 })).toBe(false); + }); + + test("does not classify an ordinary Escape keydown as composition", () => { + expect(isImeCompositionEvent({ isComposing: false, keyCode: 27 })).toBe(false); + }); +}); diff --git a/packages/ui/src/components/tree/keyboard.ts b/packages/ui/src/components/tree/keyboard.ts new file mode 100644 index 00000000..beecf099 --- /dev/null +++ b/packages/ui/src/components/tree/keyboard.ts @@ -0,0 +1,7 @@ +export type ImeKeyboardEvent = Pick; + +export function isImeCompositionEvent(event: ImeKeyboardEvent): boolean { + // Safari can clear `isComposing` on the keydown that finishes composition. + // `229` is retained as the compatibility signal that an IME is processing it. + return event.isComposing || event.keyCode === 229; +}