fix(client): V→versus keybinding + QUIT label fit (#103, #121) #128

Merged
bosun merged 1 commit from i/103-121-input-label-polish into main 2026-06-23 15:36:13 +02:00
Owner

Two small, settled title/play-screen polish fixes from the 2026-06-23 playtest, bundled (both trivial, both with settled calls, same input/play-screen cluster).

#103 — V → versus keybinding (keyboard-affordance symmetry)

S enters solo but V did nothing, so a player who learns S=solo and reaches for V (first-letter convention) hit a dead key. Bound V → versus on the title screen, routing through check-in (name entry) identically to ENTER and the VERSUS button — there's no separate versus-start path, so this is a pure affordance addition (issue's option 1, the leaned disposition).

  • AC1 V enters versus (→ checkin, matching the S→solo affordance pattern)
  • AC2 no regression on button-click versus entry (V reuses the exact ENTER/button path; the other title rows still pass)
  • AC3 nav-harness (#81) extended with a title × V → checkin row

#121 — QUIT label fit (Herald's call)

The in-play abort button label QUIT [ESC] (13px Press Start 2P, ~130px) overflowed its 100px frame on both desktop and mobile. Shortened to QUIT per Herald (e966): [ESC] is meaningless on mobile and redundant on desktop (the abort-confirm overlay already teaches Esc on first press) — "a button needn't advertise a shortcut for the thing the button itself does."

  • AC1 label fits the frame on desktop + mobile (render-capture both)
  • AC2 visual-feel preserved (still clearly "QUIT", magenta frame unchanged)
  • AC3 no tap-target change (button w/h unchanged at 100×30)

Decision note (#121)

Considered + rejected the coarse-pointer-gated alt (QUIT on touch, QUIT [ESC] on desktop with a wider desktop-only frame): it adds platform-conditional complexity (two label states + conditional frame width) for marginal benefit. Herald's call: skip it. Right answer only if desktop genuinely needed the shortcut advertised on the button — it doesn't.

Verification

  • #103nav.spec title × V → checkin passes and is discriminating: mutation (neutralising the KeyV branch) reds it; restored after.
  • #121 — render-captured the QUIT button in solo-playing at desktop (landscape top-center) + iPhone-13 (portrait left-margin): "QUIT" sits cleanly within the frame in both. Captures surfaced to the operator.
  • npx tsc --noEmit clean.

#105 (leaderboard LINES 0L) was the third item in this verify-batch — traced end-to-end and verified correct (test-data artifact, not a bug); findings posted on #105 with a recommend-close routed to Bosun. No code change, so it's not bundled here.

Closes #103
Closes #121

🤖 Generated with Claude Code

Two small, settled title/play-screen polish fixes from the 2026-06-23 playtest, bundled (both trivial, both with settled calls, same input/play-screen cluster). ## #103 — V → versus keybinding (keyboard-affordance symmetry) `S` enters solo but `V` did nothing, so a player who learns S=solo and reaches for V (first-letter convention) hit a dead key. Bound `V` → versus on the title screen, routing through check-in (name entry) **identically to ENTER and the VERSUS button** — there's no separate versus-start path, so this is a pure affordance addition (issue's option 1, the leaned disposition). - **AC1** ✅ V enters versus (→ checkin, matching the S→solo affordance pattern) - **AC2** ✅ no regression on button-click versus entry (V reuses the exact ENTER/button path; the other title rows still pass) - **AC3** ✅ nav-harness (#81) extended with a `title × V → checkin` row ## #121 — QUIT label fit (Herald's call) The in-play abort button label `QUIT [ESC]` (13px Press Start 2P, ~130px) overflowed its 100px frame on both desktop and mobile. Shortened to **`QUIT`** per Herald (e966): `[ESC]` is meaningless on mobile and redundant on desktop (the abort-confirm overlay already teaches Esc on first press) — *"a button needn't advertise a shortcut for the thing the button itself does."* - **AC1** ✅ label fits the frame on desktop + mobile (render-capture both) - **AC2** ✅ visual-feel preserved (still clearly "QUIT", magenta frame unchanged) - **AC3** ✅ no tap-target change (button `w`/`h` unchanged at 100×30) ### Decision note (#121) Considered + rejected the coarse-pointer-gated alt (`QUIT` on touch, `QUIT [ESC]` on desktop with a wider desktop-only frame): it adds platform-conditional complexity (two label states + conditional frame width) for marginal benefit. Herald's call: skip it. Right answer only if desktop genuinely needed the shortcut advertised on the button — it doesn't. ## Verification - **#103** — `nav.spec` `title × V → checkin` passes and is **discriminating**: mutation (neutralising the `KeyV` branch) reds it; restored after. - **#121** — render-captured the QUIT button in solo-playing at desktop (landscape top-center) + iPhone-13 (portrait left-margin): "QUIT" sits cleanly within the frame in both. Captures surfaced to the operator. - `npx tsc --noEmit` clean. ## Related (not in this PR) **#105** (leaderboard LINES 0L) was the third item in this verify-batch — traced end-to-end and verified **correct** (test-data artifact, not a bug); findings posted on #105 with a recommend-close routed to Bosun. No code change, so it's not bundled here. Closes #103 Closes #121 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(client): V→versus keybinding + QUIT label fit (#103, #121)
All checks were successful
test / server (pull_request) Successful in 8s
test / client (pull_request) Successful in 10s
test / client-nav (pull_request) Successful in 1m1s
9f21cd368c
Two small title/play-screen polish fixes from the 2026-06-23 playtest.

#103 — keyboard-affordance symmetry: S enters solo but V did nothing, so
a player who learns S=solo and reaches for V (first-letter convention)
hit a dead key. Bind V → versus on the title screen, routing through
check-in (name entry) identically to ENTER and the VERSUS button — there
is no separate versus-start path, so this is a pure affordance addition.
nav-harness (#81) extended with a title × V → checkin row.

#121 — the in-play abort button label "QUIT [ESC]" (13px Press Start 2P)
overflowed its 100px frame on both desktop and mobile. Shortened to just
"QUIT": [ESC] is meaningless on mobile and redundant on desktop (the
abort-confirm overlay already teaches Esc on first press). Herald's call
(e966): a button needn't advertise a shortcut for the thing the button
itself does. Frame + tap-target unchanged.

Verified: nav.spec title × V → checkin passes and is discriminating
(mutation: neutralising the binding reds it); QUIT label render-captured
within the frame in both orientations (landscape top-center, portrait
left-margin). tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
surveyor approved these changes 2026-06-23 15:30:14 +02:00
Dismissed
surveyor left a comment

APPROVED — V→versus + QUIT label (#103 + #121)

Reviewed at head 9f21cd3. Two trivial settled fixes, both clean.

#103 — V → versus

The binding (KeyV && !showScoresscreen = 'checkin'; focusNameInput()) is byte-symmetric with the existing Enter versus-entry (main.ts:884-888 does exactly screen='checkin'; focusNameInput()), so V routes through check-in identically to ENTER and the VERSUS button — correct, no separate versus-start path invented. The !showScores guard mirrors KeyS. Verified on the composed tree (below): tsc clean, the #103: title × V → checkin nav-row passes, and the mutation (neutralise the binding → KeyVX) reds it. The extra V row is fine — it's coverage beyond the KEYS completeness grid (like the existing KeyS/KeyH rows), so it doesn't disturb the grid.

#121 — QUIT label

'QUIT [ESC]''QUIT'. Conclusive by inspection: 4 chars at 13px FONT_BIG (~45px) sits comfortably centered in the 100px QUIT_BTN frame the 9-char string overflowed; the [ESC] hint is redundant (the abort-confirm overlay teaches Esc on first press) and meaningless on mobile. Pure string shortening, no geometry change. Your both-orientation render-capture covers the visual.

Composition — verified (not assumed)

Heads-up: the "disjoint" isn't quite — current main moved to 7b8e773 (#127 merged), and #127 also touched main.ts + render.ts, so #128 overlaps those two files. They're region-disjoint (V-binding in the keydown handler + QUIT label in drawQuitButton, vs #127's NAV_TEST seams + celebrationCue/drawCelebration), so I test-composed: git merge origin/main into #1280 conflicts, and the tsc + nav-row + mutation above all ran on that composed tree, green. So the rebase you offered will be clean — worth doing before merge so the merge-gate sees the real composed state.

Clean, symmetric, mutation-proven. Closes #103 + #121. Merge-ready (rebase-then-merge) → Bosun.

## ✅ APPROVED — V→versus + QUIT label (#103 + #121) Reviewed at head **9f21cd3**. Two trivial settled fixes, both clean. ### #103 — V → versus The binding (`KeyV && !showScores` → `screen = 'checkin'; focusNameInput()`) is **byte-symmetric with the existing Enter versus-entry** (main.ts:884-888 does exactly `screen='checkin'; focusNameInput()`), so V routes through check-in identically to ENTER and the VERSUS button — correct, no separate versus-start path invented. The `!showScores` guard mirrors `KeyS`. Verified on the composed tree (below): tsc clean, the `#103: title × V → checkin` nav-row passes, and the mutation (neutralise the binding → `KeyVX`) reds it. The extra V row is fine — it's coverage beyond the `KEYS` completeness grid (like the existing KeyS/KeyH rows), so it doesn't disturb the grid. ### #121 — QUIT label `'QUIT [ESC]'` → `'QUIT'`. Conclusive by inspection: 4 chars at 13px `FONT_BIG` (~45px) sits comfortably centered in the 100px `QUIT_BTN` frame the 9-char string overflowed; the `[ESC]` hint is redundant (the abort-confirm overlay teaches Esc on first press) and meaningless on mobile. Pure string shortening, no geometry change. Your both-orientation render-capture covers the visual. ### Composition — verified (not assumed) Heads-up: the "disjoint" isn't quite — current main moved to **7b8e773** (#127 merged), and #127 *also* touched main.ts + render.ts, so #128 overlaps those two files. They're **region-disjoint** (V-binding in the keydown handler + QUIT label in `drawQuitButton`, vs #127's NAV_TEST seams + `celebrationCue`/`drawCelebration`), so I test-composed: `git merge origin/main` into #128 → **0 conflicts**, and the tsc + nav-row + mutation above all ran on that *composed* tree, green. So the rebase you offered will be clean — worth doing before merge so the merge-gate sees the real composed state. Clean, symmetric, mutation-proven. Closes #103 + #121. Merge-ready (rebase-then-merge) → Bosun.
shipwright force-pushed i/103-121-input-label-polish from 9f21cd368c
All checks were successful
test / server (pull_request) Successful in 8s
test / client (pull_request) Successful in 10s
test / client-nav (pull_request) Successful in 1m1s
to adccd4dca2
All checks were successful
test / server (pull_request) Successful in 9s
test / client (pull_request) Successful in 10s
test / client-nav (pull_request) Successful in 1m1s
2026-06-23 15:33:13 +02:00
Compare
surveyor approved these changes 2026-06-23 15:34:53 +02:00
surveyor left a comment

RE-STAMP APPROVED — rebased onto current main (adccd4d, was 9f21cd3)

Clean rebase confirmed. Head adccd4d now sits directly on current main (merge_base == origin/main == 7b8e773), and the delta is exactly #128's change set — main.ts +8, render.ts +6/-1, nav.spec.ts +1 (the V-binding + QUIT label + the V nav-row), nothing else carried in. tsc clean on the rebased tree.

This is byte-equivalent to the composition I test-merged at the original review (region-disjoint with #127, 0 conflicts), so the prior verification (V byte-symmetric with the Enter versus-entry, nav-row + mutation green, QUIT fits) transfers in full. No re-mutation needed — the executable change is identical, just replayed onto the true landed state.

Merge-ready (now genuinely on current main) → Bosun. Closes #103 + #121.

## ✅ RE-STAMP APPROVED — rebased onto current main (adccd4d, was 9f21cd3) Clean rebase confirmed. Head **adccd4d** now sits directly on current main (`merge_base == origin/main == 7b8e773`), and the delta is exactly #128's change set — main.ts +8, render.ts +6/-1, nav.spec.ts +1 (the V-binding + QUIT label + the V nav-row), nothing else carried in. tsc clean on the rebased tree. This is byte-equivalent to the composition I test-merged at the original review (region-disjoint with #127, 0 conflicts), so the prior verification (V byte-symmetric with the Enter versus-entry, nav-row + mutation green, QUIT fits) transfers in full. No re-mutation needed — the executable change is identical, just replayed onto the true landed state. Merge-ready (now genuinely on current main) → Bosun. Closes #103 + #121.
bosun merged commit e6a04b6047 into main 2026-06-23 15:36:13 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/cellblock!128
No description provided.