Commit Graph
342 Commits
Author SHA1 Message Date
4grayandClaude Opus 4.7 e33cb70dec perf(database): add SQLite performance pragmas (synchronous, cache, mmap)
Pair the existing journal_mode=WAL with the companion pragmas it needs to
actually pay off, and reduce read latency on hot query paths.

- synchronous=NORMAL: ~2-10x faster writes when paired with WAL. Safe — only
  risks losing the last committed txn on power loss; DB stays consistent.
- cache_size=-64000: 64MB page cache (default is 2MB).
- temp_store=MEMORY: keep sort/group temp tables in RAM.
- mmap_size=268435456: 256MB memory-mapped I/O for reads.

Applied to both connection sites: the main read-write/read-only connection
in libs/shared/database and the EPG worker connection in electron-backend.
The per-connection pragmas (cache_size, temp_store, mmap_size) apply to the
read-only agent-backend connection too.

Inspired by matracey/iptvnator@4ad8f88; ported manually since the file has
diverged significantly and the worker connection didn't exist in that fork.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3d6901072049
2026-05-01 21:02:52 +02:00
4gray 1af287205f perf: optimize playlist favorite lookups
Use Map and Set lookups for M3U favorite aggregation, dashboard global favorite mapping, and channel-list favorite/group resolution.

Remove the duplicate NgxIndexedDB provider registration and add regression coverage for duplicate URL and earliest-match behavior.

Entire-Checkpoint: c2cea9c530e6
2026-05-01 20:44:30 +02:00
4gray 6ef9e8c4b0 fix(web): lazy load route feature boundaries
Entire-Checkpoint: c2cea9c530e6
2026-05-01 20:38:14 +02:00
4grayandClaude Opus 4.7 d24c67c6e8 test(workspace-shell): repair stale component-spec mocks and cover new overlay child
The two component specs in this lib had been carrying 7 long-standing
failures rooted in three stale-mock issues, all unrelated to public
behaviour but enough to make the suite red on every run.

Header spec (5 failures, NG0303 on app-playlist-switcher):
- MockPlaylistSwitcherComponent was missing canRefreshActivePlaylist /
  isRefreshingActivePlaylist inputs and refreshPlaylistRequested
  output. The real PlaylistSwitcherComponent grew them when refresh
  was added to the header; the mock never caught up. Re-aligned.

Workspace-shell component spec (2 failures):
- NG0301 on #dropZone="playlistDropZone" because overrideComponent's
  set.imports list dropped PlaylistDropZoneDirective and the drop
  overlay component. Added MockPlaylistDropZoneDirective and
  MockPlaylistDropOverlayComponent.
- After the recent option-D refactor that lifted the import overlay
  into its own child component, the spec also needed
  MockWorkspaceShellImportOverlayComponent so the parent template
  could resolve <app-workspace-shell-import-overlay />.
- MockWorkspaceShellHeaderComponent gained hasNoPlaylists input and
  MockWorkspaceShellContextSidebarComponent gained hasPlaylists input
  to match the real components.
- The "renders type-aware xtream import progress copy" test asserted
  on inline overlay markup that no longer lives in the parent
  template. Replaced with a structural test that the overlay child
  renders/unmounts based on facade.showXtreamImportOverlay().

New focused spec for WorkspaceShellImportOverlayComponent (6 tests)
covers the moved progress-copy assertion at the right level by mocking
the WorkspaceShellXtreamImportService directly: title, progress copy,
determinate vs indeterminate progress bar, badge tone, and cancel
forwarding.

Final state: full feature lib suite is 66/66 (was 53/60 with 7
chronic failures). pnpm nx test web, pnpm nx lint
workspace-shell-feature, pnpm nx build web --configuration=production,
and the basic + settings web-e2e suites all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-05-01 19:34:45 +02:00
4grayandClaude Opus 4.7 63f5173139 refactor(workspace-shell): push xtream import overlay and bulk action into child components
Continues the workspace-shell facade decomposition by relocating two
clusters from the parent template into the components that own them.

Xtream import overlay:
- New WorkspaceShellImportOverlayComponent injects
  WorkspaceShellXtreamImportService directly. The 70-line inline
  overlay block in workspace-shell.component.html collapses to a
  single <app-workspace-shell-import-overlay /> element, gated by
  facade.showXtreamImportOverlay().
- Overlay-specific styles move from workspace-shell.component.scss
  into the child component (~125 lines relocated).
