8000 feat(ui): steady death screen and retry-key lockout after a test ends by jrnxf · Pull Request #42 · jrnxf/thokr · GitHub
[go: up one dir, main page]

Skip to content

feat(ui): steady death screen and retry-key lockout after a test ends - #42

Merged
jrnxf merged 9 commits into
mainfrom
fm/thokr-death-steady
Oct 29, 2026
Merged

jrnxf merged 9 commits into
mainfrom
fm/thokr-death-steady

Conversation

@jrnxf
@jrnxf jrnxf commented Oct 29, 2026 •
Copy link
Copy Markdown
Owner

Intent

Follow-up to the death mode (-d) merged in #28. Today, when a death-mode run ends on a mistake, the screen switches to a death screen with a centred skull, an "expected X, pressed Y" line, the prompt with the word died on, and progress stats.

The ask: the lines being typed should not move at all from the test screen to the death screen - the jumping lines are unnecessary. Also remove the expected vs pressed line. The skull stays where it is now.

Later ask on the same PR: after any test end (natural finish or death), ignore the r and n keys for about 500ms - drop those keys, do not buffer them; esc and quit stay immediate; render the r/n hints dimmed until they go live, then normal. Remove the (esc)ape hint text everywhere it shows; esc keeps working. The browser demo gets the same behaviour, with the rule in the shared core so terminal and web cannot drift (time supplied by the caller, since wasm has no Instant::now).

Later ask on the same PR: replace the skull with a settled death look: typed text normal colour, the fatal (next expected) character drawn as a red block, the untyped remainder dimmed further than usual, and the stats line (e.g. '23 words, 95 chars, 6.1s') in red. No skull, no word 'died' anywhere. Prompt lines stay exactly where they are, the 500ms r/n lockout and dimmed hints stay.

Newest ask on the same PR: drop the initial red flash; keep only the settled look. On death go straight to the settled state. Remove the flash timing code and its tests from the shared core and the web engine (no dead code). Keep everything else.

What Changed

  • The death-mode screen now keeps the prompt lines exactly where they sat during the test, using a shared PromptLayout. The skull and the "expected X, pressed Y" line are gone. Typed text shows in normal colour, the fatal character is a red reversed block, the untyped rest is dimmed dark gray, and the progress stats line is red.
  • After any test ends, Thok records ended_at and restart_keys_live_at drops r/n/Left/Right for 500ms (END_LOCKOUT). Dropped keys are not buffered. Esc and Ctrl-C still quit at once. The (r)etry / (n)ew legend renders dimmed until the keys go live, and next_tick wakes once more to redraw it. write/on_tick/next_tick/ui::draw now take the time from the caller.
  • The (esc)ape hint is removed from the terminal and web legends. The web engine uses the same core lockout rule and clock-passing calls. New main.rs tests cover the lockout, and the snapshots are updated.

Risk Assessment

✅ Low: The newest commit only removes the death-flash timing (DEATH_FLASH, death_flashing_at, the flash branch in next_tick and render_death, and its tests), so death goes straight to the settled look; the lockout, the dimmed hints and the prompt placement are unchanged, and no flash code is left in the core, the UI or the web engine.

Testing

