Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions frontend/src/ts/input/listeners/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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) => {
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/ts/test/test-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

Expand Down