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
@@ -1,4 +1,5 @@
|
||||
import AppKit
|
||||
import OnyxDesign
|
||||
import OnyxMenuBar
|
||||
|
||||
/// Der Akku, gezeichnet wie der von macOS.
|
||||
@@ -259,9 +260,9 @@ public struct BatteryGlyphView: View {
|
||||
private var width: CGFloat { BatteryGlyph.size.width * scale }
|
||||
|
||||
public var body: some View {
|
||||
HStack(spacing: 1 * scale) {
|
||||
HStack(spacing: Onyx.Space.xxs * scale) {
|
||||
ZStack(alignment: .leading) {
|
||||
RoundedRectangle(cornerRadius: 3 * scale, style: .continuous)
|
||||
RoundedRectangle(cornerRadius: Onyx.Radius.bar * scale, style: .continuous)
|
||||
.strokeBorder(.primary.opacity(0.45), lineWidth: 1)
|
||||
|
||||
GeometryReader { geometry in
|
||||
@@ -296,7 +297,7 @@ public struct BatteryGlyphView: View {
|
||||
}
|
||||
.compositingGroup()
|
||||
|
||||
RoundedRectangle(cornerRadius: 1 * scale, style: .continuous)
|
||||
RoundedRectangle(cornerRadius: Onyx.Radius.bar * scale, style: .continuous)
|
||||
.fill(.primary.opacity(0.45))
|
||||
.frame(width: 2 * scale, height: 5 * scale)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public struct ChargeLimitControl: View {
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack(spacing: 6) {
|
||||
HStack(spacing: Onyx.Space.sm) {
|
||||
Text("charge.limit", bundle: .module)
|
||||
.font(Onyx.Font.caption)
|
||||
.foregroundStyle(Onyx.Color.textSecondary)
|
||||
@@ -62,7 +62,7 @@ public struct ChargeLimitControl: View {
|
||||
URL(string: "x-apple.systempreferences:com.apple.Battery-Settings.extension")!)
|
||||
} label: {
|
||||
Image(systemName: "arrow.up.forward.app")
|
||||
.font(.system(size: 10))
|
||||
.font(.system(size: Onyx.Symbol.small))
|
||||
.foregroundStyle(Onyx.Color.textTertiary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
@@ -12,7 +12,7 @@ struct MetricPopoverContent: View {
|
||||
let metric: MetricKind
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.lg) {
|
||||
// Ein Titel oben: bei drei gleich aussehenden Ringen sieht man
|
||||
// sonst nicht, ob man vor dem CPU- oder dem GPU-Popover steht.
|
||||
Text(String(localized: .init(metric.localizationKey), bundle: .module))
|
||||
@@ -25,7 +25,7 @@ struct MetricPopoverContent: View {
|
||||
case .temperature: SensorDetail(snapshot: model.snapshot)
|
||||
}
|
||||
}
|
||||
.padding(14)
|
||||
.padding(Onyx.Space.xl)
|
||||
.frame(width: 280)
|
||||
}
|
||||
}
|
||||
@@ -34,17 +34,6 @@ struct MetricPopoverContent: View {
|
||||
|
||||
/// Überschrift eines Abschnitts. Ohne sie ist eine lange Werteliste nur eine
|
||||
/// lange Werteliste.
|
||||
private struct SectionTitle: View {
|
||||
let text: LocalizedStringKey
|
||||
|
||||
var body: some View {
|
||||
Text(text, bundle: .module)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundStyle(Onyx.Color.accent)
|
||||
.textCase(.uppercase)
|
||||
}
|
||||
}
|
||||
|
||||
private struct Row: View {
|
||||
let label: String
|
||||
let value: String
|
||||
@@ -88,7 +77,7 @@ private struct Dial: View {
|
||||
var body: some View {
|
||||
// Sechs Punkte Abstand statt zwei: die Beschriftung klebte am Ring und
|
||||
// sah aus, als gehörte sie noch hinein.
|
||||
VStack(spacing: 6) {
|
||||
VStack(spacing: Onyx.Space.sm) {
|
||||
ZStack {
|
||||
Circle().stroke(.quaternary, lineWidth: 5)
|
||||
Circle()
|
||||
@@ -96,13 +85,13 @@ private struct Dial: View {
|
||||
.stroke(tint ?? Onyx.Color.accent,
|
||||
style: .init(lineWidth: 5, lineCap: .round))
|
||||
.rotationEffect(.degrees(-90))
|
||||
Text(text).font(.system(size: 15, weight: .medium).monospacedDigit())
|
||||
Text(text).font(Onyx.Font.headline.monospacedDigit())
|
||||
}
|
||||
.frame(width: 62, height: 62)
|
||||
.alignmentGuide(.dialCenter) { $0[VerticalAlignment.center] }
|
||||
Text(caption).font(.caption2).foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(.bottom, 2)
|
||||
.padding(.bottom, Onyx.Space.xxs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +104,7 @@ private struct ProcessorDetail: View {
|
||||
var body: some View {
|
||||
let snapshot = model.snapshot
|
||||
|
||||
HStack(spacing: 14) {
|
||||
HStack(spacing: Onyx.Space.xl) {
|
||||
Dial(value: metric == .cpu ? snapshot.cpu.total : (snapshot.gpu ?? 0),
|
||||
caption: metric == .cpu ? "CPU" : "GPU",
|
||||
text: MetricFormat.percent(metric == .cpu ? snapshot.cpu.total
|
||||
@@ -135,11 +124,11 @@ private struct ProcessorDetail: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
if metric == .cpu, !model.snapshot.cpu.perCore.isEmpty {
|
||||
SectionTitle(text: "popover.perCore")
|
||||
OnyxPopoverTitle(String(localized: "popover.perCore", bundle: .module))
|
||||
CoreGrid(values: model.snapshot.cpu.perCore)
|
||||
}
|
||||
|
||||
SectionTitle(text: "popover.power")
|
||||
OnyxPopoverTitle(String(localized: "popover.power", bundle: .module))
|
||||
if let cpu = snapshot.power.cpu {
|
||||
Row(label: String(localized: "popover.power.cpu", bundle: .module),
|
||||
value: MetricFormat.watts(cpu))
|
||||
@@ -154,7 +143,7 @@ private struct ProcessorDetail: View {
|
||||
}
|
||||
|
||||
if !snapshot.fans.isEmpty {
|
||||
SectionTitle(text: "popover.fans")
|
||||
OnyxPopoverTitle(String(localized: "popover.fans", bundle: .module))
|
||||
ForEach(snapshot.fans) { fan in
|
||||
Row(label: fan.name, value: "\(Int(fan.rpm)) U/min")
|
||||
}
|
||||
@@ -181,13 +170,13 @@ private struct CoreGrid: View {
|
||||
let values: [Double]
|
||||
|
||||
var body: some View {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 3), count: 8),
|
||||
spacing: 3) {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: Onyx.Space.xs), count: 8),
|
||||
spacing: Onyx.Space.xs) {
|
||||
ForEach(Array(values.enumerated()), id: \.offset) { _, value in
|
||||
RoundedRectangle(cornerRadius: 2, style: .continuous)
|
||||
RoundedRectangle(cornerRadius: Onyx.Radius.bar, style: .continuous)
|
||||
.fill(Onyx.Color.accent.opacity(0.18))
|
||||
.overlay(alignment: .bottom) {
|
||||
RoundedRectangle(cornerRadius: 2, style: .continuous)
|
||||
RoundedRectangle(cornerRadius: Onyx.Radius.bar, style: .continuous)
|
||||
.fill(Onyx.Color.accent)
|
||||
.frame(height: max(14 * value, 1.5))
|
||||
}
|
||||
@@ -205,12 +194,12 @@ private struct MemoryDetail: View {
|
||||
var body: some View {
|
||||
let memory = snapshot.memory
|
||||
|
||||
HStack(spacing: 14) {
|
||||
HStack(spacing: Onyx.Space.xl) {
|
||||
Dial(value: memory.usedFraction,
|
||||
caption: String(localized: "popover.memory.used", bundle: .module),
|
||||
text: MetricFormat.percent(memory.usedFraction),
|
||||
tint: pressureColor)
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.xs) {
|
||||
legend(Onyx.Color.accent, "popover.memory.app", memory.app)
|
||||
legend(.purple, "popover.memory.wired", memory.wired)
|
||||
legend(.teal, "popover.memory.compressed", memory.compressed)
|
||||
@@ -218,7 +207,7 @@ private struct MemoryDetail: View {
|
||||
}
|
||||
}
|
||||
|
||||
SectionTitle(text: "popover.memory.swap")
|
||||
OnyxPopoverTitle(String(localized: "popover.memory.swap", bundle: .module))
|
||||
// Auslagerung ist der Wert, der zählt: solange nicht ausgelagert wird,
|
||||
// ist ein voller Speicher kein Problem — macOS füllt ihn absichtlich.
|
||||
Row(label: String(localized: "popover.memory.swapUsed", bundle: .module),
|
||||
@@ -226,7 +215,7 @@ private struct MemoryDetail: View {
|
||||
emphasis: memory.swapUsed > 512 * 1024 * 1024 ? Onyx.Color.warning : nil)
|
||||
|
||||
if !snapshot.topMemoryProcesses.isEmpty {
|
||||
SectionTitle(text: "popover.processes")
|
||||
OnyxPopoverTitle(String(localized: "popover.processes", bundle: .module))
|
||||
ForEach(snapshot.topMemoryProcesses) { process in
|
||||
Row(label: process.name, value: bytes(process.bytes))
|
||||
}
|
||||
@@ -242,7 +231,7 @@ private struct MemoryDetail: View {
|
||||
}
|
||||
|
||||
private func legend(_ color: Color, _ key: LocalizedStringKey, _ value: UInt64) -> some View {
|
||||
HStack(spacing: 5) {
|
||||
HStack(spacing: Onyx.Space.sm) {
|
||||
Circle().fill(color).frame(width: 7, height: 7)
|
||||
Text(key, bundle: .module).font(.caption)
|
||||
Spacer(minLength: 8)
|
||||
@@ -263,7 +252,7 @@ private struct BatteryDetail: View {
|
||||
|
||||
var body: some View {
|
||||
if let battery = snapshot.battery {
|
||||
HStack(alignment: .dialCenter, spacing: 14) {
|
||||
HStack(alignment: .dialCenter, spacing: Onyx.Space.xl) {
|
||||
Dial(value: battery.charge,
|
||||
caption: String(localized: "metric.battery", bundle: .module),
|
||||
text: MetricFormat.percent(battery.charge),
|
||||
@@ -275,7 +264,7 @@ private struct BatteryDetail: View {
|
||||
// darunter waren zweimal dieselbe Auskunft an zwei Stellen —
|
||||
// und die zweite stand allein unter dem Diagramm, wo sie
|
||||
// aussah, als gehörte sie zu dessen Beschriftung.
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.xs) {
|
||||
if let remaining = battery.timeRemaining {
|
||||
Text(duration(remaining)).font(.title3.monospacedDigit())
|
||||
Text(battery.isCharging ? "popover.battery.untilFull"
|
||||
@@ -302,11 +291,11 @@ private struct BatteryDetail: View {
|
||||
// eine Akkusache, und man greift danach, während man auf den
|
||||
// Ladestand schaut.
|
||||
if let control {
|
||||
Divider().overlay(Onyx.Color.hairline)
|
||||
OnyxHairline()
|
||||
ChargeLimitControl(control: control, roomy: true)
|
||||
}
|
||||
|
||||
SectionTitle(text: "popover.battery.condition")
|
||||
OnyxPopoverTitle(String(localized: "popover.battery.condition", bundle: .module))
|
||||
if let health = battery.health {
|
||||
Row(label: String(localized: "popover.battery.health", bundle: .module),
|
||||
value: MetricFormat.percent(health))
|
||||
@@ -320,7 +309,7 @@ private struct BatteryDetail: View {
|
||||
value: MetricFormat.temperature(sensor.celsius))
|
||||
}
|
||||
|
||||
SectionTitle(text: "popover.power")
|
||||
OnyxPopoverTitle(String(localized: "popover.power", bundle: .module))
|
||||
if let watts = battery.watts {
|
||||
Row(label: String(localized: battery.isCharging ? "popover.battery.charging"
|
||||
: "popover.battery.draining",
|
||||
@@ -354,7 +343,7 @@ private struct BatteryDetail: View {
|
||||
isPluggedIn: battery.isPluggedIn,
|
||||
charge: battery.charge,
|
||||
limit: control?.chargeLimit)
|
||||
HStack(spacing: 6) {
|
||||
HStack(spacing: Onyx.Space.sm) {
|
||||
Image(systemName: symbol(for: activity))
|
||||
.font(.caption)
|
||||
.foregroundStyle(tint(for: activity))
|
||||
@@ -401,7 +390,7 @@ private struct SensorDetail: View {
|
||||
let snapshot: MetricsSnapshot
|
||||
|
||||
var body: some View {
|
||||
SectionTitle(text: "popover.temperature")
|
||||
OnyxPopoverTitle(String(localized: "popover.temperature", bundle: .module))
|
||||
ForEach(snapshot.sensors) { sensor in
|
||||
HStack {
|
||||
Text(sensor.name).font(.callout)
|
||||
@@ -421,9 +410,9 @@ private struct SensorDetail: View {
|
||||
}
|
||||
|
||||
if !snapshot.fans.isEmpty {
|
||||
SectionTitle(text: "popover.fans")
|
||||
OnyxPopoverTitle(String(localized: "popover.fans", bundle: .module))
|
||||
ForEach(snapshot.fans) { fan in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
VStack(alignment: .leading, spacing: Onyx.Space.xxs) {
|
||||
HStack {
|
||||
Text(fan.name).font(.callout)
|
||||
Spacer(minLength: 12)
|
||||
|
||||
Reference in New Issue
Block a user