From 13b75f46bf8dc5b78e9e8450ff57cb30b038b090 Mon Sep 17 00:00:00 2001 From: Leonabcd123 <156839416+Leonabcd123@users.noreply.github.com> Date: Sat, 15 Nov 2025 11:46:31 +0200 Subject: [PATCH] feat(test): add indicate typos both (@Leonabcd123) (#7072) ### Description Added a "both" option to indicate typos that keeps the replace functionality, and makes the below functionality show the correct instead of the incorrect letters. We just check whether the mode is `both` when deciding whether to pass `input` to `createHintsHtml` or to pass `currentWord`. All functionality of replace and below is kept by just adding or operators to check whether indicateTypos is `both` OR replace/below. Implementing #7024 --------- Co-authored-by: Jack --- frontend/src/html/pages/settings.html | 7 +++++-- .../src/ts/controllers/input-controller.ts | 6 +++++- frontend/src/ts/test/test-ui.ts | 20 +++++++++++++------ packages/schemas/src/configs.ts | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html index 945469999..2e1a828ba 100644 --- a/frontend/src/html/pages/settings.html +++ b/frontend/src/html/pages/settings.html @@ -538,13 +538,16 @@
- Shows typos that you've made. Below shows what you typed below the - letters and replace will replace the letters with the ones you typed. + Shows typos that you've made. "Below" shows what you typed below the + letters, "replace" will replace the letters with the ones you typed and + "both" will do the same as replace and below, but it will show the + correct letters below your mistakes.
+
diff --git a/frontend/src/ts/controllers/input-controller.ts b/frontend/src/ts/controllers/input-controller.ts index ce69932a6..32ef91efc 100644 --- a/frontend/src/ts/controllers/input-controller.ts +++ b/frontend/src/ts/controllers/input-controller.ts @@ -691,7 +691,11 @@ async function handleChar( !TestUI.lineTransition // TestInput.input.current.length > 1 ) { - if (Config.mode === "zen" || Config.indicateTypos === "replace") { + if ( + Config.mode === "zen" || + Config.indicateTypos === "replace" || + Config.indicateTypos === "both" + ) { if (!Config.showAllLines) void TestUI.lineJump(activeWordTopBeforeJump); } else { TestInput.input.current = TestInput.input.current.slice(0, -1); diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index b51fd955d..226d62f26 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -331,7 +331,7 @@ async function updateHintsPosition(): Promise { if ( ActivePage.get() !== "test" || TestState.resultVisible || - Config.indicateTypos !== "below" + (Config.indicateTypos !== "below" && Config.indicateTypos !== "both") ) return; @@ -436,7 +436,7 @@ function updateWordWrapperClasses(): void { $("#wordsWrapper").removeClass("blind"); } - if (Config.indicateTypos === "below") { + if (Config.indicateTypos === "below" || Config.indicateTypos === "both") { $("#words").addClass("indicateTyposBelow"); $("#wordsWrapper").addClass("indicateTyposBelow"); } else { @@ -791,7 +791,7 @@ export async function updateActiveWordLetters( !(containsKorean && !correctSoFar) ) { ret += `${ - Config.indicateTypos === "replace" + Config.indicateTypos === "replace" || Config.indicateTypos === "both" ? inputChars[i] === " " ? "_" : inputChars[i] @@ -806,13 +806,16 @@ export async function updateActiveWordLetters( } else { ret += `` + - (Config.indicateTypos === "replace" + (Config.indicateTypos === "replace" || Config.indicateTypos === "both" ? inputChars[i] === " " ? "_" : inputChars[i] : currentLetter) + ""; - if (Config.indicateTypos === "below") { + if ( + Config.indicateTypos === "below" || + Config.indicateTypos === "both" + ) { const lastBlock = hintIndices[hintIndices.length - 1]; if (lastBlock && lastBlock[lastBlock.length - 1] === i - 1) lastBlock.push(i); @@ -839,7 +842,12 @@ export async function updateActiveWordLetters( if (hintIndices?.length) { const activeWordLetters = activeWord.querySelectorAll("letter"); - const hintsHtml = createHintsHtml(hintIndices, activeWordLetters, input); + let hintsHtml; + if (Config.indicateTypos === "both") { + hintsHtml = createHintsHtml(hintIndices, activeWordLetters, currentWord); + } else { + hintsHtml = createHintsHtml(hintIndices, activeWordLetters, input); + } activeWord.insertAdjacentHTML("beforeend", hintsHtml); const hintElements = activeWord.getElementsByTagName("hint"); await joinOverlappingHints(hintIndices, activeWordLetters, hintElements); diff --git a/packages/schemas/src/configs.ts b/packages/schemas/src/configs.ts index 020a17007..605c55630 100644 --- a/packages/schemas/src/configs.ts +++ b/packages/schemas/src/configs.ts @@ -50,7 +50,7 @@ export type CaretStyle = z.infer; export const ConfidenceModeSchema = z.enum(["off", "on", "max"]); export type ConfidenceMode = z.infer; -export const IndicateTyposSchema = z.enum(["off", "below", "replace"]); +export const IndicateTyposSchema = z.enum(["off", "below", "replace", "both"]); export type IndicateTypos = z.infer; export const TimerStyleSchema = z.enum(["off", "bar", "text", "mini"]);