Files
onyx/Onyx/PowerActivity.swift
T
ScarriffleandClaude Opus 5 9b600de9a9 Live Activities unter der Notch, nicht daneben
Neben ihr wären die Arme zwangsläufig gleich breit — die Silhouette zeichnet den
Steg mittig um die Hardware. Ein Symbol links und ein Balken rechts hieße dann
132 Punkte Schwarz für ein Symbol. Darunter hängt die Fläche frei und wird so
breit, wie ihr Inhalt es braucht.

Lautstärke, Strom, Titelwechsel und Helligkeit. Gemeldet wird nur, was sich
**geändert** hat: `PowerWatcher` schweigt beim ersten gelesenen Zustand, sonst
begrüßte Onyx jeden Start mit „am Netz".

Die Helligkeit kostete einen Spike. Von drei Wegen trägt nur `DisplayServices`,
und nur für den eingebauten Bildschirm. `CoreDisplay` — der Weg, den jede
Anleitung empfiehlt — gibt für alle Bildschirme 1.0 zurück, auch für den, der
auf 62 % stand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:05:13 +02:00

128 lines
4.4 KiB
Swift

import SwiftUI
import ActivityProvider
import MetricsProvider
import OnyxDesign
/// Was am Strom geschieht — Netzteil dran, ab, Limit erreicht, Akku fast leer.
///
/// **Nicht bei jedem Messwert, sondern bei jedem Wechsel.** Der Ladestand
/// ändert sich ständig; eine Notch, die das begleitet, wäre eine Leuchtreklame.
/// Angemeldet wird nur, wenn sich der *Zustand* ändert.
struct PowerActivity: NotchActivity {
/// Was gemeldet wird.
enum Kind: Equatable {
case charging
case plugged
case unplugged
case atLimit(Int)
case low
var text: String {
switch self {
case .charging: String(localized: "activity.power.charging")
case .plugged: String(localized: "activity.power.plugged")
case .unplugged: String(localized: "activity.power.unplugged")
case .atLimit: String(localized: "activity.power.atLimit")
case .low: String(localized: "activity.power.low")
}
}
/// „Fast leer" ist das eine, das man nicht verpassen darf.
var rank: Int { self == .low ? ActivityRank.alert : ActivityRank.notice }
/// Eine Warnung darf länger stehen als eine Beiläufigkeit.
var duration: TimeInterval { self == .low ? 4 : 2.2 }
}
let id = "power"
let kind: Kind
let charge: Double
let isCharging: Bool
let isPluggedIn: Bool
var rank: Int { kind.rank }
var duration: TimeInterval? { kind.duration }
var size: CGSize { CGSize(width: 232, height: 34) }
func makeContent() -> AnyView {
AnyView(
HStack(spacing: Onyx.Space.lg) {
BatteryGlyphView(charge: charge,
isCharging: isCharging,
isPluggedIn: isPluggedIn,
height: 13)
Text(kind.text)
.font(Onyx.Font.footnote)
.foregroundStyle(Onyx.Color.textPrimary)
.lineLimit(1)
Spacer(minLength: Onyx.Space.sm)
Text("\(Int((min(max(charge, 0), 1) * 100).rounded())) %")
.font(Onyx.Font.metricSmall)
.foregroundStyle(kind == .low ? Onyx.Color.critical
: Onyx.Color.textSecondary)
}
.padding(.horizontal, Onyx.Space.xl)
.frame(maxWidth: .infinity, maxHeight: .infinity)
)
}
}
/// Was gerade spielt — beim Wechsel des Titels.
///
/// Ohne Fortschritt und ohne Knöpfe: das steht im Panel. Hier geht es um die
/// eine Frage, die man beim Wechsel hat — was ist das jetzt?
struct MediaActivity: NotchActivity {
let id = "media"
let rank = ActivityRank.notice
let duration: TimeInterval? = 3
var size: CGSize { CGSize(width: 264, height: 40) }
let title: String
let artist: String
let artwork: Data?
func makeContent() -> AnyView {
AnyView(
HStack(spacing: Onyx.Space.lg) {
Group {
if let artwork, let image = NSImage(data: artwork) {
Image(nsImage: image).resizable()
} else {
// Kein Cover ist kein Fehler — Radiostreams haben keins.
Onyx.Color.elevated
.overlay {
Image(systemName: "music.note")
.font(Onyx.Font.micro)
.foregroundStyle(Onyx.Color.textTertiary)
}
}
}
.frame(width: 26, height: 26)
.clipShape(RoundedRectangle(cornerRadius: Onyx.Radius.chip, style: .continuous))
VStack(alignment: .leading, spacing: Onyx.Space.xxs) {
Text(title)
.font(Onyx.Font.micro)
.foregroundStyle(Onyx.Color.textPrimary)
.lineLimit(1)
if !artist.isEmpty {
Text(artist)
.font(Onyx.Font.micro)
.foregroundStyle(Onyx.Color.textTertiary)
.lineLimit(1)
}
}
Spacer(minLength: 0)
}
.padding(.horizontal, Onyx.Space.xl)
.frame(maxWidth: .infinity, maxHeight: .infinity)
)
}
}