Commit Graph
17 Commits
Author SHA1 Message Date
4grayandClaude Fable 5 ce24e16e32 feat(portals): two-state VOD detail pages (browse ↔ watch) with season tabs (#1127)
* feat(ui): slim ContentHero to non-scrolling hero block

The page scroll container moves out of the hero into the upcoming
PortalDetailShellComponent; the hero no longer hosts a default content
slot for player/seasons/extras.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ui): add PortalDetailShellComponent with browse/watch states

Two-state detail layout shell: owns page scroll, hero collapse animation
(~300ms, reduced-motion aware), Escape-to-close-player handling, and an
About block that re-stamps host-provided *detailTags/*detailMeta
templates in watch state. The [detail-player] slot is never wrapped in a
shell conditional so the host-owned player subtree survives state
changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(playback): restyle inline player header as now-playing bar

Adds a back button (emits the existing closed output — in watch state
back returns to browse, not route navigation) and replaces the
hardcoded header strings with i18n keys (PORTALS.NOW_PLAYING,
CLOSE_PLAYER, BACK_TO_BROWSE).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(xtream): migrate VOD and serial detail templates to detail shell

Hero chips/meta/actions become *detailTags/*detailMeta/*detailActions
templates; the inline player moves to the full-width [detail-player]
slot; trailer and similar rail move to [detail-extras]. Escape and the
now-playing back button close the inline player back to browse.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(stalker): migrate shared VodDetails and series view to detail shell

The shared VodDetailsComponent (Stalker VOD, collections, search) and
StalkerSeriesViewComponent move their hero content into
*detailTags/*detailMeta/*detailActions templates and host the inline
player in the full-width [detail-player] slot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ui): season tabs replace two-level seasons navigation

SeasonTabsComponent renders a pill row (dropdown beyond 6 seasons), an
optional season description and a back-to-playing chip. The season
container auto-selects a season (inline-playing episode's season →
most recent in-progress → first), emits seasonSelected for
auto-selections so lazy-load/enrichment hooks keep firing, highlights
the inline-playing episode, and loses the seasons grid + 'Back to
seasons' button. Download/progress helpers move to pure utils; season
strings are now translatable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(portals): season descriptions under the season tabs

Xtream reads season overviews from get_series_info; Stalker exposes the
TMDB season overview through TmdbEnrichmentService.getSeason (same
cache rows as the episode enrichment) and keys it per tmdbId so views
reused across detail navigations cannot leak descriptions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: document two-state detail layout and season tabs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): app-prefix detail template directive selectors, build fixes

detailTags/detailMeta/detailActions → appDetailTags/appDetailMeta/
appDetailActions per @angular-eslint/directive-selector; type the
Escape host listener as Event; drop a redundant ?? in season tabs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(i18n): translate new detail-page keys across all 17 locales

13 PORTALS.* keys (now-playing bar, About block, season tabs, episode
empty states) translated via the i18n-fill workflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(playback): keep now-playing bar actions inside the viewport

The nowrap title made the header grid track grow to min-content and
pushed Copy/Close out of the page; min-width: 0 on the header/title row
lets the title truncate instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ui): expandable season description and episode info dialog

Season descriptions get the same show more/less toggle as the hero
(new shared ExpandableTextComponent); episode cards and list rows get
an info button that opens a dialog with the full plot, duration, air
date and a Play action — card heights stay fixed and click-to-play
semantics stay intact. Also: drop the per-frame backdrop blur on the
player card and make the watch-enter scroll instant so the hero morph
stops competing for frame budget.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(portals): re-run TMDB season enrichment when the show match arrives

With season tabs the first seasonSelected fires as soon as seasons
load — usually before the async show-level TMDB enrichment has written
tmdb_id — so the season fetch silently no-oped and was never retried,
leaving episode stills/plots and the season description empty. Both
detail hosts now key the fetch on (tmdb_id, selected season) in an
effect, so it runs whichever arrives last. Also adds breathing room
above the About divider in watch state.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): move episode info button into the card body, compact overlay actions

Three overlay buttons crowded the thumbnail on hover and the new info
button had no styling at all. Info belongs with the content text: it
now sits as a quiet ghost button at the end of the title row (revealed
on card hover, MDC touch target clamped so it cannot swallow the
play-on-click card area). Download + watched-toggle stay on the
artwork, shrunk to 28px with a subtle edge border. List view already
used the shared action style and is unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): single watched indicator-toggle on episode cards

Watched state was signalled three times on one card: the green badge,
the hover toggle in the top-right cluster, and a checkmark appended to
the title. Now one control does both jobs — a toggle at the top-left
of the artwork that looks like the old green badge when watched
(always visible) and appears as a ghost circle on hover when not.
The right cluster keeps only the download action; the title checkmark
is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(playback): back arrow in watch mode navigates straight to the list

Leaving the theater took two clicks: back to browse, then back to the
list. Watch state already shows everything browse offers (episodes,
About, extras), so the bar's back arrow is now route-level back
(new backClicked output wired to each host's goBack), while Close
player and Escape keep exiting to browse without navigating. Replaces
PORTALS.BACK_TO_BROWSE with a generic top-level BACK key (all locales).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(ui): compact episode stills in the list view

When a season has genuinely distinct per-episode art (TMDB stills or
provider images), list rows show a 96x54 thumbnail with the episode
number riding on it instead of the number square. When every episode
repeats the same image (providers often send the series poster), the
plain number square stays — a column of identical posters is worse
than none.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(ui): compact list-thumbnail spec under the max-lines limit

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): address Greptile review — real fallback asset, untracked overflow measure

default-episode.png never existed (pre-existing in the grid card, copied
into the list thumbs) — both onerror handlers now fall back to the real
default-poster.png. The expandable-text and content-hero measuring
effects read the expanded signal via untracked so toggling show
more/less no longer rebuilds their ResizeObservers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 21:28:00 +02:00
4grayandClaude Fable 5 8e0abe6feb feat(ui): custom title bar with window controls for Windows and Linux (#1042)
* feat(ui): add custom title bar window controls for Windows and Linux

Hide the native title bar on win32/linux (titleBarStyle: 'hidden', frame
untouched so native resize borders and snapping keep working) and render
minimize / maximize-restore / close buttons in the renderer, mirroring the
existing macOS traffic-light setup.

- New WINDOW:* IPC contract (minimize, toggle-maximize, close, get-state)
  handled in window.events.ts, resolved from the sender WebContents;
  close goes through win.close() so window-bounds persistence still runs.
- WINDOW:STATE_CHANGED pushed on maximize/unmaximize/fullscreen so the
  maximize/restore glyph stays correct for OS-triggered changes; controls
  hide while fullscreen.
- WindowControlsComponent mounts once in app-root as a manual popover so
  it stays in the browser top layer above CDK overlays (dialogs,
  multi-EPG) - same behavior as macOS traffic lights.
- Theme-aware via CSS vars (--app-on-surface, --app-hover-overlay);
  Windows-red close hover. Drag regions get right padding through a
  body-level frameless-platform class.
- Gated by RuntimeCapabilitiesService.usesCustomWindowControls; PWA and
  macOS never mount the controls.

Includes unit specs for the component and IPC handlers, an Electron E2E
suite (window-controls.e2e.ts), and a window-chrome section in
docs/architecture/workspace-shell.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(linux): upgrade Electron to 41 for frameless window decorations on Wayland

With the native title bar hidden, Linux windows lost the WM-drawn shadow
and rounded corners. Electron draws client-side decorations only on
native Wayland, and frameless-window CSD (GTK drop shadow + extended
resize boundaries) landed in Electron 41 - before that, frameless
windows render as plain rectangles.

- electron ^39.8.5 -> ^41.7.2 (Wayland auto-detected since 38.2; X11
  sessions remain undecorated, matching other frameless Electron apps;
  Windows keeps its DWM shadow and rounded corners).
- better-sqlite3 pinned to exactly 12.9.0: the last release shipping
  prebuilt binaries for both Node 20 (ABI 115, Jest) and Electron 41
  (ABI 145, runtime). 12.10.0 dropped the Node 20 prebuilds, forcing a
  from-source build that fails without a C++ toolchain.
- pnpm override node-abi 3.85.0 -> 3.92.0 so electron-builder
  install-app-deps can map Electron 41 to ABI 145.

Reviewed Electron 40/41 breaking changes: only the renderer clipboard
deprecation, which this app does not use.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(e2e): address review feedback and window-managerless Linux CI

- Skip the three window-manager-dependent E2E assertions (maximize
  toggle, main-process state sync, minimize) on Linux CI: GitHub's
  ubuntu runners drive Electron under xvfb without a window manager, so
  maximize/minimize state never materializes there. Windows CI and
  local Linux/macOS runs keep the coverage.
- WINDOW:TOGGLE_MAXIMIZE now returns the requested state instead of
  re-reading isMaximized() right after the call, which races on Linux
  window managers where maximize()/unmaximize() complete
  asynchronously; the WINDOW:STATE_CHANGED push stays authoritative.
- Skip attaching window-state push listeners on macOS, where the
  custom controls never mount and the IPC traffic had no subscriber.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): gate custom window controls on the full bridge surface

Include getWindowState and onWindowStateChange in the
usesCustomWindowControls capability check — the controls rely on both
for initial state and for keeping the maximize/restore glyph in sync
with OS-triggered changes, so a partial bridge should not mount them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 12:03:27 +02:00
4gray 6cbf2a1cfb feat(m3u): add context menus to recent channels
Entire-Checkpoint: f957cd9849e0
2026-05-05 00:27:15 +02:00
4grayandClaude Opus 4.7 e888841b21 refactor(ui): unify live-tv channel skeleton across m3u, xtream, stalker
The loading placeholders for live channel rows were inconsistent and
visibly heavier than the real channel-list-item: each module rendered
its own skeleton with a colored card surface, larger padding/radius,
mismatched logo border-radius, and only a single tiny right-side dot
instead of the real 34×34 action buttons. Stalker had no initial-load
skeleton at all.

Extract a shared app-channel-list-item-skeleton that mirrors
channel-list-item 1:1 (transparent surface, 8/10/8/12 padding, 12px
radius, 44×44 logo with 10px radius, optional EPG progress placeholder,
0–3 button placeholders) plus a thin app-channel-list-skeleton wrapper
that renders N rows with deterministic width variance, and route every
live-tv loading state through it (m3u channel-list-loading-state,
xtream portal-channels-list, stalker live-stream-layout via a derived
isInitialChannelsLoading signal, and the unified favorites/recent
collection page). Drops the bespoke channel-skeleton-shimmer keyframes
in favor of the shared skeleton-shimmer animation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 58a2d50756dd
2026-05-02 00:52:38 +02:00
4gray 0f72efb749 refactor(m3u): add loading state for channel list and improve playlist handling
- Implemented a loading state for channel lists in various components, enhancing user experience during data fetching.
- Added `ChannelListLoadingStateComponent` to display loading skeletons for channels and groups.
- Updated `M3uWorkspaceRouteSession` to manage loading states more effectively when fetching playlists.
- Modified `VideoPlayerComponent`, `SidebarComponent`, and `ChannelListContainerComponent` to utilize the new loading state.
- Enhanced tests to cover new loading behaviors and ensure proper dispatching of loading actions.
2026-04-11 13:20:22 +02:00
4gray bf427f7226 refactor: remove info overlay component and related files 2026-04-11 10:42:31 +02:00
4gray 7ac2fd66b8 refactor: update nx monorepo structure 2026-03-09 23:24:34 +01:00
4gray 0ad1eeb785 refactor: update nx monorepo structure 2026-03-09 20:28:31 +01:00
4gray e24967fc68 feat: implement Stalker portal normalization and watchdog functionality in playlists and session services
feat: enhance EPG list component with channel icon fallback and scrolling behavior
style: update styles for EPG list and playlist item components for improved UI
fix: add selection state to playlist items in recent playlists component
2026-03-05 23:35:24 +01:00
4gray 6c52ee349d feat: add resizable directive and styles for dynamic element resizing 2026-01-30 20:36:36 +01:00
4gray b9b7fc8a63 feat: add playlist switcher component UI and styles 2026-01-10 15:18:57 +01:00
4gray afad80e61b feat(ui): add ContentHero component and detail view styles
Add a new standalone ContentHero component (template and TS) that
provides a cinematic hero section with backdrop, poster, title,
description, tags/meta/action slots, and a back button event emitter.
The component chooses backdrop or poster as the background, falls back
to a default poster image on load error, and exposes inputs for title,
description, posterUrl, and backdropUrl.

Introduce extensive cinematic styles for the detail view:
- define CSS custom properties for light and dark themes, poster size,
  shadows, and color tokens
- implement gradient vignette, layout sizing, and theming via
  :host and :host-context(.dark-theme)
- change container sizing behavior to full-height and hide overflow

These changes create a reusable hero UI and themed detail-page styles
to support a Netflix-style streaming detail experience.
2026-01-05 21:54:54 +01:00
4gray b82d47f6b6 feat(ui): add AddPlaylistMenu component for playlist imports
Introduce a reusable AddPlaylistMenuComponent that provides a Material
menu with options to add playlists via file, URL, text, Xtream or Stalker
portal. Define a PlaylistType union and expose a playlistTypeSelected
output so parent components can react to the selected import type.

- Add component TypeScript with Angular metadata and required Material
  imports.
- Add template with accessible menu items, icons, translation keys and
  click handlers that emit the selected PlaylistType.
- Organize menu items with a divider to separate common file/URL/text
  options from provider-specific imports.

This adds a focused UI building block for playlist import flows and
standardizes how import type selection is communicated to parent scopes.
2025-12-06 21:44:25 +01:00
4gray 028ccf1580 feat(ui/components): export and reuse overlay injection token
Introduce a shared InjectionToken for OverlayRef used by the multi-EPG
overlay component and export it from the components module.

- Add overlay-ref.token.ts defining COMPONENT_OVERLAY_REF to provide
  OverlayRef for dynamically created overlay components.
- Replace a locally created InjectionToken with an injected
  COMPONENT_OVERLAY_REF in multi-epg-container.component to avoid
  token duplication and ensure consistent DI across modules.
- Export the token from libs/ui/components/src/index.ts so other
  consumers can import and use the same token.

This reduces duplicated token definitions, fixes DI inconsistencies,
and makes the overlay ref reusable by external components.
2025-11-22 11:26:22 +01:00
4gray 260f3b3da7 refactor(nx): app restructuring 2025-10-14 09:00:31 +02:00
4gray 2d4fdcfc1b refactor(nx): add restructuring 2025-10-13 01:33:50 +02:00
4gray 7028ff4049 refactor(nx): app restructuring 2025-10-13 00:24:54 +02:00