- Facade drops 14 import re-export lines plus the cancelXtreamImport
  delegation. The two facade tests that exercised label generation
  now resolve the service via TestBed.inject and assert against it
  directly.

Header bulk action:
- WorkspaceShellHeaderComponent gains headerBulkAction input and
  headerBulkActionRequested output, plus a button that renders the
  action when present (next to the existing headerShortcut button).
- The parent template wires facade.headerBulkAction() →
  facade.runHeaderBulkAction(). The facade keeps the cross-cutting
  computed and the multi-store dispatch method since both span
  multiple stores.

Verification:
- workspace-shell.facade.spec.ts: 20/20 passing (two assertions
  retargeted at WorkspaceShellXtreamImportService).
- Full feature lib: 53/60 passing — same 7 pre-existing failures.
- pnpm nx build web --configuration=production: success.
- pnpm nx lint workspace-shell-feature: clean.
- pnpm nx run web-e2e:e2e-ci--src/basic.e2e.ts: 3/3 across browsers.
- pnpm nx run web-e2e:e2e-ci--src/settings.e2e.ts: 12/12 across
  browsers.

Facade: 917 → 897 lines. The bulk-action button now actually renders
in the header — closing a latent gap where the computed existed on
the facade but had no template binding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-05-01 19:12:19 +02:00
4grayandClaude Opus 4.7 0ad54e7939 fix(workspace-shell): accept readonly viewCommands in palette builder
Production build (stricter than test/dev) flagged the helper signature
as requiring a mutable array while WorkspaceViewCommandService.commands
returns readonly. The body uses spread, so widening the parameter to
readonly is the correct fix and unblocks `nx build web` plus E2E.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-05-01 18:59:24 +02:00
4grayandClaude Opus 4.7 d1cd805622 refactor(workspace-shell): split 1666-line facade into helpers and sub-services
The WorkspaceShellFacade had grown to 1666 lines, well past the 300/400
LOC target in CLAUDE.md. The component template binds to 54 facade
members directly, so the split preserves the public API by reference
re-export and keeps the template untouched.

Phase 1 — extract pure helpers (no DI, no behavior change):
  - helpers/workspace-shell-constants.ts: i18n keys, RAIL_TOOLTIP_KEYS,
    XtreamImportPhaseTone, WorkspaceHeaderBulkAction
  - helpers/workspace-shell-route-utils.ts: toQueryString,
    getRouteQueryParam, syncSearchQueryParam, bumpRefreshQueryParam,
    getProviderFromPlaylist
  - helpers/workspace-shell-search-labels.ts: resolveSearchPlaceholderKey,
    resolveSearchScopeLabel, resolveActiveCategoryLabel, getRailTooltipKey,
    translateRailLinks (each takes TranslateFn + snapshots as params)
  - helpers/workspace-shell-import-labels.ts: build* helpers for the 14
    Xtream import overlay labels and formatLocalizedNumber
  - helpers/workspace-shell-command-builders.ts: buildCommandPaletteItems
    plus the three command-definition builders, navigation command
    factory, resolveCommand, comparePaletteCommands, getCommandGroupOrder

Phase 2 — extract the two component-scoped sub-services with the
cleanest boundaries:
  - WorkspaceShellXtreamImportService: read-only labels, cancel action,
    isImportRunning. Reads only XtreamStore + TranslateService.
  - WorkspaceShellCommandPaletteService: MatDialog lifecycle, recent
    commands integration, eager WorkspacePlayerCommandsContributor
    bootstrap. Receives a typed CommandBuilderContext via function
    argument from the parent — no circular DI.

The parent facade re-exposes every member by reference assignment so
signal identity is preserved for change detection. workspace-shell.
component.html is unchanged. workspace-shell.component.ts gains only
two provider entries for the new sub-services.

Verification:
  - workspace-shell.facade.spec.ts: 20/20 passing
  - pnpm nx lint workspace-shell-feature: clean
  - workspace-shell-feature suite: 53/60 passing — same 7 pre-existing
    failures (header & shell component specs hit unrelated NG0301
    on playlistDropZone export); no regressions introduced
  - CDP smoke: command palette opens with Recently Used + Global
    Actions groups, settings nav disables search bar correctly

Facade reduces from 1666 to 917 lines. Each new helper/service is
under 250 lines except command-builders.ts (462) which is mostly
declarative command definitions. The parent facade is still over the
400-line hard max because cross-cutting orchestration
(searchCapability, runHeaderBulkAction, rail link computeds) reads
from multiple clusters; further reduction would require moving logic
into child components, which is deferred per the plan.

