Skip to content

fix(controller): A types in a field, the keyboard keys move, and up reaches the toolbar - #101

Open
lippdev wants to merge 1 commit into
mainfrom
fix/controller-navigation
Open

lippdev wants to merge 1 commit into
mainfrom
fix/controller-navigation

Conversation

@lippdev

@lippdev lippdev commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the three controller problems found in a hands-on test.

1. A on a text field opens the keyboard (instead of Y).

  • spatial.js returns edit when A lands on a text field, and InputRouter then opens the controller keyboard for the page. This works on every page, including ours (the new tab's search box).
  • The preload no longer opens the keyboard on any focusin. That used to pop it up when the D-pad walked over a field or a site used autofocus. It now opens only when the focus follows a real pointer press (mouse, or A in Cursor mode), and this hook also runs on internal pages now.
  • The address field opens the keyboard on mousedown instead of on focus, so reaching it with the D-pad doesn't open it on the way past.

2. The keyboard keys didn't move.

  • keyboard-close hands the window's focus back to the tab. From the second opening on, the D-pad walked the keys in a document without system focus, so Chromium drew no focus and the keyboard looked dead. openKeyboard now always calls focusChrome().
  • The first press lands on the first key, not on the close button. The dialog still takes the initial focus so screen readers announce it.

3. Up from the top of the page reaches the toolbar and the tabs.

  • spatial.js returns edge on up when nothing is above and the page is at its top. Shell.enterChrome reveals the bar (in TV mode it stays visible while it has the focus), takes the window's focus and puts it on the address field (new shell:focus-chrome channel).
  • In the bar, the D-pad walks the tab strip and the toolbar and A presses (on the address field it opens the keyboard). B, or down with nothing below, returns to the page through the new focus-page command, which is validated.
  • Minimize, maximize and close stay off the D-pad, so one A can't close the app.
  • If the keyboard is opened from the bar and cancelled, the focus stays in the bar.

Spatial scoring (both bugs were found by the new tests):

  • Page: sharing a column or a row weighs more than where the centres sit. The D-pad used to jump over a wide field under a short link.
  • Shell: distance is measured edge to edge, so left from the address field reaches reload instead of the tab strip. Whether a control counts as "ahead" is still judged by its centre, so the close button inside a tab card stays reachable. The test that plans paths in the tab grid mirrors this calculation.

docs/CONTROLLER-TEST.md gains two scenarios for the TV session.

Test plan

  • npm run typecheck
  • npm test (104 passed)
  • npm run test:e2e (51 passed, 3 skipped), with two new tests in controller.spec.ts:
    • the field is reached without opening the keyboard; A opens it with the tab holding the focus (the reported case); the D-pad walks the keys; A types into the page; it works the second time too
    • up from the top reaches the address field (no keyboard); A and then B keep the focus in the bar; left reaches reload, up the tabs, down returns to the page
  • Screenshots on the real Windows screen: the key, the "+" and the tab are each drawn with the focus ring
  • Physical controller on the TV (docs/CONTROLLER-TEST.md scenarios 5 and 6)

🤖 Generated with Claude Code

@lippdev
lippdev force-pushed the fix/controller-navigation branch from 6bd2ad4 to e7248d4 Compare October 21, 2026 19:22
…eaches the toolbar

- A on a text field (any page, the new tab's box included) opens the
  controller keyboard. Walking over a field or a page's autofocus no
  longer pops it up; a real click still does (mouse, or A in Cursor mode).
- The keyboard always takes the window's focus. Closing it hands the
  focus back to the tab, so from the second time on the D-pad walked the
  keys while Chromium drew no focus on them, and the keyboard looked dead.
  The first press now lands on the first key, not on the close button.
- Up from the top of a page moves the D-pad to the toolbar (address field,
  back, forward, reload, favorite, menu) and the tab strip; A presses, the
  address field opens the keyboard, and down past the toolbar or B goes
  back to the page. Minimize, maximize and close stay off the D-pad. In TV
  mode the bar stays on screen while it has the focus.
- The address field opens the keyboard on a click instead of on focus, so
  reaching it with the D-pad does not open it on the way past.
- Spatial focus: the first press takes the first item instead of looking
  past the middle of the screen; sharing a column or a row counts more than
  where the centres sit, which made the D-pad jump over wide text fields.
  The shell walks its own controls edge to edge as well, so left from the
  address field reaches reload instead of the tab strip, and the close
  button inside a tab card stays reachable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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