Commit Graph
3020 Commits
Author SHA1 Message Date
4grayandClaude Fable 5.1 d71a92cec8 fix(portals): twenty-second review round on the cinematic details
- A Stalker launch that already started reports its failure even though
  the host's own inline teardown retired the request id.
- An Xtream episode launch's bookkeeping applies only while the page
  still shows the series it started on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:46:37 +02:00
4grayandClaude Fable 5.1 6de998094a fix(portals): a reopened series keeps its queued episode choice
One settle handler per owner hands the latest queued choice to the start
callback that came with it, so the page the viewer left cannot consume
the choice a reopened page made.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:37:59 +02:00
4grayandClaude Fable 5.1 39ec848110 fix(portals): a queued episode choice stays with its series
The choice made while a forced launch settles is kept per playlist:series
owner by the launch helper and only starts if the page still shows that
series, so a Similar-rail navigation cannot play it under another title.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:28:56 +02:00
4grayandClaude Fable 5.1 5b2fbe296b fix(portals): nineteenth review round and the web E2E line limit
- A queued Xtream episode launch checks the page owner before it closes
  the running player, so a viewer who left the series keeps playback.
- A Play or episode choice made while a forced launch settles starts
  once it settled instead of being dropped; only the latest choice runs.
- Both series menus disable the bulk watched actions while a start has
  not published its session yet.
- The series-menu E2E helpers are shared, which brings the Stalker web
  spec back under the line limit; the inline episode state derivation
  moves into ui/playback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:21:46 +02:00
4grayandClaude Fable 5.1 956e7c36b7 fix(portals): a forced Xtream episode launch blocks other starts and the reset
The launch helper keeps a reactive per-series pending count; the
playback service exposes it, refuses a non-forced start while a forced
launch settles, and the series menu disables the reset row for that
window. The inline progress row moves into a shared helper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:08:09 +02:00
4grayandClaude Fable 5.1 f7c71c222a fix(portals): queue a different Xtream episode behind a settling launch
Only a repeat of the same episode is ignored while its launch settles;
another episode of the series waits its turn and then replaces the
running player like any later start.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 14:04:35 +02:00
4grayandClaude Fable 5.1 65d7804b21 fix(portals): seventeenth review round on the cinematic details
- A superseded Stalker launch that fails stays silent; the newer start
  reports for the movie.
- Xtream episode launches are serialized per series until the launch
  settles, so a repeat before the first session is published cannot start
  a second player.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:53:26 +02:00
4grayandClaude Fable 5.1 95197baf98 fix(portals): playlist-scoped hero and pending-start keys for series and movies
Provider ids are playlist-scoped: the Stalker series hero key and its
pending-start key, and the Xtream series and movie hero keys, include
the playlist so a cross-playlist navigation to the same number gets its
own layout decision and does not inherit a pending start.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:33:27 +02:00
4grayandClaude Fable 5.1 4ad5f471e4 fix(portals): the shared movie page keys its hero layout by playlist too
Collection details reuse the mounted view across playlists whose movies
share a numeric id; each playlist's movie now gets its own decision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:21:14 +02:00
4grayandClaude Fable 5.1 0524429fbd fix(portals): fourteenth review round on the cinematic details
- The hero keys its one-time stage/compact decision on a stable content
  identity (provider + id) that every host passes, so a TMDB rename plus
  a late backdrop no longer flips the visible hero.
- An explicit Stalker movie launch joins its host's start generation: it
  supersedes an earlier start and is dropped once a Play, Start Over or
  later launch begins, on the controller, search, collection and catalog
  hosts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 13:14:07 +02:00
4grayandClaude Fable 5.1 4e18306d06 fix(portals): a reset finished under another playlist leaves its store alone
The store positions are only reloaded for the playlist the reset started
on while that playlist is still current; a playlist switched meanwhile
keeps its own positions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:56:07 +02:00
4grayandClaude Fable 5.1 b3c422c9b5 fix(portals): twelfth review round on the cinematic details
- The Stalker explicit-launch guard tracks every in-flight movie, so a
  repeat of a pending launch is rejected even after another movie's
  launch was started in between.
- The Xtream movie reset only touches the route's state while it still
  shows the movie the reset started on.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:52:22 +02:00
