From d4427d1dd3fc701ec7f45adcbb55124cf3063fe4 Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 12:19:11 +0200 Subject: [PATCH 1/9] chore: missing continue --- frontend/src/ts/test/test-logic.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index cfca54c26af5..8e8cda56b4ce 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1025,6 +1025,7 @@ function compareCompletedEvents( mismatchedKeys.push(key); console.error(`Completed event mismatch on key ${key}:`, val1, val2); } + continue; } // if (key === "chartData") { From 966514a0b64c29e392916e8bc3f192ae92ac65c8 Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 12:41:55 +0200 Subject: [PATCH 2/9] chore: do not count commit space on last word --- frontend/__tests__/utils/strings.spec.ts | 2 +- frontend/src/ts/utils/strings.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/__tests__/utils/strings.spec.ts b/frontend/__tests__/utils/strings.spec.ts index 293cd57c01bc..0e6df5645f71 100644 --- a/frontend/__tests__/utils/strings.spec.ts +++ b/frontend/__tests__/utils/strings.spec.ts @@ -828,7 +828,7 @@ describe("string utils", () => { allCorrect: 4, correctWord: 0, incorrect: 1, - extra: 1, + extra: 0, missed: 0, }, }, diff --git a/frontend/src/ts/utils/strings.ts b/frontend/src/ts/utils/strings.ts index ff10c2e8b724..55e550906433 100644 --- a/frontend/src/ts/utils/strings.ts +++ b/frontend/src/ts/utils/strings.ts @@ -449,6 +449,13 @@ export function countChars( if (!(lastWord && shouldLastPartialWordCount)) { missed += 1; } + } else if ( + lastWord && + inputChar === " " && + targetChar === undefined && + !targetWord.endsWith(" ") + ) { + // trailing confirm space on incorrect last word — not counted } else if ( targetChar === undefined || (targetChar === " " && inputChar !== " " && !inputWord.includes(" ")) From 19c0b10299d9000a888e87086fd2805d58e9f3fb Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 13:13:56 +0200 Subject: [PATCH 3/9] chore: update start to first and last to end calc --- frontend/__tests__/test/events/stats.spec.ts | 40 +++++++++++--------- frontend/src/ts/test/events/stats.ts | 28 +++++++------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/frontend/__tests__/test/events/stats.spec.ts b/frontend/__tests__/test/events/stats.spec.ts index b8b5273b2fe4..f2536eac6c81 100644 --- a/frontend/__tests__/test/events/stats.spec.ts +++ b/frontend/__tests__/test/events/stats.spec.ts @@ -253,16 +253,17 @@ describe("stats.ts", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); logTestEvent("keydown", 1500, keyDown()); + logTestEvent("input", 1510, input()); logTestEvent("keyup", 1600, keyUp()); logTestEvent("timer", 2000, timer("step", 1)); logTestEvent("timer", 3000, timer("step", 2)); - // last keypress at testMs 500, end at testMs 4000 → lkte = 3500 + // last input at testMs 510, end at testMs 4000 → lkte = 3490 logTestEvent("timer", 5000, timer("end", 4)); const events = getAllTestEvents(); const boundaries = statsTesting.getTimerBoundaries(events); - // adjusted end = 4000 - 3500 = 500, steps at 1000 and 2000 are past it - expect(boundaries).toEqual([500]); + // adjusted end = 4000 - 3490 = 510, steps at 1000 and 2000 are past it + expect(boundaries).toEqual([510]); }); it("skips end boundary when endMs rounds up to whole second", () => { @@ -347,15 +348,17 @@ describe("stats.ts", () => { }); describe("getStartToFirstKeypressMs", () => { - it("returns time from start to first keydown", () => { + it("returns time from start to first input", () => { logTestEvent("timer", 1000, timer("start", 0)); logTestEvent("keydown", 1150, keyDown()); + logTestEvent("input", 1160, input()); - expect(getStartToFirstKeypressMs()).toBe(150); + expect(getStartToFirstKeypressMs()).toBe(160); }); - it("returns 0 if keydown comes before start", () => { + it("returns 0 if input comes before start", () => { logTestEvent("keydown", 900, keyDown()); + logTestEvent("input", 910, input()); logTestEvent("timer", 1000, timer("start", 0)); expect(getStartToFirstKeypressMs()).toBe(0); @@ -364,7 +367,7 @@ describe("stats.ts", () => { it("returns 0 in zen mode", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("keydown", 1150, keyDown()); + logTestEvent("input", 1160, input()); expect(getStartToFirstKeypressMs()).toBe(0); }); @@ -375,20 +378,20 @@ describe("stats.ts", () => { }); describe("getLastKeypressToEndMs", () => { - it("returns time from last keydown to end", () => { + it("returns time from last input to end", () => { logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("keydown", 1500, keyDown()); - logTestEvent("keyup", 1600, keyUp()); - logTestEvent("keydown", 1800, keyDown()); + logTestEvent("input", 1510, input()); + logTestEvent("input", 1810, input({ charIndex: 1 })); + logTestEvent("keydown", 1900, keyDown()); logTestEvent("timer", 2000, timer("end", 1)); - expect(getLastKeypressToEndMs()).toBe(200); + expect(getLastKeypressToEndMs()).toBe(190); }); it("returns 0 in zen mode", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("keydown", 1500, keyDown()); + logTestEvent("input", 1510, input()); logTestEvent("timer", 2000, timer("end", 1)); expect(getLastKeypressToEndMs()).toBe(0); @@ -558,16 +561,16 @@ describe("stats.ts", () => { it("clamps a pre-start first keydown so the timing invariant holds", () => { // A keydown can be recorded before timer:start (e.g. a stray Ctrl+H // pressed seconds before the user starts typing). cleanupData keeps the - // last pre-start keydown by design, and getStartToFirstKeypressMs clamps - // its negative offset to 0 — so the first spacing must clamp the same - // way, else sum(keySpacing) inflates by |firstKeydown| and breaks - // the testDuration vs key timings check. + // last pre-start keydown by design — keySpacing clamps its negative + // offset to 0 so sum(keySpacing) doesn't inflate by |firstKeydown| + // and break the testDuration vs key timings check. (Config as { mode: string }).mode = "time"; logTestEvent("keydown", -16240, keyDown()); logTestEvent("timer", 0, timer("start", 0)); logTestEvent("input", 0, input()); logTestEvent("keyup", 100, keyUp()); logTestEvent("keydown", 500, keyDown("KeyS")); + logTestEvent("input", 500, input({ charIndex: 1 })); logTestEvent("keyup", 580, keyUp("KeyS")); logTestEvent("timer", 1000, timer("step", 1)); logTestEvent("timer", 1000, timer("end", 1)); @@ -590,13 +593,16 @@ describe("stats.ts", () => { (Config as { mode: string }).mode = "time"; logTestEvent("timer", 0, timer("start", 0)); logTestEvent("keydown", 500, keyDown()); + logTestEvent("input", 510, input()); logTestEvent("keyup", 580, keyUp()); logTestEvent("keydown", 700, keyDown()); + logTestEvent("input", 710, input({ charIndex: 1 })); logTestEvent("keyup", 780, keyUp()); logTestEvent("timer", 1000, timer("step", 1)); logTestEvent("timer", 1000, timer("end", 1)); // user keeps typing through the fade logTestEvent("keydown", 1120, keyDown()); + logTestEvent("input", 1130, input({ charIndex: 2 })); logTestEvent("keyup", 1170, keyUp()); cleanupData(); diff --git a/frontend/src/ts/test/events/stats.ts b/frontend/src/ts/test/events/stats.ts index e55ebe94bafa..647125022cdb 100644 --- a/frontend/src/ts/test/events/stats.ts +++ b/frontend/src/ts/test/events/stats.ts @@ -71,16 +71,16 @@ export function getStartToFirstKeypressMs(): number { const events = getAllTestEvents(); - let firstKeypress: number | undefined; + let firstInput: number | undefined; let start: number | undefined; for (const event of events) { - if (firstKeypress !== undefined && start !== undefined) { + if (firstInput !== undefined && start !== undefined) { break; } - if (firstKeypress === undefined && event.type === "keydown") { - firstKeypress = event.testMs; + if (firstInput === undefined && event.type === "input") { + firstInput = event.testMs; } if ( @@ -92,11 +92,11 @@ export function getStartToFirstKeypressMs(): number { } } - if (firstKeypress === undefined || start === undefined) { + if (firstInput === undefined || start === undefined) { return 0; } - const calc = firstKeypress - start; + const calc = firstInput - start; return calc < 0 ? 0 : roundTo2(calc); } @@ -105,7 +105,7 @@ export function getStartToFirstKeypressMs(): number { function getRawLastKeypressToEndMs(): number { const events = getAllTestEvents(); - let lastKeypress: number | undefined; + let lastInput: number | undefined; let end: number | undefined; for (let i = events.length - 1; i >= 0; i--) { @@ -116,12 +116,12 @@ function getRawLastKeypressToEndMs(): number { break; } - if (lastKeypress !== undefined && end !== undefined) { + if (lastInput !== undefined && end !== undefined) { break; } - if (lastKeypress === undefined && event.type === "keydown") { - lastKeypress = event.testMs; + if (lastInput === undefined && event.type === "input") { + lastInput = event.testMs; } if ( @@ -133,11 +133,11 @@ function getRawLastKeypressToEndMs(): number { } } - if (lastKeypress === undefined || end === undefined) { + if (lastInput === undefined || end === undefined) { return 0; } - const calc = end - lastKeypress; + const calc = end - lastInput; return calc < 0 ? 0 : roundTo2(calc); } @@ -391,8 +391,8 @@ export function getKeypressSpacing(): number[] { const spacing = event.testMs - lastKeydownTime; spacings.push(spacing); } - // clamp to 0 so a pre-start keydown matches getStartToFirstKeypressMs, - // keeping startToFirstKey + sum(keySpacing) + lastKeyToEnd ≈ testDuration + // clamp pre-start keydowns to 0 so sum(keySpacing) doesn't inflate by + // |firstKeydown|, keeping startToFirstKey + sum(keySpacing) + lastKeyToEnd ≈ testDuration lastKeydownTime = Math.max(0, event.testMs); } } From d251e975a212cd24654db476506a4721d51810da Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 13:36:01 +0200 Subject: [PATCH 4/9] chore: bump version --- frontend/src/ts/test/test-logic.ts | 2 +- packages/contracts/src/results.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index 8e8cda56b4ce..fe1d7ed93b11 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1309,7 +1309,7 @@ function compareCompletedEvents( difficulty: ce.difficulty, duration: ce.testDuration, funboxes: getActiveFunboxNames().join(","), - version: 10, + version: 11, data: { words: TestWords.words.list.join(" "), events: getAllTestEvents(), diff --git a/packages/contracts/src/results.ts b/packages/contracts/src/results.ts index 618e77fcd80a..09b4a64078e3 100644 --- a/packages/contracts/src/results.ts +++ b/packages/contracts/src/results.ts @@ -75,7 +75,7 @@ export const ReportCompletedEventMismatchRequestSchema = z.object({ difficulty: DifficultySchema.optional(), duration: z.number().max(200).optional(), funboxes: z.string().max(100).optional(), - version: z.literal(10), + version: z.literal(11), data: z.object({ words: z.string().max(10000), events: z.array(z.record(z.unknown())), From 26ae70ea6a3fb5332950e69c636ec91c0764916a Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 17:16:16 +0200 Subject: [PATCH 5/9] revert: update start to first and last to end calc This reverts commit 19c0b10299d9000a888e87086fd2805d58e9f3fb. --- frontend/__tests__/test/events/stats.spec.ts | 40 +++++++++----------- frontend/src/ts/test/events/stats.ts | 28 +++++++------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/frontend/__tests__/test/events/stats.spec.ts b/frontend/__tests__/test/events/stats.spec.ts index f2536eac6c81..b8b5273b2fe4 100644 --- a/frontend/__tests__/test/events/stats.spec.ts +++ b/frontend/__tests__/test/events/stats.spec.ts @@ -253,17 +253,16 @@ describe("stats.ts", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); logTestEvent("keydown", 1500, keyDown()); - logTestEvent("input", 1510, input()); logTestEvent("keyup", 1600, keyUp()); logTestEvent("timer", 2000, timer("step", 1)); logTestEvent("timer", 3000, timer("step", 2)); - // last input at testMs 510, end at testMs 4000 → lkte = 3490 + // last keypress at testMs 500, end at testMs 4000 → lkte = 3500 logTestEvent("timer", 5000, timer("end", 4)); const events = getAllTestEvents(); const boundaries = statsTesting.getTimerBoundaries(events); - // adjusted end = 4000 - 3490 = 510, steps at 1000 and 2000 are past it - expect(boundaries).toEqual([510]); + // adjusted end = 4000 - 3500 = 500, steps at 1000 and 2000 are past it + expect(boundaries).toEqual([500]); }); it("skips end boundary when endMs rounds up to whole second", () => { @@ -348,17 +347,15 @@ describe("stats.ts", () => { }); describe("getStartToFirstKeypressMs", () => { - it("returns time from start to first input", () => { + it("returns time from start to first keydown", () => { logTestEvent("timer", 1000, timer("start", 0)); logTestEvent("keydown", 1150, keyDown()); - logTestEvent("input", 1160, input()); - expect(getStartToFirstKeypressMs()).toBe(160); + expect(getStartToFirstKeypressMs()).toBe(150); }); - it("returns 0 if input comes before start", () => { + it("returns 0 if keydown comes before start", () => { logTestEvent("keydown", 900, keyDown()); - logTestEvent("input", 910, input()); logTestEvent("timer", 1000, timer("start", 0)); expect(getStartToFirstKeypressMs()).toBe(0); @@ -367,7 +364,7 @@ describe("stats.ts", () => { it("returns 0 in zen mode", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("input", 1160, input()); + logTestEvent("keydown", 1150, keyDown()); expect(getStartToFirstKeypressMs()).toBe(0); }); @@ -378,20 +375,20 @@ describe("stats.ts", () => { }); describe("getLastKeypressToEndMs", () => { - it("returns time from last input to end", () => { + it("returns time from last keydown to end", () => { logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("input", 1510, input()); - logTestEvent("input", 1810, input({ charIndex: 1 })); - logTestEvent("keydown", 1900, keyDown()); + logTestEvent("keydown", 1500, keyDown()); + logTestEvent("keyup", 1600, keyUp()); + logTestEvent("keydown", 1800, keyDown()); logTestEvent("timer", 2000, timer("end", 1)); - expect(getLastKeypressToEndMs()).toBe(190); + expect(getLastKeypressToEndMs()).toBe(200); }); it("returns 0 in zen mode", () => { (Config as { mode: string }).mode = "zen"; logTestEvent("timer", 1000, timer("start", 0)); - logTestEvent("input", 1510, input()); + logTestEvent("keydown", 1500, keyDown()); logTestEvent("timer", 2000, timer("end", 1)); expect(getLastKeypressToEndMs()).toBe(0); @@ -561,16 +558,16 @@ describe("stats.ts", () => { it("clamps a pre-start first keydown so the timing invariant holds", () => { // A keydown can be recorded before timer:start (e.g. a stray Ctrl+H // pressed seconds before the user starts typing). cleanupData keeps the - // last pre-start keydown by design — keySpacing clamps its negative - // offset to 0 so sum(keySpacing) doesn't inflate by |firstKeydown| - // and break the testDuration vs key timings check. + // last pre-start keydown by design, and getStartToFirstKeypressMs clamps + // its negative offset to 0 — so the first spacing must clamp the same + // way, else sum(keySpacing) inflates by |firstKeydown| and breaks + // the testDuration vs key timings check. (Config as { mode: string }).mode = "time"; logTestEvent("keydown", -16240, keyDown()); logTestEvent("timer", 0, timer("start", 0)); logTestEvent("input", 0, input()); logTestEvent("keyup", 100, keyUp()); logTestEvent("keydown", 500, keyDown("KeyS")); - logTestEvent("input", 500, input({ charIndex: 1 })); logTestEvent("keyup", 580, keyUp("KeyS")); logTestEvent("timer", 1000, timer("step", 1)); logTestEvent("timer", 1000, timer("end", 1)); @@ -593,16 +590,13 @@ describe("stats.ts", () => { (Config as { mode: string }).mode = "time"; logTestEvent("timer", 0, timer("start", 0)); logTestEvent("keydown", 500, keyDown()); - logTestEvent("input", 510, input()); logTestEvent("keyup", 580, keyUp()); logTestEvent("keydown", 700, keyDown()); - logTestEvent("input", 710, input({ charIndex: 1 })); logTestEvent("keyup", 780, keyUp()); logTestEvent("timer", 1000, timer("step", 1)); logTestEvent("timer", 1000, timer("end", 1)); // user keeps typing through the fade logTestEvent("keydown", 1120, keyDown()); - logTestEvent("input", 1130, input({ charIndex: 2 })); logTestEvent("keyup", 1170, keyUp()); cleanupData(); diff --git a/frontend/src/ts/test/events/stats.ts b/frontend/src/ts/test/events/stats.ts index 647125022cdb..e55ebe94bafa 100644 --- a/frontend/src/ts/test/events/stats.ts +++ b/frontend/src/ts/test/events/stats.ts @@ -71,16 +71,16 @@ export function getStartToFirstKeypressMs(): number { const events = getAllTestEvents(); - let firstInput: number | undefined; + let firstKeypress: number | undefined; let start: number | undefined; for (const event of events) { - if (firstInput !== undefined && start !== undefined) { + if (firstKeypress !== undefined && start !== undefined) { break; } - if (firstInput === undefined && event.type === "input") { - firstInput = event.testMs; + if (firstKeypress === undefined && event.type === "keydown") { + firstKeypress = event.testMs; } if ( @@ -92,11 +92,11 @@ export function getStartToFirstKeypressMs(): number { } } - if (firstInput === undefined || start === undefined) { + if (firstKeypress === undefined || start === undefined) { return 0; } - const calc = firstInput - start; + const calc = firstKeypress - start; return calc < 0 ? 0 : roundTo2(calc); } @@ -105,7 +105,7 @@ export function getStartToFirstKeypressMs(): number { function getRawLastKeypressToEndMs(): number { const events = getAllTestEvents(); - let lastInput: number | undefined; + let lastKeypress: number | undefined; let end: number | undefined; for (let i = events.length - 1; i >= 0; i--) { @@ -116,12 +116,12 @@ function getRawLastKeypressToEndMs(): number { break; } - if (lastInput !== undefined && end !== undefined) { + if (lastKeypress !== undefined && end !== undefined) { break; } - if (lastInput === undefined && event.type === "input") { - lastInput = event.testMs; + if (lastKeypress === undefined && event.type === "keydown") { + lastKeypress = event.testMs; } if ( @@ -133,11 +133,11 @@ function getRawLastKeypressToEndMs(): number { } } - if (lastInput === undefined || end === undefined) { + if (lastKeypress === undefined || end === undefined) { return 0; } - const calc = end - lastInput; + const calc = end - lastKeypress; return calc < 0 ? 0 : roundTo2(calc); } @@ -391,8 +391,8 @@ export function getKeypressSpacing(): number[] { const spacing = event.testMs - lastKeydownTime; spacings.push(spacing); } - // clamp pre-start keydowns to 0 so sum(keySpacing) doesn't inflate by - // |firstKeydown|, keeping startToFirstKey + sum(keySpacing) + lastKeyToEnd ≈ testDuration + // clamp to 0 so a pre-start keydown matches getStartToFirstKeypressMs, + // keeping startToFirstKey + sum(keySpacing) + lastKeyToEnd ≈ testDuration lastKeydownTime = Math.max(0, event.testMs); } } From e1402ff9dd61bdda8ac9d6f3f233e501f390893b Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 17:21:55 +0200 Subject: [PATCH 6/9] chore: fix calculation for early space, last word --- frontend/__tests__/utils/strings.spec.ts | 16 ++++++++++++++++ frontend/src/ts/utils/strings.ts | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/frontend/__tests__/utils/strings.spec.ts b/frontend/__tests__/utils/strings.spec.ts index 0e6df5645f71..c3cc233abd55 100644 --- a/frontend/__tests__/utils/strings.spec.ts +++ b/frontend/__tests__/utils/strings.spec.ts @@ -816,6 +816,22 @@ describe("string utils", () => { missed: 0, }, }, + { + description: "incorrect, last word, early space", + input: { + inputWord: "he ", + targetWord: "hello", + lastWord: true, + shouldLastPartialWordCount: false, + }, + expected: { + allCorrect: 2, + correctWord: 0, + incorrect: 0, + extra: 0, + missed: 3, + }, + }, { description: "incorrect, last word, noquick end", input: { diff --git a/frontend/src/ts/utils/strings.ts b/frontend/src/ts/utils/strings.ts index 55e550906433..df1b253d0551 100644 --- a/frontend/src/ts/utils/strings.ts +++ b/frontend/src/ts/utils/strings.ts @@ -456,6 +456,16 @@ export function countChars( !targetWord.endsWith(" ") ) { // trailing confirm space on incorrect last word — not counted + } else if ( + lastWord && + inputChar === " " && + targetChar !== undefined && + targetChar !== " " + ) { + // early submit space on last word — count slot as missed, not incorrect + if (!(lastWord && shouldLastPartialWordCount)) { + missed += 1; + } } else if ( targetChar === undefined || (targetChar === " " && inputChar !== " " && !inputWord.includes(" ")) From 109347bc6e0d9e7d97190d291485477d6b17c981 Mon Sep 17 00:00:00 2001 From: Miodec Date: Mon, 8 Jun 2026 17:40:05 +0200 Subject: [PATCH 7/9] chore: bump version, better log, ignore zen --- frontend/src/ts/test/test-logic.ts | 8 ++++++-- packages/contracts/src/results.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/ts/test/test-logic.ts b/frontend/src/ts/test/test-logic.ts index fe1d7ed93b11..4a6f382b2425 100644 --- a/frontend/src/ts/test/test-logic.ts +++ b/frontend/src/ts/test/test-logic.ts @@ -1126,7 +1126,7 @@ function compareCompletedEvents( if (a !== b) { const diff = Numbers.roundTo2(Math.abs(a - b)); const dir = a > b ? "ce1 larger" : "ce2 larger"; - notMatching.push(`${key} (off by ${diff}, ${dir})`); + notMatching.push(`${key} (off by ${diff}, ${dir}, ${a} vs ${b})`); mismatchedKeys.push(key); console.error(`Completed event mismatch on key ${key}:`, a, b); } else { @@ -1281,6 +1281,10 @@ function compareCompletedEvents( ignoreMismatch = true; } + if (Config.mode === "zen") { + ignoreMismatch = true; + } + if (ALWAYSREPORT) { if (ignoreMismatch) { showNoticeNotification( @@ -1309,7 +1313,7 @@ function compareCompletedEvents( difficulty: ce.difficulty, duration: ce.testDuration, funboxes: getActiveFunboxNames().join(","), - version: 11, + version: 12, data: { words: TestWords.words.list.join(" "), events: getAllTestEvents(), diff --git a/packages/contracts/src/results.ts b/packages/contracts/src/results.ts index 09b4a64078e3..8f6db596a54a 100644 --- a/packages/contracts/src/results.ts +++ b/packages/contracts/src/results.ts @@ -75,7 +75,7 @@ export const ReportCompletedEventMismatchRequestSchema = z.object({ difficulty: DifficultySchema.optional(), duration: z.number().max(200).optional(), funboxes: z.string().max(100).optional(), - version: z.literal(11), + version: z.literal(12), data: z.object({ words: z.string().max(10000), events: z.array(z.record(z.unknown())), From 13f73d1e01273a3d78048b7a56fc3f8da6136320 Mon Sep 17 00:00:00 2001 From: Christian Fehmer Date: Mon, 8 Jun 2026 17:50:18 +0200 Subject: [PATCH 8/9] refactor: use solid simple modals (@fehmer) (#8044) Co-authored-by: Miodec --- frontend/__tests__/utils/zod.spec.ts | 61 + frontend/src/html/popups.html | 49 +- frontend/src/styles/popups.scss | 203 --- frontend/src/styles/tailwind.css | 7 + frontend/src/ts/auth.tsx | 293 ++++- .../ts/components/layout/overlays/Banners.tsx | 6 +- .../ts/components/modals/DevOptionsModal.tsx | 95 +- frontend/src/ts/components/modals/Modals.tsx | 2 + .../src/ts/components/modals/SimpleModal.tsx | 28 +- .../account-settings/AddPasswordAuthModal.tsx | 101 ++ .../account-settings/ReauthConfirmModals.tsx | 205 +++ .../RemoveAuthMethodModal.tsx | 67 + .../account-settings/UnlinkDiscordModal.tsx | 42 + .../account-settings/UpdateEmailModal.tsx | 84 ++ .../account-settings/UpdateNameModal.tsx | 84 ++ .../account-settings/UpdatePasswordModal.tsx | 85 ++ .../account-settings/ViewApeKeyModal.tsx | 58 + .../ts/components/pages/account/Filters.tsx | 124 +- .../src/ts/components/pages/login/Login.tsx | 18 +- .../ts/components/pages/login/Register.tsx | 14 +- .../src/ts/components/ui/form/InputField.tsx | 18 +- .../account-settings/ape-key-table.ts | 254 ++-- frontend/src/ts/elements/simple-modal.ts | 488 ------- frontend/src/ts/index.ts | 1 - frontend/src/ts/modals/simple-modals-base.ts | 55 - frontend/src/ts/modals/simple-modals.ts | 1116 ----------------- frontend/src/ts/pages/account-settings.ts | 52 +- frontend/src/ts/pages/friends.ts | 154 ++- frontend/src/ts/states/account-settings.ts | 5 + frontend/src/ts/states/modals.ts | 3 +- frontend/src/ts/utils/zod.ts | 52 +- 31 files changed, 1497 insertions(+), 2327 deletions(-) create mode 100644 frontend/__tests__/utils/zod.spec.ts create mode 100644 frontend/src/ts/components/modals/account-settings/AddPasswordAuthModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/ReauthConfirmModals.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/RemoveAuthMethodModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/UnlinkDiscordModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/UpdateEmailModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/UpdateNameModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/UpdatePasswordModal.tsx create mode 100644 frontend/src/ts/components/modals/account-settings/ViewApeKeyModal.tsx delete mode 100644 frontend/src/ts/elements/simple-modal.ts delete mode 100644 frontend/src/ts/modals/simple-modals-base.ts delete mode 100644 frontend/src/ts/modals/simple-modals.ts create mode 100644 frontend/src/ts/states/account-settings.ts diff --git a/frontend/__tests__/utils/zod.spec.ts b/frontend/__tests__/utils/zod.spec.ts new file mode 100644 index 000000000000..c6bbfe12bdb1 --- /dev/null +++ b/frontend/__tests__/utils/zod.spec.ts @@ -0,0 +1,61 @@ +import { describe, it, expect } from "vitest"; +import { z, ZodString, ZodNumber } from "zod"; +import { unwrapSchema } from "../../src/ts/utils/zod"; + +describe("unwrapSchema", () => { + it("unwraps optional", () => { + const schema = z.string().optional(); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodString); + }); + + it("unwraps default", () => { + const schema = z.string().default("hello"); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodString); + }); + + it("unwraps nullable", () => { + const schema = z.number().nullable(); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodNumber); + }); + + it("unwraps branded", () => { + const schema = z.string().brand("UserId"); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodString); + }); + + it("unwraps effects", () => { + const schema = z.string().transform((v) => v.toUpperCase()); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodString); + }); + + it("unwraps multiple nested wrappers", () => { + const schema = z + .string() + .brand("X") + .optional() + .nullable() + .default("test") + .transform((v) => v); + + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBeInstanceOf(ZodString); + }); + + it("returns the same schema if no wrappers exist", () => { + const schema = z.string(); + const unwrapped = unwrapSchema(schema); + + expect(unwrapped).toBe(schema); + }); +}); diff --git a/frontend/src/html/popups.html b/frontend/src/html/popups.html index 216026bc2132..736ca9f527b9 100644 --- a/frontend/src/html/popups.html +++ b/frontend/src/html/popups.html @@ -45,10 +45,6 @@ - -