Files
iptvnator/libs/workspace/dashboard/feature
4grayandClaude Opus 5.5 f6ad98255e fix(dashboard): keep the hero height stable across slides (#1855)
* fix(dashboard): keep the hero height stable across slides

The hero laid out only the shown slide, so every automatic rotation
between slides of different heights resized the banner and moved every
rail below it by 7px, every 8 s on an idle dashboard (a layout-shift
score of 0.005 per rotation cycle).

Every slide's content now sits in the same grid cell at the bottom of
the banner. The hero is therefore as tall as its tallest slide whichever
one is shown, and it still grows for an unusually full slide. Inactive
slides are inert and visibility: hidden, and carry no test hooks. Enter
on the hero follows the shown slide's primary action.

The rotation dots keep one fixed width. The active pill is the same
18px bar with its clip-path opened, so a slide change no longer moves
the neighbouring dots or the pill itself.

The legibility E2E now records which nodes shift. It allows under 0.001
in all, with nothing inside the hero moving. It measures 0 on this
branch and fails on master (0.0054).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(dashboard): guard the hero rotation at the narrow width too

The rotation guard ran only at the wide width, before any slide was
enriched. It now runs a second unattended rotation after the contrast
pass, at the narrow width, where slides wrap the most and every slide
carries a rating and a two-line overview. On master's hero that second
rotation also fails, because its dots move.

measureBackdropTextContrast clipped its screenshot to the range of the
element's line boxes. A title cut by the 2-line clamp has line boxes
below the visible box, so the probe measured the pill row there and
scored about 1.0. The Xtream mock gives the recently-added slide a
different title per run, so this failed whenever a long one came up.
The clip is now the line boxes intersected with the element's own box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:36:07 +02:00
..

Workspace Dashboard Feature

This library owns the workspace dashboard rails UI. The dashboard is a read-only surface over existing playlist, recent, favorites, EPG, and Xtream catalog data; it should not introduce Electron IPC, SQLite schema, or route contracts on its own.

Dashboard Surfaces

The dashboard renders a surface only when the matching setting is enabled. Data rails also require the underlying data slice to have at least one item.

  • hero shows the full-width cinematic banner that rotates between the newest unfinished title, a favourite (or recent) channel with a programme on air, and favourite / recently added titles. Contract: docs/architecture/workspace-dashboard.md#cinematic-hero.
  • continueWatching shows recent movies and series from DashboardDataService.globalRecentVodItems() using cover cards with playback progress when a saved resume position is available.
  • liveFavorites shows favorited live TV channels from DashboardDataService.globalFavoriteLiveItems() using the channel layout with EPG title, time range, and progress when XMLTV data is available.
  • recentlyWatchedLive shows recently watched live TV channels from DashboardDataService.globalRecentLiveItems() using the same channel layout.
  • favoriteMoviesAndSeries shows favorited movies and series from DashboardDataService.globalFavoriteItems(), excluding live favorites, using cover cards.
  • recentSources shows recently used playlist/source entries.
  • xtreamRecentlyAdded shows recently added Xtream catalog items.
  • tmdbRecommendations shows "Because you watched X" — TMDB recommendations seeded from recently watched movies/series, kept to titles that exist in an imported Xtream library. Needs the TMDB opt-in and the Electron DB worker (hidden in the PWA), and hides itself below five matched cards. Data: DashboardRecommendationsService in workspace/dashboard/data-access.
  • tmdbTrending shows TMDB's weekly trending titles, matched against the imported Xtream libraries. Same TMDB/Electron gating; unmatched cards open the global search prefilled. Data: DashboardTrendingService.

Settings

Per-surface visibility lives on Settings.dashboardRails as a DashboardRailsSettings object. Every surface defaults to enabled. Stored settings are deep-merged with DEFAULT_DASHBOARD_RAILS_SETTINGS by SettingsStore, so existing users and older partial settings keep newly added surfaces visible unless they explicitly turn them off.

The global showDashboard flag remains a top-level Settings property because workspace startup and route guards already depend on that contract. The Settings UI groups showDashboard and the per-surface checkboxes in the Dashboard section. When showDashboard is off, the per-surface checkboxes are disabled because the dashboard route itself is hidden.

Navigation

Rail cards use the navigation state provided by DashboardDataService for the underlying item. Rail "See all" links may also pass router state:

  • live rails open the relevant collection on Live TV.
  • cover rails for movies/series open Global Recent or Global Favorites on Movies when movie items are present, otherwise on Series.

This keeps the global collection pages from defaulting to Live TV when a dashboard rail is clearly about movies or series.