Each video row carried its own sheets: tappableVideo added the queue
action and resume sheets, the context menu and swipe actions added
add-to-playlist and download options, so a row could declare up to six
sheets, owned by a row that a list may recycle.
Rows now present those through the nearest sheet host, as does the
video info view's resume prompt. The queue action sheet for a tapped
description link becomes a global request instead of being declared
at both the app root and the expanded player.
Sheets take the color scheme where their host sits, so the floating
details panel's dark override no longer reaches them and the
presentationColorScheme workaround is gone.
The unpinned floating details panel forces a dark color scheme on its
content so it reads over the video. The play now / play next and resume
sheets opened from a related video inherited that scheme, giving white
text on the light system sheet background.
The panel now publishes the real system scheme via a
presentationColorScheme environment value and the sheets reapply it.
With "Partially Watched Videos" set to Ask, opening a live stream showed
the resume sheet with a playback timestamp. A live position is relative
to the live edge, not a fixed timeline, so it is meaningless as a
playhead.
Live views are still recorded in history, but carry no resume position:
- WatchEntry gains a persisted isLive flag plus recordLiveWatch(), which
stores no position and clears state a drifting HLS duration may have
left behind. progress is always 0 and toVideo() propagates the flag so
history rows render the LIVE badge.
- from(video:) clamps duration, keeping Piped's -1 live sentinel out of
storage.
- DataManager routes live saves through recordLiveWatch() and clears the
flag on the non-live path (before updateProgress, so the auto-finish
check is not suppressed by the hard-0 live progress), letting an entry
heal once the stream becomes a VOD.
- PlayerService skips completion saves, always starts at the live edge,
and drops any startTime passed for a live video.
- The resume prompt, the "Continue at" button label, thumbnail progress
bars, "X remaining", Continue Watching and tvOS Top Shelf all exclude
live entries.
- CloudKit syncs isLive with the rest of the watch state: the conflict
resolver carries it with watchedSeconds/isFinished and the sync engine
applies it when merging into an existing entry. It is read leniently,
so the schema version stays at 2 and older clients keep parsing these
records.