Kacheln und Fenster sprechen dieselbe Sprache
Alle Kacheln, Popovers und Fenster auf die Designschicht umgestellt: rund hundert gesetzte Schriftgrößen und 159 gesetzte Abstände und Radien sind jetzt Token. Übrig bleibt `spacing: 0` — das ist eine Aussage, keine geratene Zahl. Die Einstellungen bekommen eine Seitenleiste statt sieben Reitern, deren deutsche Beschriftungen zusammen 880 Punkte brauchten. Panel-Token stehen nicht mehr in Fenstern: dort gilt Systemsemantik, damit der Hellmodus nicht schwarz auf schwarz endet. Neu ist ein Schaufenster, das alle Bauteile nebeneinander zeigt — ohne das lässt sich eine Änderung an einem Token nur prüfen, indem man die App bedient, bis jede Kachel einmal zu sehen war. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9b600de9a9
commit
edb327cabe
+26
-52
@@ -16,7 +16,7 @@ struct FanView: View {
|
||||
let roomy: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.row) {
|
||||
switch control.installState {
|
||||
case .installed:
|
||||
if let report = control.report, !report.fans.isEmpty {
|
||||
@@ -24,10 +24,10 @@ struct FanView: View {
|
||||
FanRow(control: control, index: index, fan: fan, roomy: roomy)
|
||||
}
|
||||
if let hottest = report.hottestCelsius {
|
||||
Divider().overlay(Onyx.Color.hairline)
|
||||
HStack(spacing: 5) {
|
||||
OnyxHairline()
|
||||
HStack(spacing: Onyx.Space.sm) {
|
||||
Image(systemName: "thermometer.medium")
|
||||
.font(.system(size: 10))
|
||||
.font(.system(size: Onyx.Symbol.small))
|
||||
Text("\(Int(hottest.rounded())) °C")
|
||||
.font(Onyx.Font.metricSmall).monospacedDigit()
|
||||
Spacer(minLength: 0)
|
||||
@@ -35,14 +35,24 @@ struct FanView: View {
|
||||
.foregroundStyle(Onyx.Color.textTertiary)
|
||||
}
|
||||
} else {
|
||||
notice("fans.noData", symbol: "fan")
|
||||
OnyxNotice(symbol: "fan",
|
||||
text: String(localized: "fans.noData"),
|
||||
placement: .leading)
|
||||
}
|
||||
case .requiresApproval:
|
||||
notice("fans.approval.needed", symbol: "hand.raised") {
|
||||
control.openApprovalSettings()
|
||||
OnyxNotice(symbol: "hand.raised",
|
||||
text: String(localized: "fans.approval.needed"),
|
||||
placement: .leading) {
|
||||
OnyxPillButton(String(localized: "fans.install")) {
|
||||
control.openApprovalSettings()
|
||||
}
|
||||
}
|
||||
case .notInstalled:
|
||||
notice("fans.notInstalled", symbol: "fan") { control.install() }
|
||||
OnyxNotice(symbol: "fan",
|
||||
text: String(localized: "fans.notInstalled"),
|
||||
placement: .leading) {
|
||||
OnyxPillButton(String(localized: "fans.install")) { control.install() }
|
||||
}
|
||||
case .failed(let reason):
|
||||
Text(reason)
|
||||
.font(Onyx.Font.caption)
|
||||
@@ -54,29 +64,6 @@ struct FanView: View {
|
||||
.onAppear { control.refreshInstallState() }
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func notice(_ key: LocalizedStringKey, symbol: String,
|
||||
action: (() -> Void)? = nil) -> some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Image(systemName: symbol)
|
||||
.font(.system(size: 15, weight: .light))
|
||||
.foregroundStyle(Onyx.Color.textTertiary)
|
||||
Text(key)
|
||||
.font(Onyx.Font.caption)
|
||||
.foregroundStyle(Onyx.Color.textSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
if let action {
|
||||
Button(action: action) {
|
||||
Text("fans.install")
|
||||
.font(Onyx.Font.caption)
|
||||
.foregroundStyle(Onyx.Color.surface)
|
||||
.padding(.horizontal, 10).padding(.vertical, 4)
|
||||
.background(Onyx.Color.accent, in: .capsule)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct FanRow: View {
|
||||
@@ -86,8 +73,8 @@ private struct FanRow: View {
|
||||
let roomy: Bool
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 6) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.xs) {
|
||||
HStack(spacing: Onyx.Space.sm) {
|
||||
Text("fans.fan \(fan.index + 1)")
|
||||
.font(Onyx.Font.caption)
|
||||
.foregroundStyle(Onyx.Color.textSecondary)
|
||||
@@ -98,29 +85,16 @@ private struct FanRow: View {
|
||||
.foregroundStyle(Onyx.Color.textPrimary)
|
||||
}
|
||||
|
||||
HStack(spacing: 8) {
|
||||
HStack(spacing: Onyx.Space.md) {
|
||||
// Automatik ist der Ruhezustand und bleibt einen Klick entfernt:
|
||||
// wer manuell geregelt hat, muss ohne Nachdenken zurückkönnen.
|
||||
// Als gefüllte Pille, wenn sie greift — ein blauer Text sieht
|
||||
// aus wie ein Link und nicht wie ein Zustand.
|
||||
Button { control.setAutomatic(index: index) } label: {
|
||||
Text("fans.auto")
|
||||
.font(Onyx.Font.metricSmall)
|
||||
.foregroundStyle(fan.isManual ? Onyx.Color.textTertiary
|
||||
: Onyx.Color.surface)
|
||||
.padding(.horizontal, 7)
|
||||
.padding(.vertical, 2)
|
||||
.background {
|
||||
Capsule()
|
||||
.fill(fan.isManual ? Color.clear : Onyx.Color.accent)
|
||||
.overlay {
|
||||
Capsule().strokeBorder(
|
||||
fan.isManual ? Onyx.Color.hairline : .clear,
|
||||
lineWidth: 1)
|
||||
}
|
||||
}
|
||||
OnyxPillButton(String(localized: "fans.auto"),
|
||||
isFilled: !fan.isManual,
|
||||
size: .compact) {
|
||||
control.setAutomatic(index: index)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.disabled(!fan.isManual)
|
||||
|
||||
// Ein Regler über einen leeren Bereich stürzt ab. Meldet der
|
||||
@@ -184,7 +158,7 @@ final class FanMenuBarModule: MenuBarModule {
|
||||
|
||||
func makePopoverView() -> AnyView {
|
||||
AnyView(FanView(control: control, roomy: true)
|
||||
.padding(14)
|
||||
.padding(Onyx.Space.xl)
|
||||
.frame(width: 260))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user