Changelog
What changed, straight from the code
This isn't a marketing page: it's the actual CHANGELOG.md shipped inside the downloadable zip, read automatically on every visit — never retyped by hand, so it never drifts out of sync.
Current version : v1.4.0 Download this version
[1.1.2] - 2026-08-13
Ajouté (2026-08-10 — mise à jour automatique depuis slydeforge.com, charte SlydeForge riche, aide F1)
- Retour utilisateur — « ajouter à l'ouverture un contrôle si c'est bien la dernière version installée et sinon proposer d'installer automatiquement la nouvelle version » : nouveau
services/update_service.py::check_site(vérification publique viahttps://www.slydeforge.com/api/version-check.php, distincte du contrôle GHE interne existant) — si une version plus récente est publiée, une popup au démarrage propose de télécharger et d'installer automatiquement la mise à jour (réutilise le flux d'application depuis un .zip déjà existant : confirmation avec notes de version, progression en direct, proposition de redémarrage). - Retour utilisateur — « Les couleurs sont un peu flashy [...] intègre les éléments graphiques ci-joint. Gérer une image de fond moderne et [...] compléter avec 3/4 layouts [...] configurer les icônes + images » : la charte SlydeForge passe d'une simple palette à une charte riche complète (
examples/themes/slydeforge.json, chargée comme Prisme) — palette assourdie (moins saturée que les couleurs vives du site), image de fond dégradée générée, 4 types de slides prêts à l'emploi (Couverture, Contenu, Multi-format, Clôture) intégrant les vrais éléments graphiques fournis (logo, icônes des 3 fonctionnalités clés). - Retour utilisateur (capture) — « le design [du nouveau projet vierge] fait vraiment basique [...] pas vraiment aligné avec le design moderne du site » : nouveau
core/presets.py::sample_deck_from_charte— le deck de démonstration d'un nouveau projet vierge est désormais construit à partir des VRAIS gabarits de la charte SlydeForge (Couverture → Multi-format → Clôture, avec image de fond/icônes/logo) plutôt que d'une structure générique à bandeaux de couleur plate. Cette charte est en outre désormais toujours utilisée pour un nouveau projet vierge, indépendamment de la charte par défaut personnalisable de l'utilisateur. - Retour utilisateur — « ajouter la touche F1 pour ouvrir l'aide » : raccourci standard ajouté sur Aide > Guide utilisateur….
- 4 nouvelles rubriques dans
docs/guide-utilisateur.md: lancement du diaporama, mode présentateur (avec lecture vocale automatique), génération de vidéo, éditeur de workflow.
Corrigé (2026-08-10 — suppression de tag indécouvrable)
- Retour utilisateur (capture) — « Je ne vois pas comment supprimer complètement un tag à partir de la liste de tags » : root cause — les boutons renommer/supprimer d'un tag (
ui/tag_dialog.py) utilisaient des glyphes de police Unicode (✎/✕) invisibles sur la machine de l'utilisateur (rendu peu fiable selon les polices installées), rendant la suppression totalement indécouvrable bien que fonctionnelle. Remplacés par de vraies icônes vectorielles (ui/icons.py), même règle déjà appliquée ailleurs dans l'application. - Retour utilisateur — « si [un tag est] déjà utilisé il faut demander lister les projets concernés et demander confirmation » : la confirmation de suppression d'un tag liste désormais les projets concernés (nom, jusqu'à 15 affichés) plutôt qu'un simple message générique.
Renommé (2026-08-10 — SlideForge devient SlydeForge)
- Retour utilisateur — « renommer l'application SlydeForge avec un "y" [...] intégrer le nouveau set d'icônes/logo [...] créer une charte aux couleurs de ce nouveau logo et l'utiliser pour la présentation de base à l'ouverture » : renommage complet du produit à travers tout le code, les tests, la documentation et les scripts de packaging (108 fichiers). Dossier de données utilisateur
%APPDATA%\SlydeForgeavec migration automatique (une seule fois, best-effort élément par élément si un fichier est verrouillé) depuis l'ancien%APPDATA%\SlideForge— aucune donnée d'un utilisateur existant ne doit disparaître après la mise à jour. - Nouvelle icône applicative (
assets/icon.svgréel, régénèreassets/icon.ico/assets/icon.pngviapackaging/make_icon.py) et logotype (assets/branding/wordmark.png, affiché sur l'écran de démarrage). - Nouveau préréglage de charte « SlydeForge » (
core/presets.py::theme_slydeforge) — couleurs extraites directement des tracés du logo (fond marine#141736, dégradé orange#FA6117/#E8421E, violet#6936DE). Devient la charte par défaut sur une installation neuve (jamais sur une installation existante ayant déjà un défaut explicite) — utilisée par la présentation affichée à l'ouverture de l'application. - Dossier racine renommé
SlideForge→SlydeForge.
Ajouté (2026-08-10 — composants de workflow : dates, structure de génération imposée, connecteurs SharePoint/FTP/SFTP, trace détaillée)
- Retour utilisateur — « pouvoir récupérer la date du jour, la formater et l'ajouter dans une variable pour l'intégrer dans un nom de fichier de sortie » : nouveau nœud
core.current_date— démontré dans l'exemple bundlé (nom de fichier exporté désormais daté). - Retour utilisateur — « un composant pour paramétrer les layouts à utiliser par slide pour donner la logique sur la structure à respecter » : nouveau nœud
core.layout_plan(sélection de charte + liste de slideslayout | type de slide | titre) — son portplancourt-circuite la planification automatique par IA decore.ai_generate_deck. - Retour utilisateur — « récupérer un fichier source depuis un SharePoint avec les authentifications de l'utilisateur » : nouveau
connectors.sharepoint_source(Microsoft Graph, authentification par device code avec mise en cache locale — un seul geste ponctuel requis côté IT : inscription d'une application Azure AD cliente publique). - Retour utilisateur — « des composants pour gérer des accès de type FTP, SFTP [...] pour récupérer un fichier mais aussi déposer un fichier » : nouveaux
connectors.file_download/connectors.file_upload(FTP/FTPS/SFTP). - Retour utilisateur — « rendre le log d'exécution plus verbeux [...] expliquer le traitement en entrée et en sortie » : chaque message de progression émis par une brique pendant son exécution est désormais capitalisé dans la trace de debug (groupe « Étapes » dépliable), pas seulement affiché en direct puis perdu.
- Bouton ▶ Exécuter et menu contextuel (Supprimer le lien) sur les liens du canevas de workflow.
Ajouté (2026-08-08, retours sur la 5e vague, 9e round — tags d'animation synchronisés à la lecture vocale)
- Retour utilisateur — « lorsqu'il y a une lecture autonome à partir de notes mais en plus qu'il y a une animation sur les objets d'un slide alors il fait l'animation des éléments de la slide que à la fin. Est-ce qu'il y aurait moyen d'intégrer dans les notes de la slide des tags que la voix ne prendrait pas encore mais que l'animation automatique en fonction de la lecture pourrait utiliser pour gérer l'animation » : nouveau
core/notes_tags.py— marqueur[[anim]]insérable dans le texte des notes, jamais lu à voix haute (retiré avant toute synthèse ET avant tout export PPTX). En lecture autonome,ui/present_window.pydécoupe désormais les notes en segments à chaque tag et les synthétise/joue l'un après l'autre — l'étape d'animation suivante se déclenche exactement entre deux segments, plus seulement en toute fin de narration. Sans tag, comportement de lecture strictement inchangé. - Retour utilisateur — « il faudrait que la génération de notes puisse être informée de l'animation existante pour créer ces tags lorsqu'il génère les notes » :
services/notes_suggest_service.py::suggest_notesconnaît désormais le nombre réel d'étapes d'animation de la slide (ui/present_window.py::slide_step_count, déjà en place pour le lecteur) et insère automatiquement le bon nombre de tags aux endroits naturels — uniquement si la slide est réellement animée, jamais mentionné sinon. - Retour utilisateur — « un mode IA pour si une note existante retirer les tags s'ils existent et en proposer d'autres toujours basé sur les éléments de l'animation existante » : nouveau
suggest_tags_for_notes— ne réécrit JAMAIS le texte, seulement où replacer les marqueurs par rapport à l'animation ACTUELLE. Nouveau bouton 🏷️ Retaguer (IA) à côté du champ Notes de la slide courante. - Retour utilisateur — « il faudrait également un mode pour revoir les tags des animations dans les notes de l'ensemble des slides [...] une assistance pour gérer les tags manuellement pour nous aider à les définir en fonction de la séquence de l'animation » : nouveau
ui/tag_review_dialog.py(menu Présentation > Revoir les tags d'animation…) — liste de TOUTES les slides avec un badge (✓/⚠) comparant le nombre de tags posés au nombre réel d'étapes, et pour la slide sélectionnée : la séquence d'animation détaillée (« Étape 1 : Titre (fondu) »…), un éditeur où les tags sont directement visibles/modifiables, un bouton pour en insérer un au curseur, et le même retaguage IA que ci-dessus — combine en un seul endroit la revue globale et l'assistance manuelle demandées. - +53 tests (nouveaux fichiers
tests/test_notes_tags.py— 16,tests/test_tag_review_dialog.py— 17 ; extension detests/test_voice_reading.py— 6,tests/test_notes_suggest_service.py— 8,tests/test_slide_notes_ui.py— 4,tests/test_notes_import_export_roundtrip.py— 2).
Ajouté (2026-08-08, retours sur la 5e vague, 11e round — export vidéo de la présentation)
- Retour utilisateur — « génération de vidéos basées sur les animations, transitions, notes et voix. Il faudra certainement une popup pour valider les paramètres nécessaires [...] et déterminer le dossier de destination (par défaut dossier output de appdata roaming) » : le moteur bas niveau existait déjà (rendu d'image par étape d'animation, ffmpeg) mais n'était relié à rien. Nouveau
services/video_export.py::export_video— orchestre rendu d'image, synthèse vocale des notes et assemblage ffmpeg en une vraie vidéo MP4, VÉRIFIÉ de bout en bout avec un ffmpeg réel (vidéo+audio produits, durées correctes). La narration est synchronisée aux tags[[anim]]quand ils sont présents (chaque segment devient un clip audio séparé, l'étape d'animation se déclenche exactement à la frontière — réutilise le même mécanisme que la lecture autonome) ; sans tag, comportement historique déjà testé (un seul audio par slide, réparti également). Limite assumée pour cette 1ère version (même principe d'honnêteté que le lecteur live, cf. familles de transitions) : les transitions entre slides restent des coupes franches dans la vidéo, pas un fondu enchaîné ffmpeg — trop risqué à livrer sans vérification visuelle poussée dans le temps imparti. - Nouveau
ui/video_export_dialog.py(menu Fichier > Exporter en vidéo…) — popup de paramètres : destination (pré-remplie avecprefs.exports_dir(), le dossieroutputdéjà utilisé pour les exports PPTX/projet), résolution, statut ffmpeg, puis progression en direct (slide en cours) dans la même fenêtre. - Retour utilisateur — « s'il manque des librairie python ou autre à installer pour la génération de la vidéo il faudra assister l'utilisateur lambda pour que ça soit simple à faire » : ffmpeg n'est pas un paquet pip (contrairement à Edge/Piper) — nouveau
services/video_installer.py, installation guidée en un clic viawinget(gestionnaire officiel Windows, déjà présent par défaut) avec progression en direct (ui/video_install_dialog.py, même patron que l'installateur de moteurs de voix). Le binaire fraîchement installé est immédiatement détecté sans redémarrer SlydeForge. - +39 tests (nouveaux fichiers
tests/test_video_installer.py— 7,tests/test_video_install_dialog.py— 7,tests/test_video_export_dialog.py— 12 ; extension detests/test_video_export.py— 13).
Corrigé (2026-08-08, retours sur la 5e vague, 12e round — délais d'animation synchronisés, bouton Retaguer grisé sans animation)
- Retour utilisateur — « les temps de pause de la voix sur les animations sur les objets sont un peu longues, est-ce que tu peux mieux les synchroniser avec le temps prévu de l'animation ? » : le délai entre deux étapes d'animation (lecture autonome ET export vidéo) était un délai FIXE (900 ms), sans rapport avec la durée réellement configurée de l'animation qui vient de se jouer. Nouveau
ui/present_window.py::step_duration_ms— utilise désormais leduration_msréel de l'animation déclenchée à cette étape (le plus long si plusieurs éléments apparaissent ensemble), avec un repli sur l'ancien délai fixe seulement si aucune durée n'est renseignée. Réutilisé aussi parservices/video_export.pypour les étapes sans narration propre. - Retour utilisateur — « juste au-dessus de la zone de note il y a retaguer IA, mais s'il n'y a pas d'animation sur la slide ça ne sert à rien et le bouton peut être grisé » : le bouton 🏷️ Retaguer (IA) est désormais désactivé (avec une infobulle explicative) tant que la slide sélectionnée n'a aucune étape d'animation, au lieu d'attendre un clic pour l'apprendre.
- +12 tests (extension de
tests/test_present_window.py— 6,tests/test_voice_reading.py— 2,tests/test_video_export.py— 1,tests/test_slide_notes_ui.py— 3).
Corrigé (2026-08-08, retours sur la 5e vague, 8e round — touches Tab et Entrée en mode présentation)
- Retour utilisateur — « la touche Tab du clavier ne permet pas de passer à la diapositive suivante » puis « ajoute la touche Entrée pour passer à la slide suivante » : root cause (Tab) — Qt intercepte Tab AVANT
keyPressEventpour déplacer le focus clavier entre widgets (comportement par défaut, même avecStrongFocus) ;PresentWindow.focusNextPrevChildrefuse désormais explicitement ce déplacement de focus. Tab et Entrée avancent maintenant la diapositive au même titre que→/Espace/PageDown. - +3 tests (extension de
tests/test_present_window.py).
Corrigé (2026-08-08, retours sur la 5e vague, 7e round — audio d'écoute périmé, installation Piper réellement simple, catalogue français élargi, message d'erreur figé, chrono auto)
- Retour utilisateur (capture) — « lorsque je change de voix et que je fais écouter, je suis toujours sur l'ancienne voix [...] je dois fermer la popup et la réouvrir » : root cause — le fichier temporaire d'essai avait un nom FIXE par langue (
slydeforge_voice_test_fr.mp3), si bien qu'un essai encore en cours pouvait écraser après coup le résultat d'un essai plus récent (et certains lecteurs média ne redétectent pas toujours un contenu changé sous un chemin identique). Chaque tentative écrit désormais dans un fichier au nom UNIQUE. - Retour utilisateur (capture) — « je ne peux toujours pas installer simplement piper [...] il faut que ça soit simple comme pour edge neutral » : root cause CONFIRMÉE en reproduisant le problème —
pip install piper-ttsréussit et rend le paquet Python importable, mais le scriptpiperqu'il déclare n'atterrit pas toujours sur le PATH (installation utilisateur, dossier Scripts absent du PATH…) ; SlydeForge ne cherchait QUE ce script, jugeait donc Piper « non installé » à tort et tentait quand même une synthèse qui échouait avec le message brut d'installation.services/tts.py::_piper_command()retombe désormais surpython -m piper(l'interpréteur courant, où le paquet vient d'être installé) dès que le script n'est pas sur le PATH mais que le module l'est — un simple clic sur Installer… (déjà en place) devient enfin suffisant, à parité avec Edge. - Retour utilisateur — « pour les voix française de piper, tu ne trouves pas de voix avec plus haute qualité, même en canadien ou belge [...] merci de compléter la liste et de vraiment proposer les voix les plus haute qualité et appréciées » : catalogue Piper vérifié en DIRECT (index HuggingFace réel) — 7 voix françaises seulement, TOUTES en fr_FR, qualité maximale « medium » (jamais « high »), aucune voix canadienne/belge : une limitation réelle du projet Piper lui-même, pas un choix de SlydeForge, honnêtement non comblée par une invention. Côté Edge (qui, lui, propose vraiment ces variantes), le catalogue est complété avec des voix RÉELLES vérifiées via un appel direct à
edge_tts.list_voices(): Canada (Sylvie, Antoine), Belgique (Charline, Gérard), Suisse (Ariane, Fabrice), et les deux nouvelles voix « Multilingual » (génération la plus récente et la plus naturelle d'Edge) — devenues le nouveau choix par défaut en français et en anglais. - Retour utilisateur (capture) — « lorsque je change de moteur de voix il conserve le message d'erreur du moteur précédent (ici je suis sur edge et je vois le message pour piper) » : le statut d'écoute (
_tts_test_status) n'était vidé nulle part au changement de moteur — désormais réinitialisé dès que le moteur change. - Retour utilisateur — « en mode présentateur lorsque je clique sur lecture vocale il ne lance pas automatiquement le chrono » : même filet de sécurité que la toute première avance de slide — activer la lecture vocale démarre désormais le chronomètre s'il ne tournait pas déjà.
- +11 tests (extension de
tests/test_tts.py— 7,tests/test_voice_settings_dialog.py— 2,tests/test_voice_reading.py— 2 ;tests/test_tts_settings.pymis à jour pour la nouvelle valeur par défaut, sans test supplémentaire).
Ajouté (2026-08-08, retours sur la 5e vague, 6e round — suggestion IA de notes sur tout le deck, comparaison et édition avant validation)
- Retour utilisateur — « je souhaite pouvoir générer des notes + proposition de temps sur l'ensemble les slides et s'il existe déjà des notes il faut pouvoir les comparer et les valider. Il faudrait même pouvoir éditer en live la version que l'on va conserver si on souhaite changer quelques éléments avant validation. » : la suggestion IA de notes (jusqu'ici mono-slide) fonctionne désormais sur une sélection de slides ou le deck entier — nouveau
services/notes_suggest_service.py::suggest_notes_for_deck(un appel par slide, une slide sans rien à dire n'interrompt jamais les autres) et nouveauui/notes_suggest_dialog.py::DeckNotesSuggestDialog: vue maître-détail avec, pour la slide sélectionnée, les notes ACTUELLES affichées en lecture seule au-dessus des notes PROPOSÉES, librement éditables avant validation — le texte réellement appliqué est celui laissé dans le champ au moment de valider, édité ou non. Chaque slide reste cochable/décochable individuellement. Le temps de lecture est estimé automatiquement pour toute slide qui n'en avait pas encore (même règle que la version mono-slide, jamais en écrasant un temps déjà réglé). - Accessible via clic droit sur une ou plusieurs slides (« Proposer des notes (IA)… », même portée que « Proposer des transitions (IA)… ») et via le menu Présentation > Proposer des notes pour tout le deck (IA)….
- +30 tests (nouveaux fichiers
tests/test_notes_suggest_ui.py— 11,tests/test_notes_suggest_dialog.py— 14 ; extension detests/test_notes_suggest_service.py— 5).
Ajouté (2026-08-08, retours sur la 5e vague, 5e round — bibliothèque de voix recherchable par moteur)
- Retour utilisateur — « comment est-ce qu'il est possible d'ajouter de nouvelle voix facilement ? Possible de proposer une bibliothèque et de rechercher à partir de critère (genre, âge, ...) une voix, la sélectionner et l'installer [...] il faut gérer un moteur de recherche différencié par moteur de voix car chacun va utiliser des bibliothèques (sources différentes) » : nouveau
services/voice_library.py, avec une recherche RÉELLE propre à chaque moteur (pas un catalogue statique unique) — Edge interroge son catalogue LIVE de ~500 voix Microsoft (edge_tts.list_voices, filtrable langue/genre, rien à installer : une voix choisie est immédiatement utilisable) ; Piper interroge l'index HuggingFacerhasspy/piper-voices(filtrable par langue, un vrai téléchargement du modèle.onnx+.onnx.jsondans le dossier des voix de SlydeForge est nécessaire avant utilisation) ; ElevenLabs interroge la bibliothèque partagée du compte configuré (filtrable langue/genre/âge, nécessite la clé d'API déjà renseignée). Chaque moteur expose les filtres qu'il sait RÉELLEMENT honorer (SUPPORTED_FILTERS) — l'âge, par exemple, n'existe que côté ElevenLabs, les autres filtres restent masqués plutôt que de proposer un choix qui ne ferait jamais rien. - Nouveau
ui/voice_library_dialog.py(VoiceLibraryDialog) — recherche en arrière-plan (texte libre + filtres pertinents pour le moteur courant), liste de résultats avec statut « ⬇ à installer » le cas échéant, bouton Installer (Piper, avec progression en direct) ou Utiliser cette voix (sélection immédiate), double-clic pour aller plus vite. Ouvert via un nouveau bouton 📚 Bibliothèque… à côté de chaque combo de voix FR/EN dansui/voice_settings_dialog.py. - +40 tests (nouveaux fichiers
tests/test_voice_library.py— 24,tests/test_voice_library_dialog.py— 13 ; extension detests/test_voice_settings_dialog.py— 3).
Ajouté (2026-08-08, retours sur la 5e vague, 4e round — cohérence lecture vocale, barre de progression, bandeau discret)
- Retour utilisateur — « il faudrait que le bouton lecture/pause du mode présentation soit lié avec le mode audio lorsqu'il est activé pour garder une cohérence » : le bouton ▶/⏸ du chronomètre (console présentateur) pilote désormais AUSSI la narration en cours quand la lecture vocale est active — l'arrêter met en pause l'audio en train de jouer ET le minuteur d'enchaînement (entre étapes d'animation ou avant la slide suivante), en mémorisant le temps restant pour reprendre exactement où on s'était arrêté ; le redémarrer reprend l'audio (ou lance la synthèse restée en attente si elle s'est terminée pendant la pause) et relance le minuteur avec le temps qu'il restait. Changer manuellement de slide efface toujours une pause en cours (on repart en lecture, jamais sur une pause fantôme).
ui/audio_player.pygagnepause()/resume()/is_paused()en plus deplay_file()/stop()/is_playing(). - Retour utilisateur — « il faudrait que l'on puisse avancer avec une barre de type slice sur le son » :
ui/audio_player.py::AudioPlayerrelaie désormaispositionChanged/durationChanged(relais par slot Python,QMediaPlayerémettant enqlonglongincompatible avec une connexion signal-à-signal directe vers unSignal(int)) et exposeposition()/duration()/set_position(). Nouvelle barre de progression (QSlider) dans la console présentateur, connectée à ces signaux — un glisser en cours n'est jamais écrasé par une mise à jour de position entrante, le relâcher cherche dans l'audio viaset_position(). - Retour utilisateur — « je vois que tu indiques en haut de la slide le fait que la lecture automatique vocale est activée [...] il faudrait que ce soit plus fondu et que soit on puisse la fermer manuellement soit que au bout d'un certain temps elle se ferme tout de seule » : le bandeau de statut (
ui/present_window.py::_show_voice_status) passe d'un fond plein contrasté à un fond translucide discret, se ferme au clic (curseur en pointeur) et disparaît automatiquement après 4,5s — ce n'est qu'une notification transitoire, jamais un indicateur d'état permanent. - +23 tests (extension de
tests/test_audio_player.py— 10,tests/test_voice_reading.py— 9,tests/test_presenter_window.py— 4).
Corrigé (2026-08-07, retours sur la 5e vague, 3e round — combo voix, message d'écoute, raison des échecs de lot, langue de narration évolutive)
- Retour utilisateur (capture) — « je vois que si je change de moteur, j'ai toujours la même voix proposée [...] j'imagine que d'un moteur à l'autre ça ne peut pas être les mêmes » : root cause — un libellé du catalogue de l'ANCIEN moteur (ex. « Français — Denise (femme, neutre et posée) », un libellé Edge) était à tort traité comme une valeur personnalisée tapée par l'utilisateur et réapparaissait tel quel une fois basculé sur un AUTRE moteur, faute de distinguer fiablement une vraie saisie libre d'un simple libellé de catalogue hérité (
QComboBox.currentData()reste périmé aprèssetEditText(), comportement Qt vérifié empiriquement — nouveau_is_free_typed, basé sur la comparaison texte affiché ↔ libellé de l'item courant, seul signal fiable). Bug plus profond trouvé au passage : le même problème faisait qu'une voix personnalisée TAPÉE À LA MAIN pouvait être silencieusement remplacée par la donnée périmée d'un item déjà sélectionné avant la frappe — corrigé de la même façon. - Retour utilisateur (capture) — « je vois encore ce type de message [...] lorsqu'un moteur de voix n'est pas encore installé [...] il faut pas afficher ce message (sauf si on tente la lecture) mais surtout afficher le bouton pour permettre l'installation » : cliquer « Écouter » sur un moteur non installé tentait la synthèse et affichait le texte BRUT et multi-paragraphe de l'erreur d'installation comme si c'était un crash (« Erreur inattendue : ... »). Vérifie désormais la disponibilité du moteur AVANT de tenter quoi que ce soit, et renvoie vers le bouton Installer… déjà présent plutôt que de dupliquer/aggraver le même message.
- Retour utilisateur (2e signalement) — « pour la transition IA sur l'ensemble des slides j'ai maintenant une erreur, mais je ne comprends pas vraiment pourquoi [...] Même en relançant c'est pareil » : l'exception réelle était totalement avalée (
except Exception: return None), impossible à diagnostiquer. Elle est désormais journalisée ET propagée jusqu'au message affiché à l'utilisateur (« Raison : ... »). Taille de lot réduite par prudence (8 → 5 slides par appel) pour limiter le risque de heurter une limite d'images/taille de requête propre à certains backends, en plus du budget de tokens déjà adapté. - Retour utilisateur — « comment est géré depuis le mode présentation le choix de la langue entre le français et l'anglais ? [...] il faudra pouvoir ajouter d'autres langues dans le futur [...] Le système doit être évolutif » : root cause de la question —
deck["language"](champ de schéma) n'était écrit NULLE PART dans l'application, donc toujours "fr" par défaut même pour un deck en anglais, sans le moindre moyen de le changer. Nouveaucore/languages.py, registre UNIQUE (fr/en par défaut) dont dérivent désormais le stockage de voix (services/tts_settings.py, une clévoice_<code>générée par langue du registre, plus de binaire "fr" / "tout le reste") et un vrai sélecteur de langue dans la console présentateur, modifiable à chaud pendant la présentation. Ajouter une langue (italien, espagnol, allemand…) est désormais une seule ligne à ajouter au registre. - +34 tests (nouveau fichier
tests/test_languages.py— 9 ; extension detests/test_voice_settings_dialog.py— 10,tests/test_voice_reading.py— 8,tests/test_tts_settings.py— 3,tests/test_animation_suggest_service.py— 2,tests/test_animation_suggest_ui.py— 2).
Corrigé (2026-08-07, retours sur la 5e vague, 2e round — fenêtre Voix séparée, installateur guidé, transitions IA sur tout le deck, transitions visuellement distinctes)
- Retour utilisateur — « peux-tu mettre la configuration de la voix dans une autre fenêtre que les autres éléments liés à l'IA ? » : la page Voix a quitté
Paramètres > IA…pour un dialogue dédié (ui/voice_settings_dialog.py, nouvelle entrée Paramètres > Voix…) —ui/settings_dialog.pyne contient plus rien de lié à la voix. - Retour utilisateur — « si les moteurs de voix ne sont pas installés [...] c'est trop technique pour un utilisateur lambda, il faut lui demander confirmation et ouvrir une fenêtre qui va faire l'installation pour lui où il peut suivre l'avancement » : nouveau
services/tts_installer.py(pip installdu paquet du moteur, via l'interpréteur courant) +ui/tts_install_dialog.py— un bouton Installer… apparaît dès qu'un moteur installable (Edge/Piper) est sélectionné mais absent du poste ; confirmation demandée, puis fenêtre avec le journal d'installation affiché EN DIRECT (ligne de sortie pip par ligne de sortie), jamais une commande à copier-coller. ElevenLabs (clé d'API, pas un paquet) n'a pas de bouton d'installation — rien à automatiser. - Retour utilisateur (capture) — « bizarre les propositions de transitions de slides par IA ne m'a été proposé que sur les dernières slides. Pourquoi pas sur tout ? » : sur un deck de 15 slides découpé en 2 lots (8+7), le 1er lot avait échoué SANS que rien ne le signale — ses slides disparaissaient silencieusement du résultat. Un lot en échec est désormais RETENTÉ une fois avant abandon (un aléa isolé, ex. latence de démarrage du 1er appel réseau de la session, ne doit plus condamner tout un lot), et toute slide dont le lot échoue malgré tout est explicitement nommée dans un message dédié (« Analyse impossible pour N slide(s) : … ») plutôt que de disparaître sans explication.
- Retour utilisateur — « lorsque je suis en mode présentation, j'ai l'impression que les transitions sont toujours identiques alors qu'elles sont paramétrées avec des valeurs différentes » : root cause — le lecteur live (
ui/present_window.py::_animate_transition) ignorait totalementkind, toujours un simple fondu d'opacité quelle que soit la transition choisie parmi les 21 du catalogue. Chaque transition est désormais catégorisée dans l'une de 4 familles visuellement distinctes (core/animation_catalog.py::transition_family: none/cut = passage sec, fade/dissolve = fondu d'opacité inchangé, push/cover/pull/wipe/blinds/comb/strips/random_bar/split/checker = glissement horizontal, wedge/wheel/circle/diamond/plus/newsflash/zoom = zoom depuis le centre) — pas un rendu géométrique pixel-parfait par transition (hors de portée raisonnable pour l'aperçu écran ; l'export PPTX, lui, reste fidèle à chaque transition individuellement), mais la famille choisie se voit désormais vraiment. - +71 tests (nouveaux fichiers
tests/test_voice_settings_dialog.py— 22,tests/test_tts_installer.py— 10,tests/test_tts_install_dialog.py— 7,tests/test_present_window_transitions.py— 16 ; extension detests/test_animation_suggest_service.py— 4,tests/test_animation_suggest_ui.py— 5,tests/test_animation_catalog.py— 7).
Corrigé (2026-08-07, retours sur la 5e vague — console présentateur, notes IA, transitions IA sur tout le deck)
- Retour utilisateur (captures) — « je vois bien le bouton pour lire la voix mais je ne vois pas le menu de paramétrage qui est mentionné » : root cause probable — le dialogue Paramètres (Backend + Images + Dictée + désormais Voix) dépasse facilement la hauteur de l'écran, sans le moindre moyen d'atteindre le bas (
QVBoxLayoutposé directement sur leQDialog, jamais défilable). Tout le contenu est maintenant dans unQScrollArea(boutons Enregistrer/Annuler restant, eux, toujours visibles hors du scroll). Nouvelle entrée de menu dédiée Paramètres > Voix…, qui ouvre ce même dialogue en faisant défiler directement jusqu'à cette section. - Retour utilisateur — « en mode présentateur si je clique sur le premier slide il faut démarrer automatiquement le chrono si j'ai oublié de le faire » : la toute première avance depuis le tout début de la présentation (clic, flèche, Espace, bouton Suivant) démarre l'horloge si elle ne tourne pas encore déjà — un oubli reste rattrapable, un démarrage manuel explicite au préalable reste respecté.
- Retour utilisateur — « en mode présentateur lorsque je clique dans le slide alors l'outil de conception de SlydeForge passe au-dessus de la fenêtre de management pour le présentateur. C'est vraiment gênant. » : root cause — sous Windows, un top-level Qt « possédé » (parent Qt +
Qt.Window) fait remonter son PROPRIÉTAIRE juste derrière lui à chaque activation (ex. un clic) ; le diaporama était possédé par la fenêtre principale (« l'outil de conception »), qui remontait donc au-dessus de la console présentateur (elle-même hors de cette chaîne de propriété). Le diaporama est désormais un top-level INDÉPENDANT (sans parent Qt) — sa fermeture à la sortie de SlydeForge est gérée explicitement puisqu'elle ne peut plus compter sur la suppression en cascade d'un parent. - Retour utilisateur — « si je suis en mode présentateur, que j'ai plusieurs écrans et que je sélectionne l'écran où se trouve la fenêtre de gestion de la présentation alors il faut mettre la fenêtre de gestion sur un autre écran [...] (sinon elle est cachée) » : choisir un écran de diffusion qui recouvrirait la console la relocalise désormais automatiquement sur un autre écran disponible — jamais cachée derrière le diaporama plein écran.
- Retour utilisateur — « lorsque l'IA génère un texte de note pour une slide, il faudrait également qu'elle propose un temps de lecture si aucun temps n'est renseigné » : nouveau
core/timing.py::estimate_reading_seconds(rythme oral ~130 mots/minute, même repère que le message « ~X min à l'oral » déjà affiché) — renseigné automatiquement UNIQUEMENT si la slide n'a encore aucun temps prévu, jamais en écrasant un réglage manuel existant. - Retour utilisateur — « j'ai voulu faire une analyse de transition IA sur l'ensemble des slides [15] et j'ai eu une erreur [...] Aucune transition n'a pu être proposée » : root cause — tout le deck partait en UN SEUL appel (15 images + 1 de contexte), avec un budget de tokens fixe (1200) bien trop court pour une réponse à 15 entrées JSON ; la réponse tronquée faisait échouer le parsing, et l'appel unique retombait entièrement à vide.
propose_deck_transitionsdécoupe désormais les gros lots (> 8 slides) en plusieurs appels bornés, chacun avec un budget de tokens proportionné à sa taille et sa propre image de contexte (la VRAIE slide de deck précédente, pour garder une continuité de style) — l'échec d'un lot n'efface plus plus les propositions déjà obtenues pour les autres. - +59 tests (nouveau fichier
tests/test_presenter_bugfixes_2.py; extension detests/test_slide_notes_ui.py,tests/test_timing.py,tests/test_animation_suggest_service.py,tests/test_settings_dialog_voix.py).
Ajouté (2026-08-07, 5e vague — sélecteur d'écran multi-moniteur, Paramètres > Voix, lecture vocale automatique)
- Retour utilisateur (poste à 3 écrans) — « il faut pouvoir sélectionner facilement l'écran sur lequel on souhaite diffuser la présentation [...] passer de l'un à l'autre facilement et que la diffusion s'adapte à la taille de l'écran automatiquement » : sélecteur d'écran fonctionnant pour N écrans (pas seulement une bascule entre 2) — combo « Écran de diffusion » dans la console présentateur (mis à jour à chaque navigation, un vidéoprojecteur peut être branché/débranché en cours de séance) et raccourci S côté diaporama (overlay listant tous les écrans détectés, avec leur résolution). Réutilise
_move_window_to_screen(déjà corrigé pour le DPI) et redessine immédiatement la diapositive à la résolution du nouvel écran. - Retour utilisateur — « je ne vois pas le menu pour paramétrer les voix et je ne comprends pas comment on peut l'utiliser sur les slides. Je rappelle que pour les voix je souhaite un exemple en français + anglais de très haute qualité (niveau pro) » :
services/tts.pyexistait déjà (Edge/Piper/ElevenLabs) mais sans AUCUNE interface pour le configurer — nouvelle page Voix dans Paramètres (moteur, voix FR/EN avec le catalogue Edge Neural par défaut — Denise/Henri en français, Aria/Guy en anglais —, vitesse, volume, clé ElevenLabs, avertissement de confidentialité pour les moteurs en ligne, bouton « 🔊 Écouter » par langue). Nouveauservices/tts_settings.py(config persistée par utilisateur) etui/audio_player.py(lecture audio partagée, wrapper QMediaPlayer/QAudioOutput). Usage sur les slides : bouton « 🔊 Lecture vocale » de la console présentateur, ou touche V pendant le diaporama — chaque diapositive est alors narrée depuis son champ Notes, puis ses étapes d'animation s'enchaînent automatiquement (rythme fixe) avant de passer à la suivante ; toute navigation manuelle relance la narration sur la nouvelle diapositive ; la fin du deck arrête proprement la lecture. - Bug réel corrigé en écrivant la page Voix : le combo de voix (éditable, pour permettre une valeur personnalisée) renvoyait le LIBELLÉ affiché (« Français — Henri (homme, chaleureux) ») au lieu de l'identifiant réel de la voix (
fr-FR-HenriNeural) dès qu'une entrée du catalogue était sélectionnée via la liste déroulante — la synthèse aurait reçu un nom de voix inutilisable.currentData()est désormais utilisé en priorité, avec repli surcurrentText()uniquement pour une valeur tapée à la main. - +76 tests (nouveaux fichiers
tests/test_screen_picker.py,tests/test_tts_settings.py,tests/test_audio_player.py,tests/test_settings_dialog_voix.py,tests/test_voice_reading.py).
Corrigé (2026-08-07, retours sur la 4e vague — sélecteurs numériques, écrans, laser, chronomètre)
- Retour utilisateur (captures) — « problème sur le sélectionneur de temps prévu sur une slide, idem sur les durées de transitions et animations » : les flèches +/- de ces champs étaient illisibles. Root cause : ils utilisaient un
QSpinBoxBRUT alors queui/spin_widgets.py::IntSpinBoxexiste précisément pour ce bug (déjà signalé auparavant par capture — Fusion peint tout le contrôle en un bloc natif sans point d'interception QSS pour les flèches). Les 4 champs concernés (temps prévu d'une slide, durée de transition, durée d'animation dans les 2 dialogues, durée dans le panneau de propriétés) utilisent désormaisIntSpinBox, plus un test garde-fou qui échoue si unQSpinBoxbrut est réintroduit ailleurs dansui/. - Retour utilisateur — « le mode présentateur, l'écran avec la présentation ne respecte pas bien la taille de l'écran ça déborde sur un autre écran » :
setGeometry(screen.geometry())suivi deshowFullScreen()ne suffit pas dès que les écrans n'ont pas le même facteur d'échelle (DPI) — la géométrie était posée dans l'échelle de l'écran d'origine puis réinterprétée sur la destination. La fenêtre est désormais rattachée à l'écran cible au niveau de saQWindownative (setScreen) après être sortie du plein écran, avant d'y être remise. - Retour utilisateur — « le laser ne suit pas la souris » :
mouseMoveEventn'était jamais appelé — la diapositive est peinte dans unQLabelenfant qui couvre toute la fenêtre, et Qt ne délivre les déplacements sans bouton enfoncé qu'au widget survolé (qui n'avait pas le suivi de souris), sans jamais remonter au parent. Remplacé par un suivi par minuteur sur la position GLOBALE du curseur : indépendant du widget survolé, et insensible à l'ajout d'autres calques (notes, aide, grille). - Retour utilisateur — « le chrono se lance tout de suite il faudrait un bouton de démarrage » : boutons ▶ Démarrer / ⏸ Pause et ↺ Remise à zéro dans la console. Le chronomètre de la console et l'alerte de dépassement de l'écran public suivent désormais la MÊME horloge (source unique côté diaporama) — sans quoi ouvrir la console dix minutes avant de parler affichait une alerte de dépassement au public dès la première diapositive.
- +20 tests (
tests/test_presenter_bugfixes.py).
Ajouté / Corrigé (2026-08-07, 4e vague — catalogue d'animations enrichi, mode présentateur, minutage, retours d'assistant)
- Retour utilisateur — « il n'existe que 2 types de transitions entre les slides, est-ce qu'il serait possible d'en proposer d'autres ? Idem est-ce que l'on peut proposer plus d'effet / animation sur un objet ? Il faut que l'IA ait accès à tout ce qui est possible » : nouveau
core/animation_catalog.py, source unique des transitions et effets — 21 transitions (contre 3) et 19 effets d'entrée (contre 5), tous vérifiés comme exportables en OOXML de base ECMA-376 (aucun risque de fichier « à réparer »). Le schéma JSON, les combos de l'UI, l'export PPTX, le mode présentation et les prompts IA en dérivent désormais tous : ajouter un effet au catalogue suffit à le rendre proposable par l'IA, sans retoucher un seul prompt (c'est précisément la dérive entre ces 5 copies qui faisait que l'IA ne « voyait » que 2 transitions). - Retour utilisateur — « la proposition de transition entre les slides ne fonctionne pas [...] je fais l'action depuis la slide 7 et il me dit que c'est sur le slide 1 : Analyse des transitions possibles pour 1 slide(s)… / Aucune transition n'a pu être proposée » : deux causes distinctes corrigées. (1) Le libellé donnait un COMPTE (« pour 1 slide(s) »), lu comme un NUMÉRO — les slides concernées sont désormais nommées (« pour la slide 7 »). (2) Sur une seule slide, l'IA n'avait aucun contexte — or une transition décrit l'ENTRÉE sur la slide, donc l'enchaînement depuis la précédente : sans la voir, le modèle répondait « none », systématiquement filtré ensuite. La slide précédente est maintenant jointe comme image de contexte, et toute slide ciblée sans proposition exploitable retombe sur un fondu (décochable) plutôt que de disparaître silencieusement. Un échec RÉSEAU, lui, continue de ne rien inventer.
- Retour utilisateur — « comment est-ce que je peux visualiser mes notes qd je suis en mode diaporama ? [...] Powerpoint affiche sur un autre écran ce type d'information [...] il faut pouvoir décider au lancement [...] et basculer d'un mode à l'autre facilement même en cours de diffusion » : nouveau
ui/presenter_window.py::PresenterWindow— console du présentateur avec diapositive courante, diapositive suivante, notes, minuteur, position dans le deck, boutons de navigation et rappel permanent des raccourcis. Envoyée sur le 2e écran quand il y en a un (le diaporama part alors sur l'autre), proposée au lancement, et basculable à chaud par P. Les fonctions existantes mais invisibles (laser, grille, notes) sont désormais listées dans une aide à l'écran (H). - Retour utilisateur — « j'aimerais que l'on puisse définir par slide un temps limite et que si l'on dépasse [...] une alerte apparait discrètement [...] et passe en rouge si on dépasse [...] voir le décompte global et celui définit sur la slide courante » : nouveau
core/timing.py(règle du temps global implémentée telle que formulée : somme des slides si TOUTES en ont un, sinon le total manuel, sinon aucun total), champ « Temps prévu » par slide à côté des notes, pastille d'alerte discrète en haut à droite de l'écran public (orange à l'approche, rouge et compte négatif en dépassement), et double décompte (global + slide courante) dans la console du présentateur. Nouveauservices/timing_suggest_service.pypour challenger l'IA sur les temps : sans temps global elle estime librement chaque slide ; avec un temps global imposé elle ne donne que des POIDS relatifs et la répartition exacte est faite arithmétiquement (la somme retombe à la seconde près). - Retour utilisateur — « lorsque l'on utilise l'IA pour générer des notes il n'y a pas d'explication dans l'assistant SlydeForge sur ce qu'il se passe » : message explicite avant l'appel (nommant la slide) et après (nombre de mots, durée approximative à l'oral, et où le texte a été inséré).
- Retour utilisateur — « lorsqu'il y a un nouvel évènement dans l'assistant [...] et que l'utilisateur n'a pas fait bougé manuellement la scrollbar récemment [...] alors il faut toujours montrer le dernier message » : défilement automatique vers le dernier message, suspendu uniquement si l'utilisateur vient de faire glisser l'ascenseur à la main (15 s de grâce) sans être revenu en bas, ou s'il a une sélection de texte en cours.
- Retour utilisateur — « dans la popup de gestion des animations [...] il est difficile d'identifier l'élément [...] indiquer sur la slide le numéro de référence pour faire le lien visuel » : pastilles rondes numérotées posées sur les éléments animés du canevas pendant toute l'ouverture du dialogue, portant le même numéro que la ligne correspondante — et renumérotées en direct à chaque Monter/Descendre/Supprimer.
- Retour utilisateur — « j'aurais aimé que lorsque je clique sur un objet voir le paramétrage de son animation dans la propriété de cet objet » : nouveau groupe « Animation » dans le panneau de propriétés (effet, déclencheur, durée + rappel du rang dans la séquence). L'ordre relatif (
order) y est préservé et non réécrit, puisqu'il se règle au niveau de la slide entière. - Retour utilisateur — « pourquoi les notes ne sont pas ouvertes par défaut lorsque l'on ouvre l'application ? » : panneau Notes déplié d'emblée.
- Bug préexistant trouvé en passant : un PowerPoint sans diapositive exploitable affichait « Import impossible : Deck non conforme au schéma : slides : [] should be non-empty » — la validation de schéma passait AVANT le test « aucune diapositive » dont le message clair était pourtant déjà écrit. Ordre corrigé. Ce chemin ouvrait un dialogue modal non neutralisé qui bloquait indéfiniment la suite de tests complète (jamais menée à terme jusqu'ici).
- Retour utilisateur — « intégrer un système de lecture des notes automatique [...] charger des fichiers voix en indiquant la langue + infos techniques (vitesse, volume) [...] une voix de haute qualité et pro [...] un connecteur compatible avec elevenlabs.io [...] gestion des problèmes de phonétique, intonation [...] avec le moins d'effet robot possible » : nouveau
services/tts.py, couche de synthèse vocale ENFICHABLE à trois moteurs — Edge Neural (voix neuronales gratuites, sans clé, fr-FR Denise/Henri et en-US Aria/Guy), Piper (neuronal 100 % LOCAL, pour les notes confidentielles) et un connecteur ElevenLabs (clé d'API). Toute dépendance est importée paresseusement : SlydeForge démarre et fonctionne normalement sans qu'aucun moteur ne soit installé, et chaque absence produit un message disant précisément quoi installer. Un avertissement de confidentialité (volontairement générique, ne nommant aucune organisation) précède l'usage d'un moteur en ligne et oriente vers l'alternative locale. Traitement phonétique : lexique de prononciation respectant les limites de mots et la casse (« ROI » l'indicateur est corrigé, « roi » le monarque ne l'est pas) et respirations ajoutées pour casser le débit plat des listes à puces. - Retour utilisateur — « être capable d'extraire la présentation dans un format vidéo où l'ensemble pourra être animé et lu (texte des notes) en automatique » : nouveau
services/video_export.py— chaque ÉTAPE d'animation est rendue en image via exactement la même logique que le mode présentation (la vidéo montre ce que le public verrait, jamais un rendu parallèle qui pourrait diverger), la durée de chaque diapositive est le plus long entre son audio et son temps prévu (une phrase n'est jamais coupée), les diapositives masquées sont exclues. ffmpeg est DÉTECTÉ, jamais téléchargé automatiquement : son absence affiche la commande d'installation pour Windows/macOS/Linux. - Nouvelle documentation
docs/VOIX.md: choix du moteur, avertissement de confidentialité, installation, où trouver des voix libres et gratuites (bibliothèque Piper, Common Voice, Coqui), où déposer les modèles, réglages vitesse/volume, conseils de prononciation et d'intonation, et installation de ffmpeg pour l'export vidéo. - +150 tests (nouveaux fichiers
tests/test_animation_catalog.py,tests/test_chat_autoscroll.py,tests/test_properties_panel_animation.py,tests/test_canvas_animation_badges.py,tests/test_presenter_window.py,tests/test_timing.py,tests/test_timing_suggest_service.py,tests/test_slide_duration_ui.py,tests/test_tts.py,tests/test_video_export.py; extension detests/test_transitions_animations.py,tests/test_animation_suggest_service.py,tests/test_element_animations_dialog.py,tests/test_slide_notes_ui.py). Suite complète vérifiée verte (2047 tests, en deux moitiés — cf. note sur le crash natif d'infrastructure ci-dessous).
Corrigé (2026-08-07, 3e vague — retours après la 2e vague : chargement, IA notes, gestion manuelle des animations, badges, menu contextuel, slash)
- Retour utilisateur — « je ne vois pas la barre de chargement à l'ouverture d'un projet » : root cause — un seul
processEvents()juste aprèsshow()ne garantissait pas toujours que Windows ait eu le temps de composer/peindre la fenêtre avant que le chargement synchrone bloquant ne reprenne la main, et pour un projet qui se charge très vite l'overlay pouvait de toute façon apparaître puis disparaître en quelques millisecondes, trop rapide pour être perçu.ui/loading_overlay.py::LoadingOverlay: plusieurs passages deprocessEvents()+raise_()/activateWindow()+ reste au-dessus de la fenêtre parente (WindowStaysOnTopHint), et une durée d'affichage MINIMALE (400ms, attente via une boucle d'évènements locale — jamaistime.sleep(), qui gèlerait aussi le spinner). Même overlay réutilisé pour répondre à « lorsque je change de charte et qu'il y a beaucoup de slides ça met un temps de chargement, afficher une barre de progression » (_on_chat_theme_changed/_reapply_theme). - Retour utilisateur — « lorsque l'on fait suggérer IA pour les notes, il faudrait montrer que c'est en cours de traitement » : le bouton « ✨ Suggérer (IA) » se désactive et l'indicateur d'occupation du chat s'active pendant l'appel, se réactive à la fin (succès comme erreur).
- Retour utilisateur — « comment gérer manuellement l'animation de plusieurs objets en une seule fois ? Comment modifier ou supprimer des animations sur des objets. Ça serait bien de pouvoir lister l'ensemble des animations des objets sur une slide. Ça permettrait de définir également l'ordre des animations sur chaque objet » : nouveau
ui/element_animations_dialog.py::SlideAnimationsDialog(menu contextuel vignette > « Gérer les animations… ») — liste tabulaire de tous les éléments animés d'une slide (effet/déclencheur/durée modifiables), boutons Monter/Descendre/Supprimer. Nouveau champelement.animation.order(deck.schema.json) déterminant l'ordre de déclenchement "au clic", délibérément INDÉPENDANT de l'ordre des éléments dans le tableau (jamais mélangé avec le z-order) — repli sur la position dans le tableau si absent (comportement historique inchangé). Pris en compte à l'export PPTX (render/pptx_renderer.py) et dans le mode présentation (ui/present_window.py). - Retour utilisateur — « j'aimerais voir une icône sur les slides où il y a des animations et une autre icône lorsqu'il existe une transition entre 2 slides » : badges ronds superposés au coin haut-droit des vignettes de la bande (
ui/slide_strip.py::_with_status_badges), et rappel dans l'info-bulle de la vignette. - Retour utilisateur — « réorganiser les fonctions disponibles depuis le menu contextuel d'une preview de slide afin de permettre une meilleure lecture et compréhension. Je pense qu'ajouter des icônes permettrait de faciliter la compréhension » :
_build_context_menuregroupé en 5 blocs logiques (structure / édition / IA contenu-design / animations-transitions / visibilité-suppression), une icône par entrée (ui/icons.py:insert_slide,rename,ai_sparkle,transition,animations_list,eye, réutilisation deduplicate/delete). - Retour utilisateur — « lorsque je saisis "/" [...] je voudrais que ça soit sous forme d'un "like" [...] "/layout" [...] mais aussi "/no_layout" [...] "/slide" me permet d'avoir "/review_slide" » :
ui/chat_panel.py— l'autocomplétion de 1er niveau filtre désormais par SOUS-CHAÎNE (n'importe où dans le nom de la commande) plutôt que par préfixe strict. - Retour utilisateur — « lorsque je fais "/image" je ne vois pas bien la suggestion de la liste des fonctions » (popup tronquée, barre de défilement horizontale) :
ui/slash_command_popup.pycalcule désormais sa largeur d'après le texte le plus long à afficher (bornée entre 280 et 560px), défilement horizontal désactivé. - +49 tests (nouveaux fichiers
tests/test_element_animations_dialog.py,tests/test_manage_slide_animations.py,tests/test_slide_strip_badges_and_menu_icons.py,tests/test_slash_command_like_matching.py; extension detests/test_project_loading_overlay.py,tests/test_slide_notes_ui.py,tests/test_transitions_animations.py,tests/test_present_window.py).
Ajouté (2026-08-07, 2e vague — IA d'animation, audio, chargement, notes, panneaux repliables, mode présentation)
- Retour utilisateur — « un outil IA qui permet de proposer de les créer automatiquement pour nous sur une slide pour l'animer [...] afficher des éléments en suivant une séquence, faire des effets sur des éléments pour les rendre plus vivant et interactifs [...] ou encore pour la transition d'une slide vers une autre [...] une analyse de l'ensemble des slides pour proposer une animation sur l'ensemble ou une sélection de slides [...] trouver un moyen visuel de proposer les différentes animations et que l'utilisateur puisse les valider avant de les appliquer » : nouveau
services/animation_suggest_service.py— UN appel vision propose une séquence d'animations d'entrée par élément (déclencheurs variés : au clic / avec le précédent / après le précédent, jamais tout au clic par défaut) + une transition pour la slide (/suggest_animation, menu contextuel « Animer la slide (IA)… ») ; un second appel analyse une sélection de slides (ou tout le deck, nouveau menu « Présentation ») et propose une transition cohérente par slide (« Proposer des transitions (IA)… »). Présenté dans une liste À COCHER (ui/animation_suggest_dialog.py, tout coché par défaut, décochable ligne par ligne) — appliqué mécaniquement (jamais via patch IA, ce ne sont que des propriétés structuréestransition/animationdéjà en place). - Retour utilisateur — « on a l'insertion d'une vidéo, mais il faudrait également gérer un objet de type audio. Est-ce que le même objet peut gérer les 2 ? » : oui — nouveau champ
kind("video"/"audio") sur le type d'élémentvideoexistant, aperçu canevas distinct (carte haut-parleur + forme d'onde pour l'audio), menu Insérer > « Audio… ». L'export PPTX des deux passe par le même mécanisme (add_movie, python-pptx n'ayant aucun support natif de l'audio) — limitation de la librairie assumée et signalée à l'utilisateur, pas un choix d'architecture SlydeForge. - Retour utilisateur — « lorsque l'on ouvre un projet il faudrait une barre de chargement » : nouveau
ui/loading_overlay.py::LoadingOverlay(spinner réutilisé du splash de démarrage), affiché pendant le chargement synchrone d'un projet existant. - Retour utilisateur — « il n'y a pas de zone pour pouvoir commenter les slides. Et il faudrait que l'IA propose également le texte que l'on pourrait dire sur une slide lors d'une présentation » : nouvelle zone Notes repliable sous le canevas, liée à
deck.schema.json::slide.notes(déjà exportée en PPTX, désormais aussi RELUE à l'import — gap comblé au passage) + bouton « ✨ Suggérer (IA) » (services/notes_suggest_service.py, UN appel vision, texte au style oral inséré dans le champ, jamais appliqué sans relecture). - Retour utilisateur — « lorsque l'on replie complètement les volets avec l'assistant, les previews ou encore les propriétés il faudrait mettre une icône pour pouvoir les redéplier facilement. Et il faudrait également quand ils sont dépliés montrer par une icône dans la zone de redimensionnement que l'on peut élargir ou diminuer la zone avec la souris » : nouveau
ui/collapsible_splitter.py— bouton chevron cliquable sur la poignée dès qu'un panneau (chat/vignettes/propriétés — jamais le canevas) est replié à 0, grip pointillés quand les deux côtés sont visibles. - Retour utilisateur — « un player pour pouvoir visualiser / diffuser une présentation en plein écran [...] afin de pouvoir faire une vraie présentation directement depuis SlydeForge [...] mais aussi pouvoir tester et valider les animations, effets de transitions [...] voir les commentaires, avoir un laser de présentation, naviguer entre les slides + les flèches [...] ou aller plus rapidement vers une slide » : nouveau
ui/present_window.py::PresentWindow(menu Présentation > « Lancer le diaporama », F5) — navigation clavier complète (flèches/espace/PageUp/PageDown/Home/Fin), notes en surimpression (N), grille de vignettes pour sauter directement à une slide (G), pointeur laser (L). Transitions entre slides et animations d'entrée par élément RÉELLEMENT rejouées (fondu entre images pré-rendues — un élément pas encore déclenché a son opacité forcée à 0 pour ce rendu, réutilise la propriété opacité déjà en place). MVP mono-écran assumé : pas de fenêtre présentateur séparée sur un 2e moniteur. - +100 tests dans 9 nouveaux fichiers (
tests/test_animation_suggest_service.py,tests/test_animation_suggest_dialogs.py,tests/test_animation_suggest_ui.py,tests/test_project_loading_overlay.py,tests/test_notes_import_export_roundtrip.py,tests/test_notes_suggest_service.py,tests/test_slide_notes_ui.py,tests/test_collapsible_splitter.py,tests/test_present_window.py) + extension detests/test_insert_elements.py,tests/test_insert_video_ui.py,tests/test_insert_menu_icons_and_order.py.
Ajouté (2026-08-07, 9 évolutions — bibliothèque dans le chat, revue de slide IA, formes modernes, transitions/animations, vidéo, opacité, i18n)
- Retour utilisateur — « pouvoir depuis les fonctions "/" appeler une icone ou image de la bibliothèque afin de demander de l'utiliser lors des échanges avec l'IA » : nouvelle commande
/imagedu chat — ouvre le même sélecteur de bibliothèque que Insérer > Image… (images/icônes de charte + bibliothèque du projet), joint l'image choisie au PROCHAIN message envoyé à l'IA (multimodal, même mécanisme que les fichiers déjà joints), avec son nom rappelé dans le prompt. Affichée dans la MÊME liste de pièces jointes que les fichiers (icône dédiée, retrait individuel). - Retour utilisateur — « une fonction [...] sur la revue d'une slide pour challenger les couleurs, positions, taille des objets, police, taille de la police [...] et que l'IA propose une version plus élégante et plus pro de la slide courante », complété ensuite par « il faudrait aller un peu plus loin [...] gérer les icônes, utilisations de formes [...] maintenant qu'il y a un module pour la gestion des transitions et des effets » : nouvelle commande
/review_slide+ entrée « Revue de la slide (IA)… » dans le menu contextuel des vignettes (groupée avec « Suggérer des mises en forme… »). UN appel vision (services/slide_review_service.py) produit une critique/instruction — jugement esthétique général explicitement autorisé (contrairement au contrôle de lisibilité automatique existant), considère aussi l'ajout d'un accent décoratif ou une transition/animation adaptée — réinjectée dans le pipeline d'édition existant (aperçu avant/après, undo/version inclus). - Retour utilisateur — « est-ce que la suggestion de mise en forme pourrait intégrer des formes plus modernes (ex : hexagone, ronds de couleurs...) pour rendre le design plus moderne » : le wizard
/suggest_designpeut désormais proposer une zonedecor(accent géométrique pur — hexagone, rond, bandeau…) en étape 1, avec sa vraie silhouette dans l'aperçu wireframe (plutôt qu'un rectangle grisé générique), traduite en un élémentshaperéel par le pipeline d'édition. - Retour utilisateur — « lorsque des changements sont proposés [...] est-ce qu'il pourrait y avoir une option (décochée par défaut) qui permette d'ajouter la nouvelle proposition juste après plutôt que de remplacer » : nouvelle case à cocher dans l'aperçu avant/après IA — la slide d'origine reste inchangée, la proposition est insérée comme une nouvelle slide juste après (id régénéré, même discipline que « Dupliquer »).
- Retour utilisateur — « comment gérer le niveau de transparence d'une image ou un objet graphique, il faudrait que ce soit une propriété » : la propriété
opacitydu schéma (déjà rendue à l'écran) est désormais exposée dans le panneau de propriétés (tous types d'éléments) et exportée en PPTX (injection OOXML<a:alpha>/<a:alphaModFix>, même stratégie que l'ombre portée). - Retour utilisateur — « SlydeForge ne sait pas encore gérer toutes les transitions et animations [...] proposer une liste des évènements » : nouveau module transitions (fade/poussée par slide) + animations d'entrée (apparition/fondu/balayage/vol vers l'intérieur, déclenchées au clic/avec le précédent/après le précédent) sur les éléments — dialogues dédiés accessibles depuis les menus contextuels vignette/canevas. Export PPTX uniquement (injection OOXML
<p:transition>/<p:timing>) ; aperçu écran volontairement statique. MVP assumé : le déclencheur "avec/après le précédent" est stocké mais pas encore distingué à l'export (tout élément animé devient un clic indépendant). - Retour utilisateur — « il n'est pas possible d'ajouter d'objets de type vidéos [...] il faudrait également répondre à ce besoin d'une présentation moderne » : nouveau type d'élément
video(aperçu écran = affiche + icône lecture, export PPTX viaadd_movie— marqué EXPÉRIMENTAL par python-pptx lui-même, signalé à l'utilisateur). - Retour utilisateur — « dans le menu insérer, mettre des icônes [...] et revoir l'ordre des objets afin de proposer ce qui serait le plus logique » : icônes dessinées (cf.
ui/icons.py) sur chaque entrée du menu Insérer, regroupées par nature (Contenu → Formes → Médias → Données → Structure) plutôt que rangées au fil de l'eau. - Retour utilisateur — « faire version multi langues (français / anglais) » : infrastructure i18n (QTranslator chargé au démarrage selon la préférence Paramètres > Langue, redémarrage requis) — périmètre MVP converti :
ui/main_window.py(quasi-totalité des menus, via le helper commun_act()) + un sous-ensemble représentatif deui/chat_panel.py. Le reste de l'application reste à convertir (cf.i18n/README.md). - +90 tests (nouveaux fichiers
tests/test_image_attach_command.py,tests/test_slide_review_service.py,tests/test_review_slide_ui.py,tests/test_diff_preview_insert_after.py,tests/test_transitions_animations.py,tests/test_transition_animation_ui.py,tests/test_insert_video_ui.py,tests/test_insert_menu_icons_and_order.py,tests/test_i18n.py; extension detests/test_suggest_design_feature.py,tests/test_roundtrip_pptx.py,tests/test_properties_panel_border_shadow.py,tests/test_insert_elements.py,tests/test_settings_dialog.py).
Ajouté (2026-08-07, /suggest_design en 3 étapes — disposition, couleurs, tailles)
- Retour utilisateur — « ça reste très basique, il propose bien et gère le positionnement, mais ne fait aucune suggestion à minima sur les couleurs. Il pourrait y avoir 2 phases, on sélectionne le positionnement et dans un deuxième temps les couleurs, puis 3ème temps les tailles de polices. À chaque étape l'IA prend en compte la réponse pour faire la proposition suivante » :
/suggest_designdevient un assistant en 3 étapes successives, chacune un appel IA qui voit les réponses précédentes (une seule conversation partagée, cf.services/design_suggest_service.py) : (1) disposition (inchangé), (2) un jeu de couleurs par zone — UNIQUEMENT des rôles de la palette de la charte, jamais une couleur inventée (retour utilisateur d'origine : « en respectant les conventions des couleurs du thème ») — prévisualisé sur le wireframe DÉJÀ choisi (mêmes zones, couleurs différentes par vignette, cf.render/wireframe.py::render_wireframe_imagedésormais capable de teinter chaque zone avec contraste de texte automatique) ; (3) un préréglage de taille de la charte (title/heading/subheading/body/caption) par zone TEXTUELLE de la disposition — une image/un graphique/un tableau/un logo gardent leur propre taille, jamais de police. Annuler l'étape 2 ou 3 SAUTE cette étape (poursuit avec la charte par défaut) plutôt que d'abandonner tout le travail déjà fait ; annuler l'étape 1 (disposition) abandonne l'ensemble, comme avant. Les 3 choix sont combinés en UNE instruction pour le pipeline d'édition existant, qui produit la vraie diapositive. tests/test_suggest_design_feature.pyréécrit pour les 3 étapes (42 tests : validation par étape, annulation partielle à chaque étape, magicien bout en bout).- Retour utilisateur — « dans la suggestion de design il doit y avoir une image ou plusieurs alors il faut dans le wizard demander de charger une photo, ou utiliser la bibliothèque ou par IA comme tu l'as déjà dans un sélectionneur. Faut juste bien donner le contexte de quelle image ça concerne lors de la sélection » : nouvelle étape "images" (entre la disposition et les couleurs) — pour CHAQUE zone "image" de la disposition retenue, ouvre le même dialogue unifié que Insérer > Image… (bibliothèque de charte/projet, recherche libre de droits, génération IA), avec un bandeau bleu rappelant PRÉCISÉMENT quelle zone est concernée (« Photo 1/2 pour la disposition « X » — zone Image en x=…%, y=…%, largeur=…%, hauteur=…% »), indispensable dès qu'il y a plusieurs photos à choisir à la suite. Jamais confié à l'IA : le patch d'édition ne peut pas transporter un contenu binaire réel, la ou les vraies photos choisies sont injectées directement dans le deck APRÈS le patch (
services/design_suggest_service.py::apply_chosen_images), à la position exacte de leur zone ; l'IA est explicitement instruite de ne RIEN créer elle-même dans une zone déjà pourvue d'une photo (évite tout doublon). Fermer/annuler UNE sélection de photo laisse simplement cette zone sans image (l'IA réutilise une image déjà présente sur la diapositive si possible) plutôt que d'abandonner tout l'assistant — même discipline que les étapes couleurs/tailles. - +11 tests (extension de
tests/test_suggest_design_feature.py, désormais 53 tests).