Plan: ~/.claude/plans/workspace-shell-facade-ts-is-now-1668-crispy-planet.md
(also copied locally to .plans/2026-05-01-workspace-shell-facade-split.md;
the working-tree .gitignore change excludes .plans from tracking).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-05-01 18:48:53 +02:00
4grayandClaude Opus 4.7 2f47ed16cb feat(command-palette): add recently used section and switch-player commands
Adds a "Recently used" section at the top of the command palette (capped at 5,
persisted via StorageMap) and five "Switch player to ..." commands for one-step
player switching from anywhere. MPV/VLC are gated to Electron; the active player
entry is shown disabled. Switching applies to the next playback session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-04-30 23:54:02 +02:00
4gray 290843ef3f fix: restore recent live favorite toggles
Entire-Checkpoint: c2cea9c530e6
2026-04-30 21:35:39 +02:00
4gray b5a85fdf5f style: add shared count-badge style and update component usage
Entire-Checkpoint: c2cea9c530e6
2026-04-30 20:32:55 +02:00
4gray 4b2076fa53 feat(settings): add cover size settings and implement responsive cover sizing
Entire-Checkpoint: c2cea9c530e6
2026-04-30 18:56:06 +02:00
4gray cbedb22698 refactor: enhance live stream layout with loading state and caching
- Added `XtreamCachedContentScope` type to manage content loading states.
- Updated live stream layout to show a loading skeleton when content is being fetched.
- Introduced styles for loading content in the live stream layout.
- Enhanced unit tests to cover loading states and caching behavior.
- Modified `XtreamWorkspaceRouteSession` to handle cached content loading and state management.
- Improved handling of optional connection values in playlist data.
- Updated workspace context panel to reflect import states and loading conditions.

Entire-Checkpoint: c2cea9c530e6
2026-04-30 18:54:58 +02:00
4gray a6e886116c style(dialog): increase dialog width from 300px to 400px for better visibility
Entire-Checkpoint: c2cea9c530e6
2026-04-30 13:19:38 +02:00
4gray 20445beaf0 fix(downloads): show loading skeletons before empty states
Entire-Checkpoint: c2cea9c530e6
2026-04-29 06:47:31 +02:00
4grayandClaude Opus 4.7 f7c6cb3572 refactor(unified-collection): drop redundant player toolbar, sharpen header divider
The "channel name + close" bar above the right-pane player was redundant
with the active row in the left list (which already shows the title and
the selection chrome) and was inconsistent with the rest of the app's
live-tv views, which never had it. Closing playback was already
achievable by selecting another channel; the X added cognitive load
without earning its space (ux-similarity-consistency,
ux-cognitive-load-reduce).

Also swap the collection-header border-bottom from
--mat-sys-outline-variant to --app-separator so the divider under
"Favorites" / "Recently viewed" actually carries enough contrast to
read in dark mode (matching the live-epg-panel summary-bar pattern).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c2cea9c530e6
2026-04-29 01:26:17 +02:00
4gray 0babd92304 feat: add live epg panel to collections
Entire-Checkpoint: c2cea9c530e6
2026-04-29 01:23:42 +02:00
4grayandClaude Opus 4.7 2db99769f9 fix(scrollbar): make .app-scrollbar override component-scoped overflow
Two issues kept the scrollbar invisible on the global Favorites and
Recently viewed live-tv lists:

1. The wrong files. The visible Favorites/Recent pages render through
   <app-global-favorites-list> from portal/shared/ui (and the
   workspace-shell variant), not the channel-list-container's tabs.
   Apply .app-scrollbar to both of those components.

2. Specificity. Component CSS gets an Angular [_ngcontent-xxx]
   attribute selector, so `.nav-list { overflow-y: auto }` resolves to
   (0,2,0) — same as the doubled `.app-scrollbar.app-scrollbar` and
   wins on source order (component CSS loads after globals). Mark
   overflow-y: scroll !important on the utility so the contract holds:
   applying .app-scrollbar always reserves the gutter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 8ae011808369
2026-04-29 01:13:58 +02:00
4grayandClaude Opus 4.7 6c0c04bcba fix(scrollbar): force overflow-y: scroll in .app-scrollbar utility
The favorites and recent views had local #id { overflow-y: auto }
which has higher CSS specificity than the global .app-scrollbar class.
On macOS, overflow-y: auto means overlay scrollbars that auto-fade,
so the gutter and themed thumb were never reserved.

