diff --git a/README.md b/README.md index 67375c1..cc18fb6 100644 --- a/README.md +++ b/README.md @@ -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 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 share a consistently sized row with a subtle orange fill; the card fits the 400 × 240-point 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 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. 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. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index 78e2763..a8e7856 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -1257,6 +1257,9 @@ private struct PlayerContent: View { @Environment(\.accessibilityReduceMotion) private var reduceMotion @State private var showsRemainingTime = false @State private var dropTargeted = false + @State private var showsYouTubeInput = false + @State private var youTubeURL = "" + @FocusState private var youTubeInputFocused: Bool @State private var controlSize = CGSize(width: 440, height: 68) // Relative offsets preserve the dragged position across resize/fullscreen. @State private var controlOffset = CGSize.zero @@ -1541,26 +1544,56 @@ private struct PlayerContent: View { } Spacer(minLength: 0) } - HStack(spacing: 0) { + HStack(spacing: 10) { Button(action: model.openPanel) { Label("Open File…", systemImage: "doc") .frame(maxWidth: .infinity, minHeight: 40) .contentShape(Rectangle()) } - Divider().frame(height: 20).accessibilityHidden(true) - Button(action: model.openYouTubePanel) { + .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) + Button { + showsYouTubeInput = true + youTubeInputFocused = true + } label: { Label("YouTube URL…", systemImage: "link") .frame(maxWidth: .infinity, minHeight: 40) .contentShape(Rectangle()) } .accessibilityLabel("Open YouTube URL…") + .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) } .font(.system(size: 13, weight: .medium)) .foregroundStyle(Color(SunsetPalette.text)) .imageScale(.large) .buttonStyle(.plain) .disabled(!model.canOpen) - .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) + if showsYouTubeInput { + HStack(spacing: 8) { + TextField("YouTube video URL", text: $youTubeURL) + .textFieldStyle(.plain) + .focused($youTubeInputFocused) + .accessibilityLabel("YouTube video URL") + .onAppear { youTubeInputFocused = true } + .onSubmit { model.openYouTube(youTubeURL) } + Button { + model.openYouTube(youTubeURL) + } label: { + Image(systemName: "arrow.right") + .frame(width: 24, height: 24) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityLabel("Open YouTube Video") + .help("Open YouTube Video") + .disabled(youTubeURL.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + } + .font(.system(size: 13)) + .foregroundStyle(Color(SunsetPalette.text)) + .padding(.horizontal, 12) + .frame(height: 40) + .background(Color(SunsetPalette.actionFill), in: RoundedRectangle(cornerRadius: 8)) + .disabled(!model.canOpen) + } Text("Drop a media file or YouTube link here") .font(.system(size: 12)) .foregroundStyle(Color(SunsetPalette.secondaryText))