The timeline and volume sliders were rendering invisibly because:
- --embedded-mpv-accent resolved to var(--mat-sys-primary) with no
fallback. If the Material 3 theme tokens did not cascade into the
component scope, the variable was empty and both the thumb and the
Firefox progress fill rendered with no color.
- The track contrast was 22% of var(--mat-sys-on-surface) which has the
same scoping fragility on top of being subtle even when it does
resolve.
- The thumb had no border, so even when accent was present it could
blend into similarly-toned popover backgrounds.
- The played portion of the timeline had no fill in Chromium because
::-webkit-slider-runnable-track does not support a native progress
state.
Fixes:
- Add hard fallbacks on every theme-token reference (accent falls back to
#7dd3fc, surface to #0f1620, outline-variant to a translucent white).
- Add --embedded-mpv-track and --embedded-mpv-thumb-ring as theme-
independent CSS variables so the slider stays legible regardless of
the parent theme provider.
- Give the thumb a 2 px white ring border + a stronger drop shadow so it
reads on any background (panel glass, dark backdrop, accent-coloured
area).
- Implement played-progress fill via a linear-gradient on
::-webkit-slider-runnable-track keyed off a CSS variable
--slider-progress, set per-slider from the template based on the
current playback position / volume. Firefox uses native
::-moz-range-progress.
- Tidy disabled state styling so live-stream timelines (no duration)
are dim but still visible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
The component had grown to ~1000 lines with session lifecycle, IPC plumbing,
keyboard shortcuts, popover state, formatters, and view orchestration all
living in one file. Per CLAUDE.md's 350–400 line hard cap, split into:
- embedded-mpv-format.utils.ts (~100): pure helpers (formatTime, track
labels, volume icon/label, persisted-volume access, measureBounds) and
preset constants (SPEED_PRESETS, ASPECT_PRESETS, HIDDEN_BOUNDS).
- embedded-mpv-shortcuts.ts (~90): EmbeddedMpvShortcuts class that owns the
document keydown listener and routes through a handler interface; the
component just provides callbacks.
- embedded-mpv-ui-state.ts (~110): EmbeddedMpvMenuState (single-open
popover state machine with toggle/open/close/closeAll + anyOpen signal)
and EmbeddedMpvFeedback (transient overlay with auto-clearing flash).
- embedded-mpv-session-controller.ts (~395): component-scoped Injectable
that owns support/session/sessionId/stalled/retryToken signals, the
session-update IPC subscription, bounds-sync (resize, scroll, overlay
state), the stalled timer, and all per-session IPC operations
(togglePaused, seekBy, seekTo, applyVolume, setAudioTrack,
setSubtitleTrack, setSpeed, setAspect, retry).
- embedded-mpv-player.component.ts (now ~540, was ~1000): view-only
orchestration — view children, derived computed signals, DOM event
listeners (pointerdown/pointermove/fullscreenchange/dblclick), and three
effects (session start/teardown, overlay-active bounds sync, session
payload → volume/timeUpdate fan-out).
No behavior changes. Build clean, electron-backend tests still pass.
Component is still over the 400-line cap but the bulk of its size is now
the necessarily-coupled-to-view computed signals and constructor effects;
the remaining over-cap delta is structural to a player root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
Native addon (apps/electron-backend/native/src/embedded_mpv.mm)
- Extend SessionSnapshot with subtitleTracks, selectedSubtitleTrackId,
playbackSpeed, aspectOverride.
- Refactor track parsing into a shared updateTracksFromNode helper that
filters by mpv "type" so audio and subtitle tracks share the code path.
- Observe sid, speed, video-aspect-override; clear sub state on
MPV_EVENT_START_FILE.
- Export setSubtitleTrack (handles trackId === -1 as "no" to disable),
setSpeed (clamped to 0.25–4.0), setAspect (passthrough string for
video-aspect-override).
- Snapshot output now includes the new fields.
Service (embedded-mpv-native.service.ts) + IPC + preload
- Mirror methods on EmbeddedMpvNativeService with capability detection: each
method throws a descriptive error if the loaded addon doesn't expose the
underlying native function (i.e. user is running an older build).
- New IPC channels EMBEDDED_MPV_SET_SUBTITLE_TRACK, _SET_SPEED, _SET_ASPECT
registered in events file and exposed via preload.
- Extend EmbeddedMpvSupport with a capabilities probe so the renderer can
hide controls for features the current addon build doesn't ship.
Renderer (embedded-mpv-player.component.{ts,html})
- Three new popovers anchored above their buttons (subtitle / speed /
aspect), gated by capabilities() and (for subtitles) by track count.
- Subtitle popover includes an Off entry; speed/aspect use fixed presets.
- Error state now surfaces the same overlay as the stalled state, with a
Retry button that bumps the existing retryNonce signal — covers #8 from
the audit.
- All session-payload defaults (loading stub, error stub, refresh fallback,
dispose payload, native createSession default) updated for the new
required fields.
NOTE: Existing addon binaries do not expose the new methods. Until the
addon is rebuilt (pnpm run serve:backend:embedded-mpv or the release
build script), capabilities will report subtitles/playbackSpeed/
aspectOverride as false and the new buttons will simply not appear.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
- Volume button now toggles mute on click; the slider opens via hover/focus
in an absolute popover anchored above the button (with hover-bridge so the
cursor can reach it without crossing a dead zone). Wheel over the volume
area adjusts volume.
- Audio track menu likewise becomes a click-toggle popover anchored to its
button instead of a panel takeover; the back-arrow / mode-panel
scaffolding is gone.
- Slider gets explicit thumb/track styling for both WebKit and Firefox so
it reads as part of the design system instead of a raw native control;
aria-valuetext on the timeline announces formatted time.
- Stalled overlay: if status remains "loading" for 30 s, surface a centered
warning with a Retry button that disposes the session and recreates it
via a retryNonce signal the playback effect tracks.
- Keypress feedback overlay: ←/→ seek, ↑/↓ volume, M mute now flash a
centered pill with the icon + delta so the action is visible (especially
important in fullscreen where the controls are hidden).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
Add the standard set of player shortcuts to match the audio/HTML video
players already in the app:
- Space, K — play/pause
- F — toggle fullscreen
- Left/Right — seek -5 s / +5 s
- Up/Down — volume +/- 5 %
- M — toggle mute (restores prior volume)
Shortcuts are ignored while focus is in an input/select/textarea or while
any MatDialog (e.g. command palette) is open, so the palette's filter and
dialog inputs keep their keys. Volume changes route through a shared
applyVolume helper so keyboard/wheel/slider all persist to localStorage
and the active session.
Also bind dblclick on the player root to toggle fullscreen, with a guard
so double-clicks on buttons/sliders don't trigger it. Note: the native
MPV NSView intercepts mouse events over the video itself, so dblclick
only fires on DOM-receiving regions (currently the controls strip).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
- Switch palette and surfaces to Material 3 tokens (--mat-sys-*) so the
player respects the app theme instead of a hardcoded cyan/sky scheme.
- Stop reserving 96 px below the viewport for the control panel; the panel
now floats over the bottom with a gradient fade so the video fills the
frame (especially in fullscreen).
- Drop control panel height from 96 to 64 px (88 px on narrow widths).
- Replace the corner "Loading stream in MPV..." pill with a centered
spinner + concise label during load; move the transient status toast
to the top-right and add role="status" aria-live.
- Add a red badge with pulsing dot for live streams in place of the plain
"LIVE" text label.
- Apply font-variant-numeric: tabular-nums to time/volume readouts so
digits stop jittering as playback advances.
- Slightly loosen transport/actions gaps for a less cramped rhythm and
use ease-out easing on control fade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
- Introduced tooling for building and staging the macOS `libmpv` runtime for IPTVnator's embedded MPV player.
- Added `build-macos-runtime.mjs` for building an LGPL-compatible runtime from source.
- Created `stage-macos-runtime.mjs` for staging the built runtime artifacts.
- Implemented validation for the packaged embedded MPV runtime in `electron-after-pack.cjs` and `embedded-mpv-macos.cjs`.
- Updated packaging scripts to ensure the embedded MPV runtime is correctly integrated and validated during the build process.
- Added README files to document the expected layout and usage for the embedded MPV runtime artifacts.
Entire-Checkpoint: c6e522b4276c