Commit Graph
8 Commits
Author SHA1 Message Date
0a6f54ca15 perf(playlist): make the playlist import and shared UI components OnPush (#1820)
* perf(playlist): make the playlist import and shared UI components OnPush

Plan item C6 step 3 for libs/playlist (import/feature and shared/ui): the
twelve Eager components switch to OnPush. Two of them rendered plain fields
written after an await, outside any template event, which only an Eager
check on the next zone tick picked up:

- playlist-item's portal status dot (PWA, after the async portal check)
  now reads a signal;
- playlist-info's playlist is backed by a signal behind its existing
  getter/setter name, so the EPG source list follows removals and file
  picks that land after awaited cleanup and dialogs.

A regression test for each fails on OnPush with the plain field and passes
with the signal. The Stalker import's post-await patchValue needs no change
(see the zoneless checklist). The m3u feature-player components stay Eager
for the playback PR.

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

* test(playlist): check the OnPush dialogs without forcing a render

Review follow-ups (Greptile, Codex):

- The portal-status and EPG-row tests forced detectChanges() after their
  await, so they passed with plain fields. They now let the fixture render
  on its own; with portalStatus back on a plain field the status test fails.
- New: the playlist info dialog enables Save and shows the path after a
  native EPG file pick, without a forced render. pristine and valid read
  the form's state signals, so the OnPush dialog follows on its own.
- New render spec for the add-playlist dialog with the real URL form: Add
  enables after typing and after a patch from outside the child (as an
  auto-detect prefill does).

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-07 00:11:16 +02:00
335392afa9 perf(portal): make the portal Eager components OnPush (#1821)
* perf(portal): make the portal Eager components OnPush

Plan item C6 step 3 for libs/portal: the nine Eager components in
portal/shared/ui, portal/stalker/feature and portal/xtream/feature switch
to OnPush. Their templates read signals, signal inputs, async pipes and
template-event state; the plain fields they write outside events
(playback request ids, save throttles) are not rendered.

The already-OnPush live channel lists filled their favorites Maps in a
subscription and the Xtream list dropped programme previews after the EPG
mapping dialog, all without marking the view. They now call markForCheck
like the neighbouring handlers do, so a late favorites answer shows its
hearts without waiting for an unrelated check. A regression test for the
Xtream list fails without the call.

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

* test(portal): let the favorites handler's markForCheck render the heart

Review follow-up (Greptile): the test forced detectChanges() after the
favorites arrived, so it passed without the handler's markForCheck(). It
now lets the fixture render on its own; removing the call fails it.

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-06 22:48:19 +02:00
22a9c1f1e5 perf(web): add an opt-in zoneless change-detection build flag (#1824)
* perf(web): add an opt-in zoneless change-detection build flag

Plan item C6 step 4. app.config.ts takes its change-detection providers
from environments/change-detection.providers.ts, which keeps
provideZoneChangeDetection({ eventCoalescing: true }) for every existing
build. The new electron-performance-zoneless and electron-e2e-zoneless
web configurations are their base configuration plus one fileReplacements
swap to provideZonelessChangeDetection(), so the journeys and the Electron
E2E suite can run zoneless while nothing ships it. zone.js stays in the
polyfills until the flip.

A build-config test pins each *-zoneless configuration to its base plus
the swap and refuses the swap anywhere else.

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

* fix(epg): schedule the guide's post-render scroll without zone.js

The programme guide jumps to now once the virtual list first renders rows,
and focuses cells after keyboard scrolls, from afterNextRender hooks
registered in CDK and RxJS callbacks. zone.js followed those callbacks
with a tick; under zoneless change detection a render hook schedules no
render, so the guide opened at midnight (epg-guide.e2e.ts on the zoneless
build). The guide now marks itself when it registers one, which is
harmless with zone.js.

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

* docs(performance): record the zoneless flag measurements and E2E run

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

* docs(performance): say the zoneless flag ran with the three implemented journeys

Review follow-up (Greptile): J4 search is still planned, so the flag was
validated with J1-J3 and the Electron E2E suite, not all four journeys.

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-06 17:52:47 +02:00
e17ee53a22 perf(workspace): make the workspace shell Eager components OnPush (#1819)
Plan item C6 step 3 for libs/workspace: the seven Eager components in
workspace/shell/feature, including the workspace shell root the idle audit
found re-rendering on every idle tick. Their templates read signals,
signal inputs, computed values and template-event state only; the one
plain field written outside the template (categoryLockTarget) is not
rendered. They switch to OnPush without other changes.

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 16:31:15 +02:00
77458b3931 perf(web): make the app root and settings components OnPush (#1823)
* perf(web): make the app root and settings components OnPush

Plan item C6 step 3 for apps/web: the fifteen Eager components switch to
OnPush, among them the app root and the update notification panel that
the idle audit found re-rendering on every idle tick. Their template
state is signals from the settings facades, signal inputs and the shared
reactive settings form.

The checklist flagged the backup import, which patches the form from a
detached file input with no template event. A new spec patches only a
value, which changes no form status, and confirms the OnPush general
section still shows the new theme; it guards that path for the zoneless
flag.

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

* fix(settings): re-render OnPush sections when the form changes outside them

Review follow-ups (Greptile, Codex):

- The settings sections read form values in their templates (selected
  theme and cover size, epgField.value, form().value.player), and the
  parent changes the form outside their events: Discard and backup import
  patch it, the store hydrates it, the EPG file picker sets a control
  after an await. Under OnPush the section kept the old selection or EPG
  status. Each section now marks itself on its form's events
  (markSectionForCheckOnFormEvents).
- The value-only patch test no longer forces detectChanges(); with the
  fixture rendering on its own it fails without the marking, and so does
  a new test for a control set outside the EPG section.
- The zoneless guard counts only changeDetection metadata outside
  comments, so a comment naming the strategy is not an Eager component.

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

* test(settings): guard the unsaved-changes bar after a save off the sections

Review follow-up (Codex): Save marks the form pristine after an async
store write, also on Backup, Reset or search, where no form section is
rendered. The OnPush page re-renders anyway because pristine and valid
read the form's state signals; the new test checks that on the Backup
page without forcing a render (it waits for the scheduled one).

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-06 15:58:28 +02:00
3cc5af1492 perf(playback): make the web players and M3U player OnPush (#1822)
* perf(playback): make the web players and M3U player OnPush

Plan item C6 step 3 for playback: the eight Eager components in
libs/ui/playback (video.js, ArtPlayer, HTML5/hls/mpegts, audio player,
web player view, VOD details, sidebar, external-player dialog) and the M3U
video player and VOD detail switch to OnPush. The player libraries' events
already reach the UI through the signal-backed controls adapter or
outputs, and the players' DOM belongs to the libraries.

The M3U video player rendered three plain fields written outside template
events: the channel-number overlay, cleared by a 2 s debounce timer, and
the player choice, written from an IndexedDB read and a settings effect.
They are signals now, and a test checks that the overlay leaves the DOM
when the timer fires.

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

* test(playback): let the overlay's signal write schedule its own render

Review follow-up (Greptile): the test forced a render with
fixture.detectChanges() after the debounce timer, so it would pass even
if the signal write stopped scheduling an OnPush render. It now runs the
fixture with autoDetectChanges and only advances the fake timers; with
plain fields under OnPush the overlay never renders and the test fails.

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-06 14:12:52 +02:00
b782243760 test(performance): skip every test-only file suffix in the zoneless guard (#1831)
* test(performance): skip every test-only file suffix in the zoneless guard

#1813 added serial-details.test-stubs.ts, whose stub components set
ChangeDetectionStrategy.Eager. The zoneless checklist guard listed only
some test-only suffixes, counted the stub file as production code and has
failed the performance-harness job on master since. It now skips every
`.spec` / `.test` file with or without a suffix, test-setup.ts and
test-stubs/ directories.

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

* test(performance): accept multi-segment test-only suffixes in the zoneless guard

Review follow-up (Greptile): `(-\w+)?` allowed one suffix segment, so a
file such as `rail.test-data-stubs.ts` would be scanned as production.
The suffix now repeats, and a classifier test pins which names are
skipped and which ship.

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-05 22:55:58 +02:00
26ca8e2cb0 perf(ui): make the libs/ui Eager components OnPush (#1818)
* docs(performance): inventory the zoneless change-detection migration

Plan item C6 step 2. docs/architecture/zoneless-migration.md lists the 66
production files (67 components) that still set
ChangeDetectionStrategy.Eager, the ten places where a template-read plain
field is written outside an Angular event, the NgZone and
ChangeDetectorRef calls to remove at the flip, and the IPC, player,
observer, timer and dialog paths checked as signal-safe.

On Angular 22 an unset changeDetection already means OnPush, so only the
explicit Eager components re-render on every tick.

zoneless-migration.spec.ts in the performance harness compares the
checklist with the code: a new Eager component, or a converted one left
unticked, fails it.

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

* perf(ui): make the libs/ui Eager components OnPush

Plan item C6 step 3 for libs/ui/components and libs/ui/epg: eleven
components (twelve with the EPG trust dialog) set
ChangeDetectionStrategy.Eager and were checked on every tick, among them
the always-mounted EPG progress panel the idle audit found re-rendering
on every idle tick. Their template state is already signals, signal
inputs, immutable dialog data or fields written from template events, so
they switch to OnPush without other changes.

The epg-item-description spec mutated dialog data after creation and
marked only the fixture's host view; it now marks the component's own
view, which OnPush requires. The libs/ui playback and remote-control
components stay Eager for their own PRs.

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-04 16:44:40 +02:00