Remplace l'architecture 'une fenêtre = un processus' par une fenêtre
unique (racine Tk sur le thread principal) avec trois onglets:
- Profils : liste + éditeur intégré (ex-ProfileDialog)
- Authentification : gestion des clés API (ex-AuthDialog)
- Logs : journaux temps réel (ex-LogsWindow, désormais dans le
même processus que le moteur, donc réellement alimenté)
Fermer la fenêtre (WM_DELETE_WINDOW -> withdraw) la masque en zone de
notification sans quitter. L'icône pystray (thread dédié) réaffiche la
fenêtre dans le bon onglet via master.after(0, ...); la commutation de
profil reste directe depuis le sous-menu radio (sans afficher la fenêtre).
Supprime _tk_spawn/_tk_window_process et les trois anciens dialogues.
- Nouveau module _tk_window_process.py : point d'entrée pour le subprocess
(python -m ai_typewriter.ui._tk_window_process). Reçoit les args en
pickle sur stdin, écrit window.result en pickle sur stdout.
- _tk_spawn.py réécrit avec subprocess.Popen. Payload picklé envoyé sur
stdin, résultat lu via communicate() dans un thread poller.
- Plus besoin de multiprocessing.Queue ni de start_method.
- PYTHONPATH injecté automatiquement pour que le subprocess trouve le
package ai_typewriter.
- _tk_spawn.py : chaque fenêtre tourne dans un Process séparé (spawn,
pas fork) avec sa propre instance tk.Tk. Résultats renvoyés au parent
via multiprocessing.Queue + thread poller.
- API changée : spawn_tk_window(Class, *args, on_result=..., **kwargs)
au lieu d'une lambda factory — nécessaire pour la picklabilité.
- SecureStore n'est plus passé aux processus fils (non picklable) ;
chaque dialogue crée le sien (déjà le comportement par défaut).
- TrayApp allégé : plus de self.secure, plus d'import SecureStore.
- Nouveau helper ui/_tk_spawn.py : chaque fenêtre Tk dans son propre thread
avec sa propre racine tk.Tk (plus de racine partagée)
- app.py : thread principal pour le hotkey + attente stop_event,
thread séparé pour l'icône pystray
- tray.py : suppression de get_root/_defer_tk/_present_window,
spawn_tk_window pour chaque dialogue, callbacks on_result au lieu
de wait_window bloquant
- Ajoute _present_window() (deiconify/lift/topmost/focus_force) pour que
les Toplevel enfants d'une racine withdraw() apparaissent bien au
premier plan sous Windows (logs, profil, ajout, authentification).
- Rend le sous-menu des profils dynamique via un générateur avec items
radio + checked=, et appelle icon.update_menu() après changement de
profil pour que la coche suive l'état courant.
- Rollback gestion d'équations : écriture littérale caractère par caractère
(suppression de math_format.py et de la normalisation Unicode).
- Nouveau profil par défaut 'Mathématiques (LaTeX)' : le modèle émet du
LaTeX encadré [EQ]...[/EQ], intercepté par KeyStepper qui déclenche
Alt+= en début d'équation et -> en fin.
- Config multi-profils dans %APPDATA%/ai-typewriter/config.json
(ConfigStore + profils par défaut auto-créés).
- Icône de zone de notification (pystray) : Ouvrir les logs (temps réel),
Ajouter/Modifier un profil, Gérer l'authentification, Quitter.
- Sélecteur de modèles : modèles locaux Ollama + recherche/téléchargement
depuis la bibliothèque publique ; fournisseurs tiers (OpenAI, OpenRouter,
Gemini, custom).
- Clés d'API stockées de façon sécurisée dans le Gestionnaire d'identifiants
Windows via keyring (jamais en clair dans config.json).
- Tests : 44 tests verts (stepper, profils, clients IA, credentials,
catalogue de modèles, moteur).