Skip to content

Conversation

@4gray
Copy link
Owner

@4gray 4gray commented Nov 16, 2025

Summary

  • Add a dedicated no-EPG layout and centered presentation for channels without EPG data.
  • Move backdrop blur into a ::before pseudo-element with gradient masks and -webkit- fallbacks to avoid visual bleed.
  • Contain blur using isolation and z-index so overlay content remains above the blur.
  • Refactor info-overlay template to support two layouts: detailed EPG view and no-EPG fallback with larger channel logo and centered name.
  • Redesign EPG layout and styles: introduce .epg-layout, .content-header, program timeline, responsive image handling, animations, and improved spacing.
  • Ensure channel list shows current EPG program correctly and reset EPG fields when epgProgram is null to avoid stale data.
  • Fix UI issues: hide horizontal overflow to prevent scrollbars, add empty-favorites state, and modernize channel-list-item styles with dark-theme support.
  • Add toolbar info button and keyboard shortcut (I) to toggle the info overlay; implement showOverlay() with auto-hide after 10s.
  • Preserve favorites after auto-refresh.

This is part 2 of 2 in a stack made with GitButler:

Summary by CodeRabbit

Release Notes

  • New Features
    • Added program information overlay accessible via "I" key or new toolbar button, displaying program details, timeline, start/end times, and live progress indicator
    • Channel list now displays current program information inline with visual progress tracking
    • Enhanced toolbar with improved drawer state indicator and quick info access button

@vercel
Copy link

vercel bot commented Nov 16, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
iptvnator Error Error Nov 22, 2025 10:18am

@coderabbitai
Copy link

coderabbitai bot commented Nov 16, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'auto_review'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

The PR adds EPG program caching with a 60-second TTL to the service layer, introduces EPG state management and batch fetching in the channel list component, redesigns the info overlay to display program details with progress tracking, and adds keyboard/UI controls for triggering the overlay. Additional playlist metadata (favorites, userAgent) is preserved during auto-updates.

Changes

Cohort / File(s) Summary
Backend Playlist Updates
apps/electron-backend/src/app/events/playlist.events.ts
Preserves additional user-specific data (favorites array and userAgent) when updating playlists during auto-refresh.
EPG Service Layer
libs/services/src/lib/epg.service.ts
Introduces in-memory EPG caching with 60-second TTL; adds getCurrentProgramForChannel(), getCurrentProgramsForChannels(), and clearCache() APIs for channel-level program lookups.
M3U State Effects
libs/m3u-state/src/lib/effects.ts
Adds guard to skip EPG/IPC operations when channel is undefined in setActiveChannel flow.
Video Player Integration
apps/web/src/app/home/video-player/video-player.component.*
Adds keyboard shortcut (I key) and method toggleInfoOverlay() for manual info overlay triggering; wires toolbar output to overlay toggle; extends playlist initialization to load/clear favorites.
Toolbar Controls
libs/ui/components/src/lib/video-player/toolbar/toolbar.component.*
Adds isLeftDrawerOpened input and infoOverlayClicked output; adds info button with tooltip binding.
Channel List Container
libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts
Implements OnInit lifecycle, introduces EPG state tracking (channelEpgMap), adds 60-second EPG refresh interval, batch-fetches EPG for all channels, and rewires favorites$ via combineLatest().
Channel List Container Template & Styles
libs/ui/components/src/lib/channel-list-container/channel-list-container.component.*
Replaces mat-nav-list with div containers; increases virtual scroll item size from 50 to 68; adds [epgProgram] bindings to channel items; enforces overflow-x hidden on containers; adds empty-state styling for favorites.
Channel List Item
libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.ts
Implements OnInit, OnChanges, OnDestroy; adds showEpg input and epgProgram input; introduces progress tracking with 30-second refresh interval; adds calculateProgress() and formatTime() helpers.
Channel List Item Styles
libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.scss
Complete style overhaul: replaces minimal truncation with BEM-like structure including drag handle, content sections, EPG display with progress track/fill, animations, and theme variants.
Info Overlay Component
libs/ui/components/src/lib/info-overlay/info-overlay.component.ts
Adds showOverlay() method; handles undefined epgProgram in ngOnChanges; increases auto-hide timeout to 10000ms; adds TranslatePipe import.
Info Overlay Template & Styles
libs/ui/components/src/lib/info-overlay/info-overlay.component.*
Restructures layout to EPG-driven template with separate branches for program info (timeline, progress bar, description) and no-EPG fallback; adds progress animation, shimmer effects, and refined typography with Crimson Pro and DM Sans fonts.
EPG List Component
libs/ui/components/src/lib/epg-list/epg-list.component.ts
Changes setPlayingNow() to always dispatch setCurrentEpgProgram action, even when playingNow is null/undefined (previously skipped dispatch).
Localization
apps/web/src/assets/i18n/en.json
Adds TOP_MENU.SHOW_INFO ("Show program info (Press I)") and EPG.NO_PROGRAM_INFO ("No program information available") keys.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant VP as VideoPlayer
    participant Toolbar
    participant InfoOverlay
    participant EPGService
    
    Note over User,EPGService: Info Overlay Trigger
    User->>VP: Press 'I' key or<br/>click Info button
    alt Keyboard Shortcut
        VP->>VP: handleInfoKeyPress()
    else Toolbar Button
        Toolbar->>Toolbar: infoOverlayClicked.emit()
        Toolbar->>VP: (infoOverlayClicked)
    end
    VP->>VP: toggleInfoOverlay()
    VP->>InfoOverlay: Show overlay<br/>(10s auto-hide)
    
    rect rgb(230, 245, 255)
        Note over InfoOverlay: Display Program Info
        InfoOverlay->>InfoOverlay: Render EPG details<br/>(title, timeline, progress)
        alt EPG Available
            InfoOverlay->>InfoOverlay: Show program & progress bar
        else No EPG
            InfoOverlay->>InfoOverlay: Show "No info available"
        end
    end
