diff --git a/README.md b/README.md index aa8c219..f8d5b9c 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ Use the titlebar's **Queue** icon or **View → Show Queue** to show the editabl In a normal window, showing the queue expands the window outward while preserving the video's size; hiding it removes that extra width. The window shifts left when necessary near the screen's right edge. Fullscreen keeps its fixed bounds. +The queue count and window controls share the top bar across the full expanded window; the sidebar starts with queue navigation and Add to Queue. + 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 0473c4b..eaa720c 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -2043,6 +2043,15 @@ private struct PlayerContent: View { .frame(height: 24) .background(PlayerGlass(cornerRadius: 8).clipShape(RoundedRectangle(cornerRadius: 8)).allowsHitTesting(false)) Spacer(minLength: 12) + if model.showsQueue { + HStack(spacing: 4) { + Text("Queue").fontWeight(.semibold) + Text("\(model.queue.items.count)").foregroundStyle(.secondary) + } + .font(.system(size: 13)) + .padding(.trailing, 8) + .accessibilityElement(children: .combine) + } titleControls } .padding(.leading, model.isFullScreen ? 16 : 86) @@ -2240,17 +2249,6 @@ private struct PlayerContent: View { private var queueSidebar: some View { VStack(spacing: 0) { - HStack { - Text("Queue").font(.headline) - Text("\(model.queue.items.count)").foregroundStyle(.secondary) - Spacer() - Button { model.showsQueue = false } label: { - Image(systemName: "sidebar.right") - } - .help("Hide Queue") - .accessibilityLabel("Hide Queue") - } - .padding(12) HStack { iconButton("backward.end.fill", "Previous Queue Item") { model.advanceQueue(-1) } .disabled(!model.canPreviousQueueItem) @@ -2266,6 +2264,7 @@ private struct PlayerContent: View { HStack(spacing: 8) { Image(systemName: item.id == model.queue.currentID ? "play.fill" : "film") .foregroundStyle(item.id == model.queue.currentID ? Color.accentColor : .secondary) + .frame(width: 20, alignment: .center) .accessibilityLabel(item.id == model.queue.currentID ? "Current item" : "Queued item") Text(item.url.lastPathComponent) .lineLimit(2) @@ -2328,6 +2327,12 @@ private struct PlayerContent: View { queueSidebar } } + .overlay(alignment: .top) { + titlebar + .opacity(model.isCompact || model.compactWindow.isTransitioning ? 0 : 1) + .allowsHitTesting(!model.isCompact && !model.compactWindow.isTransitioning && model.controlsVisible) + .accessibilityHidden(model.isCompact || model.compactWindow.isTransitioning || !model.controlsVisible) + } .frame(minWidth: model.isCompact || model.compactWindow.isTransitioning ? 160 : (model.showsQueue ? 660 : 400), minHeight: model.isCompact || model.compactWindow.isTransitioning ? model.compactContentMinimumHeight : 240) .ignoresSafeArea() @@ -2549,13 +2554,6 @@ private struct PlayerContent: View { .allowsHitTesting(model.isCompact) .accessibilityHidden(!model.isCompact) - titlebar - .frame(width: geometry.size.width) - .animation(compactChromeAnimation) { content in - content.opacity(model.isCompact ? 0 : 1) - } - .allowsHitTesting(!model.isCompact && model.controlsVisible) - .accessibilityHidden(model.isCompact || !model.controlsVisible) if model.canTogglePlayback { playbackControls