Merge remote-tracking branch 'origin/master' into claude/great-herschel-e690e4

# Conflicts:
#	.github/workflows/ci.yml
#	apps/web/src/app/app.config.ts
#	apps/web/src/m3-theme.scss
#	libs/playlist/import/feature/src/lib/text-import/text-import.component.ts
#	package.json
#	tools/nx/check-form-field-defaults.mjs
#	tools/nx/check-form-field-defaults.test.mjs
#	tools/performance/journey-baselines.json
#	tsconfig.base.json
This commit is contained in:
4gray committed 2026-10-13 22:01:10 +02:00
commit a39ca37e07
42 files changed
+644 -551

No files matched your search

+14 -8
View File
@@ -97,14 +97,13 @@ therefore reaches the initial bundle only through the snack-bar, which root
services inject (and with it `button`, the icon button and the CDK overlay).
Everything else is lazy by these rules:
- **Form-field defaults.** Import `AppFormFieldModule`
(`@iptvnator/ui/components/form-field`) instead of `MatFormFieldModule`
wherever a template renders `<mat-form-field>`: it exports Material's form
field with a directive that provides the app defaults (outlined, dynamic
subscript) on the element. The defaults are not a root provider, and a
route-level one would miss dialogs opened by root services.
`pnpm run styles:form-field-defaults:validate` (CI) rejects a component that
renders a form field without the module.
- **Form-field defaults.** A component that renders `<mat-form-field>`
adds `providers: [provideFormFieldDefaults()]`
(`@iptvnator/ui/components/form-field-defaults`); the token is never
provided at the root, where its import alone put the 54 KB form-field
chunk on the initial path. `pnpm run ui:form-field-defaults:validate` (CI)
requires the provider on every such component and allows the token
nowhere else; see the Dialogs and forms notes below.
- **Dialogs from services created with the app.** A root service that opens a
dialog resolves `MatDialog` through `LazyMatDialog`
(`@iptvnator/ui/components/lazy-dialog`) and imports the dialog component
@@ -1083,6 +1082,13 @@ per-theme tokens in `apps/web/src/_settings-theme.scss`.
## Forms
Every `<mat-form-field>` is outlined with a dynamic subscript. A component
that renders one adds `providers: [provideFormFieldDefaults()]`
(`@iptvnator/ui/components/form-field-defaults`); the root injector does not
provide the defaults, because the token import there would load Material's
form field and `@angular/forms` on startup. `pnpm run
ui:form-field-defaults:validate` (in CI) reports a missing provider.
The add-source forms (M3U URL, Xtream, Stalker) and the edit dialog share one
vocabulary, so a field reads the same wherever it appears:
+1 -1
View File
@@ -1163,7 +1163,7 @@ form-field chunk on the initial path, although every form field renders in a
lazy route or dialog. Angular Material therefore stays off the startup path by
construction, with the rules and guards in the
[UI guidelines](iptvnator-ui-guidelines.md#material-off-the-startup-path):
form-field defaults travel with `AppFormFieldModule`, services created with
form-field defaults are provided per component (#1928), services created with
the app resolve `MatDialog` through `LazyMatDialog`, the panels the shell
mounts on every launch defer their Material views, the theme emits tokens only
for imported components, and the app does not provide the animations module,
+6
View File
@@ -432,6 +432,12 @@ Detail metadata rule:
[Embedded Inline Playback](./embedded-inline-playback.md). It runs before
TMDB enrichment, which keeps a non-empty provider rating and release date,
so a surviving stand-in would also block the TMDB value.
- It also runs AFTER enrichment: the series view re-normalizes the store item
on every render, so it carries every `tmdb_*` field over. The key list
(`TMDB_ENRICHMENT_KEYS`) is typed as a `Record` over `StalkerVodInfo`'s
`tmdb_*` keys, so a new enrichment field fails to compile until it is listed
there instead of silently vanishing from the view (as the genre and country
Discover chips once did).
## Stalker Identity Policy
@@ -832,8 +832,10 @@ Contracts worth keeping:
credits and no recommendations carries none of them and reads as
un-enriched. Since the column is never overwritten, getting this wrong is
unfixable after the fact — a real provider date arriving later cannot
correct it. The same marker is what `trustedReleaseYear` (the
recommendations exclusion index) must consult if it ever reads this column.
correct it. Stalker entries never reach this column, but the same marker
rides in their stored `info`, and `trustedReleaseYear` (the recommendations
exclusion index) reads it first: a date TMDB supplied does not gate the
exact tier. `createStalkerInfo()` keeps the marker through re-normalization.
- **The id is stored unvetted.** Every consumer reaches TMDB through
`TmdbEnrichmentService`, whose `detailsForProviderId` runs
`assessProviderId` and lets the title search take over when the years
+3
View File
@@ -878,6 +878,9 @@ Toolchain notes for the Electron 43 baseline:
Those dependencies require standalone PRs so their dedicated package,
worker, playback, and diagnostic-contract validation cannot be hidden by an
unrelated grouped update.
Electron majors are ignored: Electron 44 ships no `linux-armv7l` binary,
and the release still packages armv7l AppImage, DEB and Snap builds. Remove
the ignore together with those targets when 32-bit ARM support is dropped.
4. Electron 42 and newer download their development binary on the first
Electron command instead of during package `postinstall`, so `electron`
must not remain in pnpm's `onlyBuiltDependencies` allowlist. The first local