The fullscreen toggle used to slide both panels off screen while the video
frame grew into the freed space. Panels now stay at their home positions
as layers under the video, and the video frame grows over them to hide and
shrinks back to reveal. Both panels stay in the hierarchy while covered, so
scroll position and loaded content survive toggles; hit testing,
accessibility and the dismiss-pan exclusion frames are switched off while
covered (PanelResizeGrabber gets a publishesFrame flag for this).
Side column gestures (grabber overdrag, trailing edge swipe) drive the
video's trailing edge over the static column. The below panel's handle drag
keeps sliding the panel off the bottom, with the video growing in lockstep
so the edges coincide. The below panel is raised above the video only while
expanding over it, and drops back under it after the collapse spring has
fully settled.
Hiding the status bar in the fullscreen state changed the top safe area and
reflowed the whole layout mid-transition. The layout now ignores the top
safe area and pads by a fixed status bar height (remembered by
StatusBarVisibilityController while the bar is visible); the fullscreen
video grows up into that strip as part of the same animation.
Overdragging the side column's resize grabber past its minimum width now
slides the column off the trailing edge (the video grows with it), and a
swipe in from the trailing screen edge brings it back following the
finger. The seek pan skips that edge strip while the column is
collapsed. Showing the panel again restores every region hidden by its
own drag handle.
Swiping the below-video panel back up right after hiding it showed the
video clipped to a short centered box that sprang back to full size.
The hide flipped to the hidden layout in a `.logicallyComplete`
completion while the slide spring was still registered on the SwiftUI
host of the mpv render view; a reveal drag interrupting it made the
release spring start from a bogus shrunken rect. All slide-off
completions now wait for `.removed` and bail if a new drag took over.
The visibility-keyed `.animation` modifiers key on a toggle generation
bumped only by the animated show/hide paths, so drag flips never start
the toggle easing. The partnerless `matchedGeometryEffect` on the
widescreen video frame is dropped.
The corner layout side column puts a 12pt resize grabber strip before the
panel, and every page then added the standard 16pt horizontal padding, so
content sat ~28pt from the left edge but 16pt from the right.
Add a PanelHorizontalInsets environment value with a panelHorizontalPadding()
modifier that the info, comments, related and chapters pages use instead of
.padding(.horizontal). The side column trims the leading inset by the grabber
width so content is 16pt from the column's outer edge on both sides; other
hosts keep the defaults. Queue and tracks are List-backed and get the same
leading delta as a scroll content margin. The page bar follows the same insets
so the first pill lines up with the content.
Section headers in the inline info panel now toggle their content with a
rotating chevron. Collapsed state resets when the video changes. Also let
the collapsed description text take full width.
The below-video panel now renders only when the info page is assigned
to that region; comments assigned below become the portrait panel's
pill (iOS), and every other below-video page falls back to the side
column instead of disappearing. Page selection snaps to the first
configured page when the current one is disabled or reordered away,
and the page bar highlight follows the actually rendered page. The
related tab stays visible with a spinner while video details load
instead of vanishing until they arrive.
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.
Starting a column resize drag with a diagonal (downward) motion let the
window-level dismiss pan begin, cancelling the resize and dragging the
player down instead. The grabber now publishes its strip frame (with 8pt
slop on each side) to NavigationCoordinator, and the dismiss pan refuses
to begin from touches inside it — same exclusion pattern as the corner
layout's below-video panel frame.
The corner layout now always renders the info page as a portrait-style
panel below the video (collapsible description, drag to hide/expand),
while the side column holds tabs for the remaining pages. Key pieces:
- Below-video panel reuses PortraitDetailsPanel (no comments pill,
square corners, chapters/related stay in the side column tabs)
- Drag down hides the panel with the video recentering in lockstep;
swipe up on the video reveals it following the finger
- Fullscreen button toggles the panels in corner layout (controls hide
first so their resize never plays visibly); status bar and home
indicator hide only while the panels are hidden
- Panel visibility and column width are seeded at init from a warmed
PlayerControlsLayout cache, so the first frame resolves corner mode
without flashing the default layout or animating the panels in
- Dismiss pan is blocked inside the below-video panel frame
(cornerBelowPanelFrame in NavigationCoordinator)
- Resize grabber slimmed to match the panel drag handle look
Hide the page bar in the portrait details panel and fold chapters and
related videos into the info page as inline sections below a collapsible
description. The description clamps to three lines with an inline
baseline-aligned "more" fading into the last line; the whole collapsed
area is tappable to expand. The comments pill keeps its previous
behavior (teaser row, collapsing to a button on scroll) and opens the
comments overlay.
Phase 5 of the player panels redesign:
- Per-page element toggles now rendered and editable for every page:
comments (author avatars), related (thumbnails), chapters (thumbnails),
queue (previously played section), tracks (video/audio/subtitles/speed
sections)
- Page bar labels toggle (icons-only bar shows the selected page's name)
- Layout mode schematic diagram in the details panel settings
- NEW badge machinery for future page types (versionAdded on PanelPageType)
- Typed per-page settings accessors on PanelPageConfiguration and VM update
helpers for each page kind
- Unit tests for per-page settings round-trip and accessor fallbacks
The standalone quality selector sheet is unaffected: section gating defaults
to everything visible and only the embedded tracks page passes preset
settings through.
Phase 1 of the player panels redesign:
- New PanelSettings model (pages, regions, layout mode, default
visibility) stored as an optional field on PlayerControlsLayout with
an effectivePanelSettings accessor; lenient Codable so future page
types and settings payloads never corrupt saved presets.
- New Views/Player/Panels content engine: PlayerPanelContentView
renders a page bar plus the selected page (info, comments, related,
chapters, queue, tracks), with runtime hiding of empty pages.
- PortraitDetailsPanel and FloatingDetailsPanel now host the shared
content instead of their duplicated info bodies; drag machinery,
pills, and fades are untouched.
- Comments list extracted into CommentsListContent shared by the
overlay and the comments page; comments pill routes to the comments
page (expanding the portrait panel) when the page is enabled, and
falls back to the overlay when it is not.
- Tracks page embeds QualitySelectorView via a new isEmbedded mode.
- Deleted the unused video info bodies in ExpandedPlayerSheet+VideoInfo
and the dead PlayerInfoTab model.
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.