From f99c843e8b395c150a4b2471cde2fe66c4c2d2d6 Mon Sep 17 00:00:00 2001 From: Miodec Date: Fri, 5 Dec 2025 15:11:03 +0100 Subject: [PATCH 1/3] fix(tape mode): some words sometimes being removed on test restart --- frontend/src/ts/test/test-ui.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index b647c4dcdcac..edacfd0e1ac6 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -704,6 +704,7 @@ export function updateWordsWrapperHeight(force = false): void { function updateWordsMargin(): void { if (Config.tapeMode !== "off") { + wordsEl.style.marginLeft = "0"; void scrollTape(true); } else { const afterNewlineEls = From e90e35e71097e00066919f19b090a8084b4b0fb7 Mon Sep 17 00:00:00 2001 From: Miodec Date: Fri, 5 Dec 2025 15:14:35 +0100 Subject: [PATCH 2/3] fix(tape mode): tab and newline characters breaking caret alignment --- frontend/src/styles/test.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/styles/test.scss b/frontend/src/styles/test.scss index 7c04553d5a6a..1dab38ddaee2 100644 --- a/frontend/src/styles/test.scss +++ b/frontend/src/styles/test.scss @@ -242,7 +242,7 @@ } &.tabChar, &.nlChar { - margin: 0 0.25rem; + // margin: 0 0.25rem; opacity: 0.2; i { line-height: 0; From 12f5c5ecd063be11654fab4026a0600c7249d348 Mon Sep 17 00:00:00 2001 From: Miodec Date: Fri, 5 Dec 2025 15:43:03 +0100 Subject: [PATCH 3/3] chore: protect against word letter self overflow --- .../src/ts/input/handlers/before-insert-text.ts | 15 ++++++++++----- frontend/src/ts/test/test-ui.ts | 16 +++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/frontend/src/ts/input/handlers/before-insert-text.ts b/frontend/src/ts/input/handlers/before-insert-text.ts index 9ca90677ff63..b51d585a7a00 100644 --- a/frontend/src/ts/input/handlers/before-insert-text.ts +++ b/frontend/src/ts/input/handlers/before-insert-text.ts @@ -88,11 +88,16 @@ export function onBeforeInsertText(data: string): boolean { const pendingWordData = TestUI.pendingWordData.get( TestState.activeWordIndex, ); - const topAfterAppend = TestUI.getActiveWordTopWithDifferentData( - (pendingWordData ?? TestInput.input.current) + data, - ); - const wordJumped = topAfterAppend > TestUI.activeWordTop; - if (wordJumped) { + const { top: topAfterAppend, height: heightAfterAppend } = + TestUI.getActiveWordTopAndHeightWithDifferentData( + (pendingWordData ?? TestInput.input.current) + data, + ); + if (topAfterAppend > TestUI.activeWordTop) { + //word jumped to next line + return true; + } + if (heightAfterAppend > TestUI.activeWordHeight) { + // letters wrapped to next line return true; } } diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index edacfd0e1ac6..ec867261db1c 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -153,15 +153,11 @@ const wordsWrapperEl = document.querySelector( ) as HTMLElement; export let activeWordTop = 0; +export let activeWordHeight = 0; export let lineTransition = false; export let currentTestLine = 0; export let resultCalculating = false; -export function setActiveWordTop(): void { - const activeWord = getActiveWordElement(); - activeWordTop = activeWord?.offsetTop ?? 0; -} - export function setResultCalculating(val: boolean): void { resultCalculating = val; } @@ -250,6 +246,7 @@ export function updateActiveElement( newActiveWord.classList.remove("typed"); activeWordTop = newActiveWord.offsetTop; + activeWordHeight = newActiveWord.offsetHeight; console.log("activewordtopupdated"); updateWordsInputPosition(); @@ -1274,6 +1271,7 @@ export async function lineJump( onComplete: () => { currentLinesJumping = 0; activeWordTop = activeWordEl.offsetTop; + activeWordHeight = activeWordEl.offsetHeight; removeTestElements(lastElementIndexToRemove); wordsEl.style.marginTop = "0"; lineTransition = false; @@ -1732,7 +1730,10 @@ function updateLiveStatsColor(value: TimerColor): void { } } -export function getActiveWordTopWithDifferentData(data: string): number { +export function getActiveWordTopAndHeightWithDifferentData(data: string): { + top: number; + height: number; +} { const activeWord = getActiveWordElement(); if (!activeWord) throw new Error("No active word element found"); @@ -1748,11 +1749,12 @@ export function getActiveWordTopWithDifferentData(data: string): number { activeWord.append(...nodes); const top = activeWord.offsetTop; + const height = activeWord.offsetHeight; for (const node of nodes) { node.remove(); } - return top; + return { top, height }; } // this means input, delete or composition