mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 04:26:16 -08:00
docs(skills): align provider and UI guidance
This commit is contained in:
1 parent
fa6f988dd3
commit
543d3fd8c7
10 files changed
+298
-150
No files matched your search
@@ -1,31 +1,50 @@
|
||||
---
|
||||
name: iptvnator-theme-style
|
||||
description: Theme architecture, design tokens, shared SCSS library, portal header/sidebar patterns, Electron drag regions, and cross-portal style consistency.
|
||||
description: Use when changing IPTVnator SCSS tokens, shared layout mixins, portal headers, sidebars, detail views, Electron drag regions, or cross-portal visual consistency.
|
||||
---
|
||||
|
||||
# IPTVnator Theme Style
|
||||
|
||||
Use this skill when changing SCSS tokens, shared layout mixins, portal headers, sidebars, detail views, or Electron draggable regions.
|
||||
## Canonical Sources
|
||||
|
||||
## Shared Style Sources
|
||||
- Theme contexts and app tokens: `apps/web/src/m3-theme.scss`
|
||||
- Shared forwarding inventory: `libs/ui/styles/_index.scss`
|
||||
- Shared partials: `libs/ui/styles/_portal-layout.scss`,
|
||||
`libs/ui/styles/_content-grid.scss`, `libs/ui/styles/_portal-sidebar.scss`,
|
||||
`libs/ui/styles/_panel-header.scss`, `libs/ui/styles/_detail-view.scss`, and
|
||||
`libs/ui/styles/_detail-view-actions.scss`
|
||||
- UI policy and migration debt: `docs/architecture/iptvnator-ui-guidelines.md`
|
||||
|
||||
- `libs/ui/styles/_index.scss`
|
||||
- `libs/ui/styles/_portal-layout.scss`
|
||||
- `libs/ui/styles/_content-grid.scss`
|
||||
- `libs/ui/styles/_portal-sidebar.scss`
|
||||
- `libs/ui/styles/_panel-header.scss`
|
||||
- `libs/ui/styles/_detail-view-actions.scss`
|
||||
- `docs/architecture/iptvnator-ui-guidelines.md`
|
||||
The index is a barrel, not a configured Sass include path. Production
|
||||
consumers currently use relative `@use` paths to the needed partial.
|
||||
|
||||
## Rules
|
||||
## Token Boundary
|
||||
|
||||
- Prefer shared SCSS mixins and Material system tokens over local hard-coded colors.
|
||||
- Keep Electron drag behavior explicit: interactive controls need `app-region: no-drag`.
|
||||
- Do not duplicate large SCSS files across feature libraries; extract a shared partial or mixin.
|
||||
- Use relative SCSS imports only when no stable shared entrypoint exists.
|
||||
- Check Xtream, Stalker, M3U, and workspace views for cross-portal style drift when editing shared patterns.
|
||||
- App-owned surfaces, text, separators, hover states, selections, and provider
|
||||
accents use `--app-*` tokens from `m3-theme.scss`.
|
||||
- Use `--app-selection-on-color` for foregrounds placed on the selection
|
||||
accent; do not assume white has enough contrast in both themes.
|
||||
- Angular Material mixins and Material-component overrides may use Material
|
||||
tokens. Outside Material-owned components, use a `--mat-sys-*` token only
|
||||
after proving it is emitted in both light and dark contexts and supplying a
|
||||
real app-token or literal fallback.
|
||||
- Local semantic status colors are acceptable. Existing hard-coded layout,
|
||||
selection, and EPG surface colors are migration debt, not precedent.
|
||||
|
||||
## Shared Layout Rules
|
||||
|
||||
- Extend the matching partial instead of copying a large provider stylesheet.
|
||||
Provider-neutral services and UI use the existing shared data-access/UI
|
||||
libraries; repeated visual structure belongs in a shared Sass partial.
|
||||
Portal-shared consumers use their existing forwarding style modules instead
|
||||
of copied SCSS.
|
||||
- In an Electron drag region, every interactive descendant—buttons, links,
|
||||
inputs, overlays, and resize handles—must explicitly use
|
||||
`app-region: no-drag`.
|
||||
- A shared change must be checked across M3U, Xtream, Stalker, workspace,
|
||||
portal catalog/shared UI, and unified collections where relevant.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run lint/tests for the affected UI project.
|
||||
- Manually inspect the changed screen in light and dark themes for layout, selection, and drag-region regressions.
|
||||
Run the affected consumer's Nx lint/test/build target. Inspect light and dark
|
||||
themes, selected/hover states, and Electron title-bar drag/no-drag behavior.
|
||||
@@ -1,29 +1,44 @@
|
||||
---
|
||||
name: iptvnator-ui-design
|
||||
description: Repository-specific UI design guidance for IPTVnator channel rows, EPG views, settings surfaces, shared selection styles, and light/dark theme consistency.
|
||||
description: Use when changing user-visible Angular UI in IPTVnator, especially channel rows or lists, EPG views, settings and playlist surfaces, selection states, shared portal components, or light/dark styling.
|
||||
---
|
||||
|
||||
# IPTVnator UI Design
|
||||
|
||||
Use this skill when changing user-visible Angular UI in the IPTVnator app, especially channel lists, EPG panels, settings, playlist surfaces, and portal shared components.
|
||||
## Inspect First
|
||||
|
||||
## Principles
|
||||
- Policy: `docs/architecture/iptvnator-ui-guidelines.md`
|
||||
- Theme and navigation: `apps/web/src/m3-theme.scss`,
|
||||
`apps/web/src/nav-list.scss`
|
||||
- Channel row: `libs/ui/components/src/lib/channel-list-container/channel-list-item/`
|
||||
- Shared EPG timeline/list: `libs/ui/epg/src/lib/epg-timeline/`,
|
||||
`libs/ui/epg/src/lib/epg-list-view/`
|
||||
|
||||
- Prefer dense, scannable application UI over marketing-style layouts.
|
||||
- Preserve existing Material 3 token usage and light/dark theme behavior.
|
||||
- Keep repeated selection states aligned with existing `--app-selection-*` tokens.
|
||||
- Use existing shared UI components before adding local one-off markup.
|
||||
- Avoid large visual rewrites in behavior-focused changes.
|
||||
Before adding local markup, inspect `@iptvnator/ui/components`,
|
||||
`@iptvnator/ui/epg`, `@iptvnator/ui/playback`,
|
||||
`@iptvnator/ui/shared-portals`, `@iptvnator/portal/shared/ui`, and
|
||||
`@iptvnator/playlist/shared/ui`. Stateful collection loading, persistence, and
|
||||
cross-provider orchestration belong in `@iptvnator/portal/shared/data-access`,
|
||||
not UI or util.
|
||||
|
||||
## Checklist
|
||||
## Design Contract
|
||||
|
||||
1. Inspect the nearest existing component and shared UI library before editing.
|
||||
2. Check both light and dark theme styles when touching colors, borders, hover states, or selection states.
|
||||
3. Keep text and controls within fixed-width rows from resizing the layout.
|
||||
4. Prefer existing components from `@iptvnator/ui/components`, `@iptvnator/portal/shared/ui`, and `@iptvnator/ui/epg`.
|
||||
5. Add or update focused component tests for changed interaction states.
|
||||
- Prefer dense, scannable application UI. Behavior-only work must not include
|
||||
an opportunistic visual rewrite.
|
||||
- App chrome uses app-owned selection, surface, and text tokens in both themes.
|
||||
Do not extend the known hard-coded EPG/surface styling debt.
|
||||
- Dense rows use minimum dimensions rather than a fixed width: the text column
|
||||
needs `min-width: 0` plus ellipsis, while logos and trailing actions use
|
||||
`flex-shrink: 0`.
|
||||
- Assign one scroll owner per pane. Preserve sticky controls, keyboard/focus
|
||||
feedback, and loading, empty, error, disabled, hover, and selected states.
|
||||
- Providers supply controlled data to shared EPG timeline/list/panel components
|
||||
instead of rebuilding those views.
|
||||
- Shared changes require checking every affected M3U, Xtream, Stalker,
|
||||
workspace, and collection consumer in light and dark themes.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run the affected Nx project test target.
|
||||
- For visible workflow changes, run the closest Playwright E2E target or manually verify through Electron CDP as documented in `AGENTS.md`.
|
||||
Run the focused component/unit target and the closest Playwright workflow for a
|
||||
visible change. Electron CDP is a fallback for Electron-only gaps, not a
|
||||
replacement for an available E2E flow. Record any uncovered visual state.
|
||||
@@ -1,66 +1,71 @@
|
||||
---
|
||||
name: stalker-portal
|
||||
description: Repository guidance for Stalker/Ministra portal catalogs, VOD/series shapes, playback metadata, collections, EPG, and remote control.
|
||||
description: Use when changing Stalker or Ministra routes, stores, catalog or series shapes, playback progress, favorites and recent items, EPG, or remote control.
|
||||
---
|
||||
|
||||
# Stalker Portal
|
||||
|
||||
Use this skill when changing Stalker/Ministra routes, stores, catalog/detail
|
||||
views, playback, favorites/recent activity, EPG, or remote control.
|
||||
|
||||
## Read First
|
||||
|
||||
- `docs/architecture/stalker-portal.md`
|
||||
- `docs/architecture/stalker-epg.md` for ITV EPG work
|
||||
- `docs/architecture/remote-control.md` for live remote-control work
|
||||
- `docs/architecture/stalker-epg.md` for ITV EPG
|
||||
- `docs/architecture/remote-control.md` for live remote control
|
||||
|
||||
## Ownership
|
||||
|
||||
- Feature UI: `libs/portal/stalker/feature/src/lib/`
|
||||
- Store/API data access: `libs/portal/stalker/data-access/src/lib/`
|
||||
- Electron requests: `apps/electron-backend/src/app/events/stalker.events.ts`
|
||||
- Shared Stalker item normalization:
|
||||
`libs/shared/interfaces/src/lib/stalker-item.normalizer.ts`
|
||||
- Dashboard aggregation: `libs/workspace/dashboard/data-access/src/lib/`
|
||||
- Routed UI: `libs/portal/stalker/feature/src/lib/`
|
||||
- API, session, store, and normalization:
|
||||
`libs/portal/stalker/data-access/src/lib/`
|
||||
- Electron transport: `apps/electron-backend/src/app/events/stalker.events.ts`
|
||||
- Provider-neutral collections: `libs/portal/shared/data-access/src/lib/`
|
||||
|
||||
Keep provider-specific API and normalization behavior in Stalker data access.
|
||||
Keep shared portal layouts/utilities provider-neutral. Preserve full-portal
|
||||
session auth and simple IPC request paths.
|
||||
Keep Stalker request and shape rules in Stalker data access. Shared portal UI
|
||||
must remain provider-neutral.
|
||||
|
||||
## `is_series` Cross-Surface Checklist
|
||||
## Series Contract
|
||||
|
||||
Treat VOD items with `is_series` as series across every downstream surface.
|
||||
Do not stop after making the detail view render.
|
||||
Inside Stalker portal code, `isStalkerSeriesFlag()` is the sole flag
|
||||
interpretation and accepts exactly `true`, `1`, and `'1'`. The activity
|
||||
normalizer in shared interfaces remains dependency-neutral and preserves the
|
||||
same closed set for favorites/recent and dashboard classification. Preserve
|
||||
all three modes: regular `/series`, VOD with embedded `series[]`, and lazy
|
||||
Ministra VOD `is_series`.
|
||||
|
||||
1. Accept portal flags `true`, `1`, and `'1'` through the existing normalizers.
|
||||
Preserve all three modes: regular `/series`, embedded VOD `series[]`, and
|
||||
lazy Ministra VOD `is_series`.
|
||||
2. Build quick-start state through the shared series utility. Preserve
|
||||
`labelKey`, `labelParams`, and `episodeLabel` when adapting it for Stalker;
|
||||
translation parameters must reach the template.
|
||||
3. Preserve `is_series` and the VOD origin in favorites/recent activity.
|
||||
`extractStalkerItemType()` must normalize that activity to dashboard type
|
||||
`series`.
|
||||
4. Before either inline or external episode playback, persist the parent
|
||||
`seriesXtreamId` plus resolved `seasonNumber` and `episodeNumber`. Keep
|
||||
generated episode tracking IDs stable for lazy `is_series` episodes. When
|
||||
`season_number` is absent, derive the coordinate from the same naturally
|
||||
ordered season list used by quick start; do not default every season to 1.
|
||||
5. The dashboard reads saved playback positions; it must not infer episode
|
||||
numbers from provider payloads. Legacy rows without season/episode metadata
|
||||
remain badge-less until that episode is played again.
|
||||
Favorites/recent preserve the raw flag and VOD origin so reopening still uses
|
||||
the correct lazy or embedded mode. Keep quick-start translation parameters and
|
||||
the naturally ordered season fallback when `season_number` is absent.
|
||||
|
||||
## Regression Coverage
|
||||
Lazy episodes use a deterministic tracking ID scoped by parent series,
|
||||
provider episode, season key, and episode number. `legacyTrackingId` is only a
|
||||
guarded compatibility alias. Reconciliation is limited to the current parent
|
||||
series and optional matching season/episode metadata; an exact scoped row
|
||||
always wins the resolved display position, while a compatible legacy row may
|
||||
remain tracked only for cleanup. The scoped ID is the in-memory key. At the
|
||||
strict migration boundary, save the scoped row before clearing a confirmed
|
||||
legacy row, and keep legacy progress when the save fails.
|
||||
|
||||
- Series view/UI and playback handoff:
|
||||
`pnpm nx test portal-stalker-feature`
|
||||
- Stalker shape/store behavior:
|
||||
`pnpm nx test portal-stalker-data-access`
|
||||
- Dashboard classification and position lookup:
|
||||
`pnpm nx test workspace-dashboard-data-access`
|
||||
- Dashboard badge rendering when changed:
|
||||
`pnpm nx test workspace-dashboard-feature`
|
||||
Before inline or external handoff, attach parent `seriesXtreamId` and resolved
|
||||
season/episode numbers. Keep them on subsequent position writes.
|
||||
|
||||
For user-visible workflow changes, run the closest available E2E target. If no
|
||||
fixture covers the affected portal shape, record that gap and perform the
|
||||
strongest targeted unit/build validation available.
|
||||
## Live Contract
|
||||
|
||||
- Start bulk ITV EPG eagerly once channel rows exist. Rows read the bulk cache;
|
||||
only the active channel may fall back to `get_short_epg`.
|
||||
- Radio skips EPG and external players, preserves live collection identity
|
||||
with `radio: 'true'`, and uses the shared inline audio player.
|
||||
|
||||
## Validation
|
||||
|
||||
Run:
|
||||
|
||||
- `pnpm nx test shared-interfaces`
|
||||
- `pnpm nx test portal-stalker-data-access`
|
||||
- `pnpm nx test portal-stalker-feature`
|
||||
- the affected `portal-shared-data-access` / `portal-shared-ui` target for
|
||||
collection or radio behavior
|
||||
- the affected `workspace-dashboard-data-access` and
|
||||
`workspace-dashboard-feature` test targets
|
||||
|
||||
For the user workflow, run
|
||||
`pnpm nx run web-e2e:e2e-ci--src/stalker.e2e.ts` or document the missing
|
||||
fixture and strongest focused coverage.
|
||||
@@ -1,30 +1,64 @@
|
||||
---
|
||||
name: xtream-electron
|
||||
description: IPTVnator's Electron-first Xtream implementation, including feature/data-access boundaries, worker-backed DB flows, and Xtream loading/progress UX.
|
||||
description: Use when changing Xtream routes, Signal Store or data sources, content identity, SQLite-backed import, search or delete, sparse VOD playback, or Electron and PWA behavior.
|
||||
---
|
||||
|
||||
# Xtream Electron
|
||||
|
||||
Use this skill when working on Xtream routes, stores, data sources, import/search/delete behavior, or Electron-backed Xtream playback and persistence.
|
||||
## Read First
|
||||
|
||||
## Key Areas
|
||||
- `docs/architecture/xtream-portal-compatibility.md`
|
||||
- `docs/architecture/portal-detail-navigation.md`
|
||||
- `docs/architecture/sqlite-db-worker.md`
|
||||
- `docs/architecture/vod-multi-source.md`
|
||||
|
||||
- Feature UI: `libs/portal/xtream/feature/src/lib/`
|
||||
- Data access: `libs/portal/xtream/data-access/src/lib/`
|
||||
- Shared portal utilities: `libs/portal/shared/util/src/lib/`
|
||||
- Shared portal UI: `libs/portal/shared/ui/src/lib/`
|
||||
- Electron DB events: `apps/electron-backend/src/app/events/database/`
|
||||
- DB worker operations: `apps/electron-backend/src/app/database/operations/`
|
||||
## Ownership And Runtime
|
||||
|
||||
## Rules
|
||||
Routed screens live in `libs/portal/xtream/feature`; API, cache, Signal Store,
|
||||
and data sources in `libs/portal/xtream/data-access`; collection services and
|
||||
reusable multi-source discovery/resolution in
|
||||
`libs/portal/shared/data-access`; reusable views in
|
||||
`libs/portal/shared/ui`; pure contracts/helpers only in
|
||||
`libs/portal/shared/util`. Screen-session multi-source orchestration stays in
|
||||
the Xtream feature.
|
||||
|
||||
- Keep provider-specific API/cache behavior in `portal/xtream/data-access`.
|
||||
- Keep reusable layout and collection UI in `portal/shared/ui` or `portal/shared/util`.
|
||||
- Prefer worker-backed DB operations for large imports, global search, delete, and restore.
|
||||
- Preserve request cancellation and progress reporting for long imports.
|
||||
- Validate both PWA and Electron data-source paths when changing Xtream APIs.
|
||||
Select `ElectronXtreamDataSource` only through
|
||||
`RuntimeCapabilitiesService.supportsXtreamSqliteDataSource`; otherwise use the
|
||||
PWA data source. A generic `window.electron` check is not the capability
|
||||
contract; existing favorites/recent branches that still use one are migration
|
||||
debt, not precedent.
|
||||
|
||||
## Data And Navigation Contracts
|
||||
|
||||
- Xtream identity is playlist + normalized content type + provider
|
||||
`xtream_id`; collection keys include type and ID. Never resolve colliding
|
||||
live/movie/series IDs by number alone. Distinguish SQLite row IDs from
|
||||
provider `xtream_id` / `stream_id` / `series_id`.
|
||||
- Browse and search use canonical item routes. Favorites/recent use
|
||||
collection-owned inline detail. Preserve enough provider/route identity to
|
||||
recover hidden categories without sending a local database ID to the API.
|
||||
- Sparse VOD publishes the fallback selection first and recovers its provider
|
||||
category in the background. A playable source is one complete positive
|
||||
stream-ID + extension pair; never combine fields from incomplete candidates.
|
||||
- VOD multi-source is capability-gated, owner-scoped, Electron-only
|
||||
Xtream-to-Xtream movie behavior. Its reusable core belongs in portal shared
|
||||
data access; the screen session/host belongs in the Xtream feature.
|
||||
|
||||
`XtreamStore` composes portal, content, selection, search, EPG, player,
|
||||
favorites, recent items, and playback positions.
|
||||
|
||||
On the SQLite path, large import/search/delete/restore work remains
|
||||
worker-backed. Renderer-visible DB progress and cancellation use
|
||||
`operationId`. Xtream API requests use `requestId`, import sessions use
|
||||
`sessionId`, and the DB worker also has an internal transport request ID; never
|
||||
conflate any of them.
|
||||
|
||||
## Validation
|
||||
|
||||
- Run `pnpm nx test portal-xtream-data-access` and `pnpm nx test portal-xtream-feature` for store/UI changes.
|
||||
- Run `pnpm nx run web-e2e:e2e-ci--src/xtream.e2e.ts` or the closest Electron E2E target for user-visible Xtream workflow changes.
|
||||
Run both data-source specs plus affected Xtream store/feature tests. Use the
|
||||
closest atomized flow:
|
||||
|
||||
- `web-e2e:e2e-ci--src/xtream.e2e.ts`
|
||||
- `electron-backend-e2e:e2e-ci--src/xtream-responsiveness.e2e.ts`
|
||||
- `electron-backend-e2e:e2e-ci--src/xtream-vod-details.e2e.ts`
|
||||
- `electron-backend-e2e:e2e-ci--src/vod-multi-source.e2e.ts`
|
||||
@@ -289,7 +289,7 @@ This is an Nx monorepo with the following structure:
|
||||
- **portal/stalker/{data-access,feature}** - StalkerStore and routed Stalker components
|
||||
- **portal/catalog/feature** - Portal catalog UI
|
||||
- **portal/downloads/feature** - Download manager UI
|
||||
- **portal/shared/{data-access,ui,util}** - Cross-portal shared code (incl. the VOD multi-source discovery/resolve/ranking layer in `data-access/src/lib/multi-source/`)
|
||||
- **portal/shared/{data-access,ui,util}** - Cross-portal shared code: stateful collection services and VOD multi-source discovery/resolve/ranking live in `data-access`; reusable views live in `ui`; `util` is for pure contracts/helpers
|
||||
- **services** - Abstract DataService contract and shared app services (incl. the TMDB metadata enrichment module in `lib/tmdb/`)
|
||||
- **shared/interfaces** - TypeScript interfaces and types (incl. `ElectronBridgeApi`)
|
||||
- **shared/logging** - Dependency-free structured redaction for diagnostic logs
|
||||
@@ -332,14 +332,14 @@ The Xtream Codes module uses NgRx Signal Store with a layered architecture:
|
||||
│ (Composes feature stores, unified API) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────┬────────────┼────────────┬────────────┐
|
||||
▼ ▼ ▼ ▼ ▼
|
||||
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
|
||||
│ withPortal│ │withContent │ │withSelection│ │ withSearch │ │ withPlayer │
|
||||
└────────────┘ └────────────┘ └────────────┘ └────────────┘ └────────────┘
|
||||
│ │ │
|
||||
└───────────────────────────┼──────────────┘
|
||||
▼
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ withPortal · withContent · withSelection · withSearch · withEpg │
|
||||
│ withPlayer · withFavorites · withRecentItems │
|
||||
│ withPlaybackPositions │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ DATA SOURCE LAYER │
|
||||
│ IXtreamDataSource │
|
||||
@@ -391,15 +391,19 @@ Key patterns:
|
||||
|
||||
- **Feature stores**: Each `with*.feature.ts` uses `signalStoreFeature()` for focused functionality
|
||||
- **Facade pattern**: `XtreamStore` composes all features, maintaining backward compatibility
|
||||
- **Data source abstraction**: `IXtreamDataSource` interface with environment-specific implementations
|
||||
- **Factory injection**: `provideXtreamDataSource()` selects Electron or PWA implementation at runtime
|
||||
- **Data source abstraction**: `IXtreamDataSource` has SQLite-backed and
|
||||
API/in-memory implementations
|
||||
- **Factory injection**: `provideXtreamDataSource()` selects
|
||||
`ElectronXtreamDataSource` only when
|
||||
`RuntimeCapabilitiesService.supportsXtreamSqliteDataSource`; otherwise it
|
||||
selects `PwaXtreamDataSource`
|
||||
|
||||
Data strategies by environment:
|
||||
Xtream data strategies by runtime capability:
|
||||
|
||||
| Environment | Strategy |
|
||||
| ------------ | ------------------------------------------------------- |
|
||||
| **Electron** | DB-first: Check DB → fetch API if missing → cache to DB |
|
||||
| **PWA** | API-only: Always fetch from API, store in memory |
|
||||
| Capability | Strategy |
|
||||
| ---------- | -------- |
|
||||
| **Complete Xtream SQLite bridge** | DB-first: check DB → fetch API if missing → cache to DB |
|
||||
| **Bridge unavailable** | API-only: fetch from API and keep session data in memory |
|
||||
|
||||
**M3U Playlist Module Architecture**:
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ Use it when changing existing views or introducing new list-based UI in the work
|
||||
`libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.html`
|
||||
- Shared EPG timeline styles:
|
||||
`libs/ui/epg/src/lib/epg-timeline/epg-timeline.component.scss`
|
||||
- Shared EPG list:
|
||||
`libs/ui/epg/src/lib/epg-list-view/epg-list-view.component.ts`
|
||||
- Shared EPG list styles:
|
||||
`libs/ui/epg/src/lib/epg-list-view/epg-list-view.component.scss`
|
||||
- Shared list selection style:
|
||||
`apps/web/src/nav-list.scss`
|
||||
- Theme tokens:
|
||||
@@ -46,6 +50,7 @@ Use it when changing existing views or introducing new list-based UI in the work
|
||||
These tokens are the base for interactive emphasis:
|
||||
|
||||
- `--app-selection-color`
|
||||
- `--app-selection-on-color`
|
||||
- `--app-selection-surface`
|
||||
- `--app-selection-surface-strong`
|
||||
- `--app-selection-border`
|
||||
@@ -61,16 +66,16 @@ in `apps/web/src/m3-theme.scss`):
|
||||
- `--app-on-surface` — primary text
|
||||
- `--app-eyebrow-color` — secondary/muted text
|
||||
|
||||
**Do not reach for `--mat-sys-*` surface tokens.** Angular Material's system
|
||||
tokens are *not* emitted globally in this app — only `--mat-sys-on-surface`
|
||||
exists. `var(--mat-sys-surface-container-high)` and friends resolve to nothing,
|
||||
which usually goes unnoticed because the surrounding Material component
|
||||
supplies its own default and masks it. Outside a Material component — a CDK
|
||||
overlay, a projected panel — the same reference silently produces a fully
|
||||
transparent, unreadable surface.
|
||||
Angular Material mixins and Material-component overrides may use the tokens
|
||||
owned by that component. Outside a Material-owned component, prefer the
|
||||
app-owned tokens above. A `--mat-sys-*` reference is acceptable there only
|
||||
after the built light and dark theme contexts both prove that it is emitted,
|
||||
and it must still have a real app-token or literal fallback, for example:
|
||||
`var(--mat-sys-surface-container, var(--app-widget-bg))`.
|
||||
|
||||
If a `--mat-sys-*` token is genuinely needed, always give it a real fallback:
|
||||
`var(--mat-sys-on-primary, #fff)`.
|
||||
Several existing app surfaces still reference Material system tokens without
|
||||
that proof or use hard-coded layout/selection colors. Treat those references
|
||||
as migration debt, not patterns to copy.
|
||||
|
||||
Do not hardcode unrelated accent colors for selected state when these tokens already exist.
|
||||
|
||||
@@ -89,6 +94,9 @@ Apply the same visual recipe to selected list items, active channels, and curren
|
||||
- Text:
|
||||
selected text should inherit `var(--app-selection-color)`
|
||||
|
||||
Use `var(--app-selection-on-color)` when text or an icon sits directly on a
|
||||
solid `var(--app-selection-color)` fill.
|
||||
|
||||
Use this pattern for:
|
||||
|
||||
- `.nav-item.selected` / `.nav-item.active`
|
||||
@@ -112,9 +120,9 @@ the wrapper file that includes it.
|
||||
|
||||
The shared row should be reused instead of rebuilding channel markup per view.
|
||||
|
||||
### Structure
|
||||
### Current Reference Values
|
||||
|
||||
- Min height:
|
||||
- Current minimum height:
|
||||
`68px`
|
||||
- Horizontal gap:
|
||||
`12px`
|
||||
@@ -122,11 +130,16 @@ The shared row should be reused instead of rebuilding channel markup per view.
|
||||
`8px 10px 8px 12px`
|
||||
- Radius:
|
||||
`12px`
|
||||
- Logo shell:
|
||||
- Current logo shell:
|
||||
`44x44`, rounded, subtle inset treatment
|
||||
- Compact variant:
|
||||
`52px` min height with slightly tighter padding
|
||||
|
||||
These values describe the current shared row, not a fixed-width contract. Keep
|
||||
the row responsive: the text column uses `min-width: 0` and ellipsis, while
|
||||
logos, drag affordances, and trailing actions use `flex-shrink: 0`. Prefer
|
||||
minimum dimensions and flexible columns over fixed row widths.
|
||||
|
||||
### Content Layout
|
||||
|
||||
- Title is one line, medium-bold, slightly condensed
|
||||
@@ -143,6 +156,12 @@ The shared row should be reused instead of rebuilding channel markup per view.
|
||||
|
||||
## EPG Views
|
||||
|
||||
The shared timeline and list still contain local dark surfaces, blue selection
|
||||
accents, and white foregrounds. These non-semantic hard-coded colors are
|
||||
migration debt. New work should use app surface/selection/text tokens and must
|
||||
not spread those local fallbacks. Semantic live, error, and status colors may
|
||||
remain local when the meaning is explicit.
|
||||
|
||||
### Shared EPG Pane
|
||||
|
||||
- Header title stays sticky
|
||||
@@ -271,7 +290,9 @@ Settings use the same system but are flatter than content-heavy views.
|
||||
### Light Theme
|
||||
|
||||
- Prefer white or near-white cards
|
||||
- Use neutral borders from `--mat-sys-outline-variant`
|
||||
- Use app-owned neutral borders, or a proven Material token with a real
|
||||
fallback such as
|
||||
`var(--mat-sys-outline-variant, var(--app-widget-border))`
|
||||
- Keep active sections mostly defined by outline and subtle tint
|
||||
- Avoid dark translucent backgrounds
|
||||
|
||||
@@ -286,7 +307,7 @@ Settings use the same system but are flatter than content-heavy views.
|
||||
### Light Theme
|
||||
|
||||
- Flat beats glossy
|
||||
- White and surface-container layers should separate content
|
||||
- White and app-owned widget/content surface layers should separate content
|
||||
- Selection should read as a blue outline plus soft tint, not a solid slab
|
||||
|
||||
### Dark Theme
|
||||
@@ -302,7 +323,14 @@ Before creating new markup or CSS:
|
||||
1. Check whether `app-channel-list-item` can be reused.
|
||||
2. Check whether `app-epg-timeline` already provides the correct structure.
|
||||
3. Check whether `nav-list.scss` already solves the list-selection problem.
|
||||
4. Extend tokens first, duplicate styles last.
|
||||
4. Inspect the public APIs of `@iptvnator/ui/components`,
|
||||
`@iptvnator/ui/epg`, `@iptvnator/ui/playback`,
|
||||
`@iptvnator/ui/shared-portals`, `@iptvnator/portal/shared/ui`, and
|
||||
`@iptvnator/playlist/shared/ui`.
|
||||
5. Put provider-neutral collection loading, persistence, and cross-provider
|
||||
orchestration in `@iptvnator/portal/shared/data-access`, not a UI library or
|
||||
the shared util library.
|
||||
6. Extend tokens first, duplicate styles last.
|
||||
|
||||
## Implementation Workflow
|
||||
|
||||
@@ -312,13 +340,16 @@ When updating IPTVnator UI:
|
||||
2. Reuse the shared structure where possible.
|
||||
3. Keep selection, progress, and spacing in sync across Xtream, Stalker, and shared portal views.
|
||||
4. Verify in both light and dark themes.
|
||||
5. Verify in the running Electron app when the change is visual or layout-sensitive.
|
||||
5. Run the focused component/unit target and the closest Playwright workflow.
|
||||
6. Use the running Electron app through CDP only for Electron-only gaps or
|
||||
additional layout inspection; it does not replace available E2E coverage.
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
Avoid these:
|
||||
|
||||
- introducing a new selected-state color unrelated to the theme tokens
|
||||
- copying the shared EPG's hard-coded dark/blue fallbacks into new surfaces
|
||||
- duplicating channel row markup in portal-specific views
|
||||
- showing placeholder logos behind real logos
|
||||
- making entire panes scroll when only the list should scroll
|
||||
|
||||
@@ -109,10 +109,10 @@ GET load.php?type=itv&action=get_short_epg&ch_id={channel_id}&size={n}&JsHttpReq
|
||||
**Notes**
|
||||
|
||||
- The response is normalized into shared `EpgItem[]`
|
||||
- The list-preview path uses this directly
|
||||
- The active-panel fallback maps the result into controlled `EpgProgram[]`
|
||||
- Only the active-panel fallback uses this path and maps the result into
|
||||
controlled `EpgProgram[]`
|
||||
|
||||
### `get_epg_info` (bulk active-panel source)
|
||||
### `get_epg_info` (bulk row-preview and active-panel source)
|
||||
|
||||
**Request**
|
||||
|
||||
@@ -218,7 +218,8 @@ playlists.
|
||||
|
||||
1. User activates a live channel
|
||||
2. The component ensures playback link resolution as before
|
||||
3. The component calls `ensureBulkItvEpg(168)` on first use for the playlist
|
||||
3. The component ensures `ensureBulkItvEpg(168)` has run; the eager row effect
|
||||
normally started the same de-duplicated request before playback
|
||||
4. `selectedItvEpgPrograms()` feeds `app-epg-timeline`
|
||||
5. If the selected channel has no bulk programs, the component falls back to
|
||||
`get_short_epg`
|
||||
@@ -231,10 +232,11 @@ stream URL has been resolved; external playback keeps the full EPG-only panel.
|
||||
|
||||
### Channel row preview flow
|
||||
|
||||
Before the first live-channel playback, channel rows do not fetch EPG at all.
|
||||
|
||||
After bulk EPG has been loaded once for the playlist, visible row previews are
|
||||
derived locally from `bulkItvEpgByChannel`:
|
||||
Once non-radio ITV channels render, the post-reset component effect calls
|
||||
`ensureBulkItvEpg(168)`. It starts eagerly before playback and is de-duplicated
|
||||
against the active-channel path. Individual rows never issue per-row requests.
|
||||
As soon as the bulk request completes, visible row previews derive locally
|
||||
from `bulkItvEpgByChannel`:
|
||||
|
||||
- pick the current program for the channel, if one exists
|
||||
- compute progress from the cached program timestamps
|
||||
|
||||
@@ -267,9 +267,12 @@ list:
|
||||
|
||||
Stalker has multiple real-world data shapes. The current implementation supports all three:
|
||||
|
||||
Across catalog, detail, and activity normalization, `is_series` is accepted
|
||||
only as boolean `true`, numeric `1`, or string `'1'`. Unsupported
|
||||
`is_series` values do not by themselves classify a VOD item as a series.
|
||||
Within Stalker portal data access and feature code,
|
||||
`isStalkerSeriesFlag()` is the sole interpretation of `is_series`. The
|
||||
dependency-neutral activity normalizer in `libs/shared/interfaces` preserves
|
||||
the same closed set for dashboard records: boolean `true`, numeric `1`, or
|
||||
string `'1'`. Unsupported values do not by themselves classify a VOD item as a
|
||||
series.
|
||||
|
||||
1. Regular Series (`/series`):
|
||||
|
||||
@@ -308,7 +311,8 @@ only as boolean `true`, numeric `1`, or string `'1'`. Unsupported
|
||||
series ID, provider episode ID, season key, and episode number. The season
|
||||
key follows the mapping fallback (`season_number`, then name, then ID).
|
||||
- The previous season/episode hash remains available only as a compatibility
|
||||
alias. New playback positions always use the scoped tracking ID.
|
||||
alias in `legacyTrackingId`. The scoped ID is the in-memory episode key, and
|
||||
new playback positions always use it.
|
||||
- Quick-start actions preserve both their translation key and interpolation
|
||||
parameters when adapted for the Stalker CTA. Dropping `labelParams` exposes
|
||||
the raw `{{episode}}` placeholder.
|
||||
@@ -472,8 +476,9 @@ Stalker ITV now splits EPG usage:
|
||||
|
||||
- active channel panel: bulk `get_epg_info` cached once per playlist and rendered
|
||||
through the shared EPG panel (`app-epg-timeline`, or `app-epg-list-view` in list mode)
|
||||
- channel row preview: no pre-playback network requests; previews are derived
|
||||
from cached bulk EPG only after the first active-channel fetch succeeds
|
||||
- channel row preview: once ITV channels render, a post-reset effect eagerly
|
||||
starts the de-duplicated bulk `get_epg_info` load; rows issue no per-row
|
||||
request and derive previews from that cache
|
||||
- active panel fallback: `get_short_epg` when bulk EPG is missing or unsupported
|
||||
|
||||
Full details are documented in [Stalker Portal EPG Architecture](./stalker-epg.md).
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
This document captures the Xtream Codes compatibility rules shared by the
|
||||
Electron and PWA paths.
|
||||
|
||||
## Runtime Selection And Ownership
|
||||
|
||||
`provideXtreamDataSource()` selects `ElectronXtreamDataSource` only when
|
||||
`RuntimeCapabilitiesService.supportsXtreamSqliteDataSource` proves that the
|
||||
complete SQLite-backed Xtream bridge is available. Otherwise it selects
|
||||
`PwaXtreamDataSource`. A generic Electron or `window.electron` check is not the
|
||||
data-source capability contract. Older favorites/recent branches that still
|
||||
probe `window.electron` directly are migration debt, not an alternate runtime
|
||||
selection rule; changes in those paths should follow the selected data source
|
||||
and explicit capabilities.
|
||||
|
||||
Ownership follows the workspace boundaries:
|
||||
|
||||
- routed screens and screen-session orchestration:
|
||||
`libs/portal/xtream/feature`
|
||||
- Xtream API, cache, Signal Store, and data sources:
|
||||
`libs/portal/xtream/data-access`
|
||||
- provider-neutral collection services and reusable multi-source
|
||||
discovery/resolution: `libs/portal/shared/data-access`
|
||||
- reusable presentation: `libs/portal/shared/ui`
|
||||
- pure provider-neutral contracts/helpers: `libs/portal/shared/util`
|
||||
|
||||
Persisted Xtream identity is playlist-scoped and content-type-aware:
|
||||
`playlist_id + content.type + xtream_id`. Mixed collection keys likewise
|
||||
include type plus provider ID because live, movie, and series IDs can collide.
|
||||
Do not confuse the normalized SQLite row ID with provider `xtream_id`,
|
||||
`stream_id`, or `series_id`, especially when recovering a hidden provider
|
||||
category for detail playback.
|
||||
|
||||
See [Nx Workspace Boundaries](./nx-workspace-boundaries.md),
|
||||
[SQLite DB Worker](./sqlite-db-worker.md),
|
||||
[Portal Detail Navigation](./portal-detail-navigation.md), and
|
||||
[VOD Multi-Source](./vod-multi-source.md).
|
||||
|
||||
## Connection Input
|
||||
|
||||
Xtream server URLs are normalized through
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// ─── IPTVnator UI styles library ─────────────────────────────────────────────
|
||||
// Add libs/ui/styles to stylePreprocessorOptions.includePaths in project.json
|
||||
// to import these without relative paths:
|
||||
// Canonical forwarding inventory for shared IPTVnator UI styles.
|
||||
// The workspace has no global Sass include path for this directory; production
|
||||
// consumers use a relative path to the partial they need, for example:
|
||||
//
|
||||
// @use 'portal-layout' as portal;
|
||||
// @use 'content-grid' as grid;
|
||||
// @use 'portal-sidebar';
|
||||
// @use '../../../../../../ui/styles/portal-layout' as portal;
|
||||
|
||||
@forward 'portal-layout';
|
||||
@forward 'content-grid';
|
||||
@forward 'portal-sidebar';
|
||||
@forward 'panel-header';
|
||||
@forward 'detail-view';
|
||||
@forward 'detail-view-actions';
|
||||
Reference in new issue
Block a user