fix(composition): stabilize IME preedit layout for JP input (@FosterHoulston)

This commit is contained in:
Foster Houlston 2025-12-21 13:37:52 -08:00
parent 5d169e933a
commit 2ab9aca9ff
2 changed files with 26 additions and 6 deletions

View file

@ -6,6 +6,9 @@ import { setLastInsertCompositionTextData } from "../state";
import * as CompositionDisplay from "../../elements/composition-display";
import { onInsertText } from "../handlers/insert-text";
import * as TestUI from "../../test/test-ui";
import * as TestWords from "../../test/test-words";
import * as TestInput from "../../test/test-input";
import * as Strings from "../../utils/strings";
const inputEl = getInputElement();
@ -31,8 +34,22 @@ inputEl.addEventListener("compositionupdate", (event) => {
});
if (TestState.testRestarting || TestUI.resultCalculating) return;
CompositionState.setData(event.data);
CompositionDisplay.update(event.data);
const currentWord = TestWords.words.getCurrent();
const typedSoFar = TestInput.input.current;
const remainingChars =
Strings.splitIntoCharacters(currentWord).length -
Strings.splitIntoCharacters(typedSoFar).length;
// Prevent rendering more composition glyphs than the word has remaining letters,
// so IME preedit strings (e.g. romaji) don't push text to the next line.
const limitedCompositionData =
remainingChars > 0
? Strings.splitIntoCharacters(event.data)
.slice(0, remainingChars)
.join("")
: "";
CompositionState.setData(limitedCompositionData);
CompositionDisplay.update(limitedCompositionData);
});
inputEl.addEventListener("compositionend", async (event) => {

View file

@ -805,15 +805,18 @@ export async function updateWordLetters({
for (let i = 0; i < compositionData.length; i++) {
const compositionChar = compositionData[i];
let charToShow =
currentWordChars[input.length + i] ?? compositionChar;
// Render the target character (if known) during composition to keep line width stable,
// falling back to the preedit char when beyond the word length.
const targetChar = currentWordChars[input.length + i];
let charToShow = targetChar ?? compositionChar;
if (Config.compositionDisplay === "replace") {
charToShow = compositionChar === " " ? "_" : compositionChar;
charToShow =
targetChar ?? (compositionChar === " " ? "_" : compositionChar);
}
let correctClass = "";
if (compositionChar === currentWordChars[input.length + i]) {
if (compositionChar === targetChar) {
correctClass = "correct";
}