I built thokr with a nix-provided cargo and drove it in a pseudo-terminal sized 80x24, rendering the screen with pyte (dim tracked). In death mode the three prompt rows had the same text on the same rows in the last test frame and in the death frame. The ~40ms death frame already had its settled look: a red block on the expected char, typed text in the normal colour, the rest gray and dim, and the stats line in red. It matched the ~250ms frame cell for cell, so there is no flash. There was no skull, no "died" and no "expected/pressed" line. The only hint was "(r)etry / (n)ew", in italic, dimmed until 500ms and normal at ~700ms. r and n pressed inside the lockout changed nothing, and r afterwards restarted the test. Esc during the lockout exited at once. A natural finish showed the same lockout and hint dimming. I also built the web engine to wasm, ran the snapshot test (pass), and drove the built page in Chrome: arrow keys right after a finish were dropped, and at ~700ms one restarted the test. PNG screenshots, a transcript and the browser result JSON are in the evidence directory. I removed the transient web/node_modules, web/wasm and web/dist, and the worktree is clean.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Death in -d mode: the prompt lines stay on the same rows from the last test frame to the death frame ✅ pass live transcript.txt 'lines differing after death: {}'; 02-test-screen-before-fatal-key.png vs 03-death-screen-40ms.png
Death screen look: typed text normal colour, fatal char a red reversed block, remainder dimmer than usual, stats line in red ✅ pass live transcript.txt: fatal_red_block, typed_normal, rest_dimmed (gray + dim vs default + dim on the test screen), stats_red all true; 03-death-screen-40ms.png
Death screen shows no skull, no 'died' and no expected/pressed line ✅ pass live transcript.txt no_skull_died_expected=true; 03-death-screen-40ms.png
No red flash: death goes straight to the settled look ✅ pass live transcript.txt no_flash_40_vs_250ms=true (the ~40ms and ~250ms frames match cell for cell); grep finds no flash code in src or web
r and n pressed within 500ms of a death are dropped, and the hints are dim until they go live ✅ pass live transcript.txt rn_dropped_in_lockout, hint_dim_then_normal, r_after_lockout_restarts all true; 04- and 05- screenshots
Esc quits at once even inside the lockout ✅ pass live transcript.txt 'esc exited within 400ms of death: True'
Natural finish: r and n dropped during the lockout, hints dim then normal, legend has no (esc)ape ✅ pass live transcript.txt finish_rn_dropped, finish_hint_dim_then_normal, finish_legend_no_esc, finish_n_after_lockout_new all true; 07- and 08- screenshots
Browser demo: arrow keys right after a finish are dropped by the shared core lockout, and work after 500ms ✅ pass live web-lockout.json (resAfterEarlyArrows=true, statsUnchanged=true, resAfterLateArrow=false, samePromptRestarted=true); 09-web-results-after-early-arrow.png; node --test test/snapshots.test.mjs passes

Test screen just before the fatal key (100 chars typed, prompt rows 10-12)
Death screen ~40ms after the fatal key: same rows, red block on fatal char, rest dimmed, red stats, dim hints, no skull
Death screen after r/n pressed inside the lockout (ignored)
Death screen at ~700ms: hints back to normal
r after the lockout restarts the same prompt
Natural finish results inside the lockout (dim hints) and after it
Browser demo: results still shown after an ArrowLeft right at the finish

Evidence: Browser lockout timing result
{"promptLen":84,"resShownAtEnd":true,"statsAtEnd":"1008 wpm   100% acc   0.00 sd","resAfterEarlyArrows":true,"statsUnchanged":true,"resStillAt700":true,"resAfterLateArrow":false,"samePromptRestarted":true}
Evidence: Terminal transcript with per-check results
BEFORE fatal key:
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     the quick brown fox jumps over the lazy dog while seven bright purple      
     monkeys juggle heavy melons near an old wooden bridge beside the river     
     and nobody seems to notice anything at all today                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
DEATH (~40ms, expected 'a' pressed 'z'):
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     the quick brown fox jumps over the lazy dog while seven bright purple      
     monkeys juggle heavy melons near an old wooden bridge beside the river     
     and nobody seems to notice anything at all today                           
                                                                                
                            17 words, 100 chars, 0.6s                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     (r)etry / (n)ew                                                            
                                                                                
                                                                                
DEATH (~700ms):
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     the quick brown fox jumps over the lazy dog while seven bright purple      
     monkeys juggle heavy melons near an old wooden bridge beside the river     
     and nobody seems to notice anything at all today                           
                                                                                
                            17 words, 100 chars, 0.6s                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     (r)etry / (n)ew                                                            
                                                                                
                                                                                
