Zum Inhalt

Physarum Native — Audio UI Fix + Default-on-Enable Design Spec

Status: Approved · 2026-06-05 · author: Johannes (brainstormed with Claude) Builds on: the audio-reactivity v2 iteration. Two on-device fixes: the route editor UI is broken on iPhone (overlapping controls), and there is no default routing pre-wired.

1. Kontext & Feedback (Gerät, 2026-06-05)

  1. Route-Editor-UI kaputt auf iPhone: die Route-Zeile quetscht 3 Menü-Picker (Quelle/Ziel/Mode) + Remove + Depth-Slider in eine Zeile; die langen camelCase-Ziel-Labels (moveSpeed, bloomIntensity) sprengen die compact-Breite und überlappen den Slider.
  2. Kein Default-Routing vor-aktiviert: out-of-the-box reagiert nichts; man muss den „Default"-Knopf finden.

2. Festgezurrte Entscheidungen (Brainstorming 2026-06-05)

Achse Entscheidung
Default-Aktivierung Auto-apply on first enable. Audio bleibt aus bis der Nutzer „Enabled" antippt; in dem Moment, falls routes.isEmpty, werden die 5 Default-Routen + autoGain=true automatisch gesetzt → reagiert sofort. Kein Mic beim App-Start. Der „Default"-Knopf bleibt (re-apply).
Route-Editor 2-Zeilen-Karte je Route. Zeile 1: Quelle-Picker · „→" · Ziel-Picker (kurze Labels) · Remove (rechts). Zeile 2: Mode (segmented Lvl/Trans) · Depth-Slider · Wert. Nichts überläuft.
Kurze Ziel-Labels moveSpeed→Move, turnSpeed→Turn, depositAmount→Deposit, sensorDistance→Sensor, rhythmAmplitude→Rhythm, chaosStrength→Chaos, exposure→Expo, bloomIntensity→Bloom, saturation→Sat, contrast→Contr.

3. Komponenten

3.1 AppState — geteilte Default-Routen + auto-on-enable

  • Faktor die 5 Default-Routen in eine statische Quelle: static let defaultAudioRoutes: [AudioRoute] = [...] (die exakten 5 aus der Iteration: bass/transient→bloom 1.2, bass/transient→move 0.8, high/transient→exposure 0.6, mid/transient→chaos 0.7, mid/level→saturation 0.4). applyDefaultAudioWiring() nutzt diese Liste.
  • Neue Methode setAudioEnabled(_ on: Bool): pushUndo(); wenn on && edited.audioReactive.routes.isEmpty → edited.audioReactive.routes = Self.defaultAudioRoutes; edited.audioReactive.autoGain = true; dann edited.audioReactive.enabled = on. (Wenn schon Routen da sind, nur enabled setzen — der Nutzer hat bereits eine Konfiguration.)

3.2 AudioReactiveSection — Enable-Toggle nutzt setAudioEnabled

Der „Enabled"-Toggle ruft state.setAudioEnabled($0) statt direkt edited.audioReactive.enabled = $0. (Auto-fill der Routen bei erstem Enable.)

3.3 AudioReactiveSection.routeRow — 2-Zeilen-Karten-Redesign

Ersetze die einzeilige HStack { sourcePicker; targetPicker; modePicker; remove } + Slider durch:

VStack(spacing: s1) {
  HStack { sourcePicker(.menu, kompakt) ; Image("arrow.right") ; targetPicker(.menu, kurzes Label) ; Spacer() ; removeButton }
  HStack { modePicker(.segmented Lvl/Trans, feste Breite ~120) ; depthSlider ; valueText("%+.1f") }
}
.padding(.vertical, s1)
.background(Theme.Colors.overlay, in RoundedRectangle)  // dezente Karte
- Ziel-Picker zeigt das kurze Label (shortTarget(_:)-Helper) statt $0.rawValue. - Mode-Picker wird .segmented (statt .menu) — „Lvl"/„Trans", kompakt. - Alle bestehenden Bounds-Guards in den Bindings bleiben (kein OOB-Crash bei Remove). - Depth-Wert dezent rechts (String(format: "%+.1f", depth)).

4. Testing & Verifikation

  • Unit: setAudioEnabled(true) auf leeren Routen → 5 Default-Routen + autoGain + enabled; setAudioEnabled(true) mit vorhandenen Routen → Routen unverändert, nur enabled; setAudioEnabled(false) → enabled=false, Routen unangetastet. AppState.defaultAudioRoutes == die 5 erwarteten (Tabelle).
  • Build + Sim-Screenshot: Route-Editor-Karten korrekt gelayoutet (kein Überlauf) auf iPhone-Sim; Engine swift test + App-Suite grün.
  • Gerät (Johannes): „Enabled" antippen → 5 Routen erscheinen + reagiert; die Route-Karten sauber lesbar; Mode-Segmented + Depth-Slider bedienbar.
  • Gate: App-Suite grün; macOS + iOS-Sim BUILD SUCCEEDED; aufs iPhone deployed.

5. Out-of-scope / Risiken

  • Kein Mic-on-launch (bewusst — Permission/Akku/App-Store). Audio-Feel-Tuning (Silence-Gate, Over-Boost) bleibt separat.
  • Drag-Reorder der Routen, Custom-Depth-Eingabe → später.
  • Die id: \.offset-ForEach + Bounds-Guards bleiben (kein Identifiable-Umbau von AudioRoute).