Skip to content

fix(ui): let the keyboard row action follow the highlight, not the index - #296

Merged
argszero merged 1 commit into
mainfrom
fix/kbd-row-action-follows-highlight
Sep 24, 2026
Merged

argszero merged 1 commit into
mainfrom
fix/kbd-row-action-follows-highlight

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

The keyboard navigation stored its "current row" as an index (kbd.i) into the
<tbody>'s row list, and kbdEnter() clicked whatever row that index landed on.
That is an identity claim the index cannot support, with two faces:

  1. A view switch did not disarm. switchView() changed the visible view without
    clearing the armed row, so a stale kbd.i survived the switch and Enter still
    acted on the previous table's row.
  2. A repaint silently re-pointed the index. kbdEnter() trusted the index alone;
    after any repaint replaced the tbody's innerHTML, the index pointed at a different
    row than the one the user had highlighted — Enter clicked a row nobody had selected.

The highlight is what the user sees, so make it the identity: kbdEnter() acts only
on a row that carries .row-active itself (a repaint drops the old element and with
it the highlight, so any repaint invalidates the arming for free), and switchView()
calls kbdClear() after the role/guest guard accepts the destination (a rejected
switch must not clear).

Related Issue

No linked issue — the repository has no open issues; this is a self-contained
correctness fix (same shape as the previous UI-contract fixes in this series).

Changes

  • ui/js/app.js — switchView() clears the keyboard arming on an accepted destination; kbdEnter() requires the target row to carry the highlight itself
  • ui/README.md — the keyboard-navigation section states the contract and the gate's scope
  • ui/index.html — cache-bust bump for ui/js/app.js
  • src/state_gate.rs — new gate the_keyboard_row_action_follows_the_highlight_not_the_index (plus its roster and teeth self-tests)
  • No configuration or data-structure change

Tests

  • cargo test — 381 passed / 0 failed (baseline cc51f82 was 378; +3 new tests)
  • cargo fmt --check — clean
  • cargo clippy --all-targets -- -D warnings — clean
  • New tests added: the gate has two independent halves (r173_variant_drop_clear, r173_variant_drop_highlight_test each fail on their own), a positive control, a roster check and a teeth check

Instrumentation (both instruments, each declaring its legs):

  • Compiler gate A/B — the gate compiled against the pre-fix tree b0f1ef72… fails exactly on the axis rule (r1=false r2=false r3=true), and is green on the fixed tree.
  • jsdom probe — 5 variants × 11 legs, every leg ALL LEGS AS DECLARED: base reproduces the defect, fix is accepted, and the two competing fixes (m_classonly, m_switchonly) are each rejected by their own leg.
  • The probe's patched ui/js/app.js md5 2390d877a2859b7e49a7c02acf21e205 is byte-identical to the tree committed here.

Checklist

  • Branch naming follows the convention (fix/)
  • Commit message uses Conventional Commits (fix(ui): …)
  • Single responsibility, minimal diff

The keyboard navigation kept the "current row" as an index (`kbd.i`) into the
`<tbody>`'s row list, and `kbdEnter()` clicked whatever row that index landed on
— including a row the user had never highlighted, and one that belongs to a view
they had already left. Two faces:

1. `switchView()` changed views without clearing the armed row, so a stale
   `kbd.i` survived the switch and Enter still acted on the old table's row.
2. `kbdEnter()` trusted the index alone: after any repaint replaced the tbody's
   `innerHTML`, the index pointed at a different row than the one carrying the
   highlight.

Make the highlight itself the identity: `kbdEnter()` acts only on a row that
carries `.row-active` itself (a repaint drops the old element and with it the
highlight, so any repaint invalidates the arming), and `switchView()` calls
`kbdClear()` after the role/guest guard accepts the destination (a rejected
switch must not clear).

Gate `state_gate::the_keyboard_row_action_follows_the_highlight_not_the_index`
verifies the two halves independently; `ui/README.md` states the contract and
the gate's scope. Cache-bust bumped for `ui/js/app.js`.
@argszero
argszero merged commit 315ce7b into main Sep 24, 2026
1 check passed
@argszero
argszero deleted the fix/kbd-row-action-follows-highlight branch September 24, 2026 17:52
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