* feat(ui): slim ContentHero to non-scrolling hero block The page scroll container moves out of the hero into the upcoming PortalDetailShellComponent; the hero no longer hosts a default content slot for player/seasons/extras. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ui): add PortalDetailShellComponent with browse/watch states Two-state detail layout shell: owns page scroll, hero collapse animation (~300ms, reduced-motion aware), Escape-to-close-player handling, and an About block that re-stamps host-provided *detailTags/*detailMeta templates in watch state. The [detail-player] slot is never wrapped in a shell conditional so the host-owned player subtree survives state changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(playback): restyle inline player header as now-playing bar Adds a back button (emits the existing closed output — in watch state back returns to browse, not route navigation) and replaces the hardcoded header strings with i18n keys (PORTALS.NOW_PLAYING, CLOSE_PLAYER, BACK_TO_BROWSE). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(xtream): migrate VOD and serial detail templates to detail shell Hero chips/meta/actions become *detailTags/*detailMeta/*detailActions templates; the inline player moves to the full-width [detail-player] slot; trailer and similar rail move to [detail-extras]. Escape and the now-playing back button close the inline player back to browse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(stalker): migrate shared VodDetails and series view to detail shell The shared VodDetailsComponent (Stalker VOD, collections, search) and StalkerSeriesViewComponent move their hero content into *detailTags/*detailMeta/*detailActions templates and host the inline player in the full-width [detail-player] slot. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ui): season tabs replace two-level seasons navigation SeasonTabsComponent renders a pill row (dropdown beyond 6 seasons), an optional season description and a back-to-playing chip. The season container auto-selects a season (inline-playing episode's season → most recent in-progress → first), emits seasonSelected for auto-selections so lazy-load/enrichment hooks keep firing, highlights the inline-playing episode, and loses the seasons grid + 'Back to seasons' button. Download/progress helpers move to pure utils; season strings are now translatable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(portals): season descriptions under the season tabs Xtream reads season overviews from get_series_info; Stalker exposes the TMDB season overview through TmdbEnrichmentService.getSeason (same cache rows as the episode enrichment) and keys it per tmdbId so views reused across detail navigations cannot leak descriptions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: document two-state detail layout and season tabs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): app-prefix detail template directive selectors, build fixes detailTags/detailMeta/detailActions → appDetailTags/appDetailMeta/ appDetailActions per @angular-eslint/directive-selector; type the Escape host listener as Event; drop a redundant ?? in season tabs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * chore(i18n): translate new detail-page keys across all 17 locales 13 PORTALS.* keys (now-playing bar, About block, season tabs, episode empty states) translated via the i18n-fill workflow. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(playback): keep now-playing bar actions inside the viewport The nowrap title made the header grid track grow to min-content and pushed Copy/Close out of the page; min-width: 0 on the header/title row lets the title truncate instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ui): expandable season description and episode info dialog Season descriptions get the same show more/less toggle as the hero (new shared ExpandableTextComponent); episode cards and list rows get an info button that opens a dialog with the full plot, duration, air date and a Play action — card heights stay fixed and click-to-play semantics stay intact. Also: drop the per-frame backdrop blur on the player card and make the watch-enter scroll instant so the hero morph stops competing for frame budget. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(portals): re-run TMDB season enrichment when the show match arrives With season tabs the first seasonSelected fires as soon as seasons load — usually before the async show-level TMDB enrichment has written tmdb_id — so the season fetch silently no-oped and was never retried, leaving episode stills/plots and the season description empty. Both detail hosts now key the fetch on (tmdb_id, selected season) in an effect, so it runs whichever arrives last. Also adds breathing room above the About divider in watch state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): move episode info button into the card body, compact overlay actions Three overlay buttons crowded the thumbnail on hover and the new info button had no styling at all. Info belongs with the content text: it now sits as a quiet ghost button at the end of the title row (revealed on card hover, MDC touch target clamped so it cannot swallow the play-on-click card area). Download + watched-toggle stay on the artwork, shrunk to 28px with a subtle edge border. List view already used the shared action style and is unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): single watched indicator-toggle on episode cards Watched state was signalled three times on one card: the green badge, the hover toggle in the top-right cluster, and a checkmark appended to the title. Now one control does both jobs — a toggle at the top-left of the artwork that looks like the old green badge when watched (always visible) and appears as a ghost circle on hover when not. The right cluster keeps only the download action; the title checkmark is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(playback): back arrow in watch mode navigates straight to the list Leaving the theater took two clicks: back to browse, then back to the list. Watch state already shows everything browse offers (episodes, About, extras), so the bar's back arrow is now route-level back (new backClicked output wired to each host's goBack), while Close player and Escape keep exiting to browse without navigating. Replaces PORTALS.BACK_TO_BROWSE with a generic top-level BACK key (all locales). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(ui): compact episode stills in the list view When a season has genuinely distinct per-episode art (TMDB stills or provider images), list rows show a 96x54 thumbnail with the episode number riding on it instead of the number square. When every episode repeats the same image (providers often send the series poster), the plain number square stays — a column of identical posters is worse than none. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test(ui): compact list-thumbnail spec under the max-lines limit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ui): address Greptile review — real fallback asset, untracked overflow measure default-episode.png never existed (pre-existing in the grid card, copied into the list thumbs) — both onerror handlers now fall back to the real default-poster.png. The expandable-text and content-hero measuring effects read the expanded signal via untracked so toggling show more/less no longer rebuilds their ResizeObservers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
7.0 KiB
Portal Detail Navigation
This document records the current navigation contract for Xtream and Stalker detail flows, especially for favorites, recently viewed, search, and category content.
Related:
Summary
- Xtream category browsing uses a route-first detail model.
- Stalker uses an inline/store-state detail model.
- Detail pages themselves are two-state (browse ↔ watch) inside
PortalDetailShellComponent; entering/leaving watch is a layout state, not a navigation. Route-level back semantics are unchanged; the watch-state back button only closes the inline player. See Embedded Inline Playback. - Favorites and recently viewed collections now use collection-owned inline detail for non-live Xtream and Stalker items.
- Provider-scoped collection routes fall back to the matching global collection
route when
All playlistsshows a non-live item from the other portal type, so the correct detail host still opens without switching playlist context. - Dashboard
Global FavoritesandRecently Watchedwidgets hand off Xtream and Stalker movies/series into the matching global collection route with detail pre-opened. - Do not force both portals into the same browse/detail behavior unless the full portal detail architecture is being changed.
Xtream
Xtream category and search details are represented by canonical routes.
Examples:
/xtreams/:id/vod/:categoryId/:vodId/xtreams/:id/series/:categoryId/:serialId
Implication:
- Category browsing and search can still redirect to the original Xtream content route and item route.
- This keeps the URL, browser history, and detail rendering model aligned with normal Xtream browsing.
Current code paths:
libs/portal/xtream/feature/src/lib/favorites/favorites.component.tslibs/portal/xtream/feature/src/lib/search-results/search-results.component.tslibs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.ts
Collection behavior to preserve:
- Selecting a non-live Xtream item from favorites/recent should keep the current collection route and open inline detail inside the collection pane when the current collection host is already Xtream-aware.
- If a Stalker or M3U collection route is showing
All playlistsand the user selects an Xtream movie/series item, route into/workspace/global-favoritesor/workspace/global-recentwith detail pre-opened instead of trying to render Xtream detail inside the wrong host. - The current playlist context must stay unchanged even when the selected item belongs to a different Xtream source playlist.
- The workspace/sidebar category panel should stay hidden for these collection detail opens.
- Back from a collection-owned detail should restore the previous collection view state, including the active content tab and playlist/all-playlists scope.
- Live streams can still open through the player path rather than a detail route.
Dashboard behavior to preserve:
- Dashboard
Global FavoritesandRecently Watchedwidgets should route Xtream movie/series items into/workspace/global-favoritesor/workspace/global-recentwith collection detail pre-opened from navigation state. - Back from the collection detail should return to the dashboard handoff state, not switch the active playlist.
Search behavior to preserve:
- Selecting an Xtream item from search should still navigate to the canonical Xtream content type/category/item route when the item is not a live stream.
Stalker
Stalker details are represented by store state and inline detail rendering on the current screen.
Examples:
- Category content sets
selectedItemand renders details inline. - Search sets
selectedItemand stays on the search view. - Favorites and recently viewed stay on their current collection screen and open inline detail when the current collection host is already Stalker-aware.
Implication:
- Favorites, recently viewed, and search should remain in the current Stalker view when opening VOD/series details.
- This keeps Stalker behavior aligned with its normal category-content and search flow.
Current code paths:
libs/portal/stalker/feature/src/lib/stalker-favorites/stalker-favorites.component.tslibs/portal/stalker/feature/src/lib/recently-viewed/recently-viewed.component.tslibs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.tslibs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.ts(Stalker branch)
Behavior to preserve:
- Favorites/recent/search should not navigate away to a canonical Stalker detail route because Stalker does not currently use one.
- If an Xtream or M3U collection route is showing
All playlistsand the user selects a Stalker VOD/series item, route into/workspace/global-favoritesor/workspace/global-recentwith detail pre-opened so the Stalker inline detail host still renders on a compatible screen. - ITV/live items can still trigger playback immediately.
- Stalker VOD items that are displayed as series because of
is_series=1remain VOD-backed when opened from favorites/recent/global collections; the collection host must not convert them into regular/seriesdetail mode. - Dashboard
Global FavoritesandRecently Watchedwidgets should route Stalker movie/series items into/workspace/global-favoritesor/workspace/global-recentwith detail pre-opened inline, again without switching playlist context or showing the workspace category sidebar. - Back from the collection-owned detail should restore the previous collection tab and scope instead of resetting the collection screen to its defaults.
Decision Rule For Future Changes
When deciding how a favorites/recent/search click should behave:
- Follow the portal's canonical detail model.
- Prefer local consistency within the portal over cross-portal sameness.
- Only unify Xtream and Stalker behavior if the full detail architecture is being unified as well.
That means:
- Xtream browse/search: navigate to the canonical route.
- Xtream favorites/recent/global collection widgets: open collection-owned detail without switching playlist context. Use the current route when it can host Xtream detail, otherwise fall back to the matching global collection route.
- Stalker non-live items: open collection-owned detail without switching playlist context. Use the current route when it can host Stalker detail, otherwise fall back to the matching global collection route.
Refactor Guidance
If a future change proposes that Stalker favorites/recent should deep-link into category routes:
- also update Stalker category-content and search behavior
- define a canonical Stalker detail route model first
- update architecture docs and portal skills together
If a future change proposes that Xtream favorites/recent should stay inline:
- keep the existing route-based detail pages reusable from the collection-owned detail host
- verify history/back behavior, playlist preservation, and dashboard handoff behavior still make sense