Commit Graph
70 Commits
Author SHA1 Message Date
edde746 f357be4077 fix(tvos): match Siri Remote navigation to measured native focus-engine physics
Siri Remote navigation felt sluggish and then over-sensitive next to
native tvOS apps (issue #2006): swipes were priced at a fixed travel
per step, a single flick could glide into a second focus step, hard
lifts coasted several extrapolated steps, and rail scrolls snapped in
65-250ms where native glides.

Retuned the whole path against two hardware instrumentation passes on
an Apple TV 4K: committed-move telemetry through an experimental
UIFocusItem bridge (branch feat/tvos-native-focus-bridge), then a
dedicated native probe app logging every touch sample, pan velocity,
engine hint, focus step, and scroll tick across 101 swipe sessions on
160/230/300pt tiles. What the data showed, now encoded:

- step pricing follows geometry: one step costs the focused item's
  extent along the swipe axis plus ~155pt (measured 314/391/410pt on
  160/230/300pt tiles), not a fixed distance. Thresholds derive per
  axis from the primary focus rect, normalized so a wide-flat control
  steps vertically once the finger covers its height. Locked-focus
  rows (hub rows, the TV browse rail) vend their selected card's rect
  through the new LockedFocusRowNode so the row-wide focus node's
  screen-sized rect never prices the step. Scopes, the player's
  catch-all surfaces, and unbuilt cards fall back to a fixed 400pt.
- a lift never coasts more than one step: sessions with lift
  velocities up to ~11400pt/s never produced a second coast step. The
  glide is gated on a sustained drag (two consecutive same-direction
  steps), cancelled by reversal pivots and new touches, so a discrete
  flick moves exactly one item.
- the native 'inertia' feel is the scroll animation, not focus
  physics: the engine's scrollable containers settle over ~450-900ms
  of ease-out. TV rail and hub-row navigation scrolls now retarget a
  500ms easeOutCubic animation per step, so drags and hold-repeats
  chain into one continuous glide that catches up on release.
2026-08-24 13:41:44 +02:00
edde746 07463eea04 fix(explore): keep the page at the top when a tab switch focuses a hub
Switching to the Explore tab hands focus to the first hub, and
HubSection unconditionally ran Scrollable.ensureVisible, scrolling the
app bar and search field off-screen in touch mode. The scroll is now
gated on keyboard/D-pad input mode, mirroring FocusableWrapper's
autoScroll.
2026-08-23 10:12:27 +02:00
edde746 21f48382bc fix(hubs): derive wide hub-row cards from the grid's widening scheme
A continue-watching episode card was 180dp on home but 232dp in the
grid behind "see all" at 480dp: hub rows widened the resolved poster
cell (x1.5) while grids widen the max extent (x1.8) before the integral
column packing, and the TV shelf multiplied the rail's tall card by the
same 1.5. Rows now adopt the grid's packed wide cell via
MediaGridDelegate.wideCellWidth, the TV shelf passes the hub's wide
flag to TvBrowseRailLayout.cardWidthFor, and the single widening
scheme is stated in MediaGridDelegate. Grids are untouched.
2026-08-20 14:49:33 +02:00
edde746 424c14a344 fix(tv): size HubSection shelf cards with the browse rail formula
Live TV "What's On" and catalog related rows are the two TV surfaces
without a TvBrowseRail path; HubSection's own clamp ([210, 340],
unscaled) rendered ~40% larger cards than every neighboring rail and
gave 720p TVs the same 210dp floor as 4K. Delegate the shelf card
width to TvBrowseRailLayout.cardWidthFor so the clamps scale and match
the rails.
2026-08-20 14:34:39 +02:00
edde746 04673a8c8a fix(library): render Plex home videos with 16:9 thumbnails instead of cropped posters
Plex "Other Videos" libraries returned items as type=movie with
subtype=clip, which mapped to MediaKind.movie and rendered 2:3 poster
cards that cropped the generated 16:9 video-frame thumbs.

Items keep the movie kind so downloads, add-to, delete-from-server, and
detail navigation stay available, but subtype=clip now renders wide with
the thumb-first artwork clips already use. Sections marked subtype=clip
in /media/providers map to the clip library kind, giving Plex home-video
libraries the same folder-first grouping and wide grid cells as
MediaBrowser homevideos views. Wide-only hubs also keep 16:9 cards in
the poster episode modes, matching the TV rail's gate.

