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:
Scarriffle
2026-09-05 21:05:51 +02:00
co-authored by Claude Opus 5
parent 9b600de9a9
commit edb327cabe
32 changed files with 3677 additions and 968 deletions
+18 -27
View File
@@ -33,7 +33,7 @@ struct OnboardingView: View {
Divider()
ScrollView {
VStack(alignment: .leading, spacing: 0) {
OnyxFormCard {
OnboardingRow(
symbol: "calendar",
title: "onboarding.calendar",
@@ -74,9 +74,10 @@ struct OnboardingView: View {
fanControl.openApprovalSettings()
}
}
.padding(.horizontal, 22)
.padding(.vertical, 6)
.padding(.horizontal, Onyx.Space.xxl)
.padding(.vertical, Onyx.Space.lg)
}
.scrollContentBackground(.hidden)
Divider()
footer
@@ -95,10 +96,10 @@ struct OnboardingView: View {
// MARK: - Kopf und Fuß
private var header: some View {
HStack(spacing: 14) {
HStack(spacing: Onyx.Space.xl) {
Image(nsImage: NSApp.applicationIconImage)
.resizable().frame(width: 52, height: 52)
VStack(alignment: .leading, spacing: 3) {
VStack(alignment: .leading, spacing: Onyx.Space.xs) {
Text("onboarding.title").font(.title2).fontWeight(.semibold)
Text("onboarding.subtitle")
.font(.callout).foregroundStyle(.secondary)
@@ -106,7 +107,7 @@ struct OnboardingView: View {
}
Spacer(minLength: 0)
}
.padding(22)
.padding(Onyx.Space.xxl)
}
private var footer: some View {
@@ -128,7 +129,7 @@ struct OnboardingView: View {
Button("onboarding.done", action: onFinish)
.keyboardShortcut(.defaultAction)
}
.padding(22)
.padding(Onyx.Space.xxl)
}
// MARK: - Zustände
@@ -169,33 +170,25 @@ private struct OnboardingRow: View {
enum State { case granted, denied, undetermined }
let symbol: String
let title: LocalizedStringKey
let detail: LocalizedStringKey
let title: String.LocalizationValue
let detail: String.LocalizationValue
let state: State
let request: () async -> Void
let openSettings: () -> Void
/// Dieselbe Zeile wie in den Einstellungen. Vorher war es eine eigene, die
/// zufällig gleich aussah — bis auf die Trennlinie, die sie sich selbst
/// unterlegte, und die deshalb auch unter der letzten stand.
var body: some View {
HStack(alignment: .top, spacing: 14) {
Image(systemName: symbol)
.font(.system(size: 15))
.foregroundStyle(state == .granted ? Onyx.Color.positive : .secondary)
.frame(width: 22, height: 22)
VStack(alignment: .leading, spacing: 2) {
Text(title).fontWeight(.medium)
Text(detail)
.font(.callout).foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
Spacer(minLength: 8)
OnyxFormRow(String(localized: title),
hint: String(localized: detail),
symbol: symbol,
tone: state == .granted ? .normal : .normal) {
switch state {
case .granted:
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(Onyx.Color.positive)
.font(.system(size: 16))
.font(.system(size: Onyx.Symbol.large))
case .undetermined:
Button("onboarding.allow") { Task { await request() } }
case .denied:
@@ -204,7 +197,5 @@ private struct OnboardingRow: View {
Button("onboarding.openSettings", action: openSettings)
}
}
.padding(.vertical, 12)
.overlay(alignment: .bottom) { Divider() }
}
}