Loading
sequenceDiagram
    participant CLC as ChannelListContainer
    participant Items as ChannelListItem
    participant EPGService
    participant Store
    
    Note over CLC,Store: EPG State Management & Refresh
    CLC->>CLC: ngOnInit()
    CLC->>CLC: Start 60s refresh interval
    CLC->>CLC: fetchEpgForChannels(channels)
    CLC->>EPGService: getCurrentProgramsForChannels([...])
    
    loop For Each Channel
        EPGService->>EPGService: Check cache (60s TTL)
        alt Cache Hit
            EPGService->>EPGService: Return cached program
        else Cache Miss
            EPGService->>Store: Fetch EPG from backend
            Store->>EPGService: Return programs
            EPGService->>EPGService: Cache result + timestamp
        end
    end
    
    EPGService->>CLC: Return Map<channelId, program>
    CLC->>CLC: Store in channelEpgMap
    CLC->>Items: Emit programChange<br/>via binding
    Items->>Items: calculateProgress() every 30s
    Items->>Items: Render progress bar & timeline
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas requiring extra attention:

  • Channel List Container (channel-list-container.component.ts): Complex state management refactoring using BehaviorSubject and combineLatest; verify the favorites$ composition correctly merges store favorites with channel list updates and handles undefined channels.
  • Channel List Item Progress Tracking (channel-list-item.component.ts): Introduces lifecycle-managed interval-based progress updates; verify proper cleanup in ngOnDestroy, change detection triggering, and interval cancellation logic.
  • EPG Caching Layer (epg.service.ts): Verify TTL logic correctly handles cache expiration; edge cases when channels are fetched mid-request; proper error handling in batch operations.
  • Info Overlay Restructuring (info-overlay.component.*): Significant template/style changes with new layout branches; verify EPG and non-EPG paths both render correctly and animations function smoothly.
  • Behavioral Change in EPG List (epg-list.component.ts): Now always dispatches even with null programs; verify store reducers handle null/undefined programs correctly.

Possibly related PRs

  • refactor/ipc-events-handling #644: Modifies playlist metadata handling to propagate userAgent across player-launch IPC flows; aligns with the userAgent preservation in playlist auto-updates.
  • fix/m3u-auto-refresh-mechanism #648: Extends the playlist auto-update handler in apps/electron-backend; this PR builds on that foundation by preserving additional fields (favorites, userAgent).

Poem

🐰 A Rabbit's Ode to Channels & Programs

Cache the programs, sixty seconds strong,
Show the info when the key goes ding-dong!
Progress bars shimmer as programs play,
Favorites refreshed throughout the day. ✨📺

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat-info-overlay-no-epg-layout' is specific and directly reflects a primary change: adding a no-EPG layout for the info overlay component.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@4gray
Copy link
Owner Author

