diff --git a/README.md b/README.md index fb3397e..82b0a46 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ Resizing with the queue open preserves the video's aspect ratio while keeping th While the sidebar is open, its persistent top-right overlay holds the pin, Cast and settings controls above the list without a surrounding glass container, with matching top and right button insets; these and the bottom queue controls do not fade with the video controls. The native layout check verifies that all three header glyphs actually render. Closing the sidebar returns the pin, Cast and settings controls to the auto-hiding video titlebar with its glass container. The queue toggle stays in the playback controls. Space and configured playback shortcuts continue working with queue-list focus; Tab navigation, deliberately focused buttons/sliders and text entry keep their native keyboard behavior. +The sidebar, top controls and queue footer share an opaque playlist background. The top backing fades downward into the list over 20 points; the footer backing fades upward by the same amount, keeping scrolled filenames from interfering with the controls without hard edges. + Playback advances automatically and stops at the end, without wrapping. **Previous Queue Item** and **Next Queue Item** are available in the sidebar and Playback menu and are disabled at their respective ends. Reordering or removing another item does not interrupt playback; removing the current item plays its next neighbor, or stops if there is none. A playback error stays on that item rather than skipping it. The queue contains local files only and lasts until it is replaced, closed or the app quits; it has no persistence, shuffle, repeat or playlist-file import. Opening a YouTube video or resolved stream clears it. The sidebar is hidden in compact mode; Playback-menu queue navigation remains available. Sidebar appearance, drag/drop interaction and physical-receiver queue transitions still need manual verification. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index e2d59ed..5def680 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -2284,6 +2284,27 @@ private struct PlayerContent: View { ) } + private var queueBackground: Color { Color(nsColor: .textBackgroundColor) } + + private func queueChromeBackground(top: Bool) -> some View { + VStack(spacing: 0) { + if !top { + LinearGradient(colors: [queueBackground.opacity(0), queueBackground], + startPoint: .top, endPoint: .bottom) + .frame(height: 20) + } + queueBackground + if top { + LinearGradient(colors: [queueBackground, queueBackground.opacity(0)], + startPoint: .top, endPoint: .bottom) + .frame(height: 20) + } + } + .padding(top ? .bottom : .top, -20) + .allowsHitTesting(false) + .accessibilityHidden(true) + } + private var queueSidebar: some View { VStack(spacing: 0) { Color.clear.frame(height: 32) @@ -2351,6 +2372,7 @@ private struct PlayerContent: View { .onMove { model.moveQueueItems(fromOffsets: $0, toOffset: $1) } } .listStyle(.plain) + .scrollContentBackground(.hidden) .contentMargins(.horizontal, 0, for: .scrollContent) .overlay { if model.queue.items.isEmpty { @@ -2389,13 +2411,16 @@ private struct PlayerContent: View { } .padding(.horizontal, 12) .padding(.vertical, 8) + .background { queueChromeBackground(top: false) } } .frame(width: 260) - .background(PlayerGlass(cornerRadius: 0)) + .background(queueBackground) .overlay(alignment: .topTrailing) { titleControls .padding(.trailing, 4) .frame(height: 32) + .frame(maxWidth: .infinity, alignment: .trailing) + .background { queueChromeBackground(top: true) } } .dropDestination(for: URL.self) { urls, _ in guard model.canOpen, urls.contains(where: \.isFileURL) else { return false }