4grayandClaude Fable 5.1 95a3130883 fix(portals): the Stalker launch guard is per movie
A launch still resolving for the movie the user left no longer blocks
the explicit launch of the movie now on screen; only a repeat for the
same movie is ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:47:04 +02:00
4grayandClaude Fable 5.1 b47aeda543 fix(portals): one explicit Stalker launch at a time
A second "Open in external player" while the first create_link is still
resolving is ignored, and the menu row is disabled for that window, so
two resolutions cannot each start a player.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:38:25 +02:00
4grayandClaude Fable 5.1 175967e06b fix(portals): a collection series never passes as the resolving movie
The collection's selected movie id is reported only while the shown item
is a movie, so a series with the same provider number cannot let a stale
external launch start over it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:36:08 +02:00
4grayandClaude Fable 5.1 fad77eda18 fix(portals): ninth review round and the spec typecheck
- The Favorites/Recent Stalker detail wires the menu's external launch
  and progress reset to collection-owned actions.
- The Stalker search host passes its pending-start state to the detail,
  so the reset row is disabled while a launch resolves.
- A rejected forced Stalker episode launch is logged and reported.
- The spec mocks of the external playback handle are signals, as the
  contract types them (CI spec typecheck).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:27:15 +02:00
4grayandClaude Fable 5.1 23b70c524f fix(portals): keep a forced Stalker episode launch pending until it starts
The launch is awaited inside the start, so the pending state covers the
close of the previous player and the launch itself instead of clearing
as soon as the launch was kicked off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:07:48 +02:00
4grayandClaude Fable 5.1 d0fe593f92 fix(portals): eighth review round on the cinematic details
- The external player rows of every details menu follow the managed
  external player capability instead of the downloads availability.
- An explicit Stalker movie launch is a pending start for the selected
  movie while its stream resolves, so the reset row is disabled meanwhile.
- The resume clock formatter moves into the presentation helpers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 12:03:01 +02:00
4grayandClaude Fable 5.1 4e17c0533b fix(portals): count a pending Stalker start for its own series only
A start still resolving its stream for a series the view has since left
no longer keeps the new series' reset row disabled.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 11:57:31 +02:00
4grayandClaude Fable 5.1 aaae2b0572 fix(portals): seventh review round on the cinematic details
- The shared movie menu offers the external player in provider-only
  mode too; that mode hides local/download controls, not the provider's
  stream in MPV/VLC.
- The movie and Stalker series reset-progress rows are also disabled
  while a start is still resolving its stream, so a slow create_link
  cannot recreate a just-cleared row.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 11:48:59 +02:00
4grayandClaude Fable 5.1 5b8717b69c fix(portals): sixth review round on the cinematic details
- A forced Stalker episode launch compares against a generation captured
  after the inline close instead of the one that close retired, so the
  menu's MPV/VLC action starts again while stale launches still drop.
- The Stalker movie guard reports the selected id only while a movie is
  on screen; a series with the same provider number never passes it.
- The series reset-progress row is disabled while an episode plays or
  launches, independent of the bulk watched action's state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 11:34:17 +02:00
4grayandClaude Fable 5.1 2bc13c5dd3 test(e2e): point the header-Back spec at the Episodes heading
The series page's section heading is "Episodes" with its own test id
since the cinematic redesign; the spec from master still measured
".section-title" and the old "Seasons and Episodes" translation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 11:29:43 +02:00
4gray 87d7e1d46c Merge remote-tracking branch 'origin/master' into claude/iptvnator-vod-hero-redesign-2c8950
# Conflicts:
#	apps/electron-backend-e2e/src/detail-back-lane.e2e.ts
#	apps/web-e2e/src/m3u-movie-details.e2e.ts
#	docs/architecture/iptvnator-ui-guidelines.md
#	libs/ui/components/src/lib/content-hero/content-hero.component.scss
#	libs/ui/components/src/lib/portal-detail-shell/portal-detail-shell.component.scss
2026-10-03 11:25:57 +02:00
4grayandClaude Fable 5.1 87cc85a54f fix(portals): fifth review round on the cinematic details
- Stalker movie and series menu launches in MPV/VLC close the title's
  own running external session first through the new shared
  replaceOwnedExternalSession(), cancelling when teardown is not
  confirmed or the selection moved on.
- The reset-progress row is disabled while an external player owns the
  position, on the shared and the Xtream movie menus.
- Every ISO comma-bearing country name stays whole in provider lists.
- The dashboard hero spec competes a favorite movie for a slot so the
  movie-title case is actually exercised.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 11:19:22 +02:00
