Commit Graph
25 Commits
Author SHA1 Message Date
edde746 69fadc220d chore: clean up code comments 2026-08-10 20:28:41 +02:00
edde746 e3703892b3 fix(player): keep a keyboard Enter out of focus navigation
Pressing Enter over the player put the whole app into keyboard mode and
dropped focus onto Play/Pause, even with Video Player Navigation off. Two
independent paths did it. InputModeTracker promoted on any key satisfying
isNavigationKey, a set that unioned activation, dismissal and the menu key
with the arrows and consulted no setting at all; separately the surface's
Select handler always asked the chrome for focus. Escape had the same effect,
which on desktop reads as the mouse cursor vanishing mid-playback.

Both now ask one predicate. eventRequestsFocusNavigation decides whether the
app switches to keyboard mode and whether a key may hand focus to the chrome,
so the two cannot disagree and focus can never land on a control while focus
chrome is still suppressed. Activation and dismissal act on what already has
focus, so they answer no; Tab, the menu key, a remote's OK or BACK, and an
arrow that will really traverse answer yes. The one input the predicate cannot
read off the event, whether the focused feature owns arrow keys, rides on the
node as DirectionalShortcutFocusNode instead of on a subtree, so every sheet,
prompt and OSD button stays an ordinary traversal target with nothing to
re-enable.

playerDirectionalNavigationEnabled and videoPlayerNavigationPreference replace
five hand-copied pref-or-isTV expressions and a screen-level cache that
disagreed with the live getter after a toggle. Services whose input is
synthesized past HardwareKeyboard announce themselves through
InputModeTracker.reportNonPointerInput rather than two static callbacks and
three copies of a highlight-strategy write. That registration is now
identity-guarded: the bootstrap-to-app tree swap disposed the outgoing tracker
after the incoming one initialised and cleared both callbacks, so gamepad and
companion remote input had stopped switching to keyboard mode entirely.

Falling out of the same rule: a companion heartbeat no longer flips an idle
desktop host into keyboard mode, analog-stick drift promotes only past the
deadzone that actually navigates, Enter keeps toggling playback once the
chrome is up, Tab both reaches and traverses the OSD, and the player surface
claims the remote from mount rather than only when the chrome starts hidden,
so the first key on a desktop route is a playback shortcut instead of the
screen node's chrome-raising self-heal.

isNavigationKey becomes isReservedControlKey, since its real meaning is a
shell key rather than a text character and the old name is what invited the
conflation. The unreachable PlayerChromeFocusTarget.timeline goes with it.
2026-08-07 13:23:53 +02:00
edde746 a56b9a3dfb Merge the deduplication and dead-code removal pass
Consolidates duplicated logic behind shared implementations — paginated
grid tabs, focus chrome, cached remote stores, sheet selection columns,
the server artifact store and a test fixture layer — and removes code
that had become unreachable. Net reduction of about 5,500 lines with no
behaviour change.

Where a fix had landed separately in code that moved into a shared
helper, the fix was re-applied inside the helper rather than left behind
in the copy that went away.
2026-07-26 19:41:23 +02:00
edde746 71735354b9 fix(tvos): unify remote and text input ownership 2026-07-26 07:08:01 +02:00
edde746 04d8070fd4 refactor: pin the look-alike code paths that must not be merged
Several pairs of near-identical code paths differ in one load-bearing
line. Each site now carries a comment naming the invariant that forces it
apart, backed by a characterization test so a future deduplication fails
loudly instead of silently changing behaviour.

Pinned: focusable wrapper vs. chip D-pad activation policy, profile
connection cleanup's raw-id vs. ServerId-typed server projections, live TV
tab loaders, video player display matching and playback service wiring,
track selection container ordering, tracker HTTP client status ladder, and
the MediaServerHttpClient shutdown/cancellation contract versus
ManagedHttpClient's closing guard.

