Throttle SELECT-based scrubbing to seek the underlying frame ~every
150ms instead of waiting 1s after pan-end, so the visible frame keeps
up with the scrub handle. Hide the redundant storyboard panel during
live scrub (the frame itself is now the preview) but keep the chapter
capsule visible. Storyboard panel still shown for D-pad arrow-seek
where the frame doesn't move until commit.
Auto-commit scrub mode after 3s of inactivity, matching
AVPlayerViewController behavior — playback resumes via the existing
scrub-pause wiring instead of staying paused indefinitely.
Route the on-screen play/pause button through handlePlayPause() so it
follows the same visibility and auto-hide timer logic as the Siri Remote
hardware button: timer stops when paused (controls stay pinned) and
restarts on resume.
These views rendered video thumbnails without passing watchProgress, so the
progress bar was silently missing. Apply the existing pattern from
SubscriptionsView: maintain a watchEntriesMap and forward watchProgress(for:)
to VideoRowView/VideoCardView at each call site.
tvOS's sidebarAdaptable TabView leaves the previously-pushed detail view
visible after the user picks another sidebar item, until they manually
press Menu. Broadcast a notification on tab change so any pushed
TVSidebarDetailContainer dismisses itself, and reset each tab's
NavigationPath. Also drop a redundant inner NavigationStack in the tvOS
SettingsView so subpages register on the tab's outer stack.
Toast cards now follow the finger upward and dismiss on either a
sufficient drag or a fast flick (via predicted-end translation). The
auto-dismiss timer pauses while the user is dragging and re-arms if
they release without dismissing.
Previously a failed video left the user staring at a black screen / thumbnail
with no indication anything went wrong — playbackState went to .failed but
TVPlayerView never read it. Add a focusable glass overlay (Details / Retry /
Play Next or Close) gated on isFailed and a parallel one for retry-exhausted
state, with the regular controls and background tap-target disabled while
either is visible so focus stays inside the overlay. Hide the Copy/Share
toolbar items in ErrorDetailsSheet on tvOS where they aren't useful.
Drop comments restating what the code shows; hoist allowSoftwareDecodedFormats
out of the recommendedVideoStreams filter closure so the bridge property is
read once per render instead of once per stream.
Lets the auto stream selector pick formats whose codec isn't hardware
decoded on the current device. Defaults off; when on, 4K VP9/AV1 can be
auto-selected on Apple TV models without those decoders. Software-decoded
streams also move into the Recommended section so the selection stays
visible without enabling advanced stream details.
Mirrors the existing iOS/macOS option using the shared
subscriptionsShowSidebar AppStorage key. When the sidebar is hidden
the View Options button moves above the feed so it remains reachable.
The horizontal channel chip strip looks awkward on tvOS and the focus
interaction is clunky. Drop it from the tvOS branch of InstanceBrowseView;
other platforms keep it.
The Siri Remote's left/right d-pad only delivered a single discrete
seek per click — holding the button did nothing. A window-level custom
UIGestureRecognizer now tracks the actual press duration and drives a
repeating seek tick (10s → 20s → 30s acceleration) until release,
routing through the existing accumulating-seek paths so the scrubber
preview, debounced commit, and on-screen feedback all keep working.
Settings → Notifications → Manage Channels: wrap the tvOS NavigationLink
destination in TVSidebarDetailContainer(showsDismissButton: true) so the
no-subscriptions, error, and loading states all have a focusable Done.
Channels sidebar tab: lift the tvOS search field + View Options button
out of the loaded-channels branch and render it above every state. The
empty state previously had zero focusable elements, leaving the right
pane blank when swiping in from the sidebar.
TVSidebarDetailContainer now exposes a showsDismissButton flag instead of
always attaching a Done toolbar item. The button is only enabled where a
view can end up with no focusable element on its own — Device
Capabilities (informational rows) and the Import Playlists/Subscriptions
flows.
Wrap Contributors, Translators, Acknowledgements, and Device Capabilities
destinations in TVSidebarDetailContainer for the consistent sidebar look,
and make the Translators/Acknowledgements rows focusable on tvOS by
wrapping them in Buttons so the Menu remote button can pop the stack.
When all playlists/subscriptions were imported, every row collapsed to a
non-focusable checkmark and the Add All toolbar item disappeared, leaving
the view with no focusable element. The Menu button then closed the app
instead of popping the navigation stack.
Wrap the import destinations in TVSidebarDetailContainer for visual
consistency and add a Done toolbar item (cancellationAction) that is
always present on tvOS, reachable from any list row via swipe-up.
The .sheet rendering on tvOS produced a tiny floating modal where the
"Sign In" title wrapped onto two lines and form fields overflowed. Use
.fullScreenCover on tvOS and wrap the login form in
TVSidebarDetailContainer so the title/icon sit in the standard 400pt
left sidebar. iOS and macOS keep the existing sheet presentation.
Piped's session token reuses the Authorization header, so a fronting basic
auth proxy can't coexist with logged-in Piped use — the two would clobber
each other's credentials on every authenticated request.
Add a supportsHTTPBasicAuthProxy capability on Instance/InstanceType (false
for Piped, true for everything else) and route it through:
- AddRemoteServerView refuses Piped if detection only succeeded behind basic
auth, surfacing a localized "not supported" error instead of a silently
broken instance, and hides the optional credentials section for Piped.
- EditSourceView hides the basic auth fields for Piped instances and clears
any legacy stored credentials on save, in case a Piped source was added
with credentials before this change.
Color.accentColor and .foregroundStyle(.tint) resolve to the asset
catalog accent on macOS, so Home shortcut cards, section header
links, the Subscriptions "All channels" header, and the Downloads
per-channel group headers stayed blue when the user picked a
different accent. Read the color from SettingsManager and apply it
directly, matching the pattern already used for the Play button.
Remove the gear toolbar button that opened Settings as a sheet in the
NavigationSplitView sidebar column, and drop the macOS guard hiding
.settings from SidebarMainItem so it can be added to the sidebar and
rendered in the detail column like other items. The dedicated Settings
window (Cmd+,) is unchanged.
Drop the standalone iOS section for Integrations and inline its row into
the main list right above Advanced Settings. Swap the tvOS sidebar order
so Integrations appears before Advanced as well. macOS was already
correctly ordered via SettingsSection enum declaration.
Also swap the icon to puzzlepiece.extension, which better conveys that
this section houses third-party service hookups (SponsorBlock, Return
YouTube Dislike, DeArrow, short-link resolution) rather than being
YouTube-specific.
Hide the Resolve Short Links toggle on tvOS — there's no way to tap
inline description links or reach a system browser there — and tighten
the openInSystemBrowser platform guards so the iOS-only UIApplication
path isn't compiled on tvOS.
Tapping bit.ly/tinyurl/t.co/etc. in a description or comment previously
opened Safari even when the destination was a playable YouTube URL.
Added an opt-in "Resolve Short Links" toggle under YouTube Enhancements
(off by default) that follows the redirect on tap: if the target is a
YouTube/PeerTube/direct-media URL, open it in-app; otherwise prompt the
user before falling back to yt-dlp extraction or the browser.
Also added a confirmation dialog for non-shortener links that only
matched the loose .externalVideo yt-dlp fallback, so arbitrary web
pages in descriptions no longer silently kick off extraction.
Prompts live on NavigationCoordinator and are dual-hosted by YatteeApp
and ExpandedPlayerSheet so they remain visible whether or not the
expanded player is covering the main view.
New Release-DeveloperID configuration gates Sparkle behind a SPARKLE
compile flag so the App Store Release build stays Sparkle-free. Adds
SPUStandardUpdaterController wrapper, Check for Updates menu command,
Advanced Settings section with beta channel toggle, and a Ruby script
plus GitHub Actions job that signs each release and publishes the
appcast to gh-pages for consumption by Sparkle and Homebrew cask.
Comments now use DescriptionText.attributed so URLs become tappable
and route through the same in-app pipeline as description links, and
timestamp strings seek the player.
Description links to YouTube videos, channels, playlists, and external
video URLs now open in Yattee instead of Safari. When a video is
already playing, tapping a video link surfaces the existing
QueueActionSheet (Play Now / Play Next / Add to Queue) — the sheet is
hosted both at the app root and inside ExpandedPlayerSheet so it
appears above whichever layer is on screen.
Drop the redundant inner NavigationStack on non-tvOS (the outer detail
pane already provides one) and apply .listStyle(.inset) on macOS. Kept
as List to preserve drag-to-reorder for the main navigation section.
Convert detail and filter sheets to shared helpers, add inline Filter /
Export / Clear buttons next to the search bar (toolbar items weren't
surfacing in the settings detail pane), inline the Reset Filters button
at the bottom of the filter sheet, use a 'Close' text button, and trim
the macOS Share Sheet to just the scrollable log with a Copy button.