prompt lines before: {
 "10": "     the quick brown fox jumps over the lazy dog while seven bright purple",
 "11": "     monkeys juggle heavy melons near an old wooden bridge beside the river",
 "12": "     and nobody seems to notice anything at all today"
}
lines differing after death: {}
reversed cells: [(11, 35, 'a', 'cd0000', 'default')]
typed sample: [{'ch': 'm', 'fg': 'default', 'bg': 'default', 'bold': True, 'dim': False, 'rev': False, 'it': False}, {'ch': 'o', 'fg': 'default', 'bg': 'default', 'bold': True, 'dim': False, 'rev': False, 'it': False}, {'ch': 'n', 'fg': 'default', 'bg': 'default', 'bold': True, 'dim': False, 'rev': False, 'it': False}]
after sample: [{'ch': 'r', 'fg': '7f7f7f', 'bg': 'default', 'bold': True, 'dim': True, 'rev': False, 'it': False}, {'ch': 'a', 'fg': '7f7f7f', 'bg': 'default', 'bold': True, 'dim': True, 'rev': False, 'it': False}, {'ch': 'n', 'fg': '7f7f7f', 'bg': 'default', 'bold': True, 'dim': True, 'rev': False, 'it': False}]
untyped on test screen sample: [{'ch': 'r', 'fg': 'default', 'bg': 'default', 'bold': True, 'dim': True, 'rev': False, 'it': False}, {'ch': 'a', 'fg': 'default', 'bg': 'default', 'bold': True, 'dim': True, 'rev': False, 'it': False}]
stats rows: [(14, '                            17 words, 100 chars, 0.6s')]
legend: '(r)etry / (n)ew'
after r:
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
     the quick brown fox jumps over the lazy dog while seven bright purple      
     monkeys juggle heavy melons near an old wooden bridge beside the river     
     and nobody seems to notice anything at all today                           
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
esc exited within 400ms of death: True
results screen:
                                                                                
                                                                                
     60000 ────────────────────────────────────────────────────────────────     
           ⠒⠤⣀                                                                  
              ⠉⠒⠤⢄⡀                                                             
                  ⠈⠑⠢⢄⡀                                                         
                      ⠈⠉⠒⠤⣀                                                     
     40000 ────────────────⠉⠒⠢⢄⡀───────────────────────────────────────────     
                               ⠈⠑⠢⠤⣀                                            
                                    ⠉⠒⠤⣀⡀                                       
                                        ⠈⠑⠢⢄⡀                                   
                                            ⠈⠑⠒⠤⣀                               
     20000 ──────────────────────────────────────⠉⠒⠤⢄⡀─────────────────────     
                                                     ⠈⠑⠢⢄⣀                      
                                                          ⠉⠒⠤⣀                  
                                                              ⠉⠑⠢⢄⡀             
                                                                  ⠈⠑⠢⠤⣀         
         0 ────────────────────────────────────────────────────────────⠉⠒⠤⣀     
     sec                                 0.5                              1     
                           24 wpm   100% acc   0.00 sd                          
                                                                                
     (r)etry / (n)ew                                                            
                                                                                
                                                                                
{
 "lines_static": true,
 "fatal_red_block": true,
 "typed_normal": true,
 "rest_dimmed": true,
 "stats_red": true,
 "no_skull_died_expected": true,
 "no_flash_40_vs_250ms": true,
 "rn_dropped_in_lockout": true,
 "hint_dim_then_normal": true,
 "legend_no_esc": true,
 "r_after_lockout_restarts": true,
 "esc_immediate_in_lockout": true,
 "finish_rn_dropped": true,
 "finish_legend_no_esc": true,
 "finish_hint_dim_then_normal": true,
 "finish_n_after_lockout_new": true
}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
Death in -d mode: the prompt lines stay on the same rows from the last test frame to the death frame ✅ pass live transcript.txt 'lines differing after death: {}'; 02-test-screen-before-fatal-key.png vs 03-death-screen-40ms.png
Death screen look: typed text normal colour, fatal char a red reversed 8000 block, remainder dimmer than usual, stats line in red ✅ pass live transcript.txt: fatal_red_block, typed_normal, rest_dimmed (gray + dim vs default + dim on the test screen), stats_red all true; 03-death-screen-40ms.png
Death screen shows no skull, no 'died' and no expected/pressed line ✅ pass live transcript.txt no_skull_died_expected=true; 03-death-screen-40ms.png
No red flash: death goes straight to the settled look ✅ pass live transcript.txt no_flash_40_vs_250ms=true (the ~40ms and ~250ms frames match cell for cell); grep finds no flash code in src or web
r and n pressed within 500ms of a death are dropped, and the hints are dim until they go live ✅ pass live transcript.txt rn_dropped_in_lockout, hint_dim_then_normal, r_after_lockout_restarts all true; 04- and 05- screenshots
Esc quits at once even inside the lockout ✅ pass live transcript.txt 'esc exited within 400ms of death: True'
Natural finish: r and n dropped during the lockout, hints dim then normal, legend has no (esc)ape ✅ pass live transcript.txt finish_rn_dropped, finish_hint_dim_then_normal, finish_legend_no_esc, finish_n_after_lockout_new all true; 07- and 08- screenshots
Browser demo: arrow keys right after a finish are dropped by the shared core lockout, and work after 500ms ✅ pass live web-lockout.json (resAfterEarlyArrows=true, statsUnchanged=true, resAfterLateArrow=false, samePromptRestarted=true); 09-web-results-after-early-arrow.png; node --test test/snapshots.test.mjs passes
  • cargo build (debug thokr binary, nix-provided cargo 1.99, target dir in /tmp)
  • pty + pyte driver /tmp/nm-drive/scen.py: ran thokr -d -p <long prompt> at 80x24. Typed 100 correct chars, then a wrong key. Captured the frame before the fatal key and the death frames at ~40ms, ~250ms, ~300ms (after r/n) and ~700ms. Compared the row text and the cell styles (fg, dim, reverse, italic)
  • pty driver: thokr -d -p 'the quick brown fox', died, then pressed esc ~30ms later. The process exited within 400ms
  • pty driver: thokr -p ab natural finish. Pressed n/r at once (ignored, hints dim), checked hints normal at ~700ms, pressed n after the lockout (new test)
  • built the web engine for wasm32-wasip1 (rustup toolchain in /tmp) and ran npm run build:app, then node --test test/snapshots.test.mjs (engine and chart.ts against the ui.rs insta snapshots: 10/10 pass)
  • chrome-devtools-axi against the built page served locally: typed the hero prompt, then ArrowLeft/ArrowRight ~30ms after the finish (ignored, results stay), then ArrowLeft at ~700ms (restarts the same prompt)
  • cargo test (crate unit tests incl. death, lockout and next_tick tests: 88 pass, 1 ignored)
  • grep -rn flash src web/engine/src web/src (no flash code left, only words in a word list)
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 info
  • ℹ️ src/ui.rs - Could not run cargo fmt --all -- --check or cargo clippy --all-features -- -D warnings (the checks in .github/workflows/build.yml) because cargo is not on PATH here, and web/node_modules is missing so npm run typecheck could not run either. Formatting and clippy status of src/ui.rs, src/thok.rs, src/main.rs and web/engine/src/lib.rs is unverified locally. CI runs these checks.
✅ **Push** - passed

✅ No issues found.

@cloudflare-workers-and-pages
cloudflare-workers-and-pages Bot commented Oct 29, 2026 •
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
thokr-web 09b8b10 Oct 29 2026, 10:51 PM

@jrnxf jrnxf changed the title fix(ui): keep the prompt in place on the death screen fix(ui): keep death-screen prompt in place and lock out retry keys after a test ends Oct 29, 2026
@jrnxf jrnxf changed the title fix(ui): keep death-screen prompt in place and lock out retry keys after a test ends feat(ui): flash-then-settle death screen and retry-key lockout after a test ends Oct 29, 2026
@jrnxf jrnxf changed the title feat(ui): flash-then-settle death screen and retry-key lockout after a test ends feat(ui): steady death screen and retry-key lockout after a test ends Oct 29, 2026
@jrnxf
jrnxf merged commit 9d505a0 into main Oct 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0