Commit Graph
2584 Commits
Author SHA1 Message Date
4gray 39c1c10492 Merge pull request #964 from 4gray/agent/pwa-self-hosted-hardening
fix(pwa): harden self-hosted runtime boundaries
2026-05-22 10:37:35 +03:00
4gray 36bce47764 merge: resolve master conflicts for pwa hardening
- merge origin/master into PR #964 and keep embedded MPV test on the isolated playback sub-entrypoint

- centralize EPG capability through DataService.supportsEpg and update PWA web-e2e expectations

- split BrowserAccessError copy between Electron and PWA diagnostics
2026-05-22 10:20:03 +03:00
dependabot[bot] 3c7dbda398 chore(deps): bump serialize-javascript from 6.0.2 to 7.0.5
Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 6.0.2 to 7.0.5.
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.2...v7.0.5)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-version: 7.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 06:40:16 +00:00
4gray c96a805b47 Merge pull request #987 from 4gray/agent/electron-ipc-sqlite-contracts
test(electron): add sqlite ipc contract coverage
2026-05-22 09:39:25 +03:00
4gray 9d69443d01 Merge pull request #963 from 4gray/agent/embedded-mpv-ci-runtime-cache
ci: cache embedded mpv runtime
2026-05-22 09:38:26 +03:00
4gray 0345ad7e5e Merge pull request #970 from 4gray/agent/web-strict-mode
chore(web): enable strict TypeScript mode
2026-05-22 09:31:41 +03:00
4gray dce5b557af test(electron): split sqlite ipc contract fixtures 2026-05-22 03:53:35 +03:00
4gray 6cf730ddf6 test(electron): add sqlite ipc contract coverage 2026-05-22 03:42:01 +03:00
4gray 5d355b6f10 fix(web): address strict mode review feedback 2026-05-22 03:11:14 +03:00
4gray 4ab8915483 chore(web): enable strict TypeScript mode 2026-05-22 02:58:12 +03:00
4gray f3c0cd3820 ci: clarify embedded mpv cache inputs 2026-05-22 02:16:32 +03:00
4gray 55efc24608 fix(pwa): harden self-hosted runtime boundaries 2026-05-22 02:09:57 +03:00
4gray 0b19155469 ci: cache embedded mpv runtime 2026-05-22 02:02:06 +03:00
4gray ff9c13058c Merge pull request #955 from 4gray/claude/elegant-hertz-40b11b
Unified color, rebuilt dashboard, polished Settings & Multi-EPG
2026-05-22 01:58:57 +03:00
4gray 2a769ad44e Merge origin/master into PR 955 2026-05-22 01:34:56 +03:00
4gray 5cfe3aef06 fix(dashboard): localize progress labels 2026-05-22 01:02:04 +03:00
4gray 2ecb5c28f4 fix(dashboard): address PR review feedback 2026-05-22 00:59:17 +03:00
4gray 67d512d44c Merge pull request #961 from 4gray/agent/pwa-provider-target-flow
fix(docker): exclude agent worktrees from build context
2026-05-22 00:47:22 +03:00
4gray 4c2666520c fix(pwa): parallelize dashboard xtream loads 2026-05-21 21:40:24 +03:00
4gray 69b7bca9df fix(pwa): prefer xtream collection snapshots 2026-05-21 21:13:58 +03:00
4gray 87dfaec03d fix(pwa): filter stalker from xtream collections 2026-05-21 20:33:02 +03:00
4gray 6438eec49f fix(pwa): align xtream content identity 2026-05-21 20:18:39 +03:00
4gray ed0b6833d6 fix(pwa): persist xtream collection snapshots 2026-05-21 20:00:38 +03:00
4gray 3ccbfe4f99 fix(pwa): remove dashboard xtream items via data source 2026-05-21 19:52:39 +03:00
4gray 9097ff5bc1 fix(pwa): align xtream recent clearing 2026-05-21 19:45:07 +03:00
4gray d1e057887f fix(pwa): avoid persisting xtream passwords 2026-05-21 19:41:16 +03:00
4gray ec098f98b1 fix(pwa): hydrate xtream user collections 2026-05-21 19:35:36 +03:00
4gray 1ea6914f98 fix(pwa): harden xtream identity normalization 2026-05-21 19:27:40 +03:00
4gray 1d4d793aac fix(pwa): restore xtream user collections 2026-05-21 19:23:25 +03:00
4gray b2909a1186 feat(workspace): add keyboard shortcuts help (#957)
* feat(workspace): add keyboard shortcuts help

* style(workspace): polish keyboard shortcuts dialog

* fix(workspace): address shortcuts review feedback

* docs(workspace): clarify desktop shortcut availability

* refactor(workspace): split keyboard shortcut definitions
2026-05-21 19:15:46 +03:00
4gray 362595525d test(pwa): tighten self-hosted proxy assertions 2026-05-21 18:44:42 +03:00
4gray 6829c667bd fix(pwa): restore xtream rail and vod navigation 2026-05-21 14:18:11 +03:00
4gray 89df1ada0a fix(docker): exclude agent worktrees from build context 2026-05-21 13:36:32 +03:00
4gray 401d434bce test(pwa): cover self-hosted provider target flow 2026-05-21 11:19:04 +03:00
4gray 5d31c1d49a ci(docker): publish images after trusted events 2026-05-21 09:37:43 +02:00
4gray fd59447064 docs(docker): restore self-hosted PWA compose flow
* docs(docker): restore self-hosted PWA compose flow

* fix(docker): supervise self-hosted runtime processes

* fix(docker): exit cleanly on container stop
2026-05-21 09:30:57 +02:00
4gray 738397c9ed feat(website): add giscus blog comments 2026-05-20 18:02:19 +02:00
4grayandClaude Opus 4.7 7083bd2af4 fix(shell): keep Add Playlist reachable on Settings; fix dashboard e2e
Two Electron E2E regressions from this branch's redesigns:

1. Hiding the playlist switcher on /settings (a playlist-scoped control)
   also hid the global "Add playlist" button, which lives in the same
   header-actions block. A user configuring Settings before importing
   their first source had no way to add a playlist — and the settings /
   playlist-switcher E2E suites, which add a portal right after saving a
   setting, timed out waiting for the button. The Add Playlist button is
   a global action, so it now stays visible on Settings; only the
   per-playlist context shortcut and bulk actions are hidden there.

2. dashboard-activation.e2e.ts asserted on `dashboard-global-favorites-
   rail`, the mixed Favorites rail removed earlier in this branch. The
   test now targets the v0.22 rails: live favorites resolve through the
   favorites-first `dashboard-live-recent-rail`, and the played movie /
   series resolve through `dashboard-continue-watching-rail` (renamed
   from `dashboard-recently-watched-rail`). The movie-from-favorites
   assertion was dropped — that dashboard surface no longer exists; the
   favorites collection still has its own page-level coverage.

Verified locally: dashboard-activation, settings, and playlist-switcher
E2E specs all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 15:55:06 +02:00
4gray 3096115e64 docs: add external players playback article 2026-05-20 15:35:38 +02:00
4grayandClaude Opus 4.7 a8253c5801 test(e2e): adapt to v0.22 dialog + theme picker redesigns
The redesign rollup in this branch replaced the Add Playlist dialog's
2-level type × subtype tabs with a single flat 5-card radiogroup, and
replaced the Settings theme list with a compact segmented control whose
options are just "Light"/"Dark"/"System" (no "theme" suffix). E2E tests
that pinned to the old roles/labels failed in CI:

  - basic / xtream / stalker / self-hosted: getByRole('tab', ...) on
    "Add via file upload" / "Xtream" / "Stalker" — there are no tabs in
    the new dialog. Switched to getByRole('radio', { name: /M3U file/i })
    and friends, matched against the new radio labels.
  - settings: getByRole('radio', { name: 'System theme' }) — the new
    picker uses just "System". Scoped to the [data-test-id="select-theme"]
    radiogroup so it doesn't collide with the identically-labelled
    cover-size options below.

The Electron `clickDialogSegmentedOption` helper grew a radio-role lookup
as its primary path and keeps the old tab/button/legacy-selector
fallbacks so a future redesign won't break every fixture again. The
"M3U" parent category becomes a no-op (the new picker has no standalone
M3U tile — callers always specialise to M3U URL/file/text immediately
after, which is what we want anyway).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:07:26 +02:00
4gray 5b091809ef fix(xtream): ignore future recently added dates
## Summary
- Normalize Xtream recently-added timestamps across UI, import, and dashboard query paths.
- Filter future/invalid provider dates before ranking rails and migrate legacy millisecond cache rows.
- Add regression coverage for future timestamps, series date priority, and DB migration behavior.

## Validation
- GitHub checks passed, including Unit Tests and Typechecks, Web E2E, Electron E2E on macOS/Ubuntu/Windows, CodeQL, builds, and Greptile Review.
2026-05-19 14:39:38 +02:00
4grayandClaude Opus 4.7 8091dec074 feat(dashboard): show S·E badge on Continue Watching series cards
For series in the Continue Watching rail, surface which episode the user
left off on as a small "S2·E5" chip next to the card subtitle. Sources
the season/episode straight off the matched PlaybackPositionData so the
information is always in sync with the resume target.

Fixes a latent bug in the same patch: getPlaybackPositionForItem could
not resolve series whose recent_items row carried the series id (the
landing-page path), because playback_positions are keyed by the episode
id. The lookup now matches both shapes (contentXtreamId === xtreamId OR
seriesXtreamId === xtreamId) and prefers the most recently updated
episode, which also restores the resume progress bar that had been
silently dropping out for series.

Localised in all 17 non-English locales — most use language-specific
short forms (St·F in German, T·E in Spanish/Portuguese, С·Э in
Russian/Belarusian, 시즌N N화 in Korean) rather than the English S·E.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:32:13 +02:00
4grayandClaude Opus 4.7 6891a1ca30 i18n(epg): translate multi-EPG toolbar tooltips
The Zoom/Filter/Search Programs buttons in the Multi-EPG header
hard-coded English matTooltip strings, so they never localized — even
when the rest of the toolbar (Close, Previous/Next day) used keys. Adds
six new EPG.* keys (ZOOM_IN, ZOOM_OUT, FILTER_CHANNELS, CLOSE_FILTER,
SEARCH_PROGRAMS, CLOSE_PROGRAM_SEARCH), wires both [matTooltip] and
[attr.aria-label] through `| translate`, and fans the strings out to
all 17 non-English locales via the i18n-fill pipeline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:15:35 +02:00
4grayandClaude Opus 4.7 55215a5059 feat(radio): add tooltips + aria-labels on every player button
Radio audio-player buttons (skip-prev, play/pause FAB, skip-next, mute,
volume slider) had zero matTooltip or aria-label. The UX audit flagged
this: "Skip-prev / Skip-next — what do they skip? In radio, is the
next 'track' the next station? Next in favourites? Random? Without a
tooltip the buttons trade silently." Same critique applied to mute
and play/pause for screen-reader users.

Add hover tooltips and screen-reader labels on all 5 controls. Skip
prev/next say "Previous station" / "Next station" so the behaviour is
explicit (they walk the active filtered list). Play/Pause flips its
label with state. Mute/unmute flips when isMuted() || volume === 0.
Volume slider gets its own label so the slider thumb is announced
correctly.

i18n: 7 new AUDIO_PLAYER.* keys translated across all 17 non-English
locales by per-locale agents; all 18 locales pass coverage and
placeholder integrity checks.

52/52 ui-playback tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 09:03:14 +02:00
4grayandClaude Opus 4.7 86ec1685a5 chore(settings): drop uppercase eyebrow on rail title
After switching from mono to sans, the SETTINGS label still used
uppercase + 0.1em letter-spacing — an eyebrow treatment that doesn't
appear anywhere else in the workspace rail. The neighbouring items
("Dashboard", "Sources", "Global favorites") are all sentence case,
so the uppercase header stood out as a one-off.

Use sentence case "Settings" with the same typography family as the
rail items: same font, same case, small + muted so it still reads as
a quiet caption rather than competing with the clickable items below.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:05:14 +02:00
4grayandClaude Opus 4.7 231c22f7d4 chore(settings): switch SETTINGS eyebrow to inherited sans-serif
The v0.22 mockup uses JetBrains Mono for eyebrow labels as part of its
broader editorial design language. The previous commit ported that one
element verbatim, but the rest of the workspace shell never uses mono
for chrome — only for code-adjacent data (EPG time codes, kbd glyphs,
port numbers). So the SETTINGS rail title became a one-off mono label
in an otherwise all-sans shell.

Drop the `font-family: 'JetBrains Mono'…` declaration so the eyebrow
inherits DM Sans. Bump the weight from 500 → 600 to compensate for
mono's higher visual density. Uppercase + 0.1em letter-spacing + small
size are kept — those carry the eyebrow effect regardless of typeface.

If we ever want to port the broader mono-eyebrow system across rails
and chips, that's a separate (bigger) design decision.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:03:50 +02:00
4grayandClaude Opus 4.7 b246f8e51b fix(settings): drop dead bottom padding below the action bar
`--settings-action-bar-space: 80px` added padding-bottom to the form
*after* its last child (the action bar), giving 80px of scrollable
empty space below the bar — visible when scrolling to the end as a
big dark band the page kept scrolling past the last setting.

The reservation was originally tuned for the OLD floating-chip
layout, where the bar hovered 24px above the bottom edge and content
needed to be pushed above it. In the new sticky-footer model the bar
IS the bottom edge, so no padding is needed below it.

Set to 0px. CDP verifies form.bottom === bar.bottom after the change
— the scroll now ends cleanly at the action bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:02:15 +02:00
4grayandClaude Opus 4.7 27becf5b2a fix(settings): remove transient box-shadow pulse on scroll
The settings-section-scroll directive ran a 260ms Element.animate() on
whichever section became "active" as the user scrolled — three
keyframes painting an `inset 0 0 0 1px var(--settings-group-active-ring)`
ring plus a soft glow on the section block. With the new flat layout
(no card chrome, no static active ring), that pulse drew a brief 1px
border around each block as you scrolled past — the user-reported
"short border kind of highlight around blocks" effect.

The rail's left active state already announces which section you're on,
so the inline pulse is redundant. Removed the entire active-section
animation effect (the pendingScrollTarget scroll-into-view effect is
untouched — that's the click-the-nav-and-jump behaviour).

CDP verifies: all .settings-group elements report `box-shadow: none`
after scrolling. 50/50 settings tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 02:00:56 +02:00
4grayandClaude Opus 4.7 66f70b62c7 fix(settings): pin save bar flush to viewport bottom
`--settings-action-bar-offset: 24px` was tuned for the previous
floating-chip layout (border-radius:18px, margin-left:auto) so the bar
visually hovered above the bottom edge like a card. After converting
it to a full-width sticky footer the 24px gap just left a strip of
empty content peeking out below the bar — the user-reported "not
perfectly aligned to the footer" gap.

Set the offset to 0px so the bar sits flush against the bottom of the
scroll container. CDP verifies: barBottom === viewport height after
the change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 01:56:25 +02:00
4grayandClaude Opus 4.7 734ab3c580 refactor(settings): align look-and-feel with v0.22 mockup
The earlier Settings polish (kill triple header, compact theme picker,
sticky footer, hide playlist switcher) addressed structure. This round
addresses typography and chrome to bring the look in line with the
v0.22 mockup spec captured in redesign-screens-2.jsx.

Three changes:

1. Flat sections — no card chrome
   - Each .settings-group was a rounded panel (border-radius:22px,
     filled background, box-shadow, .settings-group--active ring +
     glow). Combined with the per-section icon-circle next to the
     title, every section looked like a feature card, not a settings
     region anchor. Strip all of it: the rail's active state on the
     left already announces "you are here".
   - Hide .settings-group__header-icon via display:none so the icon-
     circle markup in each section template stays intact while the
     visual chrome goes away — saves touching 6 templates for a
     CSS-only change.

2. Promote section titles to a large flat heading
   - Was h3 1.08rem 700 weight nested in the card chrome. Now h3
     1.5rem (24px) 600 weight with a 1px bottom-border separator,
     matching the mockup's 28px h1 + sub anchor pattern (24px is a
     reasonable density compromise for the denser app layout). Active-
     section variant keeps the blue title accent so users scrolling
     the right pane don't lose the anchor.
   - .setting-item drops the `margin: 0 18px` card inset so rows
     align flush with the section title — flat list rhythm.

3. Quiet mono eyebrow for the left nav title
   - .panel-title was a 20px 500 weight h2 ("Settings") competing for
     "biggest text on screen" against the section titles on the right.
     Switch to mono 11px uppercase 0.1em letter-spaced text-secondary
     — quiet rail header per the mockup. Now the section titles on
     the right are unambiguously the dominant heading.

Visual confirmation via agent-browser shows a much closer match to the
mockup: subtle SETTINGS eyebrow above the rail list, large flat
"General" anchor with bottom border, compact pickers, full-width sticky
footer.

50/50 settings tests still pass; build clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 01:41:42 +02:00