mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(composer): keep IME-composition Enter out of the slash-skill catalog `handlePromptTextareaKeyDown` called `handleSkillSuggestionKeyDown` with no composition guard, unlike its two siblings: the inline-skill editor wraps the same call in `isIMEComposing(...)` with a comment naming this exact hazard, and `handlePromptHistoryKeyDown` checks it too. `PromptInputTextarea` calls the prop's `onKeyDown` *before* its own `isIMEComposing` check, so the suggestion handler won the event. With an IME active and the `/` catalog open, pressing Enter to commit the composition instead ran `event.preventDefault()` and applied the highlighted suggestion — discarding the text still being composed and selecting a skill the user never chose. The catalog opens from the live `textInput.value`, and React `onChange` fires during composition, so a CJK user typing `/res` to reach a skill hits it directly. Guard the main composer's call site the same way the inline-skill editor does, and pin it with a happy-dom regression test: `keyCode: 229` (the keydown a browser reports for a key an active IME consumed) must leave both the catalog and the draft untouched, while a plain Enter still selects the highlighted skill. Verified: - new test `tests/unit/components/workspace/input-box-skill-suggestion-ime.dom.test.tsx` fails on main (the IME case applies the skill) and passes here; the plain-Enter control case passes on both - `rstest run --pool threads tests/unit/components/workspace` -> 69 files / 448 tests passed - `tsc --noEmit`, `eslint`, `prettier --check` clean on the touched files * fix(composer): ignore the Enter that follows IME compositionend Safari emits the confirming Enter after compositionend, with neither isComposing nor keyCode 229. Drop that Enter in PromptInputTextarea for a short window, before the skill catalog or prompt history can treat it as a plain key. --------- Co-authored-by: Shxiao101 <Shxiao101@users.noreply.github.com>
64 lines
1.6 KiB
TypeScript
64 lines
1.6 KiB
TypeScript
import { describe, expect, it } from "@rstest/core";
|
|
import type { KeyboardEvent } from "react";
|
|
|
|
import {
|
|
COMPOSITION_CONFIRM_ENTER_MS,
|
|
isCompositionConfirmEnter,
|
|
} from "@/lib/ime";
|
|
|
|
function enter(
|
|
overrides: Partial<KeyboardEvent<HTMLElement>> = {},
|
|
): KeyboardEvent<HTMLElement> {
|
|
return {
|
|
key: "Enter",
|
|
shiftKey: false,
|
|
keyCode: 13,
|
|
nativeEvent: { isComposing: false },
|
|
...overrides,
|
|
} as KeyboardEvent<HTMLElement>;
|
|
}
|
|
|
|
describe("isCompositionConfirmEnter", () => {
|
|
const endedAt = 1_000;
|
|
|
|
it("matches the Enter that arrives just after compositionend", () => {
|
|
expect(isCompositionConfirmEnter(enter(), endedAt, endedAt)).toBe(true);
|
|
expect(
|
|
isCompositionConfirmEnter(
|
|
enter(),
|
|
endedAt,
|
|
endedAt + COMPOSITION_CONFIRM_ENTER_MS - 1,
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("lets a later Enter through", () => {
|
|
expect(
|
|
isCompositionConfirmEnter(
|
|
enter(),
|
|
endedAt,
|
|
endedAt + COMPOSITION_CONFIRM_ENTER_MS,
|
|
),
|
|
).toBe(false);
|
|
expect(isCompositionConfirmEnter(enter(), 0, endedAt)).toBe(false);
|
|
});
|
|
|
|
it("does not claim Shift+Enter or a keydown that is still composing", () => {
|
|
expect(
|
|
isCompositionConfirmEnter(enter({ shiftKey: true }), endedAt, endedAt),
|
|
).toBe(false);
|
|
expect(
|
|
isCompositionConfirmEnter(enter({ keyCode: 229 }), endedAt, endedAt),
|
|
).toBe(false);
|
|
expect(
|
|
isCompositionConfirmEnter(
|
|
enter({
|
|
nativeEvent: { isComposing: true } as KeyboardEvent["nativeEvent"],
|
|
}),
|
|
endedAt,
|
|
endedAt,
|
|
),
|
|
).toBe(false);
|
|
});
|
|
});
|