From 6d09b94bbb3ff1cd52822a298af5a0c475471331 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Sun, 13 Sep 2026 05:38:36 +0200 Subject: [PATCH] feat(keys): j/k and the arrows walk Home's rails and every Plex grid and rail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second slice of the E11 keyboard layer (S4b). S4a gave the feed, the library and the channel page `j`/`k`, the arrows and Enter; Home's shelves and the whole Plex module were still Tab-stop country — a Plex poster is its own Tab stop, and a rail's cards sat behind every control of the cards before. - `lib/listNav`: a list that mounts every item no longer declares a count or columns — both are measured off the DOM when a key is pressed, so an auto-fill grid's columns follow its width and a rail is one row. ↑ on a list's top row and ↓ on its bottom row go on to the neighbouring list, onto the item horizontally nearest the one left (owner decision D4: ←→ within a rail, ↑↓ to the rail above or below). The first `j` skips cards a rail has scrolled out to the left. - `Carousel keyboardNav` registers the scroller; the scroller gets a 4px inset (against a -4px margin, layout unchanged) so the focus ring is not clipped, and `[data-nav-index]` a 2.5rem inline scroll margin so a card stepped into view clears the rail's edge fade and paging arrow. - `ui/NavGrid`: the plain, fully mounted grid that registers itself. - `navIndex` on PlexPosterCard / SeasonCard / EpisodeCard (the card root is the item, the poster the open element — the ring goes round poster and title, D2), PosterTile and Home's ShelfTile (the button is the item); cast, related and collection-strip buttons carry the attrs directly. - Wired: Home's comfort and resume rails; Plex Browse titles + episodes, Discover rails + genres, collection view, playlists/collections browse, person known-for + filmography, show seasons, season episodes, cast/related strips, info-page cast + collection strips. - `listNav.wiring.contract.test`: every `Carousel` says `keyboardNav`, every card says `navIndex`, and a card with `navIndex` sits inside a NavGrid / Carousel / VirtualGrid — both halves are opt-in, and either one missing leaves a list the keyboard silently skips. --- CLAUDE.md | 2 +- frontend/src/components/Home.dom.test.tsx | 44 ++++++ frontend/src/components/Home.tsx | 11 +- frontend/src/components/PlexBrowse.tsx | 15 +- .../src/components/PlexCollectionView.tsx | 8 +- .../src/components/PlexDiscover.dom.test.tsx | 39 ++++- frontend/src/components/PlexDiscover.tsx | 19 ++- frontend/src/components/PlexInfo.tsx | 13 +- frontend/src/components/PosterTile.tsx | 4 + .../listNav.wiring.contract.test.ts | 138 ++++++++++++++++++ frontend/src/components/plexBrowseLists.tsx | 15 +- .../src/components/plexCards.dom.test.tsx | 49 ++++++- frontend/src/components/plexCards.tsx | 24 ++- frontend/src/components/plexViews.tsx | 31 ++-- .../src/components/ui/Carousel.dom.test.tsx | 38 ++++- frontend/src/components/ui/Carousel.tsx | 24 ++- frontend/src/components/ui/NavGrid.tsx | 23 +++ frontend/src/components/ui/README.md | 2 +- frontend/src/index.css | 2 + frontend/src/lib/listNav.dom.test.tsx | 96 +++++++++++- frontend/src/lib/listNav.ts | 83 +++++++++-- 21 files changed, 615 insertions(+), 65 deletions(-) create mode 100644 frontend/src/components/listNav.wiring.contract.test.ts create mode 100644 frontend/src/components/ui/NavGrid.tsx diff --git a/CLAUDE.md b/CLAUDE.md index b93f9bfc..42c11893 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,7 +90,7 @@ CQRS, a repository/DI framework, a router library, a generic CRUD-router factory | FE per-account state | `usePref` | raw `readAccount`/`writeAccount` or bespoke hooks | | FE action button | `ui/Button` — pick a `SIZES` rung | a hand-rolled `