4gray commented Nov 16, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/m3u-state/src/lib/effects.ts (1)

130-140: Guard currently risks an infinite setActiveChannel loop

setActiveChannel$ listens for PlaylistActions.setActiveChannel and, in the new guard, returns the same action when channel is falsy. Because this effect has dispatch: true (default), that re-dispatches setActiveChannel, which re-enters the same effect and can create an infinite loop whenever channel is undefined.

Consider filtering out falsy channels before map, or mapping to a different action that the effect does not listen to. For example:

@@
-    return this.actions$.pipe(
-        ofType(PlaylistActions.setActiveChannel),
-        map((action) => {
-            const { channel } = action;
-
-            // Guard against undefined channel
-            if (!channel) {
-                return action;
-            }
+    return this.actions$.pipe(
+        ofType(PlaylistActions.setActiveChannel),
+        // Skip the effect entirely when channel is falsy
+        filter((action) => !!action.channel),
+        map((action) => {
+            const { channel } = action;
@@
             return PlaylistActions.setActiveChannelSuccess({
                 channel: action.channel,
             });
         })
     );

Don’t forget to add filter to the RxJS imports at the top.

🧹 Nitpick comments (9)
apps/electron-backend/src/app/events/playlist.events.ts (1)

154-161: User data preservation looks good!

The preservation of favorites and userAgent during auto-refresh correctly maintains user customizations across playlist updates. The favorites fallback to an empty array is a good defensive pattern.

For consistency, consider applying a similar defensive approach to userAgent:

                 ...playlistObject,
                 _id: playlist._id,
                 autoRefresh: playlist.autoRefresh,
                 favorites: playlist.favorites || [], // Preserve favorites
-                userAgent: playlist.userAgent, // Preserve custom user agent
+                ...(playlist.userAgent && { userAgent: playlist.userAgent }), // Preserve custom user agent

This avoids adding userAgent: undefined to the object when no custom user agent is set, keeping the object shape cleaner.

libs/ui/components/src/lib/epg-list/epg-list.component.ts (1)

208-227: Align setPlayingNow with null-clearing intent and avoid nested subscriptions

The new behavior (always dispatching setCurrentEpgProgram, even when no program matches “now”) makes sense for clearing stale EPG, but a couple of details could be improved:

  1. Nested subscription and potential accumulation

setPlayingNow subscribes to this.items$ while ngOnInit already subscribes to the same stream and calls handleEpgData, which in turn calls setPlayingNow. Over time this can build up multiple subscriptions to items$.

A simpler and safer pattern is to derive playingNow directly from the array you already have in handleEpgData:

// handleEpgData
if (programs.length > 0) {
  this.setPlayingNow(programs);
} else {
  this.channel = {} as EpgChannel;
  this.store.dispatch(resetActiveEpgProgram());
}
setPlayingNow(programs: EpgProgram[]): void {
  const nowIso = new Date().toISOString();
  const playingNow =
    programs.find((item) => {
      const start = new Date(item.start).toISOString();
      const stop = new Date(item.stop).toISOString();
      return nowIso >= start && nowIso <= stop;
    }) ?? null;

  this.playingNow = playingNow;
  this.store.dispatch(setCurrentEpgProgram({ program: playingNow }));
}

This avoids the extra subscription entirely. Alternatively, if you prefer to keep using items$, at least add take(1) so each call to setPlayingNow completes its subscription.

  1. Type-safety for “no program”

You’re intentionally dispatching null/undefined to clear the current program, but playingNow is declared as a non-null EpgProgram and is assigned via playingNow!. To better reflect the actual behavior, consider:

-    /** EPG selected program */
-    playingNow!: EpgProgram;
+    /** EPG selected program (null when nothing is currently playing) */
+    playingNow: EpgProgram | null = null;

And then dispatch program: playingNow (which can be null) instead of relying on an unsafe assertion.

These adjustments keep the new “clear stale EPG” behavior while making the implementation safer and easier to reason about.

libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.ts (1)

27-166: Consider stopping click propagation when toggling favorites

The new layout, EPG display, and progress calculation look solid (OnPush + markForCheck, interval cleanup, clamped percentage, etc.). One behavioral detail to double‑check:

<div
  class="channel-list-item"
  ...
  (click)="clicked.emit()"
>
  ...
  <button
    *ngIf="showFavoriteButton"
    mat-icon-button
    class="favorite-button"
    ...
    (click)="favoriteToggled.emit($event)"
  >

Because the root div listens for click, clicking the favorite button will also bubble up and trigger clicked.emit(), effectively selecting the channel when the user only intended to toggle the favorite.

If you’d prefer these actions to be distinct, you can stop propagation on the button:

- (click)="favoriteToggled.emit($event)"
+ (click)="$event.stopPropagation(); favoriteToggled.emit($event)"

This keeps the new UX while preventing accidental channel activation via the favorite button.

libs/ui/components/src/lib/info-overlay/info-overlay.component.ts (1)

88-101: Consider clarifying the toggle behavior.

The showOverlay() method has dual behavior: it toggles off immediately if already visible, or shows for 10 seconds if hidden. While functional, this might be unexpected for users triggering it multiple times. Consider either:

  • Renaming to toggleOverlay() to make the toggle behavior explicit
  • Always resetting the 10-second timer when called, rather than immediately hiding

Current behavior: pressing 'I' when overlay is visible → hides immediately
Alternative: pressing 'I' when overlay is visible → resets 10s timer and keeps visible

libs/ui/components/src/lib/info-overlay/info-overlay.component.html (1)

1-71: Well-structured dual-layout approach.

The template cleanly separates EPG and no-EPG states with consistent structure. The conditional rendering based on epgProgram existence is clear and maintains good visual consistency between states.

Optional: The progress width calculation on line 41 could be extracted to a computed getter for better testability:

get progressWidth(): number {
  return (this.finishedDuration * 100) / this.generalDuration;
}

Then in template:

[ngStyle]="{ width: progressWidth + '%' }"
libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.scss (1)

2-2: Consider centralizing font imports.

The Google Fonts import adds an external network dependency at the component level. Consider:

  1. Moving font imports to a global stylesheet to avoid duplicate requests (same fonts are imported in info-overlay component)
  2. Or using system fonts as fallbacks to reduce external dependencies
  3. Or preloading fonts in the HTML head for better performance

Current approach works but creates multiple font requests and blocks rendering until fonts load.

libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts (1)

125-142: Remove or guard console.log statements.

The favorites$ logic correctly combines store favorites with the channel list. However, the console.log statements on lines 131 and 139 should be removed or placed behind a debug flag for production builds.

Apply this diff to remove the console statements:

     favorites$ = combineLatest([
         this.store.select(selectFavorites),
         this.channelList$,
     ]).pipe(
         map(([favoriteChannelIds, channelList]) => {
-            console.log('[ChannelList] favorites$ emit - IDs:', favoriteChannelIds.length, 'Channels:', channelList.length);
             const favorites = favoriteChannelIds
                 .map((favoriteChannelId) =>
                     channelList.find(
                         (channel) => channel.url === favoriteChannelId
                     )
                 )
                 .filter((channel): channel is Channel => channel !== undefined);
-            console.log('[ChannelList] favorites$ result:', favorites.length, 'favorites');
             return favorites;
         })
     );
libs/ui/components/src/lib/info-overlay/info-overlay.component.scss (2)

1-2: Consider self-hosting fonts for privacy and reliability.

While the Google Fonts CDN is convenient, it introduces external dependencies that could impact privacy and availability. Using display=swap is good, but consider self-hosting these fonts to:

  • Eliminate third-party requests
  • Improve privacy compliance (GDPR)
  • Ensure reliability in offline scenarios

136-167: Consider adding ARIA attributes for progress bar accessibility.

The progress bar looks great with its gradient fill and shimmer animation. However, for better accessibility, the HTML element using .progress-fill should include ARIA attributes like role="progressbar", aria-valuenow, aria-valuemin, and aria-valuemax so screen readers can announce the progress.

Note: Since this is a SCSS file, you'll need to add these attributes in the corresponding HTML template.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efee5ea and ec89b58.

📒 Files selected for processing (17)
  • apps/electron-backend/src/app/events/playlist.events.ts (1 hunks)
  • apps/web/src/app/home/video-player/video-player.component.html (1 hunks)
  • apps/web/src/app/home/video-player/video-player.component.ts (4 hunks)
  • apps/web/src/assets/i18n/en.json (2 hunks)
  • libs/m3u-state/src/lib/effects.ts (1 hunks)
  • libs/services/src/lib/epg.service.ts (3 hunks)
  • libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html (7 hunks)
  • libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss (3 hunks)
  • libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts (5 hunks)
  • libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.scss (1 hunks)
  • libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.ts (2 hunks)
  • libs/ui/components/src/lib/epg-list/epg-list.component.ts (1 hunks)
  • libs/ui/components/src/lib/info-overlay/info-overlay.component.html (1 hunks)
  • libs/ui/components/src/lib/info-overlay/info-overlay.component.scss (2 hunks)
  • libs/ui/components/src/lib/info-overlay/info-overlay.component.ts (3 hunks)
  • libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html (2 hunks)
  • libs/ui/components/src/lib/video-player/toolbar/toolbar.component.ts (1 hunks)
🔇 Additional comments (24)
libs/ui/components/src/lib/video-player/toolbar/toolbar.component.ts (1)

35-37: Toolbar input/output wiring for overlay looks consistent

isLeftDrawerOpened and infoOverlayClicked align with the updated template usage and existing signal-style outputs. The wiring is straightforward and doesn’t introduce behavioral issues on its own.

libs/ui/components/src/lib/video-player/toolbar/toolbar.component.html (1)

8-31: Menu icon binding and info button wiring look good

The menu icon now responds to isLeftDrawerOpened, and the new info button correctly emits infoOverlayClicked with the appropriate localized tooltip and icon. This aligns with the updated toolbar component API and the overlay trigger flow.

apps/web/src/assets/i18n/en.json (1)

197-198: LGTM!

The new localization keys are well-named and the text is clear and user-friendly. The keyboard shortcut hint in SHOW_INFO is a nice touch for discoverability.

Also applies to: 213-213

apps/web/src/app/home/video-player/video-player.component.html (1)

19-21: LGTM!

The new event binding and input property correctly wire up the info overlay toggle functionality and drawer state to the toolbar component.

libs/ui/components/src/lib/info-overlay/info-overlay.component.ts (1)

51-62: Proper null handling for EPG data.

The enhanced logic correctly resets all EPG-related state when no program is available, preventing stale data from being displayed. This is essential for the no-EPG layout fallback.

libs/ui/components/src/lib/channel-list-container/channel-list-container.component.scss (2)

5-5: LGTM!

The multiple overflow-x: hidden declarations effectively prevent horizontal scrollbars across different container levels in the channel list, addressing the UI issue mentioned in the PR description.

Also applies to: 24-24, 28-30


72-86: Good empty state styling.

The empty favorites styling provides clear visual feedback with appropriate padding and font sizing.

libs/ui/components/src/lib/channel-list-container/channel-list-container.component.html (3)

26-26: LGTM!

The itemSize="68" aligns with the channel list item height defined in the SCSS (line 8 of channel-list-item.component.scss).


153-162: Good empty state UX.

The empty favorites state provides helpful guidance to users with clear messaging.


44-44: No issues found—implementation already uses optimized caching.

The getEpgForChannel method performs a simple Map.get() lookup, which is an O(1) operation. The EPG data is pre-computed and cached in channelEpgMap (line 69), so there are no performance concerns. The implementation already follows best practices for this use case.

apps/web/src/app/home/video-player/video-player.component.ts (1)

336-352: LGTM!

The keyboard shortcut handler and toggle method are properly implemented. The @HostListener correctly captures the 'I' key press and delegates to the overlay component's showOverlay() method.

libs/ui/components/src/lib/channel-list-container/channel-list-item/channel-list-item.component.scss (1)

5-159: Comprehensive styling with good theme support.

The BEM-like structure with dark theme support and EPG progress visualization is well-implemented. The gradient progress bar with animation provides good visual feedback.

libs/ui/components/src/lib/channel-list-container/channel-list-container.component.ts (6)

15-15: LGTM!

The lifecycle hooks and imports are correctly implemented.

Also applies to: 36-36, 38-38, 62-62


68-79: LGTM!

The EPG state management fields are well-typed. The use of Map<string, EpgProgram | null> correctly handles channels with and without EPG data.


86-92: LGTM!

The channelList setter correctly propagates changes through the observable stream and triggers EPG fetching. The EPG service's caching layer (60-second TTL) will prevent excessive backend calls even if the setter is invoked frequently.


187-192: LGTM!

The EPG refresh interval is correctly implemented with proper cleanup. The 60-second interval is reasonable for EPG data freshness, and the caching layer in EpgService prevents redundant backend calls.


194-203: LGTM!

Proper cleanup of resources: the interval is cleared and the BehaviorSubject is completed, preventing memory leaks.


231-237: LGTM!

The helper method correctly retrieves EPG data from the map with proper null/undefined handling.

libs/services/src/lib/epg.service.ts (3)

4-4: LGTM!

The caching layer is well-designed with proper encapsulation. The 60-second TTL is appropriate for EPG data freshness, and using a Map provides efficient O(1) lookups.

Also applies to: 8-25


174-185: LGTM!

The current program logic correctly identifies the program airing at the current time using inclusive time boundaries.


255-260: LGTM!

The cache clearing utility is straightforward and well-documented.

libs/ui/components/src/lib/info-overlay/info-overlay.component.scss (3)

4-31: LGTM!

The blur effect implementation is well-crafted:

  • isolation: isolate properly contains the blur
  • Vendor prefixes (-webkit-) ensure Safari compatibility
  • Using ::before with z-index: -1 correctly layers the blur behind content

Note: backdrop-filter requires modern browsers; older browsers will see the overlay without blur, which degrades gracefully.


33-95: LGTM!

The EPG layout is well-structured with:

  • Proper flexbox composition and text truncation (min-width: 0)
  • Staggered animations for a polished reveal
  • Clear typographic hierarchy using custom fonts
  • Good use of shadows for depth and readability

169-220: LGTM!

The animations are well-implemented:

  • Smooth, performance-friendly transforms
  • Appropriate timing and easing functions
  • Clear separation between EPG and no-EPG states with helpful comments

The .hidden class's 2-second delay (line 207) pairs well with the PR's 10-second auto-hide behavior.

Introduce a separate no-EPG presentation and move backdrop blur
into an isolated pseudo-element to avoid visual bleed and improve
layout control.

- Replace direct backdrop-filter on the overlay with a ::before
  pseudo-element that applies backdrop-filter and mask-image
  gradients (with -webkit- prefixed fallbacks) for a smoother
  gradient blur effect.
- Add isolation on the overlay to contain stacking context and set
  z-index of the pseudo-element to -1 to keep content above the blur.
- Add .no-epg modifier on the overlay and new .no-epg-layout,
  .channel-logo-large and .channel-name-large styles to provide a
  centered, accessible layout when no EPG data is available.
- Update template to render two distinct layouts: one when
  epgProgram exists (original detailed view) and one fallback view
  when epgProgram is missing, including larger channel logo and
  centered channel name.

These changes improve visual fidelity of the blur effect and add a
clear fallback UI for channels without EPG data.
Refactor the info-overlay template to introduce a structured EPG
layout: replace the previous flat channel-logo + content structure with
an .epg-layout wrapper that groups .channel-logo and a richer .content
area. Split content into .content-header (separate program title and
channel name), conditional program description rendering, and a
program-timeline containing explicit start/end times and a progress
track. Remove fixed image width attribute and rely on responsive CSS.

Revamp component SCSS: import refined fonts, increase overlay max-height,
add horizontal padding, and add new styles for .epg-layout, .channel-logo,
.content, .content-header, .program-title and related elements. Add
animations, improved image handling (object-fit + drop shadow), and
layout spacing to support varying content lengths and improve visual
hierarchy.

These changes improve responsiveness, accessibility of EPG text
display, and visual polish of the overlay.
@4gray 4gray force-pushed the feat-remote-control-ui-server-integration branch from b88078c to 0823af2 Compare November 22, 2025 08:47
@4gray 4gray force-pushed the feat-info-overlay-no-epg-layout branch from a7b535b to 40e9006 Compare November 22, 2025 08:47
Reset EPG fields when epgProgram becomes null to avoid stale
displayed durations and ensure consistent behavior when no program
is available.

Improve channel list container and item styling:
- hide horizontal overflow to prevent unexpected scrollbars
  (scroll-viewport, #all-channels, .channel-list).
- add empty-favorites layout and hint styles for clearer empty
  state presentation.
- refactor channel-list-item SCSS: introduce structured layout
  (.channel-list-item, .channel-content, .channel-logo,
  .channel-details), apply responsive truncation rules and font
  imports, add hover/active visuals and dark-theme support, and
  tweak drag-icon spacing.

These changes fix UI glitches (stale EPG, horizontal scrollbars)
and modernize the channel list presentation for better readability
and theming.
Add "Show program info (Press I)" translation and an info icon
button to the video player toolbar. Emit an infoOverlayClicked event
from the toolbar and expose isLeftDrawerOpened to control the menu
icon state. Update the menu icon to reflect drawer open/closed state.

Implement showOverlay() on the info overlay component to toggle
visibility and auto-hide after 10 seconds when shown. This enables
manual toggling of the program info overlay via the new toolbar
button or keyboard shortcut, improving discoverability and user
control of program information.
Add a transient channel number overlay to the video player UI so users
see the numeric channel feedback when entering or changing channels.

- Insert conditional overlay markup in the video-player template that
  renders when showChannelNumberOverlay is true and displays the
  channelNumberInput value.
- Add styles for .channel-number-overlay and
  .channel-number-display to center the overlay, style the numeric
  display (large bold font, dark translucent background, rounded
  corners, shadow) and include a brief scale-in animation.
- Keep overlay pointer-events disabled so it doesn't block player input.

This improves user experience by giving clear visual confirmation of
channel changes.
Add compact layout support for channel list items and make EPG
display conditional based on runtime environment and stored settings.

- Introduce .compact styles in channel-list-item.component.scss to
  reduce item height, padding, and logo size when EPG is hidden.
- Add StorageMap injection and read STORE_KEY.Settings in
  ChannelListContainerComponent to determine whether EPG is configured
  (only enable EPG in Electron when epgUrl exists; disable in PWA).
- Expose shouldShowEpg flag and itemSize getter to switch virtual
  scroll item size between 68 and 48 pixels.
- Bind [class.compact] on channel list item and wire [showEpg] input
  to channel-list-item components so item rendering adapts to mode.
- Replace hardcoded itemSize in cdk-virtual-scroll-viewport with
  dynamic [itemSize] to match compact/expanded layouts.

This reduces visual clutter and avoids showing EPG in environments
(where it is not configured or supported), improving UX and resource
usage.
Revamp the Multi-EPG component stylesheet to introduce a polished,
broadcast-control-room inspired dark theme and improved layout
styling.

- Add Google fonts (Outfit, JetBrains Mono) for a clean UI and mono
  date display.
- Introduce color variables, gradients and accents for consistent
  theming and easier maintenance.
- Rework base container (.parent) with subtle grid overlay and updated
  typography.
- Implement a frosted glass navigation bar (#epg-navigation) with
  blur, glow, and accessible button styles.
- Add today-date styling using a monospace font and glowing accent.
- Expand channels column width, update background, borders, shadows,
  and hover transitions for clearer channel separation.
- Replace many hard-coded colors with variables and add transitions
  for smoother interactions.
- Add utility classes for navigation buttons and improved disabled/
  hover states.

These changes improve visual hierarchy, accessibility, and maintainabiity
of the EPG UI while setting up a consistent design system for further
enhancements.
Add explicit icon settings to packaged app configuration for macOS,
Linux and Windows. Set macOS icon to 
apps/web/src/assets/icons/favicon.ic, Linux icon directory to 
apps/web/src/assets/icons and Windows icon to 
apps/web/src/assets/icons/favicon.ico. This ensures bundled installers 
and app bundles include the intended application icon across platforms 
and prevents missing icon assets when creating artifacts. Also add 
trailing commas to keep JSON formatting
consistent.
Adjust documentation lists and code blocks for consistent
indentation and spacing across CLAUDE.md. Convert mixed dash
and nested list levels to use uniform two-space indentation for
subitems, reflow a few wrapped lines, and add blank lines where
needed to separate sections and examples. These changes improve
readability and maintain a consistent markdown structure for the
project documentation.
@4gray 4gray force-pushed the feat-info-overlay-no-epg-layout branch from 40e9006 to 0b31aac Compare November 22, 2025 10:17
@4gray 4gray merged commit 0291fa8 into feat-remote-control-ui-server-integration Nov 22, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants