mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 13:12:53 +00:00
Use the macOS capsule mini player on iOS 18 and drop its duplicate close button
The iOS 18 overlay layout appended a hardcoded close button after the preset buttons, and the default mini player preset already includes Close, so the bar showed two X buttons. Replace the full-width material bar with the same 420pt capsule macOS uses (shared capsuleLayout), and only add the fallback close button when the active preset has none. Inset the iOS 18 overlay 16pt horizontally; clear the bottom tab bar in compact width and float 12pt above the edge in regular width, where sidebarAdaptable keeps the tabs at the top.
This commit is contained in:
@@ -289,7 +289,10 @@ struct iOS18AdaptiveTabView: View {
|
||||
Spacer()
|
||||
MiniPlayerView()
|
||||
}
|
||||
.padding(.bottom, 49)
|
||||
.padding(.horizontal, 16)
|
||||
// Compact width has a bottom tab bar (49pt) to clear; regular width
|
||||
// (sidebarAdaptable) keeps its tabs at the top, so only float above the edge.
|
||||
.padding(.bottom, horizontalSizeClass == .compact ? 49 + 8 : 12)
|
||||
// Use move-only transition (no opacity) to prevent thumbnail flash during collapse
|
||||
// The opacity transition caused video to appear faded during player sheet dismiss
|
||||
.transition(.move(edge: .bottom))
|
||||
|
||||
@@ -132,12 +132,12 @@ struct MiniPlayerView: View {
|
||||
var body: some View {
|
||||
Group {
|
||||
#if os(macOS)
|
||||
macOSCapsuleLayout
|
||||
capsuleLayout
|
||||
#else
|
||||
if isTabAccessory {
|
||||
accessoryLayout
|
||||
} else {
|
||||
overlayLayout
|
||||
capsuleLayout
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -213,65 +213,25 @@ struct MiniPlayerView: View {
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
|
||||
// MARK: - Overlay Layout
|
||||
// MARK: - Capsule Layout (macOS + iOS 18 overlay)
|
||||
|
||||
private var overlayLayout: some View {
|
||||
HStack(spacing: 12) {
|
||||
// Video preview - tap for PiP (or expand if PiP unavailable)
|
||||
videoPreviewView
|
||||
.frame(width: 60, height: 34)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
handleVideoPreviewTap()
|
||||
}
|
||||
|
||||
// Title/author area - tap to expand player (restores from PiP first if needed)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
MarqueeText(
|
||||
text: displayTitle,
|
||||
font: .subheadline,
|
||||
fontWeight: .medium,
|
||||
foregroundStyle: .primary
|
||||
)
|
||||
|
||||
authorLine(font: .caption)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
expandPlayerWithPiPRestore()
|
||||
}
|
||||
|
||||
Spacer()
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
expandPlayerWithPiPRestore()
|
||||
}
|
||||
|
||||
// Dynamic buttons from settings
|
||||
buttonsView
|
||||
|
||||
// Always show close button in overlay layout
|
||||
closeButton
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
.background(.ultraThinMaterial)
|
||||
.overlay(alignment: .bottom) {
|
||||
progressLine
|
||||
}
|
||||
/// Preview size for the capsule: slightly larger on iOS for touch.
|
||||
private var capsulePreviewSize: CGSize {
|
||||
#if os(macOS)
|
||||
CGSize(width: 44, height: 26)
|
||||
#else
|
||||
CGSize(width: 56, height: 32)
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - macOS Capsule Layout
|
||||
|
||||
#if os(macOS)
|
||||
/// Compact, centered capsule shown in the main window on macOS (Music.app style).
|
||||
/// The expanded player lives in a separate window, so this stays visible alongside it.
|
||||
private var macOSCapsuleLayout: some View {
|
||||
/// Compact, centered capsule (Music.app style). On macOS it floats in the main
|
||||
/// window alongside the separate player window; on iOS 18 (no tab accessory)
|
||||
/// it floats above the tab bar with the same width limit.
|
||||
private var capsuleLayout: some View {
|
||||
HStack(spacing: 10) {
|
||||
// Video preview - tap for PiP (or expand if PiP unavailable)
|
||||
videoPreviewView
|
||||
.frame(width: 44, height: 26)
|
||||
.frame(width: capsulePreviewSize.width, height: capsulePreviewSize.height)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
@@ -289,13 +249,19 @@ struct MiniPlayerView: View {
|
||||
|
||||
authorLine(font: .caption)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
expandPlayerWithPiPRestore()
|
||||
}
|
||||
|
||||
// Dynamic buttons from settings (includes close by default - no separate close button)
|
||||
// Dynamic buttons from settings (close is part of the preset)
|
||||
buttonsView
|
||||
|
||||
// Safety net: keep a way to close when the preset has no close button
|
||||
if !miniPlayerSettings.buttons.contains(where: { $0.buttonType == .close }) {
|
||||
closeButton
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
@@ -307,7 +273,6 @@ struct MiniPlayerView: View {
|
||||
.clipShape(Capsule())
|
||||
.shadow(color: .black.opacity(0.25), radius: 8, y: 4)
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - Shared Components
|
||||
|
||||
|
||||
Reference in New Issue
Block a user