HTML overlay UI: score and game-over text, format computed in Gleam, only non-branching DOM writes in FFI
Context
Phase 7 of the approved merge-puzzler plan: the game now has a real score (Phase 2) and a real lose condition (Phase 6), but nothing on screen shows either -- the WebGPU canvas only ever draws entities, never text. Needed a minimal way to surface both.
Options considered
Decision
glemy/game.gleam gained format_score(score: Int) -> String (deliberately left untagged, not @target(javascript), even though only the JS-only loop calls it -- plain string formatting has no target-specific need, so it stays gleam test-covered on both Erlang and JavaScript). Two new minimal FFI wrappers, set_score_text(text: String) -> Nil and set_game_over(visible: Bool, message: String) -> Nil, call straight through to game_ffi.mjs's setScoreText/setGameOver -- each just a textContent assignment (and, for game-over, a `hidden` boolean assignment), no branching logic in the JS at all. glemy/game.gleam's loop calls both every frame, unconditionally, off the freshly ticked Model (score, game_over) -- idempotent re-writes are harmless and avoid needing any extra edge-detection state. index.html gained #glemy-score (starts "Score: 0") and #glemy-game-over (starts `hidden`), styled minimally alongside the existing hint text.
Verification
135 Erlang / 169 JavaScript tests passing (up from 132/166), all additive: three new format_score tests (zero, a multi-digit positive value, a single digit) run on both targets since the function itself is target-agnostic. Real-browser verification via tools/browser_check.ts: confirmed #glemy-score reads exactly "Score: 0" and #glemy-game-over is still hidden on a freshly loaded page, before any drop or loss -- direct, real-DOM proof that glemy/game.gleam's loop is actually reaching game_ffi.mjs's setScoreText/setGameOver every frame, not just that the pure Gleam formatting is correct in isolation (which format_score's own gleam test coverage already established separately).
Consequences
Any future on-page text (e.g. a restart hint, a tier-preview label) has an established, minimal pattern to follow: format in Gleam, write via a single non-branching FFI call. No score/game-over text verification was attempted through pixel readback, since tools/browser_check.ts's own prior investigation (decision 0021) already established that headless Chromium can't read back canvas pixels here -- irrelevant anyway, since this text lives in the DOM, not the canvas.