close #2036
2026-08-20 05:44:08 +02:00
edde746 c198bab04c fix(tv): keep focus on the played item when continue watching reorders
Exiting the player after enough progress moved the played item to the front of Continue Watching left D-pad focus at its old position, now occupied by a completely different item.

TvBrowseRail and HubSection now remap the focused index to follow the focused item identity when a hub reorders underneath it, falling back to the same series replacement entry when a finished episode is swapped for the next one, and HubSection focus memory is remapped so re-entering the row lands on the same item.

close #1987
2026-08-18 19:27:00 +02:00
edde746 69fadc220d chore: clean up code comments 2026-08-10 20:28:41 +02:00
edde746 27acbaf435 feat(explore): surface the catalog data providers already return
Explore shelf cards drew a poster, a title and a year. An audit of all six
catalog sources found the rest was lost at two boundaries — the wire-to-DTO
mapping and the DTO-to-CatalogItem mapping — and then simply not drawn: the
grid card fell through every branch of buildMetadataSubtitle to the year-only
case, while the list card used by search already composed certification,
runtime and rating from fields the synthesized MediaItem already held.

Extend CatalogItem with the neutral facts every provider had been dropping:
attributed rating sources, leaderboard ranks that keep their season window,
audience counters that keep their timeframe, broadcast slots, next-episode air
times, server availability and request state, exact release dates, alternate
titles, format, source material, studios, countries, languages, credits, tags,
links, artwork variants, play state, gallery art and background prose. Replace
fetchCast and fetchRelated with one fetchDetail returning the enriched item,
its cast, its recommendations and labelled franchise relations without adding
a request: sources needing two calls keep two and run them concurrently with
isolated failures.

Map those fields in all six sources, widening only field selections that cost
no extra round trip — MAL's fields list, AniList's selection set and a bounded
row cast that lets detail skip its character call, Trakt's guest stars, Seerr's
language parameter and TMDB size ladder, and Plex's includeUserState. Plex hub
artwork widens only on TV, where the spotlight is its only consumer, because it
doubles the payload.

Render them: a rating-first caption and bounded badges on the shelf card,
labelled sections on the detail screen, provider hub styles and result counts
on shelves, and logo, banner and accent art in the TV spotlight.

Verified against live Plex, AniList, Simkl and MAL responses, and on a Pixel 7.
2026-07-29 06:47:54 +02:00
edde746 4307c49cd2 refactor: remove unreachable code paths and unused members
Drops dead code across services, models, utils and widgets, including the
connection auth service, which had no implementer, and the Live TV DVR
provisioning models, which had no caller.

