zonza/assemblage.py
Ralph Mayola aa63e94239 Un lanceur Qt pour Windows, et un assemblage partage
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 20:00:03 +02:00

76 lines
3.3 KiB
Python

"""Assemblage commun aux deux lanceurs (`app.py` Cocoa, `lanceur_qt.py` Qt/Windows).
Avant ce module, `app.py` était le SEUL endroit qui savait choisir le système,
prendre le verrou d'instance, construire le transcripteur et l'overlay, puis
câbler le tout dans un `ZonzaController`. Un second lanceur qui recopierait
cette séquence créerait deux copies vouées à diverger en silence — exactement
le défaut déjà résorbé entre `engine.py` et `systeme/macos.py` (cf journal du
projet). Les deux lanceurs appellent donc les deux fonctions d'ici ; ce qui
reste propre à chacun (boucle d'événements, icône de barre, menu) reste dans
`app.py` et `lanceur_qt.py`.
Tout est injectable (`plateforme`, `run_on_main`...) pour rester testable sans
jamais ouvrir de fenêtre ni de micro : la construction du transcripteur charge
un vrai modèle, donc les tests injectent un `moteur.detecter_moteur`/
`moteur.choisir_transcripteur` doublés plutôt que de laisser tourner le vrai.
"""
import os
import moteur
from controller import ZonzaController
from core import CONFIG, modele_retenu, validate_config
from interface import classe_overlay_pour
from systeme import choisir_systeme
def acquerir_verrou(systeme, chemin_verrou=None):
"""Prend le verrou d'instance unique ; rend le handle, ou None si pris.
Une seule instance à la fois : sinon deux modèles en RAM et la même
hotkey captée deux fois (cf systeme.acquerir_verrou_instance). Le chemin
est injectable : les tests et les essais manuels d'un lanceur doivent
pouvoir viser un fichier qui n'est PAS celui de l'instance déjà en cours
de l'utilisateur.
"""
chemin = chemin_verrou or os.path.join(systeme.dossier_donnees(), "instance.lock")
return systeme.acquerir_verrou_instance(chemin)
def construire(plateforme=None, run_on_main=None, on_state=None,
detecter_moteur=None, choisir_transcripteur=None):
"""Construit le système, le transcripteur, l'overlay et le contrôleur.
Ne prend PAS le verrou (c'est le rôle d'`acquerir_verrou`, appelée à part
par le lanceur, qui décide seul de ce qu'il fait d'un refus). Ne valide
la config qu'une fois, ici, pour que les deux lanceurs en bénéficient
sans avoir à y penser.
`detecter_moteur`/`choisir_transcripteur` sont injectables : ils
permettent aux tests de construire l'assemblage entier sans charger un
vrai modèle de transcription (opération lourde, et qui échouerait sans
matériel/poids téléchargés).
"""
detecter_moteur = detecter_moteur or moteur.detecter_moteur
choisir_transcripteur = choisir_transcripteur or moteur.choisir_transcripteur
validate_config(CONFIG)
systeme = choisir_systeme(plateforme)
moteur_nom = detecter_moteur()
transcripteur = choisir_transcripteur(
nom=moteur_nom,
modele=modele_retenu(moteur_nom, CONFIG["model"]),
langue=CONFIG["language"],
amorce=CONFIG["initial_prompt"],
)
overlay = classe_overlay_pour(plateforme)()
controller = ZonzaController(
CONFIG, overlay, transcripteur, systeme, on_state=on_state,
run_on_main=run_on_main,
)
return {
"systeme": systeme,
"transcripteur": transcripteur,
"overlay": overlay,
"controller": controller,
}