mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 10:56:43 -08:00
Full audit of CLAUDE.md, AGENTS.md, README.md and docs/architecture/ against the codebase; every fix is backed by current code: - remove documented-but-unimplemented IPTVNATOR_DISABLE_HARDWARE_ACCELERATION flag (no reads anywhere in apps/, libs/, tools/) - CLAUDE.md: add epg_channel_mappings to the schema table list - m3u-playlist-module: *-tab dirs -> *-view (+recent-view), selectActivePlaylist, real PlaylistState shape, ChannelEpgMetadata instead of removed EnrichedChannel, actual /workspace/playlists routes, per-view outputs, live-epg-panel-state key - workspace-dashboard: per-rail Settings.dashboardRails toggles, three missing rails in the diagram, split live-favorites/recent-live rails, welcome-dashboard empty-state type, RECENTLY_WATCHED_LIVE_TV title key - stalker-portal: CategoryContentViewComponent for vod/series, collection-route components for favorites/recent, corrected series-view/favorites-button paths, actor/:personId route, epg panel selectors - category-management: reloadCategories lives in with-content.feature.ts, workspace-context-panel owns the dialog, XtreamPendingRestoreService flow - stalker-mock-server (+app README): scenario-seeded faker, resetAll() clears content cache too, ordinal season episode ids, handlers/ dir location - sqlite-db-worker: cancellation shipped (drop from out-of-scope), full operations module list - portal-detail-navigation: replace three removed component paths - tmdb-metadata-enrichment: details cache keys are id:<tmdbId>|v2 - electron-security: CSP frame-src youtube-nocookie exception, sandbox: !frameCopyExperiment nuance - download-manager: libs/portal/xtream instead of xtream-electron folder, data-driven downloads nav, drop removed app-search-result-item note - playlist-backup-restore: settings-backup facade owns the import handoff - workspace-shell: functional workspaceEntryRedirect, playlists route children - iptvnator-ui-guidelines: EPG card radius 11px, detail-view mixin is `base` - embedded-mpv-native, player-controls-contract: minor precision fixes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
326 lines
12 KiB
Markdown
326 lines
12 KiB
Markdown
# IPTVnator UI Guidelines
|
|
|
|
This document captures the current UI language used across IPTVnator, with emphasis on channel lists, EPG views, settings surfaces, and shared selection patterns.
|
|
|
|
Use it when changing existing views or introducing new list-based UI in the workspace, Xtream, or Stalker flows.
|
|
|
|
## Core Principles
|
|
|
|
1. Prefer shared components over duplicated markup.
|
|
The canonical channel row is `app-channel-list-item`.
|
|
|
|
2. Drive emphasis through selection state, not through constant decoration.
|
|
Neutral rows should stay quiet. Only active or current items should pick up strong color.
|
|
|
|
3. Use the same selection language everywhere.
|
|
Selected nav items, channels, and current EPG cards should feel like the same system.
|
|
|
|
4. Keep dark and light themes intentionally different.
|
|
Dark theme can carry more density and tinted surfaces.
|
|
Light theme should be flatter and cleaner, with white or near-white cards.
|
|
|
|
5. Scroll ownership must be explicit.
|
|
Headers stay visible. Lists scroll. Do not let nested panes compete for scroll.
|
|
|
|
## Canonical References
|
|
|
|
- Channel row:
|
|
`libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.html`
|
|
- Channel row styles:
|
|
`libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.scss`
|
|
- Shared EPG timeline:
|
|
`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 list selection style:
|
|
`apps/web/src/nav-list.scss`
|
|
- Theme tokens:
|
|
`apps/web/src/m3-theme.scss`
|
|
- Settings surfaces:
|
|
`apps/web/src/app/settings/settings.component.scss`
|
|
- Detail view shell styles:
|
|
`libs/ui/styles/_detail-view.scss`
|
|
|
|
## Shared Tokens
|
|
|
|
These tokens are the base for interactive emphasis:
|
|
|
|
- `--app-selection-color`
|
|
- `--app-selection-surface`
|
|
- `--app-selection-surface-strong`
|
|
- `--app-selection-border`
|
|
- `--app-selection-glow`
|
|
|
|
Use Material surface tokens for neutral surfaces:
|
|
|
|
- `--mat-sys-surface`
|
|
- `--mat-sys-surface-container-low`
|
|
- `--mat-sys-surface-container`
|
|
- `--mat-sys-surface-container-high`
|
|
- `--mat-sys-outline-variant`
|
|
- `--mat-sys-on-surface`
|
|
- `--mat-sys-on-surface-variant`
|
|
|
|
Do not hardcode unrelated accent colors for selected state when these tokens already exist.
|
|
|
|
## Selection Pattern
|
|
|
|
Apply the same visual recipe to selected list items, active channels, and current EPG items:
|
|
|
|
- Background:
|
|
`linear-gradient(135deg, var(--app-selection-surface-strong), var(--app-selection-surface))`
|
|
- Border:
|
|
`var(--app-selection-border)`
|
|
- Glow:
|
|
outer shadow using `var(--app-selection-glow)`
|
|
- Lift:
|
|
`transform: translateY(-1px)` for selected list items only
|
|
- Text:
|
|
selected text should inherit `var(--app-selection-color)`
|
|
|
|
Use this pattern for:
|
|
|
|
- `.nav-item.selected` / `.nav-item.active`
|
|
- `.channel-list-item.active`
|
|
- `.epg-item.current-program`
|
|
|
|
Do not add extra badges, left rails, or second selection systems unless there is a strong reason.
|
|
|
|
## Detail Views
|
|
|
|
VOD and series detail screens share the detail-view Sass mixin (`@mixin base`)
|
|
from `libs/ui/styles/_detail-view.scss`. Feature-local `styles/detail-view.scss`
|
|
files should only `@use` that module and `@include detail-view.base(...)` with
|
|
small typography overrides when a provider needs them.
|
|
|
|
Do not copy the full detail-view stylesheet into feature libraries. Add shared
|
|
layout changes to the mixin, and keep provider-specific differences explicit in
|
|
the wrapper file that includes it.
|
|
|
|
## Channel List Item
|
|
|
|
The shared row should be reused instead of rebuilding channel markup per view.
|
|
|
|
### Structure
|
|
|
|
- Min height:
|
|
`68px`
|
|
- Horizontal gap:
|
|
`12px`
|
|
- Padding:
|
|
`8px 10px 8px 12px`
|
|
- Radius:
|
|
`12px`
|
|
- Logo shell:
|
|
`44x44`, rounded, subtle inset treatment
|
|
- Compact variant:
|
|
`52px` min height with slightly tighter padding
|
|
|
|
### Content Layout
|
|
|
|
- Title is one line, medium-bold, slightly condensed
|
|
- Program title is a secondary line with lower emphasis
|
|
- Timeline uses three columns:
|
|
start time, progress bar, end time
|
|
- Action buttons sit on the trailing edge and inherit row color
|
|
|
|
### Logo Rules
|
|
|
|
- Show fallback icon only when no image is available or image loading fails
|
|
- Do not render placeholder and real logo at the same time
|
|
- Keep logos contained with `object-fit: contain`
|
|
|
|
## EPG Views
|
|
|
|
### Shared EPG Pane
|
|
|
|
- Header title stays sticky
|
|
- Program list is the only scrolling region
|
|
- Add bottom padding so the last program is not clipped
|
|
- Current program card uses the same selection treatment as selected channels
|
|
|
|
### Collapsible Live EPG
|
|
|
|
- Live TV layouts with an internal player render `app-epg-timeline` as the
|
|
EPG content, including playlist-specific live pages and the global
|
|
favorites/recent live tabs.
|
|
- The timeline's own panel bar owns the current-program summary and live date
|
|
navigation together; there is no separate wrapper component around it.
|
|
- Collapsed state is shared across M3U, Xtream, and Stalker with
|
|
`live-epg-panel-state`; missing or invalid values restore to expanded.
|
|
- The collapsed panel is a slim current-program strip with a trailing progress
|
|
line and an expand button. Date controls stay out of the collapsed strip.
|
|
- Do not render the collapsed strip for external MPV/VLC playback; those
|
|
layouts keep the full EPG-only panel.
|
|
- Keep the EPG content mounted while collapsed so current-program state can
|
|
continue updating.
|
|
|
|
### Collapsible Live Sidebar
|
|
|
|
- M3U, Xtream, and Stalker live layouts share a single sidebar collapse toggle
|
|
that hides the channels rail to give the player and EPG full width.
|
|
- Xtream Live TV's root view (`/live` with no selected category) follows the
|
|
same paginated `All Items` shell as VOD and Series: a widget header with the
|
|
total channel count, page-size controls, and page navigation above the shared
|
|
`app-grid-list`. Use the grid list's logo-oriented live variant so channel
|
|
logos stay contained in 16:9 thumbnails instead of being cropped like
|
|
VOD/series posters. Selecting a channel from that root grid starts playback,
|
|
selects the channel's category, highlights the active category and channel,
|
|
and scrolls the category rail plus virtual channels list to the selected rows
|
|
when those rails are visible.
|
|
- In Xtream and Stalker live TV, the same toggle also collapses the workspace
|
|
shell context sidebar (the "Live Categories" rail rendered by
|
|
`WorkspaceShellContextSidebarComponent`), matching M3U's "everything quiets"
|
|
behaviour. The shell categories rail only collapses when the active section
|
|
is `live` (Xtream) or `itv`/`radio` (Stalker); movies, series, favorites,
|
|
and recent routes leave it untouched.
|
|
- Collapsed state is owned by `LiveLayoutSidebarStateService`
|
|
(`providedIn: 'root'`) in `@iptvnator/portal/shared/util`. Every surface that
|
|
participates injects the service and reads `isCollapsed`; any toggle calls
|
|
`service.toggle()`. Persistence delegates to the existing
|
|
`live-sidebar-state` helpers, so the localStorage key stays unchanged and
|
|
missing/invalid values restore to expanded.
|
|
- A `mat-icon-button` with `chevron_left` lives in the sidebar header and
|
|
toggles state. While collapsed, a floating `chevron_right` mini-fab appears
|
|
at the left edge of `.content-container` to restore the rail (and the
|
|
categories rail, in Xtream/Stalker live).
|
|
- Keyboard shortcut: `Cmd/Ctrl+B`. The handler ignores events that originate
|
|
inside `<input>`, `<textarea>`, `<select>`, or content-editable elements via
|
|
the shared `isTypingInInput` helper.
|
|
- The CSS class `.sidebar-collapsed` (channels rail) and
|
|
`.context-panel--collapsed` (workspace shell categories rail) both override
|
|
the inline width set by the `appResizable` directive with
|
|
`width: 0 !important; min-width: 0 !important`. The directive's persisted
|
|
width is preserved so uncollapsing restores the user's previous resized
|
|
width. Both rails share the same 180 ms width transition so motion stays in
|
|
lockstep.
|
|
- Below 600 px viewport, the M3U layout's mobile bottom-drawer rule overrides
|
|
the desktop collapse to `height: 0` instead of `width: 0`, and the floating
|
|
restore handle is hidden.
|
|
|
|
### EPG Card
|
|
|
|
- Radius:
|
|
`11px` (`.epg-timeline__block` in
|
|
`libs/ui/epg/src/lib/epg-timeline/epg-timeline-track.component.scss`)
|
|
- Neutral cards use low-contrast surface treatment
|
|
- Current card uses selection surface and selection border
|
|
- Description should clamp rather than overflow
|
|
|
|
### Sticky Header
|
|
|
|
- Keep the title readable above content
|
|
- Use a solid or near-solid backing surface
|
|
- Do not let it overlap or cover player controls
|
|
|
|
## Progress Bars
|
|
|
|
Channel preview progress and EPG current-program progress should stay visually aligned.
|
|
|
|
### Track
|
|
|
|
- Height:
|
|
`6px`
|
|
- Shape:
|
|
full pill radius
|
|
- Neutral background:
|
|
medium gray or neutral surface tint
|
|
- Include a slight inset edge so the remaining duration is visible
|
|
|
|
### Fill
|
|
|
|
- Use `--app-selection-color`
|
|
- Add a subtle sheen, not a heavy gradient
|
|
- Add a restrained glow, not a neon effect
|
|
|
|
The progress bar should clearly communicate:
|
|
|
|
- completed duration
|
|
- remaining duration
|
|
|
|
Avoid making the track too faint, especially in dark theme.
|
|
|
|
## Navigation Lists
|
|
|
|
Use the shared `nav-list.scss` treatment for sidebar and context-panel list items.
|
|
|
|
### Rules
|
|
|
|
- Keep labels one line with ellipsis
|
|
- Keep icon area clear from the selection border and any decorative rail
|
|
- Hover is neutral surface, not the selected color
|
|
- Selected state uses the shared selection recipe
|
|
|
|
If the label is too long for the rail, shorten the label key instead of shrinking the component until it becomes inconsistent.
|
|
|
|
## Settings Surfaces
|
|
|
|
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`
|
|
- Keep active sections mostly defined by outline and subtle tint
|
|
- Avoid dark translucent backgrounds
|
|
|
|
### Dark Theme
|
|
|
|
- Denser tinted surfaces are acceptable
|
|
- Neutral rows can use low-opacity dark overlays
|
|
- Keep strong blue tint reserved for active sections and selected items
|
|
|
|
## Theme Guidance
|
|
|
|
### Light Theme
|
|
|
|
- Flat beats glossy
|
|
- White and surface-container layers should separate content
|
|
- Selection should read as a blue outline plus soft tint, not a solid slab
|
|
|
|
### Dark Theme
|
|
|
|
- Slight translucency is acceptable
|
|
- Background layers can be deeper and more cinematic
|
|
- Keep contrast readable without going pure white everywhere
|
|
|
|
## Reuse Strategy
|
|
|
|
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.
|
|
|
|
## Implementation Workflow
|
|
|
|
When updating IPTVnator UI:
|
|
|
|
1. Inspect the current shared component first.
|
|
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.
|
|
|
|
## Anti-Patterns
|
|
|
|
Avoid these:
|
|
|
|
- introducing a new selected-state color unrelated to the theme tokens
|
|
- duplicating channel row markup in portal-specific views
|
|
- showing placeholder logos behind real logos
|
|
- making entire panes scroll when only the list should scroll
|
|
- using dark translucent fills unchanged in light theme
|
|
- solving cramped sidebars with smaller fonts instead of shorter labels
|
|
|
|
## Definition Of Done For UI Changes
|
|
|
|
A visual change is not done until:
|
|
|
|
1. Shared component reuse was considered first.
|
|
2. Light theme and dark theme both look intentional.
|
|
3. Selection and progress states match existing IPTVnator patterns.
|
|
4. Scroll behavior is correct.
|
|
5. The result was checked in the running app for layout-sensitive work.
|