Bake overflow-y: scroll into the utility itself (its whole purpose) and
drop the now-redundant overflow-y: auto from the two local rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b2d2124cd992
2026-04-29 01:06:56 +02:00
4grayandClaude Opus 4.7 5ab6736613 refactor(channels): apply .app-scrollbar to favorites and recent views
Round out the channel-list-container views so the favorites and recent
panels get the same persistent scrollbar gutter and themed thumb as the
all-channels and groups views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2301533420c9
2026-04-29 01:03:01 +02:00
4grayandClaude Opus 4.7 59beb324b4 refactor(styles): extract .app-scrollbar utility, apply to channel lists
Move the always-visible themed scrollbar from epg-list into a global
.app-scrollbar utility class in styles.scss. Apply it to the All
channels and Groups view virtual-scroll viewports so the channel list
gets the same persistent gutter and themed thumb as the EPG list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c470abf78379
2026-04-29 01:00:42 +02:00
4grayandClaude Opus 4.7 465fdbfb80 fix(epg): always-visible scrollbar in program list
Switch to overflow-y: scroll with scrollbar-gutter: stable so the gutter
is reserved (no layout shift between scrollable and non-scrollable
days). Locally style the thumb with --app-muted-color so it's clearly
visible across themes and platforms, overriding the global 6px
near-transparent style which was being hidden by the macOS overlay
auto-fade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 918ee7ade2f3
2026-04-29 00:55:48 +02:00
4grayandClaude Opus 4.7 7841b3b4e8 refactor(epg): hairline dividers, en-dash time, single-line description
Separation: replace 4px gap with hairline dividers via ::before, hidden
around the active and current-program rows so they read as a continuous
emphasized block. Title line-height 1.4 → 1.3 + letter-spacing -0.01em
to match channel-name. Time uses en-dash, 0.78rem (parity with summary
bar), token-driven --app-muted-color. Description clamped to one line —
full text remains one click away via the info icon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: b0389ce441a1
2026-04-29 00:52:29 +02:00
4grayandClaude Opus 4.7 db0e2b658f refactor(epg): align program-item styling with channel-list aesthetic
Drop the gradient + bevel + drop-shadow chrome from EPG rows so the panel
speaks the same flat-by-default visual language as the channel list. Add
--app-live-color token, tokenise the LIVE badge, tighten typography
(weight 600, text-wrap balance/pretty), and bump the info icon to 32px
for Fitts-compliant hit area.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 4cc5371f549e
2026-04-29 00:43:45 +02:00
4gray 31258624b5 feat: unify live epg toolbar
Entire-Checkpoint: 978eaff2e478
2026-04-29 00:20:34 +02:00
4grayandClaude Opus 4.7 d163a1fbf2 feat(dashboard): contained logo + blurred backdrop for live channels
Render live TV channel logos with object-fit: contain on a soft blurred
wash sampled from the logo itself, instead of stretching small (100-256px)
logos to fill 2:3 portrait cards with object-fit: cover. Mirrors the
treatment used by Apple Podcasts and Plex Live TV. VOD/series posters
still get the full-bleed cover treatment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 6bb6f5796090
2026-04-28 20:14:04 +02:00
4gray 51347fc226 feat: add collapsible live epg panel
Entire-Checkpoint: 0518c49b948d
2026-04-28 19:56:41 +02:00
4gray 10139955e7 fix(stalker): lazy load live tv channels
Entire-Checkpoint: c6e522b4276c
2026-04-27 00:25:56 +02:00
4gray 313ff7a24f fix(command-palette): enhance command palette management with dialog reference
Entire-Checkpoint: c6e522b4276c
2026-04-26 01:06:36 +02:00
4gray f0df67b5b4 test: add missing config
Entire-Checkpoint: c6e522b4276c
2026-04-26 00:31:23 +02:00
4gray d0f7dcae78 refactor(stalker-live-stream-layout): fix streamUrl usage and enhance web player view component
Entire-Checkpoint: c6e522b4276c
2026-04-26 00:01:40 +02:00
4gray 4bc733d9c2 refactor(external-playback): update styles
Entire-Checkpoint: c6e522b4276c
2026-04-25 23:48:21 +02:00
4gray ce7ae1a251 refactor(external-playback): add translation support for close player button and update UI styles
Entire-Checkpoint: c6e522b4276c
2026-04-25 23:43:22 +02:00
4gray 4616df5d16 refactor(playlist-import): update file upload handling and emit imported event
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:32:08 +02:00
4gray b1d519183f style(empty-state): enhance empty state component with new types and responsive design
- Expanded EmptyStateType to include 'welcome-dashboard', 'welcome-sources', 'no-data'.
- Introduced FeatureCard and SourceCard interfaces for better structure.
- Added FEATURE_CARDS and SOURCE_CARDS constants for feature and source card configurations.
- Updated empty-state.component.ts to handle new types and actions.
- Created responsive styles in empty-state.responsive.scss for better mobile support.
- Added theme styles in empty-state.themes.scss for light mode.
- Developed specific styles for welcome dashboard and sources in empty-state.welcome-dashboard.scss and empty-state.welcome-sources.scss.
- Updated recent-playlists.component.html to utilize new empty state types.
- Modified recent-playlists.component.ts to support new playlist actions.
- Introduced PlaylistFileImportService for handling playlist file imports.
- Updated downloads.component.html and downloads.component.ts to integrate new empty state handling.
- Enhanced unified-collection-page.component.html and unified-collection-page.component.ts to utilize empty state for no results.