Tests that only covered deleted behaviour are removed or trimmed. No
behaviour change.
2026-07-26 06:09:47 +02:00
edde746 b41fb4fe75 fix(ui): harden settings focus and semantics 2026-07-24 03:46:50 +02:00
edde746 d5f3e581df fix: eliminate cross-app consistency drift 2026-07-13 23:13:53 +02:00
edde746 b9f828ae88 refactor(livetv): reuse standard hub cards 2026-07-12 22:11:30 +02:00
edde746 098a472670 fix(catalog): restore TV focus navigation 2026-07-12 21:42:27 +02:00
edde746 2101f1d7f0 perf: isolate focus and media rebuilds 2026-07-12 19:00:11 +02:00
edde746 f78089edf8 refactor(input): share D-pad long press state 2026-07-12 08:42:24 +02:00
edde746 05a631415e feat(music): music browsing UI — square cards, library groupings, artist/album screens
Un-gates music libraries; adds CardShape.square plumbing (circular
artists), artists/albums/tracks browse groupings with per-grouping
sorts, audio playlists tab, artist + album detail screens with dpad
track rows, music context-menu actions, and a stub MusicPlaybackService
seam for the upcoming engine.
2026-07-05 17:20:28 +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 43ab5d3246 perf: shrink semantics tree on media lists
Poster images are decorative (the card exposes one merged node), and
the default per-child AutomaticKeepAlive/_SelectionKeepAlive/
IndexedSemantics wrappers are inert here — no keep-alive clients or
SelectionArea exist. The semantics phase ran ~9ms every frame on TV
devices with an accessibility service; it scales with node count.
2026-07-02 11:41:25 +02:00
edde746 849e62df7e fix(jellyfin): honor continue watching action
close #1334
2026-06-14 07:25:40 +02:00
edde746 587609d7f6 fix(tv): hug poster with focus border and add focus glow toggle
close #1278
2026-06-12 08:50:29 +02:00
edde746 74b8dc4561 refactor: type server identifiers 2026-06-01 11:06:03 +02:00
edde746 8ec2795da1 fix(tv): stabilize long rail focus scrolling 2026-05-22 23:55:54 +02:00
edde746 cbea1c8d8f fix(tv): correct long hub row focus scrolling 2026-05-22 21:32:26 +02:00
edde746 afc94a50fc fix(tv): push sidebar content without resizing 2026-05-22 01:38:29 +02:00
edde746 2ebd730201 fix(tv): displace content beside sidebar 2026-05-22 00:14:16 +02:00
edde746 5a68711f7b feat(ui): add pointer cursors to clickable controls
close #1068
2026-05-18 15:56:41 +02:00
edde746 7690a16efe feat(tv): add spotlight browse layouts 2026-05-16 11:56:12 +02:00
edde746 e37602421b fix(discover): expose full continue watching
close #1045
2026-05-15 18:49:23 +02:00
edde746 955bc9548c refactor: adopt shared mixins, helpers, and sliver widgets 2026-05-05 03:06:56 +02:00
edde746 ed4be7b96d refactor: strip obvious comments 2026-05-04 22:40:18 +02:00
edde746 130e0d6f40 refactor: streamline settings bindings 2026-05-02 08:44:27 +02:00
edde746 31d2d9dc98 feat: jellyfin 2026-05-01 01:20:36 +02:00
edde746 3da51f9d64 chore: pre-commit ci hook, dart format 2026-04-18 12:40:35 +02:00
edde746 77893bb9ba fix: detail screen section alignment 2026-04-12 01:37:08 +02:00
edde746 8d40ba3011 fix: reduce blank space in media card bottom 2026-04-08 07:21:21 +02:00
edde746 c9db6e528e feat: more consistent poster sizing
close #798
2026-04-03 17:04:20 +02:00
edde746 b5576fa6de fix: reduce hub section vertical spacing 2026-03-21 09:05:21 +01:00
edde746 5877673755 fix: reduce UI padding between media items
close #629
2026-03-04 18:36:12 +01:00
edde746 d1d5b808bb fix: reduce widget tree depth 2026-02-24 02:40:04 +01:00
edde746 ec054fce46 feat: add View All card and dpad focus to hub detail
close #474
2026-02-20 22:10:52 +01:00
edde746 23c4c400f0 feat: focus navigation for movie detail sections
close #464
2026-02-16 02:17:10 +01:00
edde746 04438c8e45 refactor: code quality & format 2026-02-15 04:07:09 +01:00
edde746 5d40468dc2 feat: apply library density to hub rows and reduce grid sizes
close #433
2026-02-11 15:22:07 +01:00
edde746 9d37443e4e feat: open sidenav with left 2026-01-25 01:43:10 +01:00
edde746 0fc2708a51 fix: back improvements 2026-01-24 21:40:02 +01:00
edde746 efe9b86707 fix: dpad long-press 2026-01-23 20:24:20 +01:00
edde746 85d07a7f72 fix: Remove LEFT-to-sidebar from hubs 2026-01-20 22:05:52 +01:00
dwk001 9d64589c3d feat: Add natural D-pad navigation for hub sections
- Add left navigation from hubs to sidebar when at leftmost item
- Add up navigation from first hub to tab bar (Libraries) or hero section (Discover)
- Enhance HubSection with onNavigateLeft and onNavigateUp callbacks
- Update _handleVerticalNavigation to return false at top boundary for proper UP handling
- Implement _navigateToSidebar() in Library Recommended tab and Discover screen
- Update all HubSection instances with proper navigation callbacks
2026-01-18 15:01:34 -05:00
edde746 5cb4f79476 feat: add server name display on hub titles 2026-01-14 14:17:56 +01:00
edde746 abe0919324 feat: episode thumbnail display mode 2026-01-08 15:08:50 +01:00