3ba19f28a2
apple / swift (push) Waiting to run
apple / screenshots (push) Blocked by required conditions
release / apple (push) Waiting to run
ci / rust (push) Failing after 50s
ci / web (push) Successful in 57s
decky / build-publish (push) Successful in 14s
ci / docs-site (push) Successful in 1m2s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 29s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 6s
flatpak / build-publish (push) Failing after 2m14s
ci / bench (push) Successful in 5m34s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5m48s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5m46s
docker / deploy-docs (push) Successful in 23s
windows-host / package (push) Successful in 11m40s
arch / build-publish (push) Successful in 18m10s
deb / build-publish (push) Successful in 18m9s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 4m21s
android / android (push) Successful in 21m4s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m37s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 5m38s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m3s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 1m2s
The console UI now runs on tvOS through the NATIVE focus engine: carousel cards and settings rows are focusable Buttons (Siri Remote and pads both navigate; imperative scrollTo replaces the drop-prone scrollPosition binding), while iOS/macOS keep the 60 Hz poll untouched - on tvOS it carries only what focus has no concept of: X/Y screen actions and left/right value adjust with the poll's dominant-axis feel (onMoveCommand proved input-source-dependent: keyboard intercepted, pad dpad not -> double steps). Text entry uses the system fullscreen keyboard (TVTextEntry); pairing + library present as covers under the launcher; the game library defaults ON; settings values slide a quiet 14 pt in the step's direction. Session controls: controller/remote input routes EXCLUSIVELY through GameController during a stream (GCEventViewController, interaction disabled) - a pad's B no longer doubles as a UIKit menu press that ended sessions mid-game. Deliberate exits only: the cross-client escape chord (hold L1+R1+Start+Select 1.5 s - pf-client-core's contract, now implemented on all Apple platforms) and holding the remote's Back >= 1 s; the start-of-stream banner (now also on tvOS) teaches both. The Siri Remote's touch surface drives the host pointer - press = left click, Play/Pause = right click, release-tail jumps gated so motion stays truly relative. tvOS 26 regressions fixed at the root: the app-wide brand tint rendered every unfocused control as a blank pill (tint dropped on tvOS) and the 17 pt root font shrank the whole platform (29 pt there), plus 10-foot sizing across host cards, the gamepad screens, and the stats HUD (whose misleading "Press Menu" hint is gone). Acknowledgements scrolls by focus-sized chunks and Menu pops instead of suspending; full-width focusSections make the home actions reachable from any column. The presenter defaults to stage-3 glass pacing on tvOS (a 60 Hz panel fed a 60 fps stream is the sticky-FIFO worst case behind the 50 ms display stage) and is pickable from the gamepad settings; HDR capability advertises from AVPlayer.eligibleForHDRPlayback instead of the current mode's EDR headroom, so an SDR home screen no longer hides an HDR TV. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
124 lines
5.4 KiB
Swift
124 lines
5.4 KiB
Swift
// GlassStyle.swift — the app's single, availability-gated entry point to Apple's "Liquid
|
|
// Glass" (iOS / macOS / tvOS 26). Every Liquid Glass symbol (glassEffect, Glass, the
|
|
// .glassProminent button style …) is HARD-gated to OS 26: referencing one with our
|
|
// deployment targets (macOS 14 / iOS 17 / tvOS 17) is a COMPILE error, not a silent no-op,
|
|
// unless it sits behind `if #available`. So all glass in the app routes through the two
|
|
// helpers below, each of which falls back to the EXACT look the app shipped before
|
|
// (.regularMaterial / .borderedProminent) — nothing regresses on older OSes, and the gating
|
|
// lives in exactly one file.
|
|
|
|
import SwiftUI
|
|
|
|
// MARK: - Glass background
|
|
|
|
/// Liquid Glass behind a floating / overlay surface, with the pre-26 `.regularMaterial`
|
|
/// look as the fallback. Use ONLY on the floating control / overlay layer (the streaming
|
|
/// HUD, the trust card, the touch exit chip) — never on content tiles or dense forms (HIG).
|
|
///
|
|
/// `glassEffect()`'s own default shape is a Capsule, so panels MUST pass an explicit shape
|
|
/// (a RoundedRectangle / Circle) or they render as a pill. `interactive` makes the glass
|
|
/// react to press — only meaningful when the glass itself is the tap target.
|
|
private struct GlassBackground<S: Shape>: ViewModifier {
|
|
let shape: S
|
|
var interactive = false
|
|
|
|
func body(content: Content) -> some View {
|
|
if #available(iOS 26, macOS 26, tvOS 26, *) {
|
|
content.glassEffect(interactive ? .regular.interactive() : .regular, in: shape)
|
|
} else {
|
|
content.background(.regularMaterial, in: shape)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
/// Liquid Glass (26+) or the existing `.regularMaterial` (pre-26) behind a floating
|
|
/// surface. Pass the surface's shape explicitly — glass defaults to a Capsule otherwise.
|
|
func glassBackground<S: Shape>(_ shape: S, interactive: Bool = false) -> some View {
|
|
modifier(GlassBackground(shape: shape, interactive: interactive))
|
|
}
|
|
}
|
|
|
|
// MARK: - Glass primary button
|
|
|
|
/// The single prominent action on a floating / overlay or sheet surface: the Liquid-Glass
|
|
/// prominent button style on 26+, falling back to `.borderedProminent` (the app's current
|
|
/// primary style) below. Apply directly to a `Button`; role / keyboardShortcut / disabled
|
|
/// chain after it as usual. tvOS stays `.borderedProminent` always — glass chrome fights the
|
|
/// focus engine, and keeping it preserves today's tvOS look exactly.
|
|
private struct GlassProminentButton: ViewModifier {
|
|
func body(content: Content) -> some View {
|
|
#if os(tvOS)
|
|
content.buttonStyle(.borderedProminent)
|
|
#else
|
|
if #available(iOS 26, macOS 26, *) {
|
|
content.buttonStyle(.glassProminent)
|
|
} else {
|
|
content.buttonStyle(.borderedProminent)
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
/// Liquid-Glass prominent style (26+, non-tvOS) or `.borderedProminent`. Drop-in for the
|
|
/// `.buttonStyle(.borderedProminent)` on a surface's primary action.
|
|
func glassProminentButtonStyle() -> some View {
|
|
modifier(GlassProminentButton())
|
|
}
|
|
}
|
|
|
|
// MARK: - Console glass (gamepad host tiles + settings rows)
|
|
|
|
/// Liquid Glass tuned for the gamepad UI's dark "console" surfaces — the host-carousel tiles and
|
|
/// the settings rows. Unlike `glassBackground` (floating-overlay only, per HIG), this deliberately
|
|
/// clads content tiles / dense rows: a chosen part of the 10-foot console look. `tint` washes the
|
|
/// glass toward a color (the brand violet on the focused / primary surface); `interactive` makes
|
|
/// it flex on press. The pre-26 fallback is `.ultraThinMaterial` forced dark — these surfaces
|
|
/// always sit on the near-black backdrop, so the material must stay dark even in a light appearance.
|
|
private struct ConsoleGlass<S: Shape>: ViewModifier {
|
|
let shape: S
|
|
var tint: Color?
|
|
var interactive = false
|
|
|
|
func body(content: Content) -> some View {
|
|
#if os(tvOS)
|
|
// ALWAYS the material fallback on tvOS: the gamepad settings list is 15+ of these
|
|
// surfaces, and live Liquid Glass per row made the whole screen visibly laggy on the
|
|
// Apple TV's GPU (same class of call GlassProminentButton already makes — glass fights
|
|
// the 10-foot platform). The tint rides an overlay so the focused row keeps its wash.
|
|
content.background {
|
|
shape.fill(.ultraThinMaterial)
|
|
.environment(\.colorScheme, .dark)
|
|
.overlay {
|
|
if let tint { shape.fill(tint) }
|
|
}
|
|
}
|
|
#else
|
|
if #available(iOS 26, macOS 26, *) {
|
|
content.glassEffect(glass, in: shape)
|
|
} else {
|
|
content.background { shape.fill(.ultraThinMaterial).environment(\.colorScheme, .dark) }
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !os(tvOS)
|
|
@available(iOS 26, macOS 26, *)
|
|
private var glass: Glass {
|
|
var g: Glass = .regular
|
|
if let tint { g = g.tint(tint) }
|
|
if interactive { g = g.interactive() }
|
|
return g
|
|
}
|
|
#endif
|
|
}
|
|
|
|
extension View {
|
|
/// Liquid Glass for a dark console surface (a host tile / settings row), or `.ultraThinMaterial`
|
|
/// (forced dark) pre-26. Pass the surface's shape explicitly — glass defaults to a Capsule.
|
|
func consoleGlass<S: Shape>(_ shape: S, tint: Color? = nil, interactive: Bool = false) -> some View {
|
|
modifier(ConsoleGlass(shape: shape, tint: tint, interactive: interactive))
|
|
}
|
|
}
|