Plan item C6 step 5. app.config.ts provides
provideZonelessChangeDetection() directly and apps/web builds without the
zone.js polyfill, so a tick runs only when a signal, markForCheck, an
Angular event or a render hook asks for one, not after every macrotask.
The flag from #1824 (change-detection.providers*.ts and the *-zoneless
configurations) goes away; a build-config test fails if the polyfill or a
zone provider comes back.
NgZone.run/runOutsideAngular calls that only mattered with zone.js are
removed from the dashboard data services, the settings unload guard and
the embedded MPV position poll (with the spec case asserting it), and
the unused polyfills.ts, polyfills-test.ts and setup-jest.ts are deleted.
Unit tests keep zone.js/testing for fakeAsync and waitForAsync;
apps/remote-control-web still bootstraps with zone.js.
renderer.initialBytes: 1,596,201 -> 1,545,931 bytes (-50,270) on a local
production build of this base; the baseline is lowered to the measured
value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(e2e): fix type errors in four Electron E2E specs
- category-management: the poll's message and 15 s timeout were passed to
toBe(), which takes one argument, so the poll ran with the default 10 s
timeout and message. Pass them to expect.poll().
- settings: evaluate<void, HTMLVideoElement> set the argument type, not the
element type; type the callback parameter instead.
- remote-control: keep the poll callback's writes visible to the return and
fail with a clear error instead of casting null.
- downloads: read the dataset key through its index signature.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(perf): fix type errors in the benchmark harness
None of these changes behaviour for a capture the harness produces today:
- cancellation report: a null playlist id on the request record now means
unknown, like undefined, instead of matching only null DB records.
- main capture: type the worker transport map so its unavailable-reason
literals stay narrow.
- worker request performance: a parsed capture carries non-null work
timestamps; only the unavailable fallback reports null.
- Xtream iteration: hand the teardown closure the narrowed app and data
directory.
- Xtream exact schema: return early when the identity is not an exact
record instead of re-checking it three times.
- Xtream assembly: reject an unpainted UI explicitly; the null-to-0
comparison already rejected it.
- Xtream scenario driver: drop the always-null cancellation observer from
prepare; trigger() installs it.
- Electron process lifecycle: type the child process by the exit
subscription it uses, so a test double need not return a ChildProcess.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(perf): fix type errors in the benchmark harness specs
The dynamically imported helpers were checked with
assert.equal(typeof fn, 'function'), which does not narrow, so later calls
used fn?.(). assert.ok(typeof fn === 'function') narrows them. The rest
types fixtures by the module under test, keeps closure-assigned callbacks
visible after their callbacks run, and narrows nullable fixture values.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(perf): type-check the whole electron-backend-e2e project
Widen the spec config's include from the journey harness to src/**/*.ts
and the root Playwright configs, with no baseline or ignore list, and
update the validation map and the journey checklist.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(perf): build a complete renderer fixture in the request-performance spec
The fixture was cast through unknown, so the type check accepted an
incomplete RendererCaptureMetrics. Fill every required field with neutral
values; the summary reads only the probe arrays and heap fields.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* perf(web): keep the NgRx store devtools out of production bundles
app.config.ts imported @ngrx/store-devtools statically and gated it on
AppConfig.production at runtime, so the optimizer kept the module in
main.js for the production, PWA and performance builds. The providers now
come from environments/store-devtools.providers.ts, an empty list in every
build; only the development and electron-e2e configurations swap in the
devtools through fileReplacements. A build-config test keeps it that way.
renderer.initialBytes: 1,608,610 -> 1,596,045 bytes (-12,565) on a local
production build; the baseline is lowered to the measured value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(web): cite #1810 as the initial-bytes baseline evidence
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>
* 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>
* 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>