diff --git a/README.md b/README.md index c0f991f..a8167fd 100644 --- a/README.md +++ b/README.md @@ -184,12 +184,14 @@ Terebi reports facts, not watched status. A consumer such as Tubular decides com ## Playback UI -The layout follows [IINA's video-first window](https://iina.io/img/screenshot.png) and the supplied screenshots: a 32-point frosted titlebar with native traffic lights and file icon, plus a dark frosted 440 × 68-point floating controller. AppKit materials blur the video beneath the chrome; muted icons, an orange volume track, a narrow seek thumb, and zero-padded time labels follow the reference. 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 dark frosted 440 × 68-point floating controller. AppKit materials blur the video beneath the controller; muted icons, an orange volume track, a narrow seek thumb, and zero-padded time labels follow the reference. 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 HUD-glass material, 12-point rounded corners, and muted icon/text palette in a centered welcome card. File and YouTube opening actions share a consistently sized row; the card fits the 400 × 240-point 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 to frost the desktop and windows beneath it. The idle Metal surface stays attached but hidden; opening media restores a solid black background and letterboxing, and closing it restores the frosted background. macOS controls the material’s appearance, including Reduce Transparency. +Normal and compact titlebars have no separate glass strip or separator: content extends beneath AppKit’s transparent titlebar using `fullSizeContentView`, `titlebarAppearsTransparent`, and `titlebarSeparatorStyle = .none`. The title and pin sit directly over the existing background or video, with native traffic lights and window dragging preserved. + Opening video fits the window to its display aspect ratio, including non-square pixels, and constrains subsequent window resizing. Fullscreen retains the normal letterboxed renderer; audio-only playback and close release the aspect constraint. Extreme ratios that cannot fit both the screen and usable controls keep a freely resizable letterboxed window. **Compact mode** uses the same window, Metal view, and playback session—not a second window or macOS system PiP. Use the PiP-shaped button in the normal playback controls, **⌃⌘P**, or **Playback Actions → Enter Compact Mode**. The compact-mode button replaces the fullscreen button; fullscreen remains available through its menu, keyboard shortcuts, and double-clicking normal-mode video. Compact mode starts at the current screen's top-right corner with a 16-point inset and a preferred width of 320 points, preserving display aspect where usable screen bounds and the 160 × 90-point minimum allow; extreme ratios retain letterboxing. Compact mode floats above ordinary windows, joins all Spaces, and is configured to join eligible other-app fullscreen spaces and Stage Manager sets. Small hover controls provide play/pause, restore, and Playback Actions; keyboard playback controls remain available. diff --git a/Sources/App/TerebiApp.swift b/Sources/App/TerebiApp.swift index 7844cfc..f2b7f6d 100644 --- a/Sources/App/TerebiApp.swift +++ b/Sources/App/TerebiApp.swift @@ -998,7 +998,6 @@ final class PlayerModel: ObservableObject { @MainActor private struct PlayerGlass: NSViewRepresentable { - var titlebar = false var blendingMode: NSVisualEffectView.BlendingMode = .withinWindow func makeNSView(context: Context) -> NSVisualEffectView { @@ -1006,7 +1005,7 @@ private struct PlayerGlass: NSViewRepresentable { view.blendingMode = blendingMode view.state = .active view.appearance = NSAppearance(named: .vibrantDark) - view.material = titlebar ? .titlebar : .hudWindow + view.material = .hudWindow return view } @@ -1245,7 +1244,6 @@ private struct PlayerContent: View { .padding(.trailing, 12) .frame(height: 32) .foregroundStyle(.white.opacity(0.6)) - .background(PlayerGlass(titlebar: true).allowsHitTesting(false)) .onHover(perform: model.setTitleHovered) .opacity(model.controlsVisible ? 1 : 0) .allowsHitTesting(model.controlsVisible) @@ -1500,7 +1498,6 @@ private struct PlayerContent: View { .padding(8) .frame(maxWidth: .infinity) .foregroundStyle(.white.opacity(0.75)) - .background(PlayerGlass(titlebar: true)) .opacity(model.controlsVisible ? 1 : 0) .allowsHitTesting(false) .accessibilityHidden(!model.controlsVisible) @@ -1617,6 +1614,7 @@ private struct WindowChrome: NSViewRepresentable { window.styleMask.insert(.fullSizeContentView) window.titleVisibility = .hidden window.titlebarAppearsTransparent = true + window.titlebarSeparatorStyle = .none window.isOpaque = false window.backgroundColor = .clear window.acceptsMouseMovedEvents = true