New tests:
  test/focus/dpad_activation_policy_test.dart
  test/services/track_selection_container_ordinal_test.dart
  test/services/trackers/tracker_status_ladder_test.dart
  test/utils/media_server_http_client_shutdown_test.dart
2026-07-26 06:09:47 +02:00
edde746 4af77f4696 fix(app): restore playback and state lifecycle contracts 2026-07-25 16:16:04 +02:00
edde746 fb27621c75 perf(tv): cut semantics work during card navigation 2026-07-25 08:13:09 +02:00
edde746 b41fb4fe75 fix(ui): harden settings focus and semantics 2026-07-24 03:46:50 +02:00
edde746 40792e2779 fix: restore focus and interaction behavior 2026-07-13 17:00:50 +02:00
edde746 e4db04fa62 fix: align UI focus and sheet behavior 2026-07-13 11:28:32 +02:00
edde746 e6e7d8cdfd test: remove redundant coverage and shorten timers 2026-07-13 02:15:03 +02:00
edde746 2101f1d7f0 perf: isolate focus and media rebuilds 2026-07-12 19:00:11 +02:00
edde746 97f7508067 refactor(core): consolidate shared app foundations 2026-07-12 17:31:12 +02:00
edde746 f78089edf8 refactor(input): share D-pad long press state 2026-07-12 08:42:24 +02:00
edde746 df7fa08e40 refactor(focus): centralize focus node ownership 2026-07-12 08:42:20 +02:00
edde746 6d94880c2f fix(input): reset long press state safely 2026-07-12 08:42:19 +02:00
edde746 1acc83572e perf(tv): reduce navigation rebuilds and artwork fan-out 2026-07-10 12:07:19 +02:00
edde746 c4184c6148 fix(ci): restore sanity checks 2026-07-05 08:34:55 +02:00
edde746 11f7fd766d perf(library): cut scroll jank in card grids and hub rows
Profile traces showed 100-370ms UI-thread frames while scrolling the
library screen, dominated by rebuilding and inflating media cards.

- Gate per-card focus/pointer chrome on input mode: FocusableWrapper
  skips the scale/border wrappers and creates its AnimationController
  lazily outside keyboard mode, and ClickableCursor plus the card tap
  region only build MouseRegion/InkWell machinery on desktop - TV and
  touch use a bare GestureDetector. Hub cards also drop their outer
  gesture wrapper outside keyboard mode; the card's own tap region
  always won the gesture arena anyway.

- Memoize sliver children (SliverChildMemo): browse/collections grids
  and hub rows return identical widget instances for unchanged items,
  so delegate swaps from pagination, watch-state, and deletion
  setStates no longer rebuild every realized card inside layout. The
  browse tab prunes the memo in lockstep with focus-node eviction so a
  cached card can never resurrect a disposed FocusNode.

- Budget fresh inflation (CardInflationBudget): while a scrollable is
  moving in pointer/touch mode at most one new card inflates per frame,
  the rest render as SkeletonMediaCard and upgrade on following frames.
  Hub rows also stop pre-inflating 250px of off-screen cards on entry.

Device traces: worst frame 373ms -> 103ms, per-card build 3.6ms ->
2.4ms median; remaining row-entry work is spread across frames.
2026-07-05 02:23:42 +02:00
edde746 075e4aeb32 fix(player): restore OSD auto-hide
close #1360
2026-06-18 10:18:57 +02:00
edde746 d44a8994ce fix(discover): open server tasks with remote
close #1272
2026-06-08 08:23:57 +02:00
edde746 7b59dac26b fix: stabilize detail action focus
close #1218
2026-06-01 08:34:38 +02:00
edde746 097e48d705 fix(tv): trap d-pad focus at button row edges
close #1181
2026-05-29 03:31:53 +02:00
edde746 a931d5910b fix(tv): let text fields fall back to directional traversal
close #1176
2026-05-29 03:30:54 +02:00
edde746 9446f976a4 fix(tv): support hardware keyboard input 2026-05-07 00:52:34 +02:00