fix(ai): ignore IME composition enter

This commit is contained in:
t8y2 2026-05-19 15:56:34 +08:00
parent 077aaa5c06
commit 613c2f981f
3 changed files with 55 additions and 1 deletions

View File

@ -66,6 +66,7 @@ import { useDatabaseOptions } from "@/composables/useDatabaseOptions";
import { resolveDefaultDatabase } from "@/lib/defaultDatabase";
import { isSchemaAware } from "@/lib/databaseCapabilities";
import { formatAiTableMention, parseAiTableMentions, type AiTableMention } from "@/lib/aiTableMentions";
import { isAiPromptImeCompositionEvent, shouldSubmitAiPromptOnKeydown } from "@/lib/aiPromptKeyboard";
const { t } = useI18n();
const settings = useSettingsStore();
@ -104,6 +105,7 @@ const conversationId = ref("");
const conversations = ref<AiConversation[]>([]);
const showConversationList = ref(false);
const promptTextareaRef = ref<HTMLTextAreaElement | null>(null);
const promptCompositionActive = ref(false);
interface AiMentionCandidate {
schema?: string;
@ -430,6 +432,8 @@ function insertMention(candidate: AiMentionCandidate) {
}
function onPromptKeydown(event: KeyboardEvent) {
if (isAiPromptImeCompositionEvent(event, promptCompositionActive.value)) return;
if (mentionOpen.value) {
if (event.key === "ArrowDown") {
event.preventDefault();
@ -456,7 +460,7 @@ function onPromptKeydown(event: KeyboardEvent) {
}
}
if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
if (shouldSubmitAiPromptOnKeydown(event, promptCompositionActive.value)) {
event.preventDefault();
send();
}
@ -916,6 +920,8 @@ const messageRenderer = createAiMessageRenderer({ markdown: formatInlineText });
@input="refreshMentionState"
@click="refreshMentionState"
@keyup="refreshMentionState"
@compositionstart="promptCompositionActive = true"
@compositionend="promptCompositionActive = false"
@keydown="onPromptKeydown"
/>
<div class="flex items-center gap-1.5">

View File

@ -0,0 +1,17 @@
export interface AiPromptKeydownLikeEvent {
key: string;
shiftKey?: boolean;
isComposing?: boolean;
keyCode?: number;
}
export function isAiPromptImeCompositionEvent(event: AiPromptKeydownLikeEvent, compositionActive = false): boolean {
return compositionActive || !!event.isComposing || event.keyCode === 229 || event.key === "Process";
}
export function shouldSubmitAiPromptOnKeydown(event: AiPromptKeydownLikeEvent, compositionActive = false): boolean {
if (event.key !== "Enter") return false;
if (event.shiftKey) return false;
if (isAiPromptImeCompositionEvent(event, compositionActive)) return false;
return true;
}

View File

@ -0,0 +1,31 @@
import { strict as assert } from "node:assert";
import test from "node:test";
import {
isAiPromptImeCompositionEvent,
shouldSubmitAiPromptOnKeydown,
} from "../../apps/desktop/src/lib/aiPromptKeyboard.ts";
test("submits AI prompt on plain Enter", () => {
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Enter" }), true);
});
test("does not submit AI prompt while entering a newline", () => {
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Enter", shiftKey: true }), false);
});
test("does not submit AI prompt while IME composition is active", () => {
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Enter", isComposing: true }), false);
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Enter" }, true), false);
});
test("does not submit AI prompt for IME composition key events", () => {
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Enter", keyCode: 229 }), false);
assert.equal(shouldSubmitAiPromptOnKeydown({ key: "Process" }), false);
});
test("detects AI prompt IME composition key events before mention handling", () => {
assert.equal(isAiPromptImeCompositionEvent({ key: "Enter", isComposing: true }), true);
assert.equal(isAiPromptImeCompositionEvent({ key: "Enter", keyCode: 229 }), true);
assert.equal(isAiPromptImeCompositionEvent({ key: "Process" }), true);
assert.equal(isAiPromptImeCompositionEvent({ key: "Enter" }), false);
});