Tapping a related video kept the panel scrolled where it was, so the new
title was out of view, and the chapters section kept showing the previous
video's chapters until the new ones resolved.
The scroll config now carries the video identity and the shared page
scroll view scrolls to the top (without animation, resetting the reported
offset) whenever it changes. PlayerState.setCurrentVideo clears chapters
on a video id change, alongside comments and storyboards.
Chapter lists highlighted the active row by reading currentTime in the
view body, re-rendering every row many times per second during
playback. PlayerState now stores a change-guarded activeChapterID
updated when time or chapters change, so views observe only chapter
transitions.
Phase 0 of the panels redesign, extraction only, no behavior changes:
- Unify comments loading on PlayerState: loadComments/loadMoreComments
become the single loader, used by both the player sheet preload and
the comments overlay (which previously had its own duplicate copy).
- Extract PanelResizeGrabber from FloatingDetailsPanel into the new
Views/Player/Panels/ directory.
- Extract the shared queue list from QueueManagementSheet into
QueueListView plus QueuePlaybackActions, reused by the tvOS panel.
- Remove the no-op wideLayoutPanelAlignment accessor from the settings
view model; the player reads the panel side from SettingsManager, so
the preset field was dead. The Codable field stays so old presets
decode.
PlaybackRate.displayText/compactDisplayText used String(format: "%.2gx"), whose significant-figure notation dropped meaningful digits (1.25 -> "1.2x") and rounded others (1.75 -> "1.8x"). Replace with a fixed two-decimal format + trailing-zero strip so whole rates have no decimal (2 -> "2x"), halves show one (1.5 -> "1.5x"), and quarter-steps are preserved (1.25 -> "1.25x"). Adds a Swift Testing suite (PlaybackRateTests) proving red->green.
- Move Close to a top-right circular icon; bottom row reorganizes into
left (Settings/Info/Comments), center transport (Previous/PlayPause/Next),
and right (Queue) clusters with equal side frames so transport stays
geometrically centered.
- Introduce a circular icon-only `TVTransportButtonStyle` (primary variant
for Play/Pause) mirroring the new Close button look.
- Always render Previous/Next so Play/Pause position is fixed; dim and
disable when unavailable.
- Share `isTransportDisabled` on `PlayerState` and reuse it on iOS and
tvOS; apply it (plus symbol replace transition) to the tvOS Play/Pause
button.
Extract shared TimeInterval.formattedAsTimestamp replacing 8 identical
formatTime/formattedTime implementations across player views. Remove
unused currentTime parameter from GestureSeekPreviewView. Consolidate
duplicated geometry math in MacOSControlBar into seekPreviewPosition().
- Fix BGTaskScheduler assertion crash on Mac Catalyst by guarding all
iOS background task APIs with isMacCatalystApp check
- Fix iPad popover crash in UIPopoverPresentationController by adding
.presentationCompactAdaptation(.sheet) to all 27 confirmationDialogs
- Fix SwiftData assertion crash when accessing deleted Bookmark model
properties during SwiftUI hit testing in BookmarkRowView
- Fix UICollectionView invalid item count crash on queue swipe-to-delete
by using ID-based removal with withAnimation instead of stale index
- Fix Range crash in storyboard download when storyboardCount is zero
Adds a "Proxy videos" toggle in instance settings that routes video
streams through the instance instead of connecting directly to YouTube
CDN. Includes auto-detection of 403 blocks and live re-application of
proxy settings without requiring app restart or video reload.