4gray cb252940b2 fix(workspace): move detail Back into the header and drop the rail brand (#1789) 2026-10-03 11:17:34 +02:00
4gray 9c77e9e41a test(web-e2e): assert M3U movie metadata in the visible About block (#1791) 2026-10-03 11:17:16 +02:00
4grayandClaude Fable 5.1 026a69c14e fix(portals): drop a forced episode launch once the page moved on
Closing the previous external session takes time; a launch whose series
page is gone or shows another series by then no longer starts. The
service clears its bindings on destroy and exposes the page's
playlist:series owner for that check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 10:59:56 +02:00
4grayandClaude Fable 5.1 d79e6504a5 fix(portals): third review round on the cinematic details
- A forced MPV/VLC episode launch closes an episode of the same series
  that is still running externally before starting, as the movie flow
  does; a failed close keeps the running player.
- A Stalker external launch resolved after the user moved to another
  movie is dropped instead of launching the old stream or closing the
  new item's inline playback.
- The trailer's sound toggle is rendered by the hero above its content
  layer, so it can actually be clicked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 10:50:52 +02:00
4grayandClaude Fable 5.1 efd70b75e6 fix(portals): second review round on the cinematic details
- The movie menu's MPV/VLC launch is a regular route-owned start with
  the player forced: a running external session is replaced through the
  same guard instead of a second player starting beside it.
- The trailer-backdrop setting is shown for every player; it is a
  details-page feature, not a web-player control.
- The series menu matches the recent list by content type as well, so a
  movie sharing the provider id does not stand in for the series row,
  and the hide action follows the refreshed list instead of a permanent
  hidden marker.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 10:32:22 +02:00
c9d169e3dc fix(dashboard): scroll a focused rail card fully into view (#1785)
* fix(dashboard): scroll a focused rail card fully into view

Chromium skips its focus scroll when 32px or more of the element already
shows, so Tab onto the last source card of a rail that overflows by less
than a card left it half-hidden under the edge fade. The rail track now
handles focusin and scrolls to the first card-start snap position that
reveals the whole card; a plain "nearest" scroll is not enough because
mandatory snapping can round it back (seen on the live channel rail).

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

* fix(dashboard): keep mouse clicks on partly hidden rail cards

A mouse press focuses the card link on mousedown. Revealing the card at
that moment could slide it from under the pointer when the target snap
position overshoots (the live channel rail moves 316px for a 306px
card), so the click landed elsewhere. The rail now reveals a card only
for keyboard and programmatic focus, using the CDK FocusMonitor origin.

Adds an Electron E2E that checks the final layout after snapping: Tab and
focus() leave the last source card fully visible, and a mouse press keeps
the rail still and still opens the source.

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

* fix(dashboard): tell pointer focus apart without touching the DOM

FocusMonitor toggles cdk-*-focused classes on the monitored track, so a
mouse press on a source card mutated the DOM before the click. The J2
"open a source" performance journey rejects iterations with DOM activity
between its settle snapshot and the click. Read the input modality from
the CDK InputModalityDetector in a focusin handler instead: it only
listens, so the rail stays untouched until the click.

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

* fix(dashboard): reveal script-focused rail cards after a mouse click

The input modality stays "mouse" after any click, so a later focus() on
a partly hidden card left it clipped. The rail now skips the reveal only
for focus caused by a press inside the track: the focus has to arrive
within 100ms of that pointerdown (650ms for touch, whose focus comes
with the tap's compatibility mouse events, as in the CDK FocusMonitor).
Only event timestamps are compared, so the DOM still stays untouched
before the click.

The E2E now clicks elsewhere before the script focus, and unit tests
cover a tap and focus() after an earlier mouse press.

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

* fix(dashboard): keep an over-wide focused rail card in view

In a window narrower than a card (or under zoom), a focused card could
never fit, so its own snap offset fell short of the needed scroll and
the rail jumped to the next card's snap point, moving the focused card
offscreen. Such a card is now aligned at its own start instead.

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

* test(dashboard): select rail internals through stable test ids

The dashboard contract makes data-test-id hooks the supported Electron
E2E selector surface. The rail now exposes -viewport, -track and
-card-link hooks next to its existing ones, and the focus E2E selects
those (and the rail heading by role) instead of internal class names.
The dashboard doc lists the new hooks and records the focus-reveal
contract.

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 10:24:57 +02:00
4grayandClaude Fable 5.1 36ce98e18c test(e2e): follow the cinematic details in the Electron and web flows
The series menu trigger renders data-testid, so the Electron specs use
an attribute locator instead of getByTestId (data-test-id there). The
back-lane sweep targets the "Episodes" heading, the primary action is
read by its test id (its name now carries the runtime line), the M3U
inset assertion accepts the hero's own 32/20px inset, and the surface
contrast checks measure the flat episode card's artwork, which gets a
light hairline so its edge survives the light theme. The menu-closing
helper checks the panel rather than a backdrop locator that can match
a second overlay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 10:14:28 +02:00
4grayandClaude Fable 5.1 25cc6b1ced fix(portals): address the review on the cinematic details
- The "…" menu's MPV/VLC launch goes through the regular episode and
  movie starts, so recent history and the launch position are recorded
  as for the Play button.
- Resetting a Stalker movie's progress only clears the shown position
  while that movie is still selected, and retires a position read in
  flight so it cannot restore the reset row.
- The sources panel takes keyboard focus when it replaces the menu rows.
- The trailer backdrop drops its frame and waits again (muted) when the
  trailer changes, and stops while the document is hidden.
- The dashboard hero strips the season marker from series only.
- Provider country lists keep "Korea, Republic of" whole.
- The web season-watched E2E reacquires the menu row after every close.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-03 10:14:28 +02:00
4grayandClaude Opus 5.5 23a1860119 fix(ui): destructive confirmations, verb labels and provider icons (#1783)
* fix(ui): destructive confirmations, verb labels and provider icons

Confirmations: ConfirmDialogData.confirmLabel is required, so no dialog can
fall back to "Yes"/"No"; the dismiss defaults to "Cancel" and
`tone: 'destructive'` styles the confirm with .app-destructive-button. Every
caller names its action ("Remove playlist", "Clear", "Refresh playlist",
"Cancel download" with a "Close" dismiss). The confirm button has the
confirm-dialog-confirm test id and drops its no-op color="primary".

The no-op `warn` color input becomes .app-destructive-button on the EPG
mapping, playlist item, error view, EPG/reset settings, delete-all and source
cleanup buttons, and on the unsaved-changes dialog's Discard.

Provider icons come from SOURCE_TYPE_ICONS in shared/interfaces (Xtream
cloud, Stalker cast, M3U playlist_play / link / description / subject) in the
add dialog, auto-import, empty state, playlist switcher, playlist rows,
dashboard source rail, command palette, Sources filters and both reset
summaries. Stalker no longer borrows the Dashboard icon, and Xtream no longer
shares a glyph with M3U URL playlists.

The playlist error view removed a playlist through the stale
PlaylistActions.removePlaylist: it dropped the playlist from state before the
delete ran, swallowed failures, skipped the source activity guard and showed
no toast. It now uses PlaylistDeleteActionService like every other removal,
commits only a completed delete, toasts and goes home. The unused action and
its effect are removed.

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

* fix(ui): one provider icon per playlist row, imperative Korean remove label

A restored Stalker or Xtream playlist can also carry a URL, and the row's
independent checks then showed the M3U URL icon next to the provider icon.
The row now switches on resolvePlaylistSourceIconKey(), the precedence every
other surface uses, so each source shows exactly one icon.

HOME.PLAYLISTS.REMOVE now names the confirm button and the row's delete
tooltip; in Korean it read "the playlist has been removed". It now says
"remove playlist", like every other locale.

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

* fix(ui): keep the auto-refresh badge on playlist rows with one provider icon

Showing one provider icon per row moved the auto-refresh badge into the M3U
branches only, so a restored Stalker playlist with a URL and auto-refresh
lost it although the URL is still re-fetched. The row now renders one icon
container: the provider icon from the shared precedence, then the badge for
any row with a URL or a local M3U, exactly the rows that showed it before.
The Xtream portal-status dot, used without source health, keeps that corner.

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

* fix(ui): let the playlist row's cancel action render in the error color

The row's action buttons set `color: inherit`, and the selected row does so
again with more specific selectors. Both beat Material's token-driven icon
color, so the .app-destructive-button cancel action kept the row color
(selection blue on the active row). Pin the cancel button to
--mat-sys-error in both row states.

The large-deletion Electron E2E now checks the cancel color in both themes;
without this rule it reads rgb(47, 123, 255) instead of the error red.

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

* test(ui): give the dialog service spec the now-required confirm labels

ConfirmDialogData.confirmLabel became required, and the spec still built
confirmations without one. Jest only transpiles, so the suite stayed green,
but the "Typecheck Jest spec programs" CI step rejected it.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:36:16 +02:00
4grayandClaude Fable 5.1 78314c7fbc feat(ui): lower the details hero and make it compact without a backdrop
The stage for a 16:9 backdrop is min(480px, 60vh) instead of 560/72vh,
with the content's top padding at 48px, so the episodes or cast row gains
~80px in a 1200×800 window. A title with only a poster (the common case
on real Xtream and Stalker playlists) gets a compact hero sized by its
content over the blurred poster. The layout is decided once per title: a
backdrop that TMDB enrichment adds a moment later fills the compact hero
instead of growing it under the cursor.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 13:41:05 +02:00
4grayandClaude Fable 5.1 ff7b340e87 feat(portals): optional trailer backdrop behind the details hero
Settings → Playback → "Play trailers in details background" (off by
default) hands the trailer embed URL to the detail shell; the new
app-hero-trailer-backdrop plays it muted and looping under the scrim
after three idle seconds with a corner mute toggle. It never starts under
prefers-reduced-motion or with saveData, and stops while the hero is off
screen or the window is unfocused. Xtream and Stalker movies and series
are wired; M3U has no trailer source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 09:04:36 +02:00
4grayandClaude Fable 5.1 aac8021553 feat(dashboard): align the cinematic hero with the details pages
The hero's primary is the same light Continue/Play as the details pages
and its chips are the shared meta chip. A title without a 16:9 backdrop
gets the blurred poster alone as the stage (the sharp key-art copy is
gone; a live channel's logo still sits on the right). Series titles drop
their season marker. The hero is a focusable region: ←/→ switch slides,
Enter follows the primary action, and rotation pauses while the document
is hidden.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 08:55:51 +02:00
4grayandClaude Fable 5.1 c2f5d67120 feat(portals): cinematic series details with the episodes grid in the first screen
Series pages take the same hero as movies: the provider's season marker
moves out of the title into a "Season N" chip, the status chip is localized,
the primary reads Continue / "S02E03 · 18m left" or Play / the next episode,
Trailer opens the modal, and favorites plus a "…" menu complete the row.
The menu carries mark season/series watched, reset progress, open in
external player, copy the next episode's URL, download season, show in
category and hide from Continue Watching — rows a provider cannot serve
are left out. The season container's header is "Episodes" with the season
pills and count; the season actions it used to show live in the menu;
episode cards are flat with a watched bar on the artwork, the plot and
"42 min · 18m left". Cast & crew and Similar follow the episodes. The
Electron and web E2E flows and the season container specs drive the new
menu and presenters; legacy detail styles are removed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 08:49:36 +02:00
4grayandClaude Fable 5.1 075c056ba1 feat(portals): cinematic movie details for Xtream, Stalker and M3U
Rebuilds the movie hero in the order kind label → title → chips →
description → resume bar → actions → credits. The backdrop fills the hero
behind a two-layer scrim and falls back to the blurred poster when the
provider has none or sends the poster twice. The action row is a single
line: the light primary with a two-line label (Continue · time left / Play ·
duration), a Trailer button that opens the modal, 44px icon buttons for
favorites, watched and download, and a "…" menu with other sources, open in
external player, copy stream URL, start over, reset progress and show in
category. Cast & crew and Similar (title + year) move into rows under the
hero; the bottom trailer section is gone. The multi-source E2E flow and the
release screenshot driver follow the menu.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 08:25:11 +02:00
4grayandClaude Fable 5.1 3fd65c935c feat(ui): shared cinematic detail pieces (chip, light primary, icon button, more menu, cast row, trailer dialog)
Adds the building blocks of the redesigned VOD details and dashboard hero:
a pill meta chip, the light primary button with a two-line label, a 44px
ghost icon button with tooltip, the right-aligned "…" dropdown menu that
flips upward and hosts the alternative-sources panel, hero credits plus a
Cast & crew row, a horizontal rail with prev/next arrows, a Similar rail
and the trailer modal. Shared detail utilities (season suffix, remaining
time, duration, short country names) move to portal/shared/util, with the
new CTA and rating theme tokens and the i18n keys for all locales.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 07:58:06 +02:00
4gray b6357da6af fix(dashboard): keep the hero skeleton until every hero source has loaded (#1782) 2026-10-01 23:41:21 +02:00
4gray 1653ffe9fb fix(epg): scroll the programme guide to now on open and on Now/N (#1781) 2026-10-01 21:40:06 +02:00
4gray 572034f3be fix(ui): declare Material system tokens and migrate dead --mdc overrides (#1775) 2026-10-01 18:02:50 +02:00
e4cf48fdc2 test(perf): count change-detection ticks in the electron-performance build (#1776)
* test(perf): count change-detection ticks in the electron-performance build

J1 renderer.cdTicksToFirstCard, J2 renderer.cdTicksToFirstPage and the
J1 idle baseline renderer.cdTicksIdle30s. Angular's ɵsetProfiler is only
reachable through the dev-mode window.ng global, so the electron-performance
configuration alone swaps environment.ts for environment.performance.ts,
which re-exports the production AppConfig and wraps ApplicationRef._tick.
Production and PWA sources and output are unchanged.

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

* test(perf): refuse a J1 idle window that opened late after the settle point

Addresses review: the idle window opens in the settle timer's callback while
the settle point is that timer's deadline, so a late callback left ticks
uncounted between the two.

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:23:15 +02:00
5a4d7a8a11 test(perf): measure the serial IPC depth before the J1 first card (#1773)
* test(perf): measure the serial IPC depth before the J1 first card

Adds renderer.ipcSerialDepthToFirstCard to the launch journey: the length
of the longest chain of bridge calls in which each call started after the
previous one completed, among calls that completed before the first card.
The main IPC capture now records the ordered start/completion timeline;
the depth, its lower bound, the chain and the timeline are per-iteration
evidence, and the CI job summary prints the chain.

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

* test(perf): keep the IPC timeline consistent around the J2 start marker

A call that started before the start marker no longer records its
completion in the timeline, and completions of a method with calls in
flight both inside and outside the timeline are attributed outside and
counted, instead of skipping the first marker-method completion.

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 14:20:39 +02:00
4gray d1e79bdc3e fix(parental-lock): per-flow PIN dialog labels and visible mismatch error (#1777) 2026-10-01 11:21:27 +02:00
4gray 8b6fcf3560 fix(e2e): let web-e2e:e2e run outside CI (#1779) 2026-10-01 11:19:51 +02:00
6e229f85b6 test(perf): add the J3 playback journey (#1774)
* test(perf): add the J3 playback journey

J3 clicks a live channel of an Xtream portal and ends at the built-in
HTML5 player's first `playing` event, with `loadedmetadata` as a
secondary phase. It follows J2: every iteration is a fresh J1 launch on
a copy of a profile seeded through the app's dialogs, and the click
happens after the app has settled in the portal's first live category.

The portal is the mock's `live-fallback` account, whose `.ts` live URLs
serve the local H.264/AAC MPEG-TS fixture that mpegts.js plays through
MSE on every platform. The marketing accounts' local live bytes are
zero-filled and never reach `playing`. Seeding selects the HTML5 player
and the `ts` stream format; the catalog's picsum.photos logos are
cancelled from the test side so no request leaves the machine.

Counters: renderer.ipcCallsToPlaying, renderer.httpRequestsToPlaying,
renderer.domMutationsToPlaying, renderer.layoutShiftScore and
renderer.longTasks; wall-clock click->loadedmetadata and click->playing.
renderer.ipcSerialDepthToPlaying is listed as unavailable until the
serial-depth helper lands. No baseline yet.

The probe gains a media-event terminal; J2's pre-click settle moves to
journey-click-settle.ts so both journeys share it unchanged, and the
probe spec's jsdom fixtures move to a shared test helper.

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

* test(perf): show J3's HTTP boundary margins and watch 1 s after playing

Review follow-up. renderer.httpRequestsToPlaying compares the ledger's
arrival stamps with the renderer's click and playing stamps, which come
from different processes on the same host clock. Each iteration now
records the distance of the nearest request on either side of both
boundaries, so a count a clock difference could flip is visible.

Requests after playing were a single snapshot taken right after the
probe; the test now watches the ledger for a fixed 1 s after playing.
A live stream never leaves the mock quiet, so J2's quiet wait does not
apply.

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:37:02 +02:00
4grayandClaude Opus 5.5 adb4889b0f fix(player): keyboard focus, contrast and ARIA for controls and settings (#1769)
* fix(player): keyboard focus, contrast and ARIA for controls and settings

Dock and settings-panel icon buttons draw a 2px --pc-text ring on
:focus-visible, and Material's theme-coloured focus layer is off, so
keyboard focus shows on video in the light theme too. A focused selected
subtitle swatch now differs from one that is only selected.

Settings headings read --pc-text-secondary on denser glass
(--pc-glass-bg-dense, 0.86): 4.5:1 or more over mid-grey and white
frames. They wrap (overflow-wrap: anywhere, hyphens: auto), so long
German and Russian headings stay inside the sheet's heading column.

The settings panel is now radio groups only (SettingsRadioGroupDirective
over a CDK FocusKeyManager): one Tab stop per group on the checked option,
arrows, Home and End move focus without applying, and Space/Enter checks.
The dialog and its groups are named by real h2/h3/h4 headings, the
load-file action sits outside the subtitle radio group, the subtitle and
speed chips carry their value in their name ("Subtitles: English"), and
tune has aria-haspopup="dialog".

Adds a web E2E for the keyboard path in both themes with an axe check on
the open panel, and de/ru sheet heading wrapping; axe-core is a new dev
dependency for it.

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

* fix(player): arrows check settings radios; subtitle chip reads On

Review follow-up:
- Arrow keys, Home and End now check the settings radio they reach, as a
  native radio group does (the directive clicks it, so the template's
  handler applies the choice); an option the engine already reports as
  checked is not applied again.
- With subtitles on but no track marked selected yet (the engine can
  report the switch before the track list), the subtitle chip reads and
  announces "On" (new SUBTITLES_ON key, 19 locales) instead of "Off".
- The swatch row has 4px padding on every side, so the outer focus ring
  is not clipped at the scroll edge of the panel body.

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

* fix(player): re-apply a settings radio while a switch is pending

The arrow-key check skipped any option the engine still reported as
checked. Arrowing from audio track A to B and back to A before the engine
confirmed B therefore sent no command for A, and playback ended on B with
focus on A. An arrow move always lands on an option other than the last
one applied, so it now applies unconditionally.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:37:02 +02:00
5d50995c30 fix(e2e): let per-file E2E targets run without mock serve dependencies (#1772)
* fix(e2e): let per-file E2E targets run without mock serve dependencies

Since #1710 the Playwright configs start the Stalker and Xtream mocks
themselves (`node --import tsx …`), so @nx/playwright can no longer map
those webServers to Nx tasks and infers the atomized `e2e-ci--*` targets
as non-parallel. The `e2e-ci--src/*.e2e.ts` target default still made
them depend on the continuous `stalker-mock-server:serve` and
`xtream-mock-server:serve` targets, and Nx refuses to run a
non-parallel task with continuous dependencies, so every per-file
target failed before running anything.

Drop the redundant mock dependencies and keep the Electron build.
The mock-launch guard spec now also rejects any nx.json target default
that depends on a mock-server task.

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

* test(e2e): scope the mock dependency guard to E2E targets

Check only the `e2e*` target defaults in nx.json, so an unrelated
default may still depend on a mock, and also check every target in the
`apps/*-e2e` project.json files, where a mock `serve` dependency would
break the same targets.

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

* fix(e2e): build Electron only before Electron per-file E2E targets

The `e2e-ci--src/*.e2e.ts` target default also matched web-e2e, so
every browser-only per-file target built electron-backend first. Split
it into project-filtered entries: Electron targets keep `build-e2e`,
web targets get an empty dependency list (which also keeps the
inferred `web:serve` dependency, rejected by Nx on a non-parallel
target, out of them).

The mock dependency guard now also catches `^serve`-style
dependencies, which schedule the mocks through the E2E projects'
implicit dependencies.

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

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 06:27:08 +02:00
Mahmut YUMand4gray 1dba959852 fix(portals): treat an undetermined audio language as unknown (#1689)
* fix(portals): treat an undetermined audio language as unknown

ICU 78, which Electron 43 and Node 26 ship, canonicalizes the ffprobe
marker `und` to the subtag `und` instead of an empty one. The source
metadata then recorded it as a stated language, so a switch between a
copy tagged `und` and one tagged English raised the "dub may differ"
warning. CI's older ICU hid this: the existing spec only fails on the
newer runtime.

Decline `und` explicitly, and cover `und-US` plus the dub comparison.

* test(portals): cover undetermined language across runtimes

---------

Co-authored-by: 4gray <serega05@gmail.com>
2026-10-01 06:24:20 +02:00