feat(presenter): microfoon-doorvoer tijdens presenteren (#2158) #2160

Merged
brenno merged 4 commits from feature/mic-monitor-presenter into main 2026-09-22 16:21:01 +00:00
Owner

Summary

  • Nieuwe MicMonitor-naad (lib/services/mic_monitor.dart) met één dunne binding op flutter_webrtc (mic_monitor_webrtc.dart): een lokale loopback van twee peer connections in één proces, iceServers leeg — er verdwijnt niets het netwerk op.
  • Mic-knop in de presenter-cockpit (mic/mic_none, rood wanneer live) plus sneltoets V en legenda-rij; desktop-only, op web staat de knop niet.
  • Luie aanmaak: de OS-machtigingsprompt komt pas bij het eerste aan-zet, nooit bij het openen van de presentatie. Standaard uit, vindbaar aan.
  • Elke afsluitweg (Esc, sluitknop, dispose) breekt de stream af — ook wanneer de start nog loopt (busy-guard + generatie-telling in de binding).
  • Mislukte start: staat blijft uit, SnackBar zegt wat er mis is en waar te kijken (machtiging/invoerapparaat).
  • NSMicrophoneUsageDescription toegevoegd aan macos/Runner/Info.plist — zonder die key crasht getUserMedia onder TCC.
  • Drie nieuwe strings via tool/add_l10n.dart in alle 31 overlays.

Test plan

  • test/mic_monitor_presenter_test.dart: 7 tests met een fake achter de naad — aan/uit via knop en V, foutpad, busy-guard, exit tijdens hangende start en tijdens actieve doorvoer.
  • make check: alle statische poorten, volledige suite (11909 tests), dekkingsvloer én per-file-vloer groen.
  • make check-secrets (gitleaks + trufflehog): schoon. make sast (semgrep): 0 findings.
  • make test-golden: faalt op deze machine — ook op schone main (32 PNG's, allemaal 0.00%/enkele-pixels verschil; vermoedelijk drift door de Flutter-pin 3.47.4→3.47.5). Niet door deze wijziging veroorzaakt: deze PR raakt de slide-renderer niet. De goldens zijn bewust machine-specifiek en niet-required; macos-gate.yml toetst ze post-merge op de Mac-runner.
  • Handmatig op macOS: presentatie openen → V → mic-prompt → stem hoorbaar op uitvoer; Esc → mic-indicator uit.

Bewaker-notitie

Deze wijziging raakt een vertrouwensgrens (microfoon) en een dependency (flutter_webrtc, al aanwezig). Waardenafweging: soevereiniteit gaat voor — de doorvoer is sessiegedrag, staat standaard uit, wordt nergens opgeslagen (niet in deck-metadata, niet in AppSettings) en opent géén netwerkpad (lege iceServers, geen signalling). De binding kan geen opname doen: er is geen recorder, alleen render-naar-uitvoer.

Closes #2158

Generated with Devin

## Summary - Nieuwe `MicMonitor`-naad (`lib/services/mic_monitor.dart`) met één dunne binding op `flutter_webrtc` (`mic_monitor_webrtc.dart`): een lokale loopback van twee peer connections in één proces, `iceServers` leeg — er verdwijnt niets het netwerk op. - Mic-knop in de presenter-cockpit (`mic`/`mic_none`, rood wanneer live) plus sneltoets `V` en legenda-rij; desktop-only, op web staat de knop niet. - Luie aanmaak: de OS-machtigingsprompt komt pas bij het eerste aan-zet, nooit bij het openen van de presentatie. Standaard uit, vindbaar aan. - Elke afsluitweg (Esc, sluitknop, dispose) breekt de stream af — ook wanneer de start nog loopt (busy-guard + generatie-telling in de binding). - Mislukte start: staat blijft uit, SnackBar zegt wat er mis is en waar te kijken (machtiging/invoerapparaat). - `NSMicrophoneUsageDescription` toegevoegd aan `macos/Runner/Info.plist` — zonder die key crasht getUserMedia onder TCC. - Drie nieuwe strings via `tool/add_l10n.dart` in alle 31 overlays. ## Test plan - [x] `test/mic_monitor_presenter_test.dart`: 7 tests met een fake achter de naad — aan/uit via knop en `V`, foutpad, busy-guard, exit tijdens hangende start en tijdens actieve doorvoer. - [x] `make check`: alle statische poorten, volledige suite (11909 tests), dekkingsvloer én per-file-vloer groen. - [x] `make check-secrets` (gitleaks + trufflehog): schoon. `make sast` (semgrep): 0 findings. - [ ] `make test-golden`: **faalt op deze machine — ook op schone `main`** (32 PNG's, allemaal 0.00%/enkele-pixels verschil; vermoedelijk drift door de Flutter-pin 3.47.4→3.47.5). Niet door deze wijziging veroorzaakt: deze PR raakt de slide-renderer niet. De goldens zijn bewust machine-specifiek en niet-required; `macos-gate.yml` toetst ze post-merge op de Mac-runner. - [ ] Handmatig op macOS: presentatie openen → V → mic-prompt → stem hoorbaar op uitvoer; Esc → mic-indicator uit. ## Bewaker-notitie Deze wijziging raakt een vertrouwensgrens (microfoon) en een dependency (`flutter_webrtc`, al aanwezig). Waardenafweging: **soevereiniteit** gaat voor — de doorvoer is sessiegedrag, staat standaard uit, wordt nergens opgeslagen (niet in deck-metadata, niet in AppSettings) en opent géén netwerkpad (lege `iceServers`, geen signalling). De binding kan geen opname doen: er is geen recorder, alleen render-naar-uitvoer. Closes #2158 Generated with [Devin](https://devin.ai)
Voor microfoon-doorvoer tijdens presenteren: mic-invoer rechtstreeks op
de audio-uitvoer. De naad houdt de beslissingen testbaar; de binding is
dun want libwebrtc kan niet headless. De loopback gebruikt lege
iceServers — gathering levert alleen host-kandidaten, er verdwijnt
niets het netwerk op. Echo-onderdrukking aan, want juist de gemonitorde
uitvoer zou anders terug de mic in lopen.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
De presentator zet de doorvoer aan met de mic-knop of V; de monitor wordt
pas gebouwd bij het eerste aan-zet, zodat de machtigingsprompt niet bij
het openen van de presentatie komt. Elke afsluitweg — Esc, sluitknop,
dispose — breekt de stream af, ook als de start nog loopt. Bij een
mislukte start blijft de staat uit en krijgt de presentator een melding
met herstelrichting. Desktop-only: op web staat de knop niet.

Bevat ook de verhuizing van _fmtClock/_fmtElapsed/_fmtRemaining en
_slideHasRemoteMedia naar top-level functies — pure helpers zonder
state, en de klasse-plafondratchet telde ze mee bij
_FullscreenPresenterState.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Tooltip, legenda-rij en de foutmelding bij een mislukte start, via
tool/add_l10n.dart met tool/mic_monitor_l10n_spec.json als spec.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
test(presenter): mic-doorvoer via fake MicMonitor (#2158)
All checks were successful
scans / scans (pull_request) Successful in 3m7s
static-gate / static-gate (pull_request) Successful in 6m36s
7bfed21e85
Zes widgettests achter de naad: aan/uit via knop en toets V, foutpad met
melding, busy-guard tegen dubbele start, en afbraak bij exit zowel
tijdens een lopende start als bij actieve doorvoer. De echte binding
staat in uncoveredBaseline — libwebrtc vraagt een apparaat en kan niet
headless draaien.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
brenno merged commit 52802c2d69 into main 2026-09-22 16:21:01 +00:00
Sign in to join this conversation.
No description provided.