diff --git a/frontend/src/styles/test.scss b/frontend/src/styles/test.scss index 3a8c1e2ef..395e84b87 100644 --- a/frontend/src/styles/test.scss +++ b/frontend/src/styles/test.scss @@ -1,27 +1,3 @@ -@mixin highlightMode($color) { - &.highlight-next-word { - .word.active, - .word.active + .word { - color: $color; - } - } - &.highlight-next-two-words { - .word.active, - .word.active + .word, - .word.active + .word + .word { - color: $color; - } - } - &.highlight-next-three-words { - .word.active, - .word.active + .word, - .word.active + .word + .word, - .word.active + .word + .word + .word { - color: $color; - } - } -} - .highlightContainer { position: absolute; overflow: hidden; @@ -209,22 +185,84 @@ width: inherit; } - letter { - border-bottom-style: solid; - border-bottom-width: 0.05em; - border-bottom-color: transparent; - &.dead { + --correct-letter-color: var(--text-color); + --correct-letter-animation: none; + --untyped-letter-color: var(--sub-color); + --untyped-letter-animation: none; + --incorrect-letter-color: var(--error-color); + --incorrect-letter-animation: none; + --extra-letter-color: var(--error-extra-color); + --extra-letter-animation: none; + + .word { + letter { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); + border-bottom-style: solid; border-bottom-width: 0.05em; - border-bottom-color: var(--sub-color); - } - &.tabChar, - &.nlChar { - margin: 0 0.25rem; - opacity: 0.2; - i { - line-height: 0; + border-bottom-color: transparent; + &.dead { + border-bottom-width: 0.05em; + border-bottom-color: var(--untyped-letter-color); + } + &.tabChar, + &.nlChar { + margin: 0 0.25rem; + opacity: 0.2; + i { + line-height: 0; + } + } + + &.correct { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + } + + &.corrected { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + border-bottom: 2px dotted var(--main-color); + } + + &.extraCorrected { + border-right: 2px dotted var(--main-color); + } + + &.incorrect { + color: var(--incorrect-letter-color); + animation: var(--incorrect-letter-animation); + } + + &.incorrect.extra { + color: var(--extra-letter-color); + animation: var(--extra-letter-animation); + } + + &.missing { + opacity: 0.5; } } + & .hints hint { + position: absolute; + bottom: -1.1em; + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + line-height: initial; + font-size: 0.75em; + text-shadow: none; + padding: 1px; + left: 0; + opacity: 0.5; + text-align: center; + display: grid; + justify-content: center; + transform: translate(-50%); + } + } + + &.tape .word { + margin: 0.25em 0.5em 0.75em 0; } /* a little hack for right-to-left languages */ @@ -247,100 +285,141 @@ -webkit-filter: blur(4px); } - &.flipped { + &.blind { .word { - color: var(--text-color); - - & letter.dead { - border-bottom-color: var(--sub-color) !important; + & letter.extra { + display: none; } - - & letter.correct { - color: var(--sub-color); - } - - & letter.corrected { - color: var(--sub-color); - border-bottom: 2px dotted var(--main-color); - } - - & letter.extraCorrected { - border-right: 2px dotted var(--main-color); + & letter.incorrect { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); } } + } - @include highlightMode(var(--sub-color)); + &.flipped { + --correct-letter-color: var(--sub-color); + --untyped-letter-color: var(--text-color); + --incorrect-letter-color: var(--error-color); + --extra-letter-color: var(--error-extra-color); } &.colorfulMode { - .word { - & letter.dead { - border-bottom-color: var(--main-color) !important; - } - - & letter.correct { - color: var(--main-color); - } - - & letter.corrected { - color: var(--main-color); - border-bottom: 2px dotted var(--text-color); - } - - & letter.extraCorrected { - border-right: 2px dotted var(--text-color); - } - - & letter.incorrect { - color: var(--colorful-error-color); - } - - & letter.incorrect.extra { - color: var(--colorful-error-extra-color); - } - } - - @include highlightMode(var(--main-color)); + --correct-letter-color: var(--main-color); + --untyped-letter-color: var(--sub-color); + --incorrect-letter-color: var(--colorful-error-color); + --extra-letter-color: var(--colorful-error-extra-color); } &.flipped.colorfulMode { + --correct-letter-color: var(--sub-color); + --untyped-letter-color: var(--main-color); + --incorrect-letter-color: var(--colorful-error-color); + --extra-letter-color: var(--colorful-error-extra-color); + } + + &.highlight-off { .word { - color: var(--main-color); - - & letter.dead { - border-bottom-color: var(--sub-color) !important; - } - - & letter.correct { - color: var(--sub-color); - } - - & letter.corrected { - color: var(--sub-color); - border-bottom: 2px dotted var(--main-color); - } - - & letter.extraCorrected { - border-right: 2px dotted var(--main-color); - } - - & letter.incorrect { - color: var(--colorful-error-color); - } - - & letter.incorrect.extra { - color: var(--colorful-error-extra-color); + letter.correct { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); } } - - @include highlightMode(var(--sub-color)); } - &.tape .word { - margin: 0.25em 0.5em 0.75em 0; + &.highlight-word { + .word.typed { + letter { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); + } + } + .word.active { + letter { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + } + } + .word.typed.error, + .word.active:has(letter.incorrect) { + letter { + color: var(--incorrect-letter-color); + animation: var(--incorrect-letter-animation); + } + } } - @include highlightMode(var(--text-color)); + &.highlight-next-word { + .word.typed { + letter { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); + } + } + .word.active, + .word.active + .word { + letter { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + } + } + .word.typed.error, + .word.active:has(letter.incorrect) { + letter { + color: var(--incorrect-letter-color); + animation: var(--incorrect-letter-animation); + } + } + } + + &.highlight-next-two-words { + .word.typed { + letter { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); + } + } + .word.active, + .word.active + .word, + .word.active + .word + .word { + letter { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + } + } + .word.typed.error, + .word.active:has(letter.incorrect) { + letter { + color: var(--incorrect-letter-color); + animation: var(--incorrect-letter-animation); + } + } + } + + &.highlight-next-three-words { + .word.typed { + letter { + color: var(--untyped-letter-color); + animation: var(--untyped-letter-animation); + } + } + .word.active, + .word.active + .word, + .word.active + .word + .word, + .word.active + .word + .word + .word { + letter { + color: var(--correct-letter-color); + animation: var(--correct-letter-animation); + } + } + .word.typed.error, + .word.active:has(letter.incorrect) { + letter { + color: var(--incorrect-letter-color); + animation: var(--incorrect-letter-animation); + } + } + } } .word { @@ -348,7 +427,6 @@ font-size: 1em; line-height: 1em; margin: 0.25em; - color: var(--sub-color); font-variant: no-common-ligatures; border-bottom: 2px solid transparent; letter { @@ -416,53 +494,6 @@ text-shadow: none; } } -// .word letter { -// transition: .1s; -// height: 1rem; -// line-height: 1rem; -/* margin: 0 1px; */ -// } - -.word letter.correct { - color: var(--text-color); -} - -.word letter.corrected { - color: var(--text-color); - border-bottom: 2px dotted var(--main-color); -} - -.word letter.extraCorrected { - border-right: 2px dotted var(--main-color); -} - -.word letter.incorrect { - color: var(--error-color); -} - -.word .hints hint { - position: absolute; - bottom: -1.1em; - color: var(--text-color); - line-height: initial; - font-size: 0.75em; - text-shadow: none; - padding: 1px; - left: 0; - opacity: 0.5; - text-align: center; - display: grid; - justify-content: center; - transform: translate(-50%); -} - -.word letter.incorrect.extra { - color: var(--error-extra-color); -} - -.word letter.missing { - opacity: 0.5; -} #words.flipped.colorfulMode .word.error, #words.colorfulMode .word.error { @@ -626,13 +657,20 @@ .word { position: relative; margin: 0.18rem 0.6rem 0.15rem 0; - letter.correct { + & letter.corrected { + color: var(--text-color); + border-bottom: 2px dotted var(--main-color); + } + & letter.extraCorrected { + border-right: 2px dotted var(--main-color); + } + & letter.correct { color: var(--text-color); } - letter.incorrect { + & letter.incorrect { color: var(--error-color); } - letter.incorrect.extra { + & letter.incorrect.extra { color: var(--error-extra-color); } &.heatmapInherit letter { diff --git a/frontend/src/ts/controllers/input-controller.ts b/frontend/src/ts/controllers/input-controller.ts index ab14f9930..559ec4c64 100644 --- a/frontend/src/ts/controllers/input-controller.ts +++ b/frontend/src/ts/controllers/input-controller.ts @@ -261,7 +261,7 @@ async function handleSpace(): Promise { if (Config.stopOnError === "word") { dontInsertSpace = false; Replay.addReplayEvent("incorrectLetter", "_"); - void TestUI.updateWordElement(true); + void TestUI.updateWordElement(); void Caret.updatePosition(); } return; @@ -661,7 +661,7 @@ function handleChar( !thisCharCorrect ) { if (!Config.blindMode) { - void TestUI.updateWordElement(undefined, TestInput.input.current + char); + void TestUI.updateWordElement(TestInput.input.current + char); } return; } diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 68bce5065..9d799d791 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -178,7 +178,6 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => { if (typeof eventValue !== "boolean") return; if (eventKey === "flipTestColors") flipColors(eventValue); if (eventKey === "colorfulMode") colorful(eventValue); - if (eventKey === "highlightMode") void updateWordElement(eventValue); if (eventKey === "burstHeatmap") void applyBurstHeatmap(); }); @@ -238,19 +237,12 @@ export function updateActiveElement( initial = false ): void { const active = document.querySelector("#words .active"); + if (!backspace) { + active?.classList.add("typed"); + } if (Config.mode === "zen" && backspace) { active?.remove(); } else if (active !== null) { - if ( - ["word", "next_word", "next_two_words", "next_three_words"].includes( - Config.highlightMode - ) && - Config.theme !== "shadow" - ) { - active.querySelectorAll("letter").forEach((e) => { - e.classList.remove("correct"); - }); - } active.classList.remove("active"); } const activeWord = @@ -262,13 +254,11 @@ export function updateActiveElement( activeWord.classList.add("active"); activeWord.classList.remove("error"); + activeWord.classList.remove("typed"); + activeWordTop = (document.querySelector("#words .active") as HTMLElement) .offsetTop; - if (Config.highlightMode === "word") { - activeWord.querySelectorAll("letter").forEach((e) => { - e.classList.add("correct"); - }); - } + if (!initial && shouldUpdateWordsInputPosition()) { void updateWordsInputPosition(); } @@ -757,10 +747,7 @@ export async function screenshot(): Promise { }, 3000); } -export async function updateWordElement( - showError = !Config.blindMode, - inputOverride?: string -): Promise { +export async function updateWordElement(inputOverride?: string): Promise { const input = inputOverride ?? TestInput.input.current; const wordAtIndex = document.querySelector( "#words .word.active" @@ -821,26 +808,13 @@ export async function updateWordElement( } } - let wordHighlightClassString = correctSoFar ? "correct" : "incorrect"; - - if (Config.blindMode) { - wordHighlightClassString = "correct"; - } - const funbox = FunboxList.get(Config.funbox).find( (f) => f.functions?.getWordHtml ); - const isTts = FunboxList.get(Config.funbox).find((it) => it.name === "tts"); for (let i = 0; i < input.length; i++) { const charCorrect = currentWord[i] === input[i]; - let correctClass = "correct"; - if (Config.highlightMode === "off") { - correctClass = ""; - if (isTts) correctClass = "visible"; - } - let currentLetter = currentWord[i] as string; let tabChar = ""; let nlChar = ""; @@ -858,53 +832,31 @@ export async function updateWordElement( } if (charCorrect) { - ret += `${currentLetter}`; + ret += `${currentLetter}`; } else if ( currentLetter !== undefined && CompositionState.getComposing() && i >= CompositionState.getStartPos() && !(containsKorean && !correctSoFar) ) { - ret += `${ + ret += `${ Config.indicateTypos === "replace" ? input[i] === " " ? "_" : input[i] : currentLetter }`; - } else if (!showError) { - if (currentLetter !== undefined) { - ret += `${currentLetter}`; - } } else if (currentLetter === undefined) { if (!Config.hideExtraLetters) { let letter = input[i]; if (letter === " " || letter === "\t" || letter === "\n") { letter = "_"; } - ret += `${letter}`; + ret += `${letter}`; } } else { ret += - `` + + `` + (Config.indicateTypos === "replace" ? input[i] === " " ? "_" @@ -930,12 +882,7 @@ export async function updateWordElement( } else if (currentWord[i] === "\n") { ret += ``; } else { - ret += - `` + - currentWord[i] + - ""; + ret += `` + currentWord[i] + ""; } } diff --git a/frontend/static/funbox/tts.css b/frontend/static/funbox/tts.css index fbed492cc..7771195e9 100644 --- a/frontend/static/funbox/tts.css +++ b/frontend/static/funbox/tts.css @@ -1,19 +1,3 @@ -/* #words { - opacity: 0 !important; -} */ - -#words .word { - color: transparent !important; -} - -#words .word letter.visible { - color: var(--sub-color) !important; -} - -#words.flipped .word letter.visible { - color: var(--text-color) !important; -} - -#words.flipped.colorfulMode .word letter.visible { - color: var(--main-color) !important; +#words { + --untyped-letter-color: transparent !important; } diff --git a/frontend/static/themes/aurora.css b/frontend/static/themes/aurora.css index bce1e044b..044e632ee 100644 --- a/frontend/static/themes/aurora.css +++ b/frontend/static/themes/aurora.css @@ -65,15 +65,33 @@ header .config .group .buttons .textButton.active, #result .stats .group .bottom, nav .textButton:hover, header .config .group .buttons .textButton:hover, -a:hover, -#words.flipped .word { +a:hover { animation: aurora 5s linear infinite; } -#words.flipped .word letter.correct { - color: var(--sub-color); +#words { + --correct-letter-animation: aurora 5s linear infinite; } -#words:not(.flipped) .word letter.correct { +#words.flipped { + --untyped-letter-color: var(--sub-color); + --correct-letter-animation: none; + --untyped-letter-animation: aurora 5s linear infinite; +} + +#words .word.typed letter.correct, +#words.highlight-word .word.typed letter, +#words.highlight-next-word .word.typed letter, +#words.highlight-next-two-words .word.typed letter, +#words.highlight-next-three-words .word.typed letter { + animation: aurora 5s linear infinite; +} + +#words.flipped .word.typed letter { + animation: none; +} + +#words.highlight-off .word letter, +#words.highlight-off .word.typed letter { animation: aurora 5s linear infinite; } diff --git a/frontend/static/themes/fire.css b/frontend/static/themes/fire.css index b6d6af4d7..ea419ec8e 100644 --- a/frontend/static/themes/fire.css +++ b/frontend/static/themes/fire.css @@ -65,15 +65,33 @@ header .config .group .buttons .textButton.active, #result .stats .group .bottom, nav .textButton:hover, header .config .group .buttons .textButton:hover, -a:hover, -#words.flipped .word { +a:hover { animation: fire 5s linear infinite; } -#words.flipped .word letter.correct { - color: var(--sub-color); +#words { + --correct-letter-animation: fire 5s linear infinite; } -#words:not(.flipped) .word letter.correct { +#words.flipped { + --untyped-letter-color: var(--sub-color); + --correct-letter-animation: none; + --untyped-letter-animation: fire 5s linear infinite; +} + +#words .word.typed letter.correct, +#words.highlight-word .word.typed letter, +#words.highlight-next-word .word.typed letter, +#words.highlight-next-two-words .word.typed letter, +#words.highlight-next-three-words .word.typed letter { + animation: fire 5s linear infinite; +} + +#words.flipped .word.typed letter { + animation: none; +} + +#words.highlight-off .word letter, +#words.highlight-off .word.typed letter { animation: fire 5s linear infinite; } diff --git a/frontend/static/themes/grape.css b/frontend/static/themes/grape.css index 3521ee595..0ed8b4674 100644 --- a/frontend/static/themes/grape.css +++ b/frontend/static/themes/grape.css @@ -65,15 +65,33 @@ header .config .group .buttons .textButton.active, #result .stats .group .bottom, nav .textButton:hover, header .config .group .buttons .textButton:hover, -a:hover, -#words.flipped .word { +a:hover { animation: pastel 5s linear infinite; } -#words.flipped .word letter.correct { - color: var(--sub-color); +#words { + --correct-letter-animation: pastel 5s linear infinite; } -#words:not(.flipped) .word letter.correct { +#words.flipped { + --untyped-letter-color: var(--sub-color); + --correct-letter-animation: none; + --untyped-letter-animation: pastel 5s linear infinite; +} + +#words .word.typed letter.correct, +#words.highlight-word .word.typed letter, +#words.highlight-next-word .word.typed letter, +#words.highlight-next-two-words .word.typed letter, +#words.highlight-next-three-words .word.typed letter { + animation: pastel 5s linear infinite; +} + +#words.flipped .word.typed letter { + animation: none; +} + +#words.highlight-off .word letter, +#words.highlight-off .word.typed letter { animation: pastel 5s linear infinite; } diff --git a/frontend/static/themes/rainbow_trail.css b/frontend/static/themes/rainbow_trail.css index 0606c8290..03c6a53a9 100644 --- a/frontend/static/themes/rainbow_trail.css +++ b/frontend/static/themes/rainbow_trail.css @@ -73,6 +73,56 @@ header #logo .top, -webkit-text-fill-color: initial; } +#liveStatsTextTop.timerMain, +#liveStatsTextBottom.timerMain, +#liveStatsMini.timerMain { + animation: rainbow-infinite-loop 30s linear infinite; +} + +#words { + --correct-letter-animation: rainbow-infinite-loop 5s linear infinite; + --incorrect-letter-animation: error-repeat 1s alternate ease-in-out infinite; + --extra-letter-animation: error-repeat 1s alternate ease-in-out infinite; +} + +#words .word.typed letter.correct, +#words.highlight-word .word.typed letter, +#words.highlight-next-word .word.typed letter, +#words.highlight-next-two-words .word.typed letter, +#words.highlight-next-three-words .word.typed letter { + animation: rainbow 7.5s ease-in-out forwards; +} + +#words.highlight-word .word.active letter { + animation: var(--correct-letter-animation); +} + +#words.flipped { + --correct-letter-animation: none; + --untyped-letter-animation: rainbow-infinite-loop 5s linear infinite; +} + +#words.flipped .word.typed letter { + animation: none; +} + +.button:hover { + animation: glowing-button 2s alternate ease-in-out infinite; +} + +#words.highlight-off .word letter, +#words.highlight-off .word.typed letter { + animation: rainbow-infinite-loop 5s linear infinite; +} + +.row .textButton:not(.active) { + color: #999999; +} + +.textButton.active { + color: #111111; +} + @keyframes rainbow { 0% { color: #60b6ce; @@ -169,40 +219,6 @@ header #logo .top, } } -#words.flipped .word { - animation: rainbow-infinite-loop 30s linear infinite; -} - -#words.flipped .word letter.correct { - color: var(--sub-color); -} - -#words:not(.flipped) .word letter.correct { - animation: rainbow 7.5s ease-in-out forwards; -} - -#liveStatsTextTop.timerMain, -#liveStatsTextBottom.timerMain, -#liveStatsMini.timerMain { - animation: rainbow-infinite-loop 30s linear infinite; -} - -.word letter.incorrect { - animation: error-repeat 1s alternate ease-in-out infinite; -} - -.button:hover { - animation: glowing-button 2s alternate ease-in-out infinite; -} - -.row .textButton:not(.active) { - color: #999999; -} - -.textButton.active { - color: #111111; -} - @keyframes light-rainbow-infinite-loop { 0% { color: #60b6ce; diff --git a/frontend/static/themes/rgb.css b/frontend/static/themes/rgb.css index 63ba0721b..01c2fee11 100644 --- a/frontend/static/themes/rgb.css +++ b/frontend/static/themes/rgb.css @@ -100,17 +100,13 @@ a:hover, animation: rgb 5s linear infinite; } -/* .word letter.correct{ - animation: rgb 5s linear infinite; - -} */ - -#words.flipped .word letter.correct { - color: var(--sub-color); +#words { + --correct-letter-animation: rgb 5s linear infinite; } -#words:not(.flipped) .word letter.correct { - animation: rgb 5s linear infinite; +#words.flipped { + --correct-letter-animation: none; + --untyped-letter-animation: rgb 5s linear infinite; } header #logo path { diff --git a/frontend/static/themes/shadow.css b/frontend/static/themes/shadow.css index e692996af..b854c09af 100644 --- a/frontend/static/themes/shadow.css +++ b/frontend/static/themes/shadow.css @@ -48,6 +48,11 @@ a:hover { } #logo, -#typingTest .word letter.correct { +#typingTest .word letter.correct, +#typingTest #words.highlight-word .word.typed letter.correct, +#typingTest #words.highlight-next-word .word.typed letter.correct, +#typingTest #words.highlight-next-two-words .word.typed letter.correct, +#typingTest #words.highlight-next-three-words .word.typed letter.correct { + color: var(--correct-letter-color); animation: shadow 5s linear 1 forwards; } diff --git a/frontend/static/themes/trance.css b/frontend/static/themes/trance.css index 03faebc60..0bc3e9cd3 100644 --- a/frontend/static/themes/trance.css +++ b/frontend/static/themes/trance.css @@ -53,15 +53,33 @@ header .config .group .buttons .textButton.active, #result .stats .group .bottom, nav .textButton:hover, header .config .group .buttons .textButton:hover, -a:hover, -#words.flipped .word { +a:hover { animation: trance 5s linear infinite; } -#words.flipped .word letter.correct { - color: var(--sub-color); +#words { + --correct-letter-animation: trance 5s linear infinite; } -#words:not(.flipped) .word letter.correct { +#words.flipped { + --untyped-letter-color: var(--sub-color); + --correct-letter-animation: none; + --untyped-letter-animation: trance 5s linear infinite; +} + +#words .word.typed letter.correct, +#words.highlight-word .word.typed letter, +#words.highlight-next-word .word.typed letter, +#words.highlight-next-two-words .word.typed letter, +#words.highlight-next-three-words .word.typed letter { + animation: trance 5s linear infinite; +} + +#words.flipped .word.typed letter { + animation: none; +} + +#words.highlight-off .word letter, +#words.highlight-off .word.typed letter { animation: trance 5s linear infinite; }