feat(apple): wake-until-up overlay + host edit with MAC prefill

- HostWaker + WakeOverlay: after sending the Wake-on-LAN packet, wait until the host
  is really back (resend + mDNS poll, timeout, cancel/retry) before connecting.
  macOS-only in practice — WoL stays gated off on iOS/tvOS pending the multicast
  entitlement.
- Add/Edit host sheet gains a Wake-on-LAN MAC field, prefilled from the stored MAC
  or the live mDNS advert; parseMacs validates aa:bb:cc:dd:ee:ff.
- Gamepad chrome/home and glass-style polish.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-05 20:04:47 +02:00
parent 3775a39e70
commit 60121f1ea9
14 changed files with 759 additions and 245 deletions
@@ -67,3 +67,41 @@ extension 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 #available(iOS 26, macOS 26, tvOS 26, *) {
content.glassEffect(glass, in: shape)
} else {
content.background { shape.fill(.ultraThinMaterial).environment(\.colorScheme, .dark) }
}
}
@available(iOS 26, macOS 26, tvOS 26, *)
private var glass: Glass {
var g: Glass = .regular
if let tint { g = g.tint(tint) }
if interactive { g = g.interactive() }
return g
}
}
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))
}
}