Code dans frontend/. Deux pages : l'accueil avec l'interface vocale
(components/VoiceChat.tsx) et la page parcours, générée au build à partir du
corpus Markdown du RAG (indexable, lisible même backend éteint). S'y ajoutent
sitemap.xml, robots.txt, l'image Open Graph et les favicons.
NEXT_PUBLIC_API_URL: URL de l'API, intégrée au bundle statique. En productionhttps://mathiscapart.xyz/api. Obligatoire pour un build de production : sans elle, le build échoue exprès plutôt que d'envoyer chaque visiteur vers sa propre machine.CORPUS_DIR: dossier Markdown de la page parcours, relatif àfrontend/. Par défaut le vrai corpus../backend/rag/corpus.
cd frontend
npm ci
$env:NEXT_PUBLIC_API_URL = "https://mathiscapart.xyz/api"
npm run build # export statique dans out/ ; en production, c'est la mise en ligne
npx next dev # développement localContrat WebSocket /voice : le navigateur envoie des trames PCM16 mono 24 kHz
de 80 ms puis {"type":"end"} ; le serveur renvoie transcript, token, des
trames audio PCM16 24 kHz, puis sources (bloc terminal) ou error.
Au chargement, la page interroge GET /health (délai 5 s). Sans réponse ok
(PC du GPU éteint, proxy en 502/504, Qdrant ou Ollama absent), le bouton Parler
est désactivé et un message annonce l'assistant hors ligne, avec un renvoi vers
le parcours écrit et LinkedIn. Le front, lui, reste toujours en ligne.
Points propres aux navigateurs mobiles, tous couverts par des tests :
- Les AudioContext sont créés et repris dans le geste de l'utilisateur, avant toute attente : sinon Safari iOS les laisse muets.
- Ils sont réutilisés d'une question à l'autre, et le module de capture n'est chargé qu'une fois : les recréer empêchait d'enchaîner sur iOS.
- Le micro tourne à la fréquence de l'appareil et est rééchantillonné en 24 kHz
(
Reechantillonneur,lib/voix.ts). - Seuls les événements du socket actif sont écoutés : Safari émet parfois une erreur tardive sur le socket d'une question terminée.
cd frontend
npx vitest run
npx tsc --noEmitVitest et jsdom, avec @testing-library/react. Ils couvrent la conversion et
le découpage PCM, le rééchantillonnage, l'interprétation des messages /voice,
la garantie anti demi-réponse (« Réponse interrompue » si le socket se ferme
sans bloc terminal), l'enchaînement de questions et les événements tardifs d'un
ancien socket.