Physarum Native — Parameter Tooltips (paramHelp) Design Spec¶
Status: Approved · 2026-06-03 · author: Johannes (brainstormed with Claude) Builds on: the registry-driven editor (
2026-06-03-authoring-editor-design.md) + adaptive compact UI. Ports the web app's per-parameter help (src/ui/paramHelp.ts) to the native editor, following current Apple HIG / SwiftUI best practices (see memorynative-app-apple-hig).
1. Kontext & Ziel¶
Der native Editor zeigt 45 Slider-Params ohne Erklärung. Die Web-App hat pro Param einen
Ein-Satz-Hilfetext (PARAM_HELP in src/ui/paramHelp.ts), eingeblendet per Tooltip. Dieser
Increment bringt diese Hilfe nativ — selbsterklärender Editor auf macOS/iPad/iPhone — und richtet
das Surface an aktuellen HIG-Best-Practices aus (Discoverability, Popover-Adaptation, Accessibility,
Dynamic Type).
2. Festgezurrte Entscheidungen (Brainstorming 2026-06-03)¶
| Achse | Entscheidung |
|---|---|
| Surface | Label-Tap → Popover mit dem Hilfetext. macOS zusätzlich Hover-.help(...). |
| Discoverability | Ein dezentes inline-ⓘ direkt hinter dem Label-Text (tertiär, klein) signalisiert „Hilfe vorhanden" — HIG: Interaktives muss interaktiv aussehen. Keine separate Trailing-Spalte (clutter-arm). |
| Popover (compact) | .popover(isPresented:) + .presentationCompactAdaptation(.popover) → auf iPhone eine kleine verankerte Info-Blase, kein Full-Sheet. |
| Accessibility | .accessibilityHint(tooltip) am Slider → VoiceOver liest die Hilfe; nicht nur visuell. |
| Dynamic Type | Popover-Text bricht um, respektiert Schriftgröße, ~260 pt breit, padding. |
| Daten | Texte 1:1 aus src/ui/paramHelp.ts portiert in ein Swift-Dict, keyed by nativem Label (vermeidet Web↔Native-Label-Mismatch). Matrix-Zellen bekommen einen gemeinsamen Text. |
| Scope | Skalare ParamSlider-Zeilen (45, inkl. 9 Matrix). Non-Scalar-Controls (Spawn/Blend-Picker, Farben) v1-out-of-scope. |
3. Daten: ParamHelp¶
Neues Sources/Model/ParamHelp.swift:
- static let byLabel: [String: String] — keyed by nativem ParamDescriptor.label (z.B. "Agents",
"Population", "Deposit", "Decay", "Diffusion", "Fade", "Trail cap", "Rhythm",
"Head glow", "Feedback", "Bloom", sowie die deckungsgleichen "Exposure", "Sensor angle",
…). Texte wörtlich aus PARAM_HELP übernommen (eine Quelle zum Portierzeitpunkt; spiegelt die Web-Datei,
kein Auto-Sync in v1).
- static func text(for label: String) -> String — Matrix-Labels (Präfix "M[") → ein gemeinsamer
englischer Text (UI-Sprache der App, konsistent mit den portierten Texten): "How strongly the
row species senses the column species' trail. Positive attracts, negative repels; the diagonal is
a species' response to its own trail (cohesion)."; sonst byLabel[label] ?? "".
4. Registry-Anbindung¶
ParamDescriptor bekommt let tooltip: String. Der add(...)-Builder in ParamRegistry.all setzt
tooltip: ParamHelp.text(for: label). Anpasspunkt: der manuelle ParamDescriptor(...)-Init in
SpeciesSection.broadcast(_:) (Sections.swift) muss tooltip: d.tooltip durchreichen.
5. Surface in ParamSlider¶
In der Label-HStack: Label-Text + ein kleines inline-Image(systemName: "info.circle") (tertiär,
~10 pt), nur wenn descriptor.tooltip nicht leer. Label+ⓘ in einem Button/.onTapGesture, das
showHelp = true setzt (@State private var showHelp). Daran:
- .popover(isPresented: $showHelp) mit dem Text (.font(.callout), multilineTextAlignment(.leading),
.frame(width: 260), .padding(), .presentationCompactAdaptation(.popover)).
- macOS zusätzlich .help(descriptor.tooltip) am Control (Hover).
- .accessibilityHint(descriptor.tooltip) am Slider.
Koexistenz: Das Label-Tap (single) liegt auf dem Label-Subview; der bestehende Zeilen-Doppeltap-Reset liegt auf dem VStack — getrennte Subviews, kein Gesten-Konflikt. (Im Plan auf Geräte-Disambiguierung achten.)
6. Testing & Verifikation¶
- Unit-testbar (App-Test-Target):
- Jeder der 45 Descriptors hat einen nicht-leeren
tooltip(fängt fehlende/vertippte Label-Keys). ParamHelp.text(for:): bekanntes Label → erwarteter Text;"M[1,2]"→ der Matrix-Text; unbekanntes Label →"".- Build + visuell (Johannes): macOS Hover-Tooltip + Label-Tap-Popover; iPhone/iPad Label-Tap → kleine verankerte Popover-Blase (kein Sheet); VoiceOver liest den Hint; Dynamic Type vergrößert den Text.
- Gate: App-Suite grün (jetzt inkl. der neuen Tooltip-Tests); macOS + iOS-Sim BUILD SUCCEEDED.
7. Scope (in) / Out-of-scope¶
In: ParamHelp (portierte Texte, native-Label-keyed, Matrix-Sammeltext) · ParamDescriptor.tooltip
+ Registry-Befüllung + broadcast()-Anpassung · ParamSlider inline-ⓘ + Label-Tap-Popover +
macOS-.help + .accessibilityHint + Compact-Popover-Adaptation + Dynamic-Type-Sizing · Unit-Tests.
Out-of-scope (spätere Increments): Hilfe für Non-Scalar-Controls (Spawn/Blend/Farben — Picker-Titel nicht sauber antippbar ohne Umbau) · Audio-Param-Hilfe (Audio ist nativ ohnehin nicht editierbar) · Auto-Sync der Texte aus der Web-SSOT (manuelle Portierung reicht; Texte sind stabil).
8. Offene Risiken / im Plan zu klären¶
- Gesten-Disambiguierung Label-single-tap vs Zeilen-double-tap-Reset auf echtem Gerät prüfen.
.presentationCompactAdaptationist iOS 16.4+/macOS 13.3+ — bei Deployment-Target (macOS 14/iOS 17) verfügbar.- Label-Mapping-Vollständigkeit: der 45-Descriptor-Test ist das Sicherheitsnetz; jedes native Label
muss in
byLabelstehen (oder ein Matrix-Label sein).