M29: App-Einstellungen (Settings-Fenster ⌘,)

Neues natives macOS-Settings-Fenster mit Sprache, Auto-Update-Check,
Farbschema (Standard/Kontrastreich), echter Textgrößen-Skalierung,
Bedienelement-Größe und LAN-Scanner-Refreshraten/Sparkline-Einstellungen.

- AppPreferences.swift: zentrale AppStorage-Keys, ColorTheme/AppTextSize/
  UIDensity
- SettingsView.swift: 3-Tab-Settings-Scene
- .environment(\.dynamicTypeSize) erwies sich auf macOS als wirkungslos
  (per ImageRenderer-Snapshot bewiesen) — durch eigenen appFontScale-
  Mechanismus ersetzt, ~110 .font(...)-Aufrufe app-weit umgestellt
- Farbschema auf Wunsch auch auf Experte-Tab-Sidebar ausgeweitet
- Docs aktualisiert: README/HANDOFF/CHATLOG/Manual.md+PDF, found.md

Live bestätigt nach mehreren Nachbesserungsrunden ("passt, lassen wir so").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kay
2026-09-17 13:38:22 +02:00
co-authored by Claude Sonnet 5
parent 53a2931239
commit 221b914b05
25 changed files with 803 additions and 157 deletions
+44 -7
View File
@@ -85,14 +85,51 @@ Nachbesserung 2 (User-Wunsch: Trafficanzeige in MB, Sparkline-Zeitfenster auf 30
Nachbesserung 3 (User-Wunsch: Aktualisierungsrate auf 0,1s): `startTrafficPolling` von `.milliseconds(500)` auf `.milliseconds(100)` — bei 30s-Fenster damit bis zu 300 Punkte pro Sparkline/Port (bisher ~60). Nicht weiter geprüft, ob das bei vielen Ports spürbar CPU kostet — bei Bedarf zurückmelden. Manual.md "Abfrage alle 0,1s" + PDF mit aktualisiert. Build grün, App startet ohne Absturz. Noch nicht live geprüft.
### 5. App-Einstellungen / Settings — eigener Menüpunkt
**Status:** offen (nur Feature-Idee notiert, noch nicht umgesetzt)
**Status:** fixed (live bestätigt: "passt, lassen wir so")
Wunsch: eigener Einstellungen-Bereich für Personalisierung, Layout, Farbverwaltung, Refreshraten, Updates, Schriftgrößen, Responsiveness, etc.
Ist-Zustand: es gibt aktuell **eine einzige** persistierte App-Einstellung (`@AppStorage("appLanguage")`, DE/EN-Umschalter in der Toolbar). Alles andere, was unter "Settings" fallen würde, ist im Code hart verdrahtet und über die Session verstreut — u.a. gerade erst in diesem Fund-Zettel mehrfach angefasste Werte:
- Refreshraten: LAN-Scanner-Traffic-Poll `DevicesViewModel.startTrafficPolling` (aktuell 0,1s), Verbinden-Tab-Traffic-Poll `ConnectViewModel.startTrafficPolling` (3s, separat)
- Sparkline-Zeitfenster (`DevicesViewModel.appendTrafficHistory`, aktuell 30s) und -Breite (`TrafficSparkline`, aktuell 200pt)
- Farben: `OverviewStyle.color(for:)` (Node-Kategorien, Kanten-Arten), Firewall-Icons, Status-Farben im LAN-Scanner (Port-Scan rot/grün/grau)
- Übersicht-Zoom-Standardwert, Fokus-Popup-Größe/Verhalten
Rückfrage vorab geklärt (AskUserQuestion): natives macOS-Settings-Fenster (⌘,), nicht eigener Tab; alle genannten Bereiche jetzt sofort umsetzen, nicht nur ein Kernstück; Farbverwaltung als 2 vordefinierte Themes ("Standard"/"Kontrastreich"), kein freier Color-Picker pro Kategorie.
Keine echte Settings-Infrastruktur (kein Settings-Tab/-Fenster, kein zentrales Preferences-Model) vorhanden — müsste komplett neu aufgebaut werden. Größerer, noch nicht spezifizierter Umbau; sollte vor Umsetzung geklärt werden: eigener Tab vs. natives macOS-Settings-Fenster (⌘,), welche der o.g. Werte tatsächlich zuerst einstellbar sein sollen, ob pro-Gerät oder global.
Umsetzung:
- **`Core/Models/AppPreferences.swift`** (neu): zentrale `AppStorage`-Key-Konstanten + `AppPreferences.registerDefaults()` (in `RouterOSAssistantApp.init()` aufgerufen), plus drei Enums:
- `ColorTheme` (`standard`/`highContrast`) — Farben für Übersicht-Node-Kategorien, Kanten-Arten, und LAN-Scanner-Status (Traffic aktiv, Fest/Dynamisch, offener/geschlossener Port). Ersetzt die alten `OverviewStyle.color(for:)`-Funktionen (die jetzt nur noch `explanation(for:)`/`icon(for:)` halten).
- `AppTextSize` (Klein/Standard/Groß/Sehr groß) — Skalierungsfaktor (0,85/1,0/1,2/1,4), per `.environment(\.appFontScale, ...)` an der `WindowGroup`-Wurzel gesetzt.
- `UIDensity` (Kompakt/Standard/Komfortabel) — mappt auf `ControlSize`, ebenfalls an der Wurzel via `.controlSize(...)`. Deckt den "Responsiveness"-Wunsch pragmatisch als Bedienelement-Größe ab (die App ist eine feste macOS-Fensterlayout-App ohne eigentliche responsive Breakpoints — eine echte "Responsiveness"-Funktion in dem Sinn gibt es nicht, siehe unten).
- **`Features/Settings/SettingsView.swift`** (neu): `Settings { }`-Scene mit 3 Tabs — Allgemein (Sprache, Auto-Update-Check beim Verbinden), Darstellung (Farbschema, Textgröße, Bedienelemente), Netzwerk (LAN-Scanner Aktualisierungsrate/Sparkline-Zeitfenster/-Breite). Alle Controls sind reine `@AppStorage`-Bindings, kein eigenes ViewModel.
- **Live-Wirkung ohne Neustart**: `OverviewView`/`DevicesView` lesen `colorTheme` per `@AppStorage` und geben es an `EdgesCanvas`/`NodeCardView`/`NodeDetailView`/`EdgeDetailView`/`EdgeTooltipView`/`LegendView`/`DeviceRow`/`PortScanRow` durch. `DevicesViewModel.startTrafficPolling`/`appendTrafficHistory` lesen Intervall/Zeitfenster bei jedem Tick frisch aus `UserDefaults` (nicht einmalig beim Start), damit eine Änderung im Settings-Fenster sofort greift, auch während der Tab schon pollt.
- **"Beim Verbinden automatisch nach Updates suchen"** (neuer Toggle, Default aus): `ConnectViewModel.connect()` ruft nach erfolgreicher Verbindung automatisch dieselbe `checkForUpdates(for:)` auf, die sonst nur der manuelle Button im Verbinden-Tab auslöst.
- L10n.swift um alle neuen Settings-Strings ergänzt (DE/EN).
- `.xcodeproj` manuell um beide neue Dateien ergänzt (PBXBuildFile/PBXFileReference/PBXGroup/Sources-Build-Phase).
Bewusst nicht umgesetzt: Übersicht-Zoom-Standardwert und Fokus-Popup-Größe/Verhalten sind nicht in Settings aufgenommen — beides sind reine `@State`-Werte ohne bestehenden Persistenz-Mechanismus und wurden in der Rückfrage nicht explizit als "jetzt" gefordert; bei Bedarf als vierte Kategorie nachziehbar.
Nachbesserung 1 (Live-Test: "Bedienelemente und Sparkling funktioniert, bei Textgröße und Farbschemas sehe ich keine Änderung"):
- Farbschema-Bug gefunden: 3 Farben (`.service`-Kategorie, `.dhcp`- und `.wireguardPeer`-Kanten) waren in `standard` und `highContrast` exakt identisch definiert — Kopierfehler beim ersten Entwurf. Behoben, beide Paletten jetzt komplett überschneidungsfrei.
- Textgröße nachgebessert (Bereich auf macOS-Accessibility-Stufen erweitert für einen unübersehbaren Sprung).
Nachbesserung 2 (Live-Test: erneut "unverändert" bei beidem, trotz Fix 1) — Ursache mit Beweis gefunden statt weiter geraten: per `ImageRenderer`-Snapshot (Scratchpad-Skript) nachgewiesen, dass `.environment(\.dynamicTypeSize, ...)` auf macOS **gar keine Wirkung** hat — identisches Pixel-Rendering bei `.xSmall` und `.accessibility3`. Anders als iOS skalieren SwiftUI-Textstile auf macOS nicht über Dynamic-Type-Kategorien. Rückfrage an User: 3 Alternativen zur Wahl gestellt, User wählte "Echtes Text-only Scaling".
Umsetzung (komplett neuer Mechanismus, `AppTextSize.dynamicTypeSize` entfernt):
- Neuer `\.appFontScale`-Environment-Key (`CGFloat`, Default 1.0) + `AppFontStyle`-Enum mit expliziten macOS-Basis-Punktgrößen pro Textstil (title2/title3/headline/body/callout/subheadline/caption/caption2) + `.appFont(_:weight:bold:design:)`-View-Modifier, der bei jedem Aufruf `baseSize * scale` real rendert.
- Alle ~110 `.font(...)`-Aufrufe in der App auf `.appFont(...)` umgestellt (automatisiert per Scratchpad-Skript, textbasierte 1:1-Ersetzung pro bekanntem Font-Ausdruck) — **außer** zwei Stellen mit fest-breiten Pixel-Layouts, die beim Skalieren brechen würden: `OverviewView.NodeCardView` (Diagramm-Knotenkarten, feste `nodeWidth`/`nodeHeight`) und `DevicesView.DeviceRow`/`DeviceColumnHeader` (LAN-Scanner-Tabellenspalten mit festen `DeviceColumn`-Breiten + `lineLimit(1)`). Diese bleiben bewusst fix, sonst Text-Clipping/Überlappung.
- Mechanismus per `ImageRenderer`-Snapshot verifiziert (Basis-Text bei Skalierungsfaktor 0,85 vs. 1,4 rendert nachweislich unterschiedlich groß), bevor erneut um Live-Test gebeten wurde.
Build grün, alle 98 Unit-Tests grün.
Nachbesserung 3 (Live-Test: "textgröße funktioniert, die Farbschemas nicht"): Textgröße bestätigt. Farbschema noch kaputt — beim Fix in Nachbesserung 1 nur die Übersicht-Diagrammfarben (Node-Kategorien/Kanten) überarbeitet, die LAN-Scanner-eigenen Theme-Farben (`trafficActive`/`staticLease`/`dynamicLease`/`portOpen`/`portClosed`) dabei übersehen — die waren weiterhin zu ähnliche SwiftUI-Nachbarfarben (grün/mint, orange/gelb, rot/pink), an einem kleinen Punkt/Icon praktisch nicht unterscheidbar. User fragte, ob selbst am Router geprüft werden kann — nein (kein Zugang/Tool dafür), aber Bug sitzt ohnehin rein im App-Rendering, nicht am Router: per `ImageRenderer`-Snapshot der tatsächlichen Farbwerte selbst verifiziert (Swatch-Vergleich beider Themes, sichtbar unterschiedlich) statt nur zu behaupten, es sei jetzt richtig. Neue Werte: `trafficActive`/`dynamicLease`/`portClosed` grün→blau, `staticLease` orange→lila, `portOpen` rot→orange.
Bitte nochmal live testen — diesmal auch LAN-Scanner-Statuspunkte (Fest/Dynamisch, Port-Scan) einschließen, nicht nur Übersicht-Diagramm.
Nachbesserung 4 (Live-Test: "Lan-scanner und Übersicht funktionieren" — Farbschema damit bestätigt; aber "Experte-Tab zeigt noch keine Änderung" bei Textgröße): Ursache gefunden — die komplette Sidebar-Liste (`Text(schema.displayName)` in `ExpertView.swift`) und jedes einzelne Parameter/Wert-`TextField` in `ExpertMenuDetailView.swift` hatten von Anfang an **gar kein** `.font(...)`. Die automatisierte `.appFont(...)`-Umstellung ersetzte nur vorhandene `.font(...)`-Aufrufe — unstyled Text/TextField (SwiftUI-Systemstandard, fix) blieb dadurch unangetastet. Fix: `.environment(\.font, .system(size: 13 * scale))` einmal an der `WindowGroup`-Wurzel als Fallback — wirkt nur dort, wo kein eigenes `.appFont(...)`/`.font(...)` bereits einen Font setzt (der gewinnt lokal weiterhin), deckt aber jede unstyled Stelle app-weit ab, nicht nur den Experte-Tab.
Build grün, alle 98 Unit-Tests grün.
Nachbesserung 5 (User-Feedback: "keine Änderung beim Farbschema unter Experte") — war kein Bug, sondern Scope: Farbschema war nie für den Experte-Tab verdrahtet, dort gab's schlicht keine kategorie-eingefärbten Elemente (Sidebar/Icons alle `.secondary`). Rückfrage: soll das ausgeweitet werden? User: ja. Umsetzung: `ColorTheme.color(for: RouterOSMenuCategory)` (neu, `AppPreferences.swift`) bucketet die 13 `RouterOSMenuCategory`-Fälle auf die bestehende 5-Farb-Palette (alle 5 Firewall-Untermenüs → `.firewall`, VPN/WLAN/Queues/System/Tools → `.service`) — dieselben Farben wie im Übersicht-Diagramm, keine zweite Palette. `ExpertView.sectionHeader(...)` bekommt neuen optionalen `categoryColor`-Parameter, zeigt bei Kategorie-Sektionen einen 8pt-Farbpunkt vor dem Titel (bei "Eigener Menüpfad" weiterhin `nil`, keine Farbe).
Build grün, alle 98 Unit-Tests grün.
6. Mode-Button Setup: an der Seite des Router ist eine Taste "Mode" diese ist belegbar (Scripte, Deaktivierung, etc). einlesen, was geht und bauen.
7. Manual direkt in die App integrieren.
8. Abwechselnde ´Farbkombis bei Tabellenansichten - besser Lesbarkeit
9. Settings → Darstellung: anpassbare Farben — freie Farbwahl pro Kategorie/Status statt nur der 2 vordefinierten Themes (Standard/Kontrastreich aus Punkt 5). Beim ursprünglichen Rückfrage bewusst auf 2 Themes beschränkt; falls später gewünscht, bräuchte es ColorPicker pro `OverviewNode.Category`/`OverviewEdgeKind`/LAN-Scanner-Status/`RouterOSMenuCategory` statt der festen `ColorTheme`-Switch-Statements in `AppPreferences.swift`.