ui: outline welcome actions and animate URL input toggle

This commit is contained in:
2026-10-12 14:59:14 +02:00
parent e3e6a66216
commit aacd260fe7
2 changed files with 20 additions and 5 deletions
+1 -1
View File
@@ -186,7 +186,7 @@ Terebi reports facts, not watched status. A consumer such as Tubular decides com
The layout follows [IINA's video-first window](https://iina.io/img/screenshot.png) and the supplied screenshots: a 32-point transparent titlebar with native traffic lights and file icon, plus a frosted 440 × 68-point floating controller. Conjure-inspired **Sunset** styling uses translucent dark-brown glass, warm cream primary text, sand secondary text, and a fixed orange accent for the volume fill, active pin, and drop target. Native slider thumbs and buffered ranges share the palette; warning symbols retain their semantic yellow. Shared vertical padding and an optical offset for symbol/slider insets balance the controller's visible top and bottom margins in both play and pause states. The controller starts centered with a four-percent bottom inset and keeps its relative position when the window changes size. The layout follows [IINA's video-first window](https://iina.io/img/screenshot.png) and the supplied screenshots: a 32-point transparent titlebar with native traffic lights and file icon, plus a frosted 440 × 68-point floating controller. Conjure-inspired **Sunset** styling uses translucent dark-brown glass, warm cream primary text, sand secondary text, and a fixed orange accent for the volume fill, active pin, and drop target. Native slider thumbs and buffered ranges share the palette; warning symbols retain their semantic yellow. Shared vertical padding and an optical offset for symbol/slider insets balance the controller's visible top and bottom margins in both play and pause states. The controller starts centered with a four-percent bottom inset and keeps its relative position when the window changes size.
The empty player uses the same tinted HUD-glass material, faint warm outlines, 12-point rounded corners, and Sunset palette in a centered welcome card. File and YouTube opening actions are separate, equally sized buttons with a 10-point gap and subtle orange fills. The YouTube button reveals and focuses an inline URL field below the buttons, spanning their combined width including the gap; Return or the arrow button submits through the existing YouTube URL validation. Other URL-opening entry points retain their dialog. The card fits the minimum window, and the full window still accepts media-file and YouTube-link drops. The empty player uses the same tinted HUD-glass material, faint warm outlines, 12-point rounded corners, and Sunset palette in a centered welcome card. File and YouTube opening actions are separate, equally sized buttons with a 10-point gap, subtle orange fills, and matching half-point sand outlines. The YouTube button toggles an outlined inline URL field below the buttons, spanning their combined width including the gap. Showing it focuses the field; hiding it releases text-input focus and preserves the draft. The field slides/fades in and out while the card adjusts over 220 ms with ease-in/out timing; Reduce Motion disables the animation. Return or the arrow button submits through the existing YouTube URL validation. Other URL-opening entry points retain their dialog. The card fits the minimum window, and the full window still accepts media-file and YouTube-link drops.
With no media open, the full window uses native behind-window HUD glass with a 35%-opaque brown tint. Static, cached amber/rust radial gradients add ambient color only to the empty screen; they disappear during loading or errors, and are never drawn over video or playback chrome. Glass title/control/error surfaces use the same brown tint with half-point sand outlines. Decorative drawing does not intercept input. The idle Metal surface stays attached but hidden; opening media restores a solid black background and letterboxing without tinting the video, and closing it restores the ambient frosted background. macOS controls the material’s appearance, including Reduce Transparency. With no media open, the full window uses native behind-window HUD glass with a 35%-opaque brown tint. Static, cached amber/rust radial gradients add ambient color only to the empty screen; they disappear during loading or errors, and are never drawn over video or playback chrome. Glass title/control/error surfaces use the same brown tint with half-point sand outlines. Decorative drawing does not intercept input. The idle Metal surface stays attached but hidden; opening media restores a solid black background and letterboxing without tinting the video, and closing it restores the ambient frosted background. macOS controls the material’s appearance, including Reduce Transparency.
+19 -4
View File
@@ -1265,6 +1265,14 @@ private struct PlayerContent: View {
@State private var controlOffset = CGSize.zero @State private var controlOffset = CGSize.zero
@State private var dragOrigin = CGSize.zero @State private var dragOrigin = CGSize.zero
private func toggleYouTubeInput() {
if showsYouTubeInput { youTubeInputFocused = false }
withAnimation(reduceMotion ? nil : .easeInOut(duration: 0.22)) {
showsYouTubeInput.toggle()
}
if showsYouTubeInput { youTubeInputFocused = true }
}
private var showsBufferReadout: Bool { model.isNetworkMedia && model.showsBufferInfo } private var showsBufferReadout: Bool { model.isNetworkMedia && model.showsBufferInfo }
private var controlHeight: CGFloat { showsBufferReadout ? 88 : 68 } private var controlHeight: CGFloat { showsBufferReadout ? 88 : 68 }
private var compactControlHeight: CGFloat { showsBufferReadout ? 52 : 40 } private var compactControlHeight: CGFloat { showsBufferReadout ? 52 : 40 }
@@ -1551,16 +1559,19 @@ private struct PlayerContent: View {
.contentShape(Rectangle()) .contentShape(Rectangle())
} }
.background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8))
Button { .overlay(RoundedRectangle(cornerRadius: 8)
showsYouTubeInput = true .strokeBorder(Color(SunsetPalette.outline), lineWidth: 0.5)
youTubeInputFocused = true .allowsHitTesting(false))
} label: { Button(action: toggleYouTubeInput) {
Label("YouTube URL…", systemImage: "link") Label("YouTube URL…", systemImage: "link")
.frame(maxWidth: .infinity, minHeight: 40) .frame(maxWidth: .infinity, minHeight: 40)
.contentShape(Rectangle()) .contentShape(Rectangle())
} }
.accessibilityLabel("Open YouTube URL…") .accessibilityLabel("Open YouTube URL…")
.background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8))
.overlay(RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color(SunsetPalette.outline), lineWidth: 0.5)
.allowsHitTesting(false))
} }
.font(.system(size: 13, weight: .medium)) .font(.system(size: 13, weight: .medium))
.foregroundStyle(Color(SunsetPalette.text)) .foregroundStyle(Color(SunsetPalette.text))
@@ -1592,6 +1603,10 @@ private struct PlayerContent: View {
.padding(.horizontal, 12) .padding(.horizontal, 12)
.frame(height: 40) .frame(height: 40)
.background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8))
.overlay(RoundedRectangle(cornerRadius: 8)
.strokeBorder(Color(SunsetPalette.outline), lineWidth: 0.5)
.allowsHitTesting(false))
.transition(reduceMotion ? .opacity : .move(edge: .top).combined(with: .opacity))
.disabled(!model.canOpen) .disabled(!model.canOpen)
} }
Text("Drop a media file or YouTube link here") Text("Drop a media file or YouTube link here")