- Onglet Groupes : boutons Modifier et Supprimer fonctionnels via délégation
d'événements et data-attributes ; slugify() côté client synchronisé avec
le serveur ; classe CSS btn-danger pour Supprimer
- Export logs : bouton "Exporter JSON" dans l'onglet Logs (filtre niveau actif)
- Export/Import config.yaml : section dédiée dans Configuration avec dialog
système (backup automatique .bak avant import) via IPC Electron
(config:export / config:import dans main.js + preload.js)
Statut serveur :
- SERVER_URL utilisait "localhost", que le Node embarqué par Electron peut
résoudre en IPv6 (::1) en priorité ; le serveur n'écoutant qu'en IPv4
(host: 0.0.0.0), le ping de statut échouait silencieusement alors que le
serveur tournait. Bascule sur 127.0.0.1 (main.js + preload.js).
- L'erreur réelle de pingServer() n'était jamais remontée au renderer
(health.error perdu) ; elle est maintenant incluse dans la réponse IPC.
URL/QR code clients :
- L'URL affichée utilisait un remplacement de chaîne ("localhost" -> IP) qui
ne matchait plus rien depuis le passage à 127.0.0.1 ; remplacé par un
parsing d'URL qui ne réutilise que le protocole/port.
- Le QR code dépendait d'une lib chargée depuis un CDN externe, inadapté à
une app self-hosted censée fonctionner sans accès Internet sur le WiFi
d'un événement. Généré désormais côté Main Process avec la lib qrcode
(déjà en dépendance, jamais utilisée) et transmis au renderer en data URL ;
suppression du fichier placeholder et de la dépendance CDN.
- getNetworkIP() lisait /admin/config, qui renvoie la valeur YAML brute
"AUTO" (jamais résolue), donc retombait toujours sur "localhost".
Remplacé par la détection réseau du Main Process (même logique que pour
les certificats mkcert).
- Ajout d'un placeholder visuel (icône + message) tant qu'aucun QR code
n'est généré ou que le serveur est arrêté, en CSS pur.
ENABLE_HTTPS était lu depuis l'environnement sans jamais être positionné par le
flow Electron (start-desktop.sh → electron .), donc le serveur enfant tournait
toujours en HTTP malgré le setup mkcert automatique au premier lancement.
ENABLE_HTTPS est désormais activé par défaut (ENABLE_HTTPS=false pour revenir
en HTTP explicitement).
Corrections induites par ce changement de protocole par défaut :
- pingServer() utilisait le module http en dur même en HTTPS ; bascule sur
https avec rejectUnauthorized: false (ping local vers notre propre process
enfant, le module https de Node ne lisant pas le trousseau macOS où mkcert
installe sa CA, contrairement à Safari/Chrome/Electron renderer).
- Le dashboard (electron/ui/app.js) avait l'URL de l'API et celle du
WebSocket VU-mètres codées en dur en http/ws ; elles utilisent maintenant
l'URL réelle exposée par preload.js (serverUrl), cohérente avec le
protocole effectif du serveur.
AudioLevelsServer s'auto-attachait à l'événement 'upgrade' du serveur HTTP
via la lib ws (server + path), en plus du listener manuel du proxy LiveKit.
Pour toute connexion /livekit, les deux listeners s'exécutaient : le proxy
LiveKit aboutissait bien côté upstream, mais le listener ws (path
/audio-levels ne matchant pas) appelait abortHandshake(socket, 400) sur le
même socket juste après, cassant la connexion côté client en HTTPS prod.
AudioLevelsServer passe maintenant en noServer: true et expose
handleUpgrade(), appelée par un dispatcher 'upgrade' unique dans
server/index.js qui route explicitement par chemin (/livekit vs
/audio-levels).
Ajout de certs/ au .gitignore (clés privées SSL locales mkcert).
http-proxy-middleware ne gere pas correctement WebSocket upgrade avec HTTPS.
Utilisation de http-proxy natif pour proxy direct WSS vers WS.
Modifications:
- Remplacement createProxyMiddleware par httpProxy.createProxyServer
- Gestion native de l'upgrade WebSocket
- Reecriture URL /livekit -> / pour LiveKit
- Logs info niveau pour debug facilite
Correction detection du protocole pour utiliser le bon endpoint LiveKit.
Modifications:
- client/src/App.jsx: detection HTTPS au lieu de import.meta.env.DEV
- Si HTTPS: utiliser wss://host/livekit (proxy)
- Si HTTP: utiliser URL LiveKit directe
Probleme resolu:
- Mixed content error (HTTPS ne peut pas WS)
- Fonctionne maintenant en prod HTTPS build
Permet au client PWA charge en HTTPS de se connecter a LiveKit via proxy WSS.
Modifications:
- server/index.js: ajout proxy http-proxy-middleware
- Route /livekit proxie vers http://localhost:7880
- Upgrade WebSocket active pour le proxy
- server/package.json: ajout dependance http-proxy-middleware
Fonctionnement:
- Client HTTPS se connecte a wss://localhost:3000/livekit
- Serveur Express proxie vers ws://localhost:7880
- Resout probleme mixed content (HTTPS ne peut pas WS)
Ajout connexion WebSocket temps reel pour monitoring audio dans l'app Electron.
Modifications:
- electron/ui/app.js: connexion WebSocket /audio-levels
- Rendu VU-metres pour inputs/groups/outputs
- Reconnexion automatique en cas de deconnexion
- Gestion cycle de vie (demarrage/arret serveur)
- electron/ui/styles.css: styles pour VU-metres
- Barres horizontales avec couleurs (vert/jaune/rouge)
- Indicateur peak temps reel
- Animation clipping si saturation
- DESKTOP-APP.md: marque TODO comme complete
Fonctionnalites:
- Affichage niveaux RMS et peak en dBFS
- Detection clipping avec animation
- Status connexion WebSocket visible
- Mise a jour 20 fois/seconde (50ms)
- Sections separees: entrees, groupes, sorties
Problème : mainWindow.webContents.send() appelé avant que la page soit chargée
→ Events perdus, interface ne se met pas à jour
Solution :
- Nouveau flag rendererReady
- Event 'did-finish-load' pour détecter quand le renderer est prêt
- Tous les webContents.send() vérifient maintenant rendererReady
- Envoi de l'état initial du serveur quand interface charge
Résultat : Interface reçoit toujours les updates d'état serveur
Nouveau : setup-helper.js
- Détecte si mkcert est installé
- Installe mkcert automatiquement (Homebrew macOS, curl Linux)
- Installe CA locale (mkcert -install)
- Génère certificats pour localhost + IP réseau
- Détection automatique IP WiFi
Intégration dans main.js :
- Vérifie certificats au démarrage
- Si absents : dialog onboarding + setup auto
- Dialog progress "Configuration en cours..."
- Dialog success avec IP réseau
- Dialog error si échec (fallback manuel)
- Ne démarre serveur que si certificats OK
Expérience utilisateur :
1. Lancer l'app (première fois)
2. Dialog : "Première utilisation, configuration..."
3. Cliquer "Continuer"
4. Attendre 1-2 min (installation mkcert + CA + certificats)
5. Dialog : "Configuration terminée, IP: 192.168.x.x"
6. Serveur démarre automatiquement
Prochaines fois : détection certificats OK → démarre direct
L'utilisateur n'a RIEN à faire manuellement !
Les handlers ipcMain.handle() doivent être définis après app.whenReady()
sinon ipcMain est undefined
Résout: TypeError: Cannot read properties of undefined (reading 'handle')
- Logs LiveKit vont dans stderr (normal pour Go), ne pas traiter comme erreurs
- Transmettre tous les logs au renderer (stdout + stderr)
- Détecter "Serveur prêt" dans stdout pour confirmer démarrage
- Timeout augmenté à 15s avec health check avant de résoudre
- Suppression filtres logs verbeux qui cachaient les messages importants
Résout : serveur tué immédiatement après démarrage
Problème résolu : certificats self-signed bloqués par navigateurs
Solution : mkcert génère certificats automatiquement approuvés
- CA locale installée sur système
- Certificats signés par cette CA
- Navigateurs font confiance automatiquement
- Pas de warnings SSL
- 100% local, pas de cloud/domaine
Nouveau script : setup-certificates.sh
- Installe mkcert (Homebrew/apt)
- Installe CA locale (mkcert -install)
- Détecte IP réseau automatiquement
- Génère certificats localhost + IP + *.local
- Configure server/.env (SSL_CERT, SSL_KEY)
- Configure client/.env (VITE_SERVER_URL)
- Met à jour vite.config.js avec HTTPS
Serveur modifié : server/index.js
- Lit certificats depuis process.env.SSL_CERT/SSL_KEY
- Fallback : ../certs/localhost.pem
- Message erreur si certificats introuvables
Documentation :
- SSL-SETUP.md : guide complet installation manuelle/auto
- SSL-SOLUTION.md : résumé technique
- README.md : ajout étape setup-certificates.sh
Résultat :
- Cadenas vert sur desktop (Chrome/Safari/Firefox)
- WebRTC fonctionne en HTTPS
- Smartphones : accepter certificat une fois (normal)
- Valable 10 ans, pas de renouvellement
Usage : ./setup-certificates.sh (2 minutes)
Ensuite : ./start.sh --dev ou ./start-desktop.sh
- Ajout buffer d'accumulation dans PipeWireBackend (même pattern que CoreAudio)
- Ajout buffer d'accumulation dans JACKBackend
- pw-cat et jack_rec émettent aussi des chunks de taille variable
- Garantit frames fixes (960 samples) sur tous les backends
- Prévient audio haché/robotique sous Linux
Problème:
- Son complètement déformé (clipping massif)
- CoreAudio capture en 32-bit mais traité comme 16-bit
- Mixage additif sans normalisation
Solution:
1. Sox convertit 32→16 bit automatiquement
2. GroupAudioRouter divise gain par nombre de sources
Exemple: 2 inputs → groupe default = gain × 0.5 chacun
Résultat: Aucun clipping détecté, audio propre
- Conversion des routes groupes (GET/POST/PUT/DELETE) pour utiliser configManager
- Suppression de loadConfig() et saveConfig() locales (redondantes)
- Fix: les modifications de groupes sont maintenant persistées correctement
- Les groupes créés/modifiés/supprimés survivent au redémarrage du serveur
- Admin : regroupement des 3 dropdowns cartes son dans une seule section
- Admin : suppression du mode édition pour noms de canaux (directement éditables)
- Admin : unification des boutons de sauvegarde en bas de chaque section
- Admin : routing par hash URL pour persistance des onglets (#groups, #audio, etc.)
- AudioRoutingMatrix : bouton sauvegarde déplacé en bas de la matrice
- AudioRoutingMatrix : dropdowns de gain en nuance de bleu (cohérence visuelle)
- Settings : suppression paramètres inutiles (mode PTT continu, feedback audio non implémenté)
- Settings : conservation uniquement du paramètre vibrations (fonctionnel)
- PTTButton : suppression init mode continu par défaut (redondant avec geste verrouillage)
- PWAInstallPrompt : ajout fond semi-transparent et couleurs hardcodées pour lisibilité
- Admin : fix dropdowns audio qui se réinitialisaient (useRef au lieu de useState pour édition)
LiveKit renvoie Int16Array directement au lieu de Buffer.
Ajout détection Int16Array dans _bufferToFloat32 avec conversion directe.
Ajout logs RMS/dBFS pour diagnostiquer niveau audio.
Problème : LiveKit envoie des frames de 240 samples (5ms @ 48kHz)
mais GroupRouter attend 960 samples (20ms). Cela causait :
- Bruit audio (720 samples de silence ajoutés à chaque frame)
- Latence de plusieurs secondes (buffers qui s'accumulent)
Solution :
- Ajout liveKitFrameAccumulators Map pour chaque groupe
- Accumulation de 4 frames LiveKit (240×4 = 960) avant routing
- Nettoyage logs verbeux dans AudioBridge et GroupAudioRouter
Résultat attendu :
- Audio clair (pas de silence injecté)
- Latence réduite (~20ms au lieu de plusieurs secondes)
LiveKit SDK returns audio data as Uint8Array/ArrayBuffer, not Node.js Buffer.
Added Buffer.from() conversion before readInt16LE operations.
Fixes: TypeError: buffer.readInt16LE is not a function
Permet à l'audio reçu des clients d'être converti en Float32 pour lecture.
Filtrage des logs :
- LIVEKIT_LOG_LEVEL=info au lieu de debug
- Suppression logs DEBUG, signal requests/responses
- Garde uniquement INFO, WARN, ERROR importants
Rend les logs bien plus lisibles pour le debug applicatif.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problème : participant.trackPublications vide au moment de ParticipantConnected
Solution : écouter RoomEvent.TrackPublished et s'abonner au track audio dès sa publication
Flow : Client connecte → ParticipantConnected → Client publie track → TrackPublished → Souscription
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problème : RoomEvent.TrackSubscribed ne se déclenchait jamais avec @livekit/rtc-node
Solution :
- Itération manuelle sur participant.trackPublications quand participant se connecte
- Création AudioStream immédiate si track audio disponible
- Factorisation code dans _handleAudioTrack()
Cela devrait permettre de recevoir l'audio des clients PWA.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Ajout de logs pour :
- RoomEvent.TrackPublished : détecte quand un participant publie un track
- RoomEvent.TrackSubscribed : logs plus détaillés avec kind et sid
- Permet de diagnostiquer pourquoi AudioBridge ne reçoit pas les tracks audio
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Correction import et utilisation d'AudioStream :
- Import AudioStream depuis @livekit/rtc-node
- new AudioStream(track, sampleRate, channels) au lieu de new track.AudioStream(...)
Cela devrait permettre la réception des frames audio depuis les clients PWA.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Architecture refactorisée pour supporter plusieurs connexions LiveKit simultanées :
- AudioBridge : Map<groupName, LiveKitClient> au lieu d'un seul client
- AudioBridgeManager : génère un token JWT par groupe avec room dédiée
- Routing audio bidirectionnel par groupe :
* FLUX 1 (carte son → LiveKit) : envoie vers le bon client selon groupName
* FLUX 2 (LiveKit → carte son) : reçoit audio avec groupName correct
- Chaque groupe a sa propre room LiveKit (nom = groupId slugifié)
Fixes l'issue où les clients connectés à "production" ne recevaient pas
l'audio car AudioBridge était connecté uniquement à "main".
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- AudioBridge passe inputTargetDevice et outputTargetDevice à PipeWire
- Fix: PipeWire utilisait targetDevice au lieu de input/outputTargetDevice
- Devrait maintenant démarrer les streams pw-cat correctement
- /usr/bin/pactl au lieu de 'pactl' pour éviter problèmes PATH
- Correction dans getDevices(), getDefaultInputDevice(), getDefaultOutputDevice()
- Fix 'pactl: not found' malgré installation de pulseaudio-utils