Entire-Checkpoint: c6e522b4276c
2026-04-25 22:18:13 +02:00
4gray 74c8adc12e fix(stalker): series favorites handling
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:16:33 +02:00
4gray 6eb602d332 feat(playlist-switcher): add refresh button for active playlist with spinning icon
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:14:20 +02:00
4gray 92f21bd237 feat(workspace): enhance dashboard and sources components with playlist management and UI improvements
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:11:31 +02:00
4gray caed2ce396 feat(playlist-drop-overlay): add component and directive for file drop overlay functionality
Entire-Checkpoint: c6e522b4276c
2026-04-25 22:08:03 +02:00
4gray a76f09a8ce style(dashboard): enhance hero artwork handling with fallback logic and image failure management
Entire-Checkpoint: c6e522b4276c
2026-04-25 15:11:25 +02:00
4gray a2e2959b89 fix(xtream): pagination behavior (back & scroll)
Entire-Checkpoint: c6e522b4276c
2026-04-25 14:11:36 +02:00
4gray cd111b6c36 fix(stalker): enhance pagination handling and add search phrase management
Entire-Checkpoint: c6e522b4276c
2026-04-25 14:09:59 +02:00
4gray efa0385fc8 feat(dashboard): implement context menu with actions for recent sources 2026-04-24 14:27:04 +02:00
4gray 4b4d0abfab test: refactor electron window handling in PlaylistsService tests 2026-04-22 22:33:06 +02:00
4gray 7caa18308d feat: enhance unified collection with workspace commands and backdrop handling
- Added WorkspaceViewCommandService to UnifiedCollectionPageComponent for workspace layout commands.
- Implemented clear current view command for favorites and recent items.
- Updated unified favorites data service to use buildXtreamCollectionUid for UID generation.
- Enhanced unified recent data service to normalize timestamps and handle backdrop URLs.
- Introduced withRecentItems feature to manage recent item additions with backdrop support.
- Updated VodDetailsRouteComponent and SerialDetailsComponent to backfill content backdrops.
- Modified IXtreamDataSource interface to include backdrop URL handling for favorites and recent items.
- Added tests for recent items feature to ensure correct functionality and backdrop handling.
2026-04-22 22:23:50 +02:00
4gray 6904b81c47 feat: implement grid rating formatting and resolution functions 2026-04-22 22:23:30 +02:00
4gray 199ff91fcc feat: enhance recently added component with skeleton loading states and layout improvements 2026-04-22 22:23:12 +02:00
4gray 8d6a387a9f feat: add workspace view command service and related types 2026-04-22 22:22:22 +02:00
4gray a5b26065c9 feat: implement SQLite UTC timestamp normalization in stalker-item normalizer 2026-04-22 22:21:29 +02:00
4gray ad528238b2 feat: add backdrop_url to content schema and related database operations 2026-04-22 22:20:48 +02:00
4gray 0e77f7b98a refactor: rework dashboard view
- Removed global favorites related properties and methods from WorkspaceShellHeaderComponent.
- Updated WorkspaceShellFacade to streamline command palette commands and navigation.
- Refactored command palette to include type-aware xtream import progress labels.
- Enhanced WorkspaceShellComponent to display import progress with improved styling.
- Updated tests to reflect changes in command palette and import progress handling.
2026-04-22 22:19:42 +02:00