diff --git a/STATUS.md b/STATUS.md index e406d0b..df9091c 100644 --- a/STATUS.md +++ b/STATUS.md @@ -120,7 +120,7 @@ Ordre conseillé : → Audits : **[security/audit-securite-manager-service.md](security/audit-securite-manager-service.md)** et **[security/audit-manager-app.md](security/audit-manager-app.md)** (2026-07-13/14) - **manager-service** — le point critique est la **rotation des secrets committés** (JWT signing key, clés API, connection strings, MQTT, Telegram). Gravité inchangée, mais **déplacée en fin de backlog le 2026-08-11** : les 6 repos sont privés sur un Gitea auto-hébergé. ⚠️ Contrainte à ne pas perdre — **la rotation doit précéder I3** (compose/Traefik prod), sinon la prod naît avec les clés de l'historique et il faut rotationner deux fois. Puis quatre niveaux Haute → Basse. -- **manager-app** — le critique (`manager_api_new` désynchronisé, 68 erreurs de bruit) est ✅ **résolu le 2026-08-11** : `flutter analyze` passe à 3 issues et redevient un feu vert utilisable. Au passage, les 3 déclencheurs de génération OpenAPI ont été supprimés, ce qui **verrouille la règle « `manager_api_new` s'édite à la main »**. Reste Haute → Basse : duplication du shell de dialog, `build()` monolithiques, i18n contournée, clé AES en dur. +- **manager-app** — le critique (`manager_api_new` désynchronisé, 68 erreurs de bruit) est ✅ **résolu le 2026-08-11** : `flutter analyze` passe à 3 issues et redevient un feu vert utilisable. Au passage, les 3 déclencheurs de génération OpenAPI ont été supprimés, ce qui **verrouille la règle « `manager_api_new` s'édite à la main »**. Reste Haute → Basse : duplication du shell de dialog, `build()` monolithiques, i18n contournée, clé AES en dur. → **La duplication du shell de dialog a désormais sa maquette et sa carte** : `DOCS/claude design/refonte-configuration-sections.html` §6 (coquille, inventaire des 29 dialogues, deux dialogues dessinés) et `kanban/cards/5-planifie/290-…`. ⚠️ **Ordre imposé** : les 8 `showNewOrUpdate*` sont *absorbés* par l'éditeur de collection du gabarit B — extraire un scaffold pour eux d'abord, ce serait refactoriser du code à supprimer. Voir la note dans `security/audit-manager-app.md`. → **Détail complet, par priorité et avec les cases à cocher : [status/securite-dette-technique.md](status/securite-dette-technique.md)** diff --git a/claude design/mediatheque-ajout-ressource.html b/claude design/mediatheque-ajout-ressource.html new file mode 100644 index 0000000..ff94a02 --- /dev/null +++ b/claude design/mediatheque-ajout-ressource.html @@ -0,0 +1,416 @@ + +Ajouter une ressource — maquette + + + +
+ +

Ajouter une ressource

+

Le dialogue d'ajout est le dernier écran de la Médiathèque à ne pas avoir été repris. Il ne dénote pas seulement : deux de ses défauts sont des bugs de dimensionnement, et un troisième fait qu'on ne voit pas ce qu'on vient de choisir.

+ +
+
+ La thèse : les deux onglets sont le problème. + « Local » / « En ligne » est une distinction technique — où vivent les octets — imposée avant que l'utilisateur ait fait quoi que ce soit. Un seul corps suffit : une zone de dépôt, et dessous une ligne discrète « ou coller une URL ». +
+
+ +
+ + +
+
Aujourd'huitel quel dans le code
+ +
+

Nouvelle ressource

+ +
+
+
Local
+
En ligne
+
+
+
+ Fichiers + Aucun aperçu possible +
+
+
+ +
+ ↺ Annuler + ✓ Créer +
+
+ +

1 Rayon 20 — l'échelle de l'app va de 5 à 10.

+

2 Le corps se dimensionne sur l'écran, pas sur le dialogue : 960 × 540 demandés dans 512 de large.

+

4 Cinq fichiers choisis, et voilà tout ce qu'on en voit.

+

5 « Local » / « En ligne » écrits en dur, en français.

+

6 Deux boutons de même poids ; le primaire accepte le clic à vide.

+
+ + +
+
Proposémaquette
+ +
+
+

Ajouter une ressource

+ +
+ +
+
+
+ +
+ Glissez vos fichiers ici ou parcourez + JPG, PNG, GIF, MP3, MP4, WEBM, PDF, JSON +
+ +
+ + + +
+ +
+ + +
+ +
    +
  • + + salle-des-fossiles.jpg2560 × 1707 + Image + +
  • +
  • + + commentaire-salle-2-nl.mp33 min 12 s + Audio + +
  • +
  • + + brochure-2026.pdf12 pages + PDF + +
  • +
+ +
+ 3 fichier(s) · 4,8 Mo +
+
+ +
+ + +
+
+ +

Le gain principal n'est pas l'habillage : c'est la liste. On voit ce qu'on a choisi, et on peut en retirer un.

+
+
+ +

Les défauts, relevés dans le code

+

Numérotés comme sur la maquette de gauche. Les trois marqués bug ne relèvent pas du goût.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#Ce qui clocheEffet
1Rayon 20 sur le dialoguenew_resource_popup.dart:22Hors de l'échelle de l'app (5 / 8 / 10). Exactement le défaut qu'on vient de retirer de la modale de détail.
2bugLe corps se dimensionne sur MediaQueryupload_content_container.dart:33-35width * 0.5 × height * 0.5 de l'écran : sur 1920 × 1080, 960 × 540 demandés dans un dialogue de 512 de large utile. Le contenu déborde et se fait rogner.
3bugLe champ URL aussiupload_online_resources_container.dart:107 — // TODO GET SIZEMême cause : width * 0.35 de l'écran. Le TODO est resté.
4bugOn ne voit pas ce qu'on a choisiupload_content_container.dart:57-58Sur le web, après sélection : une carte titrée « Fichiers », sous-titrée « Aucun aperçu possible ». Cinq fichiers ou un seul, même affichage. Aucun moyen d'en retirer un — il faut tout recommencer.
5Onglets écrits en durresource_tab.dart:30-31« Local » et « En ligne » restent en français en anglais et en néerlandais — le trou qu'on vient de combler ailleurs dans la Médiathèque.
6Deux boutons de même poids, et une erreur évitablenew_resource_popup.dart:60-92Le primaire accepte le clic à vide, puis affiche une notification orange « aucun fichier chargé ». Désactiver le bouton supprime l'erreur au lieu de l'expliquer.
Type forcé à Image pour tout fichier localresource_tab.dart:93 et :97Code mort et trompeur : create() re-déduit le type depuis l'extension. À lire, on croit que le MP3 sera enregistré comme image.
bugfilesToSendWeb!.length sur une variable nullenew_resource_popup.dart:82Branche non-web uniquement : cliquer « Créer » sans rien choisir plante. Invisible en production tant que l'app reste web.
+
+ +

Ce que ça touche

+
+
+

Le geste change

+
    +
  • Glisser-déposer réel, plus seulement un bouton bleu.
  • +
  • Une ligne par fichier : type, nom, poids, et une croix pour en retirer un.
  • +
  • Le poids total des fichiers choisis. Avant compression : la calculer pour l'afficher referait tout le travail de l'upload une seconde fois, et l'estimation haute est du bon côté pour un quota.
  • +
  • « Ajouter 3 fichiers » plutôt que « Créer » : le bouton dit ce qui va se passer.
  • +
  • Le type se déduit de l'extension et s'affiche en pastille, ce que le code fait déjà sans le montrer.
  • +
+
+
+

Les fichiers

+
    +
  • new_resource_popup.dart — réécrit
  • +
  • upload_content_container.dart — réécrit (zone de dépôt + liste)
  • +
  • resource_tab.dartsupprimé, il ne portait que la barre d'onglets
  • +
  • upload_online_resources_container.dartsupprimé, remplacé par le champ URL dépliant
  • +
  • Une poignée de clés l10n, dans la même série media*
  • +
+
+
+ +
+
+ Implémenté le 2026-09-03. + Glisser-déposer réel (écoute des événements du document — Flutter web ne reçoit pas les fichiers du bureau), liste retirable, champ URL dépliant, bouton désactivé à vide. resource_tab.dart, upload_content_container.dart et upload_online_resources_container.dart sont supprimés ; le nouveau sélecteur vit dans Components/resource_picker.dart. Le dialogue de confirmation partagé (12 appelants) a été repris dans la foulée : « Oui »/« Non » en dur remplacés, et le bouton de confirmation passe en rouge quand l'action détruit. +
+
+ +
+
+ C'était hors périmètre du lot V1 Médiathèque. + v1-mediatheque-plan.md ne mentionne ce dialogue nulle part : le lot s'arrête à la grille, aux facettes et au panneau de détail. Ce chantier-ci est adjacent, pas inclus — à faire maintenant dans la foulée, ou à poser comme carte de suivi. C'est un arbitrage, pas une évidence. +
+
+ +
+ + diff --git a/claude design/refonte-configuration-sections.html b/claude design/refonte-configuration-sections.html new file mode 100644 index 0000000..7344544 --- /dev/null +++ b/claude design/refonte-configuration-sections.html @@ -0,0 +1,1311 @@ + +Refonte Configuration & Sections + + + + +
+ +
+
manager-app · proposition d'interface
+

Refonte Configuration & Sections

+

Le blanc de l'écran actuel ne vient pas de la mise en page de configuration_detail_screen.dart : il vient de quatre composants partagés qui imposent des pastilles pleine largeur, des labels centrés et des listes horizontales à hauteur fixe. On les corrige une fois, et les 13 types de section en héritent.

+
+ Écran Configuration + Écran Section + 3 gabarits pour 13 types + 2 dialogues + Tokens de constants.dart +
+ +
+ Rien à inventer : le système existe +

constants.dart porte déjà un système complet — kSurface2, kInk2, kLine, kSpace1kSpace8, kRadiusInput 5 / kRadiusCard 8 / kRadiusShell 10 / kRadiusPill, kTitleCard, kLabelField, kTextHint. confirmation_dialog.dart et resource_picker.dart l'appliquent déjà.

+

Ces deux écrans sont simplement antérieurs à ce système. Il n'y a donc pas de direction visuelle à trancher ici : toutes les valeurs des maquettes ci-dessous sont reprises de constants.dart, et là où une première version de cette page avait inventé un rayon ou une capitale, c'est le token qui a gagné.

+
+
+ + +
+
+
01
+
+

Ce qui fabrique le blanc

+

Quatre causes, toutes dans des composants réutilisés par tous les écrans d'édition.

+
+
+ +
+
Cause
Effet observé
Correction
+
+
Le champ texte est une pastille pleine largeur + text_field_container.dart — width: double.infinity, radius 29, margin 10px
+
« Config test » (11 caractères) occupe une barre grise de 1 400 px de large et 60 px de haut. Le compteur 11/50 se retrouve à un mètre du texte.
+
Champ à largeur intrinsèque (rang de 12 colonnes), radius 8, bordure 1 px au lieu d'un fond plein.
+
+
+
Le label est à gauche, dans un Row + string_input_container.dart · resource_input_container.dart
+
Chaque ligne a une colonne de label d'une largeur différente : rien ne s'aligne verticalement, et le champ démarre à un endroit imprévisible.
+
Label au-dessus, capitales 10,5 px lettrées. Une grille, une seule gouttière.
+
+
+
Deux composants se centrent eux-mêmes + mainAxisAlignment: MainAxisAlignment.center
+
« Titre affiché », « Langues » et « Hors ligne » flottent au milieu de la carte, désolidarisés des champs au-dessus.
+
Aucun composant ne décide de son alignement : c'est le parent qui place.
+
+
+
Les sections sont une liste horizontale à hauteur fixe + section_reorderList.dart — height: size.height * 0.3, cartes 150×150
+
30 % de la hauteur d'écran réservés quoi qu'il arrive, cartes carrées trop grandes, et les sections au-delà de la 8e sont hors champ à droite sans indice visuel.
+
Grille responsive auto-fill minmax(146px, 1fr), hauteur pilotée par le contenu, réordonnancement conservé.
+
+
+
+ + +
+
+
02
+
+

Le champ, avant et après

+

C'est la brique qui décide de la densité de tous les écrans. Elle se corrige dans trois fichiers.

+
+
+ +
+
+
Aujourd'hui
+
+
+
Identifiant :
+
Config test 11/50
+
+
+ Titre affiché: + Config Test3 / 3 langues traduites +
+
+
Deux champs, deux alignements, 210 px de hauteur. Le libellé « Identifiant » est répété par le titre de carte juste au-dessus.
+
+ +
+
Proposé
+
+
+
+ +
Config test11/50
+
Nom interne, jamais affiché aux visiteurs.
+
+
+ +
+ Config Test + FR NL EN + edit +
+
3 / 3 langues traduites.
+
+
+
+
Deux champs sur une ligne, 92 px de hauteur, une seule ligne de base. Le badge de traduction porte l'information à la place d'un sous-titre gris tronqué.
+
+
+
+ + +
+
+
03
+
+

Édition d'une configuration

+

Trois cartes empilées deviennent une colonne de travail et un rail latéral. Les actions quittent la barre flottante du bas pour rejoindre l'en-tête.

+
+
+ +
+ Pas de bento à ce niveau +

Le placement bento ne concerne pas cet écran : gridColSpan / gridRowSpan vivent sur AppConfigurationLink, donc sur le lien entre une instance d'application et une configuration. Les tuiles de la grille bento sont les configurations de l'accueil, réglées dans app_configuration_link_screen.dart via CardShapeSelector.

+

À l'intérieur d'une configuration, les sections n'ont ni span ni grille : elles ne portent qu'un order, et le visiteur les voit en liste verticale de cartes pleine largeur (SectionList.tsx, SectionCard à 160 px, accent alterné). La grille de la maquette ci-dessous est donc un outil de réordonnancement, pas une prévisualisation.

+
+ +
+ Et pas d'aperçu visiteur non plus +

Un aperçu ne se justifie que là où la décision d'édition est invisible dans le formulaire — les spans bento, le HTML traduit. Ici l'ordre se lit dans la grille, l'image est en vignette, les langues sont des pastilles : l'aperçu ne redessinerait, en plus petit, que ce qui est déjà à l'écran. En échange il faudrait maintenir un troisième moteur de rendu en Dart, alors que le bento est déjà dupliqué entre myinfomate_layout et le CSS Grid de visitapp-web — et un aperçu qui dérive du vrai rendu est pire que pas d'aperçu. Le rail droit porte donc un bouton d'ouverture et le QR : le rendu réel, sans rien à synchroniser.

+
+ +
+
+
manager.myinfomate.be · Configurations / Config test
+ +
+ arrow_back +
+
Config test edit
+
Créée le 22/12/2023 · 8 sections · 3 langues
+
+
+ download + deleteSupprimer + Annuler + checkEnregistrer +
+ +
+
+
+ +
+
tuneRéglages
+
+
+ +
Config test11/50
+
+
+ +
Config Test3/3edit
+
+
+ +
+ FRNLENDE + expand_more +
+
+
+
+ + Mode hors ligneLe contenu est téléchargé au premier lancement. +
+
+
+
+ +
+
dashboardSectionsaddAjouter
+
+ searchRechercher une section… + Tous · 8 + Contenu + Carte + Interactif +
+
+
+
1drag_indicator
+
Vue panoramique
collectionsSlider
+
+
+
2drag_indicator
+
Plan du parc
location_onMap
+
+
+
3question_answerdrag_indicator
+
Quiz des collections
question_answerQuiz
+
+
+
4drag_indicator
+
Parcours famille
routeParcours
+
+
+
5calendar_monthdrag_indicator
+
Agenda du mois
calendar_monthAgenda
+
+
+
6picture_as_pdfMasquéedrag_indicator
+
Brochure 2026
picture_as_pdfPDF
+
+
+
7sunnydrag_indicator
+
Météo Liège
sunnyMétéo
+
+
addAjouter
une section
+
+
+
+ +
+
+
imageHabillage
+
+
+ +
+ + check_circletelepherique.jpgmore_horiz +
+
+
+ +
+ + + + check_circleunov-loader.svgmore_horiz +
+
+
+
+ +
+
shareDiffusion
+
+
+
+
app.myinfomate.be/musee-liege/6d41f0…
+
+ open_in_newOuvrir + content_copyLien +
+
+
+
Le lien ouvre le rendu réel : rien à maquetter, rien qui puisse dériver.
+
+
+
+
+
+
+
+ Ce qui change — L'en-tête porte le contexte (8 sections, 3 langues) et les actions, ce qui supprime la barre flottante du bas et rend 60 px au contenu. La colonne principale contient les deux choses qu'on vient vraiment modifier ; le rail droit regroupe les médias et la diffusion. La carte « Identifiant » devient « Réglages » : le titre d'une carte ne répète plus le nom du champ qu'elle contient. +
+
+ + +
+
+
04
+
+

Édition d'une section

+

Même squelette, même grille, même rail. Seul le contenu de la carte « Configuration <type> » change d'un type à l'autre.

+
+
+ +
+
+
manager.myinfomate.be · Config test / Quiz des collections
+ +
+ arrow_back + question_answerQuiz +
+
Quiz des collections edit
+
Section 3 sur 8 · créée le 14/03/2026
+
+
+ picture_as_pdf + deleteSupprimer + Annuler + checkEnregistrer +
+ +
+
+
+ +
+
tuneRéglages
+
+
+ +
Quiz des collections20/50
+
+
+ +
Testez vos connaissances2/3edit
+
+
+
+ + Balise iBeaconDéclenche la section à l'approche du visiteur. +
+
+
+
+ +
+
question_answerQuestions6 questions · glisser pour réordonner
+
+
+
drag_indicator1Quel peintre…
+
drag_indicator2En quelle année…
+
drag_indicator3Que représente…
+
drag_indicator4Combien de salles…
+
drag_indicator5Qui a financé…
+
drag_indicator6Où se trouve…
+
addNouvelle question
+
+
+
Question 2Modifiée à l'instant
+
+
+ +
En quelle année le musée a-t-il ouvert ?3/3edit
+
+
+ +
10unfold_more
+
+
+
+ +
+
1897drag_indicator
+
1905drag_indicator
+
1923drag_indicator
+
1951drag_indicator
+
+
+ addAjouter une réponse +
+
+
+
+ +
+
+
imageHabillage
+
+ +
+ add_photo_alternateChoisir dans la médiathèque +
+
+
+ +
+
shareDiffusion
+
+
+
+
…/musee-liege/6d41f0/sections/a17c9b
+
+ open_in_newOuvrir + downloadQR +
+
+
+
Le QR imprimé pointe cette section. Identifiant : a17c9b…
+
+
+
+
+
+
+
+ Ce qui change — Le QR code et l'identifiant quittent leur carte pleine largeur pour le rail droit, sous la même forme que sur l'écran Configuration : ils servent au setup et à l'impression, pas à chaque édition. Les questions, aujourd'hui une liste horizontale de 150×150 avec une modale par question, deviennent une liste verticale et un panneau d'édition en place — le réordonnancement reste du glisser-déposer. +
+
+ + +
+
+
05
+
+

Trois gabarits, pas treize maquettes

+

Réponse à ta question : non. Les 13 types se rangent dans trois formes, et c'est la forme qui se dessine.

+
+
+ +
+ Pourquoi trois suffisent +

Les configs par type ne diffèrent que par leurs champs, pas par leur structure : slider_config, menu_config, quizz_config, article_config, map_config, agenda_config, event_config et parcours_config font tous rigoureusement la même chose — une liste réordonnable + un bouton vert flottant + une modale d'édition, chacun avec sa propre hauteur en MediaQuery.size * 0.x. Une seule brique CollectionEditor les remplace tous les huit.

+
+ +
+
+
gabarit A
+

Champs simples

+

Un à quatre champs dans la carte de type. Rien à dessiner de plus : la grille de 12 colonnes de l'écran suffit.

+
WebVidéoMétéo
+
+
+
gabarit B
+

Éditeur de collection

+

Liste ordonnée d'éléments + édition en place. Maquetté ci-dessus avec le Quiz ; l'élément édité change, la mécanique non.

+
SliderMenuQuizArticlePDFAgendaParcours
+
+
+
gabarit C
+

Éditeur avec canevas

+

Gabarit B, plus un canevas de travail à droite : la carte, la grille du puzzle, le plan d'événement. Le canevas prend la place du rail latéral, qui repasse au-dessus du canevas.

+
MapÉvénementJeu
+
+
+ +
+ + + + + + + + + + + + + + + + + +
TypeGabaritContenu de la carte de typeDette actuelle
WebAURL
MétéoAVilleChamp marqué isUrl à tort
VidéoASource (YouTube / Vimeo / ressource) + URLDéjà refait, sert de référence
PDFBListe de fichiers traduitsBloc figé 70 × 440 px centré
SliderBContenus ordonnés : image, titre, descriptionListe horizontale height: 300
MenuBSous-sections ordonnéesListe horizontale 0.35 × hauteur
ArticleBContenus + audio + lecture autoListe horizontale height: 250
QuizBQuestions, réponses, points, scoresLe plus dense : 4 zones à hauteur fixe
AgendaBÉvénements datésModale par événement
ParcoursBÉtapes, mode de progressionÉditeur dédié de 785 lignes à raccorder
MapCPoints géo, catégories, images par point753 lignes, pas d'aperçu carte à l'édition
ÉvénementCBlocs de programme, annotations sur planTrois modales imbriquées
JeuCImage, messages, lignes × colonnesTabBar dans un SizedBox(height: 650)
+
+
+ + +
+
+
06
+
+

Les dialogues

+

29 fichiers ouvrent un dialogue. Il n'en faut pas 29 maquettes : dix disparaissent ou fusionnent, la coquille est déjà tranchée, et deux seulement portent une vraie décision de design.

+
+
+ +
+ La coquille existe déjà — ne pas en inventer une deuxième +

confirmation_dialog.dart l'a fixée, et resource_picker.dart la suit : rayon kRadiusShell, bordure kLine d'1 px, pied en bande kSurface2 séparé par un filet kLineSoft, un TextButton « Annuler » à 13 px et un FilledButton en pastille kRadiusPill qui passe au rouge quand l'action détruit. Les maquettes ci-dessous ne proposent rien de neuf sur ce plan : elles appliquent cette coquille.

+
+ +
+
S · 420 Confirmation, renommage — un message et deux boutons.
+
M · 512 Ajout d'une ressource, création — un formulaire court.
+
L · 780 Traduction, choix du type — plusieurs zones à comparer.
+
+ +

Le modal de traduction

+

C'est l'interface la plus utilisée de l'app : elle s'ouvre depuis chaque titre et chaque contenu de chaque type. Aujourd'hui elle empile un éditeur Quill par langue les uns sous les autres, puis pose deux boutons de 370 × 70 px centrés dans le flux du contenu — « Appliquer à toutes les langues » et « Traduire avec l'IA ». Sur trois langues, l'action IA est sous la ligne de flottaison.

+ +
+
+
+
+
+
Titre affiché
+
Section « Quiz des collections »
+
+
+ 2 / 3 traduites + close +
+ +
+
+
+
FR
+
NL
+
EN
+
+
+
+ FR + Testez vos connaissances +
+
+ format_bold + format_italic + format_underlined + + format_list_bulleted + format_list_numbered + + link + format_clear +
+
Test uw kennis
+
+ 18 / 60 + + auto_awesome + Proposé par l'IA — à relire +
+
+
+
+ +
+ content_copyCopier le FR partout + auto_awesomeTraduire les 2 manquantes + 42 crédits restants + + Annuler + Enregistrer +
+
+
+
+ ⚠️ Le piège d'implémentation : le cycle de vie des contrôleurs Quill +

Aujourd'hui ça marche parce que l'empilement vertical monte toutes les langues en même temps : _controllers est une Map<String, QuillController> construite une seule fois dans initState (translation_input_container.dart:52) et libérée seulement dans dispose (198-202). Aucun éditeur n'est jamais démonté en cours de route.

+

Le rail de langues casse cette propriété si on l'implémente naïvement. N'afficher que la langue sélectionnée en reconstruisant l'éditeur à chaque bascule, c'est créer et détruire des QuillController en boucle — exactement le chemin fragile. La règle : construire la map une fois pour toutes les langues, comme aujourd'hui, et basculer avec un IndexedStack ou un Offstage — jamais en rebâtissant l'éditeur. Et surtout pas de TabBarView : il construit paresseusement et peut lâcher ses enfants hors écran sans AutomaticKeepAliveClientMixin.

+

Le code garde la trace d'une tentative dans ce sens : translation_tab.dart existe, en TabBar + TabBarView — mais son corps est un TextFormField, pas du Quill, et son appel est en commentaire dans multi_input_modal.dart ligne 46-51. Une version à onglets a été essayée puis abandonnée.

+

Deux chemins déjà existants libèrent et recréent les contrôleurs, et ce sont eux qu'il faut retester en priorité après la refonte : la traduction par IA (ligne 142) et « appliquer à toutes les langues » (ligne 183). Ils remplacent l'entrée de la map ; une référence gardée ailleurs deviendrait caduque.

+
+ +
+ Ce qui change — Les deux gros boutons quittent le contenu pour le pied : ce sont des actions, pas du texte. Le rail de gauche donne l'état des trois langues d'un coup d'œil — pleine, vide, ou à relire — au lieu de le faire deviner en scrollant. Et la langue source reste affichée au-dessus de l'éditeur : on traduit depuis une référence, c'est le geste réel. Le quota IA passe à côté du bouton qu'il conditionne, plus sous le formulaire. +
+
+ +

Le choix du type de section

+

C'est la première interface que voit un nouveau client, et c'est aujourd'hui un menu déroulant de 13 entrées dans un SizedBox(height: 250), avec le champ du nom enfermé dans un height: 100. Un menu déroulant demande de connaître les noms avant de choisir ; une grille les montre.

+ +
+
+
+
+
+
Nouvelle section
+
Configuration « Config test »
+
+
+ close +
+ +
+
+ +
Salle des maquettes19/50
+
+ +
+
Contenu
+
+
collectionsSliderSuite d'images légendées
+
articleArticleTexte, images, audio
+
picture_as_pdfPDFDocuments à consulter
+
ondemand_videoVidéoYouTube, Vimeo, fichier
+
webWebPage externe intégrée
+
+
+ +
+
Lieux et déplacements
+
+
location_onMapPlan avec points d'intérêt
+
routeParcoursÉtapes guidées, énigmes
+
eventÉvénementProgramme et plan du site
+
+
+ +
+
Interactif
+
+
question_answerQuizQuestions et scores
+
sports_esportsJeuPuzzle d'image
+
appsMenuRegroupe des sous-sections
+
+
+ +
+
Informations pratiques
+
+
calendar_monthAgendaÉvénements datés
+
sunnyMétéoPrévisions d'une ville
+
+
+
+ +
+ Le type ne pourra plus être changé après la création. + + Annuler + addCréer la section +
+
+
+
+ Ce qui change — Les 13 types sont visibles, regroupés par famille, avec une ligne qui dit ce qu'ils font : le client choisit sans avoir à connaître le vocabulaire du produit. Les icônes sont celles de getSectionIcon, déjà écrites — la grille ne coûte que la mise en page. Et l'avertissement qui compte (le type est définitif) est là où il sert, à côté du bouton qui valide. +
+
+ +

Le reste de l'inventaire

+
+ + + + + + + + + + + + + + + + +
DialogueSortPourquoi
8 × showNewOrUpdate*suppriméQuestion de quiz, image de slider, point géo, catégorie, fichier PDF, événement d'agenda, bloc de programme, annotation de plan — tous absorbés par le panneau d'édition en place du gabarit B.
TraductionmaquettéL'interface la plus utilisée de l'app. Ci-dessus.
Nouvelle sectionmaquettéPremière impression client. Ci-dessus.
Nouvelle configurationalignéFormulaire court : la coquille M et la grille de champs suffisent, rien à décider.
ConfirmationfaitDéjà repris, et c'est lui la référence.
Ajouter une ressourcefaitMaquette dédiée du 03/09, déjà publiée à part.
Sélecteur de ressourcecadre seulIl embarque ResourcesScreen : son contenu appartient au lot Médiathèque (v1-mediatheque-plan.md §3.3, « inchangé si possible »). Seul son cadre est à reprendre — voir ci-dessous.
Traduction sans HTML
multi_input_modal.dart
fusionnéLe jumeau du modal de traduction. multi_string_input_container appelle l'un ou l'autre selon isHTML : même travail, deux implémentations (560 px de large, une zone de 500 × 200, des boutons de 180 px). La maquette ci-dessus couvre les deux — une fois fusionnés en un seul, la barre de format en moins quand le champ n'accepte pas de HTML.
Point géo, géométrie
geoloc_ / geometry_input_container.dart
absorbéDeux AlertDialog de 0,75 × largeur pour poser un point ou tracer une zone : c'est précisément ce que le canevas du gabarit C met à plat dans l'écran. Placer un point sur une carte enfermée dans une modale est le geste le plus contraint qu'on puisse offrir.
Fichiers PDF, catégories de carte, sélecteur de couleuralignéTrois corps de dialogue honnêtes dans trois cadres cassés (0,65 × 0,7 d'écran, boutons de 180 × 70). Ils héritent de la coquille sans rien décider. Au passage : pdf_file_input_container et category_input_container sont un copier-coller l'un de l'autre — mêmes dimensions, même structure — sauf que le second a ses libellés en dur (« Annuler », « Valider » au lieu des traductions) et un onGetResult(result) commenté. À vérifier : une catégorie modifiée ne remonte peut-être pas à la volée.
Éditeur de parcours
guided_path_editor.dart
à part785 lignes avec ses propres dialogues. Il a déjà sa maquette (sectionparcours-refonte-flux.html) : à raccorder, pas à redessiner.
Lien d'application, device kiosk, utilisateurs, clés API, audit, guide IA, menu principal, notificationsalignéHors périmètre de cette refonte, mais héritent de la coquille et des boutons dès qu'on y touche.
+
+ +
+ Le sélecteur de ressource : le cadre, pas le contenu +

showSelectResourceModal encastre l'écran Médiathèque entier dans un AlertDialog. Son contenu a déjà deux sources de vérité — v1-mediatheque-plan.md §3.3 et la maquette « Ajouter une ressource » — et je n'en ajoute pas une troisième. En revanche son cadre a un vrai défaut, pas seulement un défaut de goût.

+

Le débordement. Un AlertDialog s'insère avec 24 px de marge verticale : il dispose de hauteur − 48. Le contenu en demande 0,85 × hauteur, plus un titre, plus une ligne d'actions de 70 px — soit environ 895 px demandés pour 852 disponibles sur un écran de 900 de haut. C'est la raison d'être du SingleChildScrollView qui l'entoure, et la conséquence est un défilement dans un défilement : on fait glisser le dialogue au lieu de la grille de médias.

+

Ce qui change, et rien de plus. Coquille standard avec en-tête à la place du title: Center(Text(…)), kRadiusShell au lieu du rayon 20, le bouton « Annuler » de 180 × 70 px descendu dans le pied — il est seul, dans un Row en spaceEvenly, puisque la sélection se fait au clic sur la vignette. La hauteur figée cède la place à un corps souple sous un maxHeight : plus de débordement, plus de double défilement. Et showValues, lignes 64-78, est du code mort — comme la copie qu'en garde multi_input_modal.dart ligne 186 : son unique appel, ligne 53, est en commentaire. Les deux peuvent partir.

+

Ce qui ne change pas, volontairement : la largeur. Le size.width * 0.85 est conservé tel quel. La règle du rail de facettes, en §3.3 du plan Médiathèque, est écrite sur cette hypothèse — « elle fait déjà size.width * 0.85, et un rail de 194 px y est acceptable ». Rétrécir la modale invaliderait une décision déjà codée dans un autre lot. On corrige donc la hauteur et la coquille, pas la largeur.

+
+ +
+
+
+
+
+
Sélectionner une image
+
Champ « Image principale » — Config test
+
+
+ Images seulement + close +
+ +
+
+
TYPE
+
USAGE
+
CONFIGURATION
+
+
+ +
+
+
+
image
+
+
image
+
+
image
+
image
+
lockContenu inchangé — c'est ResourcesScreen, il appartient au lot Médiathèque.
+
+
+
+ +
+ Un clic sur une vignette sélectionne et referme. + + Annuler +
+
+
+
+ Seul le cadre est dessiné. L'en-tête, le pied et les contraintes de taille sont à moi ; le rail de facettes et la grille sont schématisés à dessein — ils sont déjà spécifiés ailleurs et déjà codés. Le pied ne porte qu'« Annuler », puisqu'il n'y a rien à valider : la sélection se fait au clic. La largeur reste celle d'aujourd'hui. +
+
+
+ + +
+
+
07
+
+

Où ça se code, dans l'ordre

+

Les trois premiers fichiers portent l'essentiel du gain visuel — ils sont importés par tous les écrans d'édition, y compris ceux qui ne sont pas dans cette proposition.

+
+
+ +
+
+
1Components/text_field_container.dart
Components/rounded_input_field.dart
+
Sortir le width: double.infinity et le radius 29. Bordure 1 px, radius 8, marges rendues au parent. Tous les écrans changent d'un coup — à vérifier au passage sur Ressources et Instances.
+
+
+
2Components/string_input_container.dart
Components/multi_string_input_container.dart
Components/resource_input_container.dart
Components/check_input_container.dart
+
Label au-dessus du champ, suppression des MainAxisAlignment.center et des tailles en dur (width: 200, height: 54, la case à cocher de 50 px). Le composant ne décide plus de sa largeur ni de sa position.
+
+
+
3Screens/Configurations/section_reorderList.dart
Screens/Configurations/listView_card_section.dart
+
Grille responsive au lieu de la liste horizontale, hauteur pilotée par le contenu, carte au format 16/9 avec badge d'ordre, poignée et type. Le bouton vert flottant devient une tuile « Ajouter » dans la grille.
+
+
+
4Screens/Configurations/configuration_detail_screen.dart
Screens/Configurations/Section/section_detail_screen.dart
+
Grille à deux colonnes, actions remontées dans l'en-tête, suppression du _buildFooter. Le _cardQR devient le panneau « Diffusion » du rail droit.
+
+
+
5Components/multi_string_input_html_modal.dart
Components/translation_input_container.dart
Screens/Configurations/new_section_popup.dart
Screens/Resources/select_resource_modal.dart
+
Les dialogues : coquille standard, boutons sortis du contenu vers le pied, grille de types à la place du menu déroulant. Le sélecteur de ressource ne reçoit que son cadre.
+
+
+
6Components/collection_editor.dart (à créer)
+
La brique du gabarit B, puis migration des huit configs qui la dupliquent — et suppression des huit dialogues showNewOrUpdate*. C'est le plus gros morceau, et le seul qui puisse s'étaler dans le temps : chaque type migré est indépendant.
+
+
+ +
+ Ce n'est que du visuel — sauf sur deux points +

Aucune donnée, aucune migration, aucun contrat d'API n'est touché, et tout est réversible par git. Le risque n'est pas la gravité, c'est la portée : les lots 1 et 2 changent des composants importés par une trentaine d'écrans, et manager-app n'a que trois tests, aucun sur ces composants. La vérification est donc manuelle.

+

Deux pièges concrets. D'abord, retirer une pastille pleine largeur change la hauteur des lignes — et il reste des conteneurs à hauteur figée autour (height: 70, height: 100, SizedBox(height: 250)) : ce qui rentrait juste peut déborder, et un débordement Flutter est visible, pas silencieux. Ensuite, ces composants ne sont pas purement décoratifs : dans section_detail_screen.dart, MultiStringInputContainer.onGetResult déclenche un save(true, …) à la ligne 270, et la case iBeacon un save(false, …) à la ligne 381. Il y a un chemin d'écriture câblé dans le composant — à ne pas perdre ni dédoubler en le refactorisant.

+
+
+ + + +
+ + diff --git a/claude design/refonte-gabarit-canevas.html b/claude design/refonte-gabarit-canevas.html new file mode 100644 index 0000000..af52817 --- /dev/null +++ b/claude design/refonte-gabarit-canevas.html @@ -0,0 +1,514 @@ +Le canevas de Map et Événement + + + + + + +
+ +
+
Refonte manager-app · gabarit C
+

Le canevas de Map et Événement

+

Tout le reste de la refonte est codé. Il manque une seule chose, et c'est la même dans les deux écrans : poser un point ou une zone sur la carte de l'écran, au lieu d'ouvrir une carte dans une modale ouverte depuis une modale.

+
+ +
+
+ 01 +
+

Trois niveaux de modale pour placer un point

+

C'est le geste le plus contraint que l'app propose aujourd'hui, et c'est aussi le plus fréquent sur une section Map.

+
+
+ +
+
+
Aujourd'hui
+
Écran de la section
La grille des points, 8 colonnes, sous les réglages de carte.
+
Modale « Point / Zone »
0,88 × largeur · neuf champs traduits, une image, une catégorie, une liste de contenus.
+
Modale « Géométrie »
La carte où l'on dessine enfin. On ne voit plus ni la fiche, ni les autres points.
+
+
+
Proposé
+
Écran de la section
La liste des points, la fiche du point sélectionné, et la carte — les trois en même temps. Cliquer sur la carte place le point ouvert dans la fiche.
+
Aucune modale. La carte est le troisième volet, pas une fenêtre.
+
+
+ +
+ Ce que ça règle, concrètement +

On place un point en voyant les autres — impossible aujourd'hui, alors que c'est la seule chose qui compte quand on répartit des points d'intérêt sur un plan. Et la fiche reste lisible pendant qu'on dessine, donc on sait quel point on est en train de déplacer.

+
+
+ +
+
+ 02 +
+

Le gabarit C, dessiné

+

Liste, fiche, canevas. C'est le gabarit B auquel on ajoute une troisième colonne — et le rail droit de l'écran (Habillage, Diffusion) repasse au-dessus, comme annoncé.

+
+
+ +
+
+
+
+ Plan du parc + Map + + Annuler · Enregistrer +
+
+ +
+
Réglages de la cartedéjà codé
+
+
+
Google
+
hybrid
+
18
+
pin-parc.svg
+
4 catégories
+
Activée
+
+
+
+ +
+
Points d'intérêt12 points · le centre de la carte se règle ici aussi
+
+ +
+
Entrée nord
+
Serre tropicale
+
Volière
+
Buvette
+
Toilettes est
+
Aire de jeux
+
+ Nouveau point
+
+ +
+
+
Serre tropicale3/3
+
À voir
+
Palmiers, orchidées et bassin à nénuphars.3/3
+
serre.jpg
+
+ +
+ Informations pratiques + + 2 / 5 remplies +
+ +
+ +
+ + + + +
+
+
+ +
+ + +
+ 📍 + + + +
+ + + + + + + + + +
+ 50.42933, 4.89143 + + Serre tropicale +
+
+ +
+
+ +
+
+
+
+ Comment ça se lit — Les points non sélectionnés restent visibles en gris : c'est l'information qui manquait. Le point ouvert dans la fiche est en couleur, et c'est lui que le clic sur la carte déplace. La barre d'outils en haut à gauche est celle de MapGeometryPicker, déplacée telle quelle : point, ligne, zone, couleur. Le pied de carte dit ce qu'on modifie et où, pour qu'aucun clic ne soit ambigu. +
+
+ +
+ Neuf champs traduits, ça ne tient pas à plat — et ça n'a pas à tenir +

Un point porte titre, description, site, prix, téléphone, e-mail, horaires, image et catégorie. Les quatre premiers d'un musée sont presque toujours remplis, les cinq autres presque jamais. La fiche montre donc titre, description, catégorie, image, et replie les informations pratiques derrière une ligne qui dit combien sont remplies. Rien n'est retiré, seulement rangé par fréquence d'usage réelle.

+
+ +
+ Faut-il tout centrer sur la carte ? Non — mais deux modes, oui +

Tentant, et je pense que ce serait une erreur en permanence : un point porte neuf champs traduits, une image, une catégorie et une liste de contenus ; la carte n'en porte qu'un seul, la géométrie. Donner en permanence le plus de place au champ qu'on modifie le moins ferait de la saisie — le vrai travail de cet écran — le parent pauvre. Le back-office est un outil de saisie ; la belle carte, le visiteur l'a déjà dans l'app.

+

En revanche, 300 px ne suffisent pas pour juger d'une répartition de points. Le canevas passe donc à 420 px de haut par défaut, et gagne un bouton « agrandir » qui lui donne toute la largeur : la liste reste en rail étroit, la fiche se replie. Deux moments distincts, deux dispositions — on écrit une fiche, ou on répartit des points, rarement les deux dans la même minute.

+
+ +
+ Le centre de la carte n'a plus besoin de son propre sélecteur +

Le canevas sait déjà afficher une carte et recevoir un clic. Le point central se règle donc par un mode du canevas, et geoloc_input_container — repris à la coquille standard cette semaine — n'a plus d'appelant sur cet écran. Il reste utile ailleurs tant que d'autres écrans le référencent.

+
+
+ +
+
+ 03 +
+

Événement : le même canevas, un autre fond

+

Un événement pose des annotations sur le plan du site plutôt que des points d'intérêt sur une carte. La mécanique ne change pas, le fond de plan si.

+
+
+ +

Le canevas d'Événement affiche la carte de base choisie dans baseSectionMapId, et les annotations globales se posent dessus, exactement comme les points. Trois différences, et elles sont toutes dans le contenu, pas dans la forme :

+ +
+ + + + + + + +
Ce qu'on poseGabaritPourquoi
Annotations du planC — canevasElles ont une position : elles se posent sur le plan, comme les points de Map. Même liste, même fiche, même canevas.
Blocs de programmeB — listeIls ont une heure de début et de fin, pas une position. Une liste ordonnée suffit — et c'est déjà ce qu'ils sont.
Parcours de l'événementdéjà faitL'éditeur à fenêtre unique est raccordé et reste tel quel.
+
+ +
+ ⚠️ Une question à laquelle le code ne répond pas +

Quand aucune carte de base n'est choisie (baseSectionMapId vaut null), le canevas n'a pas de fond. Deux réponses possibles : afficher une carte vide comme sur Map, ou afficher un message qui invite à choisir un plan d'abord. Je propose la seconde — poser des annotations sur un fond qui va changer, c'est du travail à refaire — mais c'est un choix produit, pas technique.

+
+
+ +
+
+ 04 +
+

Ce qui disparaît

+

Le canevas n'ajoute pas une brique : il en absorbe quatre.

+
+
+ +
+ + + + + + + + + +
FichierSortDétail
showNewOrUpdateGeoPoint.dart
362 lignes
suppriméAbsorbé par la fiche du gabarit C. Ses champs ne changent pas, ils changent de contenant.
geometry_input_container.dartsuppriméLe champ qui ouvrait la carte de dessin n'a plus lieu d'être quand la carte est à l'écran. ⚠️ Deux autres appelants à traiter d'abord : etape_fields et showNewOrUpdateEventAgenda.
map_geometry_picker.dartdéplacéLe dessin existe déjà et fonctionne. Il sort de son dialogue pour devenir le canevas. C'est le cœur du lot, et c'est du déplacement, pas de l'écriture.
showNewOrUpdateMapAnnotation.dart
182 lignes
suppriméMême chose côté Événement.
geoloc_input_container.dartconservéPlus d'appelant sur Map, mais il reste référencé ailleurs. On le garde tant qu'il sert.
+
+
+ +
+
+ 05 +
+

Dans quel ordre

+

Les points d'un Map vivent côté serveur : l'éditeur de collection sait déjà leur parler depuis cette semaine.

+
+
+ +
    +
  1. 1Fait. MapCanvas est sorti du dialogue : il reçoit une géométrie, la rend modifiable, rend la main à chaque changement, et affiche les autres tracés en fond. MapGeometryPicker n'est plus qu'une coquille autour de lui, pour ses deux appelants restants.
  2. +
  3. 2Poser le gabarit C : liste, fiche, canevas, avec la liste en mode distant (les points sont chargés et écrits par l'API, comme les questions de Quiz).
  4. +
  5. 3Migrer les champs du point depuis showNewOrUpdateGeoPoint, avec le repli des informations pratiques, puis supprimer la modale.
  6. +
  7. 4Brancher les filtres qui existent déjà — recherche et catégories — sur la liste et sur le canevas, pour qu'ils cachent les mêmes points des deux côtés.
  8. +
  9. 5Refaire le même geste sur Événement, avec la carte de base en fond, et traiter les deux autres appelants de GeometryInputContainer.
  10. +
+ +
+ Ce que ça ne touche pas +

Aucun DTO, aucun endpoint, aucune migration. GeoPointDTO garde ses neuf champs traduits, sa géométrie et sa couleur ; MapDTO garde ses catégories. Le risque est dans le comportement de la carte, pas dans les données — et il est réversible par git.

+
+
+ + + +
diff --git a/kanban.html b/kanban.html index 5774990..ceb287f 100644 --- a/kanban.html +++ b/kanban.html @@ -460,9 +460,9 @@
1Urgent
1Migration v3
-
1Bugs ouverts
+
2Bugs ouverts
10À tester
-
32Planifié
+
36Planifié
8Bascule prod
60Fait récemment
@@ -520,7 +520,7 @@
-

Bugs ouverts

1
+

Bugs ouverts

2
@@ -532,6 +532,16 @@ todo-features.md — Quota stockage
+
+
visitappofflinestorecondition de publication sur le Play Store
+

La taille du téléchargement n'est jamais affichée

+

Le visiteur ne sait jamais combien il télécharge. Le dialogue de configurations_list.dart annonce « il est nécessaire de télécharger du contenu supplémentaire » sans chiffre, et l'écran de progression n'affiche qu'un pourcentage. Or une visite du Fourneau, c'est un ordre de grandeur de plusieurs centaines de Mo sur la 4G du visiteur.

+

Google Play exige que la taille des téléchargements complémentaires soit annoncée avant de les lancer — c'est une condition de publication, pas un confort.

+

Rien à ajouter côté serveur : ResourceDTO.sizeBytes existe déjà et arrive dans ExportConfigurationDTO.resources. La somme se calcule côté client, et sur les seules ressources réellement à télécharger — isResourceOutdated filtre déjà celles qui sont à jour, donc une mise à jour de visite doit annoncer le delta, pas le total.

+

Deux endroits : le dialogue de confirmation (avant), et l'écran de progression (« 128 / 412 Mo » plutôt qu'un pourcentage nu). ⚠️ La taille annoncée dans le dialogue suppose d'avoir déjà appelé configurationExport pour connaître la liste — aujourd'hui cet appel n'a lieu qu'après la confirmation.

+ relevé le 2026-09-04 — configurations_list.dart, downloadConfiguration.dart +
+
@@ -629,7 +639,7 @@
-

Planifié

32
+

Planifié

36
@@ -678,6 +688,27 @@ v1-plan.md lot D — D4 · STATUS.md §K5
+
+
visitappcartoofflineprérequis pour vendre un parcours GPS sur un site sans réseau
+

Carte hors ligne — tile packs Mapbox

+

Aujourd'hui aucune carte ne fonctionne hors ligne, et ce n'est pas une question de données : MapDTO porte déjà points, centerLatitude, zoom et iconResourceId, et les icônes sont téléchargées avec la visite. C'est le fond qui manque — les tuiles sont chargées en réseau, sans cache.

+

Le GPS, lui, n'est pas le problème : geolocator ^13.0.0 lit le GNSS, qui est autonome. Seule réserve à annoncer au client : sans A-GPS, le premier point peut demander 30 à 60 s sous couvert forestier.

+

Le chemin court existe déjà dans le pubspec : mapbox_maps_flutter ^2.0.0 expose OfflineManager (style packs) et TileStore (tile regions) — on télécharge une emprise bornée au moment du téléchargement de la visite. Pour un domaine comme le Fourneau Saint-Michel, quelques dizaines de Mo. MapProvider.MapBox existe déjà côté modèle.

+

Google est une impasse, et pas seulement techniquement. Le SDK Maps pour Android n'expose aucune API de tuiles hors ligne — les « zones hors connexion » sont une fonction de l'app Google Maps, pas du SDK intégrable. Et le code n'utilise même pas ce SDK pour les sections carte : il passe par flutter_map sur https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}, un endpoint non documenté dont la mise en cache est explicitement interdite. Conclusion : Mapbox devient le fournisseur du mode hors ligne, Google reste en ligne seulement.

+

⚠️ Tant que ce chantier n'est pas fait, SectionType.Map reste volontairement hors de offlineCapableSectionTypes (downloadConfiguration.dart) : l'ajouter donnerait une carte grise, ce qui est pire que ne pas la proposer. Une fois les tuiles packagées, c'est une ligne à ajouter dans cette constante.

+ analyse du code le 2026-09-04 — MapProvider, flutter_map, mapbox_maps_flutter +
+ +
+
cartoofflinemanager-apphors ligne par construction, et c'est ce qu'un musée dessine déjà
+

Plan illustré géoréférencé — un 3e fournisseur de carte

+

Un musée de plein air ne distribue pas un fond OpenStreetMap, il distribue son plan dessiné. Aujourd'hui MapProvider ne connaît que Google et MapBox : il n'existe aucun moyen de téléverser une image de plan et d'y placer les points.

+

Ce qu'il faut : une troisième valeur de MapProvider, une ressource image portée par la section, et un calage — deux points d'ancrage suffisent (coin haut-gauche / bas-droit en coordonnées réelles) pour projeter un GeoPoint sur l'image et y afficher la position du visiteur.

+

Pourquoi c'est le meilleur des trois : aucune tuile, donc hors ligne par construction — l'image part avec la visite comme n'importe quelle ressource, et le mécanisme de téléchargement n'a rien de nouveau à apprendre. Le rendu est aussi plus lisible qu'un fond routier sur un site de plusieurs dizaines de bâtiments.

+

Périmètre réel : c'est le plus gros des trois chemins carto — backend (modèle + migration), manager-app (téléversement du plan et pose des ancres), mymuseum-visitapp et visitapp-web (rendu). À faire après Carte hors ligne — tile packs Mapbox, qui débloque le besoin immédiat avec un fond classique.

+ analyse du code le 2026-09-04 — MapProvider n'a que Google et MapBox +
+
3 reposContredit le code

Les swagger.yaml embarqués sont périmés

@@ -874,6 +905,57 @@ v2/studio-plan.md — lots 0 à 10
+
+
manager-appUIdialoguesMaquette prête, code à faire
+

Refonte visuelle des écrans Configuration & Section, et de leurs dialogues

+

Lots 1 à 5 codés le 04/09 (flutter analyze sans erreur, flutter build web passe ; rien de vérifié à l'écran). Pilote select_resource_modal : coquille standard, corps souple, plus de double défilement, showValues mort supprimé, largeur 0,85 conservée. Lot 1 : text_field_container sans width: double.infinity ni rayon 29, compteur discret dans rounded_input_field. Lot 2 : libellé au-dessus et plus d'auto-centrage dans string_/multi_string_/resource_/check_input_container. Lot 3 : grille responsive de sections, réordonnancement par Draggable/DragTarget (ReorderableListView ne fait pas de grille, et pas de dépendance nouvelle), tuile « Ajouter » à la place du bouton vert. Lot 4 : les deux écrans de détail en EditorHeader + EditorColumns + Pane (nouveau Components/pane.dart), _buildFooter supprimé, le QR devient le rail « Diffusion ». Lot 5 : grille de types dans new_section_popup, modal de traduction en rail de langues + IndexedStack, et multi_input_modal réduit à une redirection isHTML: false. 24 clés i18n ajoutées en FR/EN/NL.

+ +

🐛 Le bug des catégories est confirmé et corrigé : showCreateOrUpdateCategories recevait un newValues vide de l'appelant et le renvoyait tel quel — valider sans rien toucher effaçait les catégories. Le tampon part maintenant des catégories existantes, et les libellés en dur sont passés par AppLocalizations.

+ +

Lot 6 entamé : Components/collection_editor.dart existe — liste ordonnée à gauche (réordonnancement par poignée), élément édité en place à droite, ligne « Ajouter » en fin de liste, suppression dans l'en-tête du détail. Trois types migrés : Slider et Article (via un ContentFields partagé, extrait de showNewOrUpdateContentSlider) et PDF (le bloc figé 70 × 440 disparaît). Les filtres d'envoi au backend sont conservés à l'identique (contenus sans ressource exclus, champs d'article recopiés).

+ +

⚠️ Trois fichiers sont devenus du code mort mais ne sont pas supprimés : pdf_file_input_container.dart, pdf_list.dart, new_update_pdfFile.dart. Ils portent des modifications non committées d'un autre lot — les effacer perdrait ce travail. À supprimer une fois l'arbre propre.

+ +

Lot 6 terminé pour le gabarit B. Décision prise le 04/09 (artifact 4af75171) : CollectionEditor reçoit un mode distant (RemoteCollection : create / delete / reorder asynchrones, voile de chargement, retour à l'état d'avant si l'appel échoue). Quiz l'utilise : la modale de question disparaît, les messages de score remontent en tête de carte, les réponses gardent QuizzResponseList tel quel. Menu garde la navigation vers sub_section_edit_screen — une sous-section est une section complète — et prend la grille de tuiles, extraite en SectionGrid et partagée avec les sections d'une configuration. Parcours : liste qui suit son contenu, les deux SizedBox(height: 500) de event_config et section_parcours_config retirés, éditeur à fenêtre unique inchangé.

+ +

🐛 Le rang en double est corrigé dans Quiz et Menu : ils n'écrivaient que l'order de l'élément déplacé, les autres gardaient le leur. La liste entière est renumérotée et envoyée en un Future.wait, comme le faisait déjà Parcours.

+ +

Agenda n'a pas été migré, et c'est délibéré : ses événements sont groupés par langue (_displayGroups, pastilles de langue quand un groupe en compte plusieurs) et n'ont pas d'ordre — une liste ordonnée à édition en place perdrait le groupement. Il reçoit ce qui lui revient : hauteur pilotée par le contenu (le height: 600 saute), lignes au système de tokens, modale conservée.

+ +

Gabarit C — Jeu est fait, Map et Événement sont dégrossis. game_config devient champs à gauche + canevas à droite : le canevas montre le découpage réel de l'image en rows × cols, seule décision que le formulaire ne montrait pas. Au passage, son TabBar n'en était pas un — les deux onglets construisaient le même formulaire et ne servaient qu'à écrire gameType : c'est devenu un choix à deux valeurs, et le SizedBox(height: 650) a disparu. geoloc_input_container passe à la coquille standard et affiche les coordonnées retenues au lieu d'un bouton « Changer la localisation », la carte prenant la hauteur disponible. Côté map_config : en-tête en grille de tokens, height: 700 et height: 70 retirés. Côté event_config : height: 600 retiré, la liste des blocs suit son contenu.

+ +

Reste : le canevas de Map et d'Événement — et il est maintenant maquetté : DOCS/claude design/refonte-gabarit-canevas.html (diffusion : artifact caf400b5). Liste des points, fiche, carte côte à côte ; les points non sélectionnés restent visibles en gris ; MapGeometryPicker sort de son dialogue pour devenir le canevas (déplacement, pas écriture) ; les informations pratiques d'un point (5 champs sur 9) sont repliées derrière un compteur. Absorbe showNewOrUpdateGeoPoint (362 l.), geometry_input_container et showNewOrUpdateMapAnnotation (182 l.). Une question produit reste ouverte : que montre le canevas d'Événement quand baseSectionMapId est nul — carte vide, ou invitation à choisir un plan d'abord (ma proposition).

+ +

Étape 1 du canevas faite le 04/09 : Components/map_canvas.dart — le dessin est sorti du dialogue. Il reçoit une géométrie, la rend modifiable, rend la main à chaque changement (pas de bouton « Enregistrer » : c'est l'écran qui enregistre), et affiche les autres tracés en fond via MapCanvasGhost. Barre d'outils compacte sur la carte, pied qui donne les coordonnées et ce qu'on modifie, bouton « agrandir » optionnel. MapGeometryPicker n'est plus qu'une coquille autour de lui, donc etape_fields et showNewOrUpdateEventAgenda ne bougent pas. Au passage, ses libellés « Ligne » / « Polygone » en dur sont passés par AppLocalizations. Taille tranchée : 420 px par défaut + mode agrandi, pas une interface centrée carte — un point porte neuf champs traduits, la carte n'en porte qu'un.

+ +

Étapes 2 à 4 faites : le gabarit C est en place dans Map. Map/geo_point_editor.dart — liste des points à gauche, fiche au centre, carte à droite ; les points non sélectionnés apparaissent en fond ; le mode agrandi replie la fiche et garde le rail. Les neuf champs traduits sont rangés par fréquence : titre, catégorie, description et image visibles, les cinq informations pratiques repliées derrière un compteur. map_config passe de 937 à ~510 lignes : la modale showNewOrUpdateGeoPoint n'est plus appelée, getElement et son boxDecoration sont supprimés, et recherche + catégories filtrent désormais la liste et la carte par un seul calcul. Les points sont créés, modifiés et supprimés directement par l'API (sectionMapCreate / Update / Delete), la suppression passant par la confirmation destructive.

+ +

Étape 5 faite : le canevas d'Événement. Les annotations passent dans l'éditeur de collection en mode distant, avec le canevas dans le panneau d'édition. Plan par défaut retenu (décision Thomas) : le premier plan disponible est pris quand baseSectionMapId est nul, plutôt que de bloquer. 🐛 Trouvé en chemin : MapAnnotationDTO portait déjà un champ geometry que l'ancienne modale ne remplissait jamais — elle ne réglait que geometryType. Une annotation déclarait donc une forme sans jamais recevoir de coordonnées ; le canevas les lui donne. Enfin showMultiStringInputAndResourceHTML est passé à la coquille standard : plus aucun dialogue de ce chantier sur l'ancienne.

+ +

Plan de test : DOCS/test-plan-refonte-config-section.md, 14 sections. Rien n'a été vérifié à l'écran — flutter analyze propre et flutter build web qui passe, c'est tout.

+ +

Reste : supprimer le code mort une fois l'arbre git propre — showNewOrUpdateGeoPoint, showNewOrUpdateMapAnnotation, pdf_file_input_container, pdf_list, new_update_pdfFile, listView_card_subSection, new_update_question_quizz ; et geometry_input_container quand etape_fields et showNewOrUpdateEventAgenda auront leur canevas.

Restent aussi showMultiStringInputAndResourceHTML sur l'ancienne coquille, et le code mort à supprimer une fois l'arbre propre : pdf_file_input_container, pdf_list, new_update_pdfFile, listView_card_subSection, new_update_question_quizz.
⚠️ À vérifier manuellement en priorité : les deux chemins qui recréent les contrôleurs Quill (traduction IA, « appliquer à toutes les langues ») ; les hauteurs figées restées autour des champs devenus plus hauts (height: 70, 100) dans les écrans non repris — showNewOrUpdateGeoPoint, showNewOrUpdateEventAgenda, sub_section_edit_screen, new_configuration_popup.

+ +

Maquette prête et complète : DOCS/claude design/refonte-configuration-sections.html (diffusion : artifact 4d0c2ad6). Elle contient le diagnostic, les deux écrans, les 3 gabarits qui couvrent les 13 types de section, la coquille de dialogue, et deux dialogues dessinés — traduction et choix du type. Rien à décider côté design : tout est tranché.

+ +

Le blanc ne vient pas des écrans mais de 4 composants partagés. text_field_container.dart impose width: double.infinity + rayon 29 ; string_input_container.dart met le label à gauche dans un Row ; multi_string_input_container et resource_input_container se centrent eux-mêmes ; section_reorderList.dart réserve 0,3 × hauteur pour une liste horizontale de cartes 150×150. On les corrige une fois, les 13 types en héritent.

+ +

⚠️ Aucune direction visuelle à inventer : constants.dart porte déjà le système (kSpace1..8, kRadiusInput 5 / Card 8 / Shell 10 / Pill, kTitleCard, kLabelField, kTextHint) et confirmation_dialog.dart + resource_picker.dart l'appliquent déjà. Ces deux écrans sont simplement antérieurs au système.

+ +

Premier pas conseillé : select_resource_modal.dart, pas les composants partagés. Un fichier, ~15 lignes, zéro ligne dans ResourcesScreen, et ça corrige un vrai bug — AlertDialog dispose de hauteur − 48 mais le contenu en demande 0,85 × hauteur plus titre plus actions, d'où un défilement dans un défilement. Ça sert de pilote avant de toucher aux composants qui irriguent 30 écrans. ⛔ Ne pas toucher à la largeur 0,85 : la règle du rail de facettes de v1-mediatheque-plan.md §3.3 est écrite sur cette hypothèse et déjà codée.

+ +

⚠️ Deux pièges qui ne sont pas du visuel. D'abord, manager-app n'a que 3 tests, aucun sur ces composants : vérification manuelle, et il reste des conteneurs à hauteur figée (height: 70, 100, SizedBox(height: 250)) autour — ce qui rentrait juste débordera. Ensuite un chemin d'écriture est câblé dans les composants : section_detail_screen.dart:270 déclenche un save(true, …) depuis MultiStringInputContainer.onGetResult, et la ligne 381 un save(false, …) depuis la case iBeacon. À ne pas perdre ni dédoubler.

+ +

⚠️ Le piège Quill (remonté de mémoire par Thomas, confirmé dans le code). Ça marche aujourd'hui parce que l'empilement vertical monte toutes les langues : _controllers est bâtie une fois dans initState (translation_input_container.dart:52) et libérée seulement dans dispose. Le rail de langues de la maquette casse cette propriété s'il reconstruit l'éditeur à chaque bascule. Règle : map construite une fois pour toutes les langues, bascule par IndexedStack/Offstage, jamais de TabBarView (construction paresseuse, enfants lâchés hors écran sans AutomaticKeepAliveClientMixin). Trace d'une tentative abandonnée : translation_tab.dart existe en TabBar, son appel est en commentaire dans multi_input_modal.dart:46-51. À retester en priorité : la traduction IA (ligne 142) et « appliquer à toutes les langues » (ligne 183), les deux chemins qui libèrent et recréent les contrôleurs.

+ +

Deux fusions et un ménage décidés dans la maquette : multi_input_modal.dart est le jumeau sans HTML du modal de traduction (même travail, deux implémentations) → une coquille, deux corps, Quill si isHTML et champ simple sinon ; les 8 showNewOrUpdate* et les 2 sélecteurs géo disparaissent dans les gabarits B et C ; showValues est mort en double (select_resource_modal.dart:64 et multi_input_modal.dart:186, appel commenté ligne 53).

+ +

⚠️ Conflit résolu avec l'auditsecurity/audit-manager-app.md recommandait d'extraire un FormDialogScaffold pour les 15 occurrences des showNewOrUpdate*. Or la refonte les supprime : les extraire d'abord, ce serait refactoriser du code à jeter. Ordre imposé : absorber d'abord (gabarits B et C), extraire ensuite sur ce qui survit — traduction, nouvelle section, nouvelle configuration, sélecteur de couleur, fichiers PDF, catégories. L'audit et STATUS.md ont été annotés en ce sens le 2026-09-04.

+ +

🐛 Trouvé en passant, à vérifier : pdf_file_input_container.dart et category_input_container.dart sont un copier-coller — mêmes dimensions, même structure — mais le second a ses libellés en dur (« Annuler », « Valider ») et son onGetResult(result) commenté. Une catégorie de carte modifiée ne remonte peut-être pas à la volée. Lecture de code, pas testé.

+ DOCS/claude design/refonte-configuration-sections.html +
+
manager-serviceStudio IAPrérequis dur

Socle de stockage serveur — le backend ne sait pas écrire dans le bucket

@@ -908,6 +990,17 @@ v2/vr-quest-unity-plan.md §1, §2 (lots V-1 à V-3)
+
+
studiovideoIAquelques euros, une soirée — débloque une décision produit
+

Test « faire vivre un lieu » — 8 s au Fourneau

+

Une photo d'un bâtiment du Fourneau → sa version d'époque → un personnage en boucle de 8 s. Le but n'est pas de produire un asset, c'est de répondre à la seule question dont dépend tout le reste : est-ce que ça a l'air crédible, ou est-ce que ça décroche ? En patrimoine la crédibilité est la valeur — si le rendu est inquiétant, la fonctionnalité n'existe pas, quels que soient le prix et la facilité.

+

Le principe à valider : le text-to-video pur est écarté (il invente la géométrie du lieu). Le lieu réel reste la base, l'IA n'ajoute que le mouvement — c'est « Point de départ » du Studio transposé à la vidéo, donc aucun mécanisme nouveau en base : Kind = Video et sourceFidelity existent déjà, c'est un gabarit de plus.

+

Respecter les contraintes pendant le test, sinon il ne prouve rien : 6-10 s en boucle, une seule personne, mouvements simples, caméra calme, plan large ou moyen, jamais de gros plan sur les mains ou le visage.

+

⚠️ Deux choses déjà tranchées, à ne pas rejouer. Le motion transfer part en prestation Unov, pas dans le produit : il demande un tournage et un consentement écrit, ce qui échoue par construction le critère du §0 (« un conservateur produit sans intervention »). Et le cadrage de vente est resserré à « là où il ne reste rien » — bâtiment disparu, métier éteint, site réduit à ses fondations : partout où des archives existent, une vraie photo d'époque bat une boucle générée.

+

Ne bloque rien et n'est bloqué par rien — c'est justement l'intérêt de le faire tôt. Mais la fonctionnalité, elle, reste derrière le prérequis dur du Studio : IResourceBlobService n'expose toujours que IsConfigured et DeleteAsync, le backend ne sait pas écrire dans le bucket.

+ v2/studio-plan.md §3.3 — décidé le 04/09, section conditionnelle +
+
XRV2 · subventionné · go/no-go

Meta Quest — app Unity + Meta XR SDK

diff --git a/kanban/cards/3-bugs-ouverts/020-taille-du-telechargement-jamais-affichee.md b/kanban/cards/3-bugs-ouverts/020-taille-du-telechargement-jamais-affichee.md new file mode 100644 index 0000000..e6bc83f --- /dev/null +++ b/kanban/cards/3-bugs-ouverts/020-taille-du-telechargement-jamais-affichee.md @@ -0,0 +1,12 @@ +--- +title: La taille du téléchargement n'est jamais affichée +area: visitapp +horizon: v1 +tags: visitapp, offline, store +flag: critical | condition de publication sur le Play Store +src: relevé le 2026-09-04 — configurations_list.dart, downloadConfiguration.dart +--- +

Le visiteur ne sait jamais combien il télécharge. Le dialogue de configurations_list.dart annonce « il est nécessaire de télécharger du contenu supplémentaire » sans chiffre, et l'écran de progression n'affiche qu'un pourcentage. Or une visite du Fourneau, c'est un ordre de grandeur de plusieurs centaines de Mo sur la 4G du visiteur.

+

Google Play exige que la taille des téléchargements complémentaires soit annoncée avant de les lancer — c'est une condition de publication, pas un confort.

+

Rien à ajouter côté serveur : ResourceDTO.sizeBytes existe déjà et arrive dans ExportConfigurationDTO.resources. La somme se calcule côté client, et sur les seules ressources réellement à télécharger — isResourceOutdated filtre déjà celles qui sont à jour, donc une mise à jour de visite doit annoncer le delta, pas le total.

+

Deux endroits : le dialogue de confirmation (avant), et l'écran de progression (« 128 / 412 Mo » plutôt qu'un pourcentage nu). ⚠️ La taille annoncée dans le dialogue suppose d'avoir déjà appelé configurationExport pour connaître la liste — aujourd'hui cet appel n'a lieu qu'après la confirmation.

diff --git a/kanban/cards/5-planifie/055-carte-hors-ligne-tile-packs-mapbox.md b/kanban/cards/5-planifie/055-carte-hors-ligne-tile-packs-mapbox.md new file mode 100644 index 0000000..9b94e41 --- /dev/null +++ b/kanban/cards/5-planifie/055-carte-hors-ligne-tile-packs-mapbox.md @@ -0,0 +1,13 @@ +--- +title: Carte hors ligne — tile packs Mapbox +area: visitapp +horizon: v2 +tags: visitapp, carto, offline +flag: warn | prérequis pour vendre un parcours GPS sur un site sans réseau +src: analyse du code le 2026-09-04 — MapProvider, flutter_map, mapbox_maps_flutter +--- +

Aujourd'hui aucune carte ne fonctionne hors ligne, et ce n'est pas une question de données : MapDTO porte déjà points, centerLatitude, zoom et iconResourceId, et les icônes sont téléchargées avec la visite. C'est le fond qui manque — les tuiles sont chargées en réseau, sans cache.

+

Le GPS, lui, n'est pas le problème : geolocator ^13.0.0 lit le GNSS, qui est autonome. Seule réserve à annoncer au client : sans A-GPS, le premier point peut demander 30 à 60 s sous couvert forestier.

+

Le chemin court existe déjà dans le pubspec : mapbox_maps_flutter ^2.0.0 expose OfflineManager (style packs) et TileStore (tile regions) — on télécharge une emprise bornée au moment du téléchargement de la visite. Pour un domaine comme le Fourneau Saint-Michel, quelques dizaines de Mo. MapProvider.MapBox existe déjà côté modèle.

+

Google est une impasse, et pas seulement techniquement. Le SDK Maps pour Android n'expose aucune API de tuiles hors ligne — les « zones hors connexion » sont une fonction de l'app Google Maps, pas du SDK intégrable. Et le code n'utilise même pas ce SDK pour les sections carte : il passe par flutter_map sur https://mt1.google.com/vt/lyrs=m&x={x}&y={y}&z={z}, un endpoint non documenté dont la mise en cache est explicitement interdite. Conclusion : Mapbox devient le fournisseur du mode hors ligne, Google reste en ligne seulement.

+

⚠️ Tant que ce chantier n'est pas fait, SectionType.Map reste volontairement hors de offlineCapableSectionTypes (downloadConfiguration.dart) : l'ajouter donnerait une carte grise, ce qui est pire que ne pas la proposer. Une fois les tuiles packagées, c'est une ligne à ajouter dans cette constante.

diff --git a/kanban/cards/5-planifie/057-plan-illustre-georeference-3e-fournisseur.md b/kanban/cards/5-planifie/057-plan-illustre-georeference-3e-fournisseur.md new file mode 100644 index 0000000..7d53970 --- /dev/null +++ b/kanban/cards/5-planifie/057-plan-illustre-georeference-3e-fournisseur.md @@ -0,0 +1,12 @@ +--- +title: Plan illustré géoréférencé — un 3e fournisseur de carte +area: manager backend visitapp +horizon: v2 +tags: carto, offline, manager-app +flag: good | hors ligne par construction, et c'est ce qu'un musée dessine déjà +src: analyse du code le 2026-09-04 — MapProvider n'a que Google et MapBox +--- +

Un musée de plein air ne distribue pas un fond OpenStreetMap, il distribue son plan dessiné. Aujourd'hui MapProvider ne connaît que Google et MapBox : il n'existe aucun moyen de téléverser une image de plan et d'y placer les points.

+

Ce qu'il faut : une troisième valeur de MapProvider, une ressource image portée par la section, et un calage — deux points d'ancrage suffisent (coin haut-gauche / bas-droit en coordonnées réelles) pour projeter un GeoPoint sur l'image et y afficher la position du visiteur.

+

Pourquoi c'est le meilleur des trois : aucune tuile, donc hors ligne par construction — l'image part avec la visite comme n'importe quelle ressource, et le mécanisme de téléchargement n'a rien de nouveau à apprendre. Le rendu est aussi plus lisible qu'un fond routier sur un site de plusieurs dizaines de bâtiments.

+

Périmètre réel : c'est le plus gros des trois chemins carto — backend (modèle + migration), manager-app (téléversement du plan et pose des ancres), mymuseum-visitapp et visitapp-web (rendu). À faire après Carte hors ligne — tile packs Mapbox, qui débloque le besoin immédiat avec un fond classique.

diff --git a/kanban/cards/5-planifie/290-refonte-visuelle-config-section-et-dialogues.md b/kanban/cards/5-planifie/290-refonte-visuelle-config-section-et-dialogues.md new file mode 100644 index 0000000..74657ad --- /dev/null +++ b/kanban/cards/5-planifie/290-refonte-visuelle-config-section-et-dialogues.md @@ -0,0 +1,53 @@ +--- +title: Refonte visuelle des écrans Configuration & Section, et de leurs dialogues +area: manager +horizon: v1 +tags: manager-app, UI, dialogues +flag: good | Maquette prête, code à faire +src: DOCS/claude design/refonte-configuration-sections.html +--- +

Lots 1 à 5 codés le 04/09 (flutter analyze sans erreur, flutter build web passe ; rien de vérifié à l'écran). Pilote select_resource_modal : coquille standard, corps souple, plus de double défilement, showValues mort supprimé, largeur 0,85 conservée. Lot 1 : text_field_container sans width: double.infinity ni rayon 29, compteur discret dans rounded_input_field. Lot 2 : libellé au-dessus et plus d'auto-centrage dans string_/multi_string_/resource_/check_input_container. Lot 3 : grille responsive de sections, réordonnancement par Draggable/DragTarget (ReorderableListView ne fait pas de grille, et pas de dépendance nouvelle), tuile « Ajouter » à la place du bouton vert. Lot 4 : les deux écrans de détail en EditorHeader + EditorColumns + Pane (nouveau Components/pane.dart), _buildFooter supprimé, le QR devient le rail « Diffusion ». Lot 5 : grille de types dans new_section_popup, modal de traduction en rail de langues + IndexedStack, et multi_input_modal réduit à une redirection isHTML: false. 24 clés i18n ajoutées en FR/EN/NL.

+ +

🐛 Le bug des catégories est confirmé et corrigé : showCreateOrUpdateCategories recevait un newValues vide de l'appelant et le renvoyait tel quel — valider sans rien toucher effaçait les catégories. Le tampon part maintenant des catégories existantes, et les libellés en dur sont passés par AppLocalizations.

+ +

Lot 6 entamé : Components/collection_editor.dart existe — liste ordonnée à gauche (réordonnancement par poignée), élément édité en place à droite, ligne « Ajouter » en fin de liste, suppression dans l'en-tête du détail. Trois types migrés : Slider et Article (via un ContentFields partagé, extrait de showNewOrUpdateContentSlider) et PDF (le bloc figé 70 × 440 disparaît). Les filtres d'envoi au backend sont conservés à l'identique (contenus sans ressource exclus, champs d'article recopiés).

+ +

⚠️ Trois fichiers sont devenus du code mort mais ne sont pas supprimés : pdf_file_input_container.dart, pdf_list.dart, new_update_pdfFile.dart. Ils portent des modifications non committées d'un autre lot — les effacer perdrait ce travail. À supprimer une fois l'arbre propre.

+ +

Lot 6 terminé pour le gabarit B. Décision prise le 04/09 (artifact 4af75171) : CollectionEditor reçoit un mode distant (RemoteCollection : create / delete / reorder asynchrones, voile de chargement, retour à l'état d'avant si l'appel échoue). Quiz l'utilise : la modale de question disparaît, les messages de score remontent en tête de carte, les réponses gardent QuizzResponseList tel quel. Menu garde la navigation vers sub_section_edit_screen — une sous-section est une section complète — et prend la grille de tuiles, extraite en SectionGrid et partagée avec les sections d'une configuration. Parcours : liste qui suit son contenu, les deux SizedBox(height: 500) de event_config et section_parcours_config retirés, éditeur à fenêtre unique inchangé.

+ +

🐛 Le rang en double est corrigé dans Quiz et Menu : ils n'écrivaient que l'order de l'élément déplacé, les autres gardaient le leur. La liste entière est renumérotée et envoyée en un Future.wait, comme le faisait déjà Parcours.

+ +

Agenda n'a pas été migré, et c'est délibéré : ses événements sont groupés par langue (_displayGroups, pastilles de langue quand un groupe en compte plusieurs) et n'ont pas d'ordre — une liste ordonnée à édition en place perdrait le groupement. Il reçoit ce qui lui revient : hauteur pilotée par le contenu (le height: 600 saute), lignes au système de tokens, modale conservée.

+ +

Gabarit C — Jeu est fait, Map et Événement sont dégrossis. game_config devient champs à gauche + canevas à droite : le canevas montre le découpage réel de l'image en rows × cols, seule décision que le formulaire ne montrait pas. Au passage, son TabBar n'en était pas un — les deux onglets construisaient le même formulaire et ne servaient qu'à écrire gameType : c'est devenu un choix à deux valeurs, et le SizedBox(height: 650) a disparu. geoloc_input_container passe à la coquille standard et affiche les coordonnées retenues au lieu d'un bouton « Changer la localisation », la carte prenant la hauteur disponible. Côté map_config : en-tête en grille de tokens, height: 700 et height: 70 retirés. Côté event_config : height: 600 retiré, la liste des blocs suit son contenu.

+ +

Reste : le canevas de Map et d'Événement — et il est maintenant maquetté : DOCS/claude design/refonte-gabarit-canevas.html (diffusion : artifact caf400b5). Liste des points, fiche, carte côte à côte ; les points non sélectionnés restent visibles en gris ; MapGeometryPicker sort de son dialogue pour devenir le canevas (déplacement, pas écriture) ; les informations pratiques d'un point (5 champs sur 9) sont repliées derrière un compteur. Absorbe showNewOrUpdateGeoPoint (362 l.), geometry_input_container et showNewOrUpdateMapAnnotation (182 l.). Une question produit reste ouverte : que montre le canevas d'Événement quand baseSectionMapId est nul — carte vide, ou invitation à choisir un plan d'abord (ma proposition).

+ +

Étape 1 du canevas faite le 04/09 : Components/map_canvas.dart — le dessin est sorti du dialogue. Il reçoit une géométrie, la rend modifiable, rend la main à chaque changement (pas de bouton « Enregistrer » : c'est l'écran qui enregistre), et affiche les autres tracés en fond via MapCanvasGhost. Barre d'outils compacte sur la carte, pied qui donne les coordonnées et ce qu'on modifie, bouton « agrandir » optionnel. MapGeometryPicker n'est plus qu'une coquille autour de lui, donc etape_fields et showNewOrUpdateEventAgenda ne bougent pas. Au passage, ses libellés « Ligne » / « Polygone » en dur sont passés par AppLocalizations. Taille tranchée : 420 px par défaut + mode agrandi, pas une interface centrée carte — un point porte neuf champs traduits, la carte n'en porte qu'un.

+ +

Étapes 2 à 4 faites : le gabarit C est en place dans Map. Map/geo_point_editor.dart — liste des points à gauche, fiche au centre, carte à droite ; les points non sélectionnés apparaissent en fond ; le mode agrandi replie la fiche et garde le rail. Les neuf champs traduits sont rangés par fréquence : titre, catégorie, description et image visibles, les cinq informations pratiques repliées derrière un compteur. map_config passe de 937 à ~510 lignes : la modale showNewOrUpdateGeoPoint n'est plus appelée, getElement et son boxDecoration sont supprimés, et recherche + catégories filtrent désormais la liste et la carte par un seul calcul. Les points sont créés, modifiés et supprimés directement par l'API (sectionMapCreate / Update / Delete), la suppression passant par la confirmation destructive.

+ +

Étape 5 faite : le canevas d'Événement. Les annotations passent dans l'éditeur de collection en mode distant, avec le canevas dans le panneau d'édition. Plan par défaut retenu (décision Thomas) : le premier plan disponible est pris quand baseSectionMapId est nul, plutôt que de bloquer. 🐛 Trouvé en chemin : MapAnnotationDTO portait déjà un champ geometry que l'ancienne modale ne remplissait jamais — elle ne réglait que geometryType. Une annotation déclarait donc une forme sans jamais recevoir de coordonnées ; le canevas les lui donne. Enfin showMultiStringInputAndResourceHTML est passé à la coquille standard : plus aucun dialogue de ce chantier sur l'ancienne.

+ +

Plan de test : DOCS/test-plan-refonte-config-section.md, 14 sections. Rien n'a été vérifié à l'écran — flutter analyze propre et flutter build web qui passe, c'est tout.

+ +

Reste : supprimer le code mort une fois l'arbre git propre — showNewOrUpdateGeoPoint, showNewOrUpdateMapAnnotation, pdf_file_input_container, pdf_list, new_update_pdfFile, listView_card_subSection, new_update_question_quizz ; et geometry_input_container quand etape_fields et showNewOrUpdateEventAgenda auront leur canevas.

Restent aussi showMultiStringInputAndResourceHTML sur l'ancienne coquille, et le code mort à supprimer une fois l'arbre propre : pdf_file_input_container, pdf_list, new_update_pdfFile, listView_card_subSection, new_update_question_quizz.
⚠️ À vérifier manuellement en priorité : les deux chemins qui recréent les contrôleurs Quill (traduction IA, « appliquer à toutes les langues ») ; les hauteurs figées restées autour des champs devenus plus hauts (height: 70, 100) dans les écrans non repris — showNewOrUpdateGeoPoint, showNewOrUpdateEventAgenda, sub_section_edit_screen, new_configuration_popup.

+ +

Maquette prête et complète : DOCS/claude design/refonte-configuration-sections.html (diffusion : artifact 4d0c2ad6). Elle contient le diagnostic, les deux écrans, les 3 gabarits qui couvrent les 13 types de section, la coquille de dialogue, et deux dialogues dessinés — traduction et choix du type. Rien à décider côté design : tout est tranché.

+ +

Le blanc ne vient pas des écrans mais de 4 composants partagés. text_field_container.dart impose width: double.infinity + rayon 29 ; string_input_container.dart met le label à gauche dans un Row ; multi_string_input_container et resource_input_container se centrent eux-mêmes ; section_reorderList.dart réserve 0,3 × hauteur pour une liste horizontale de cartes 150×150. On les corrige une fois, les 13 types en héritent.

+ +

⚠️ Aucune direction visuelle à inventer : constants.dart porte déjà le système (kSpace1..8, kRadiusInput 5 / Card 8 / Shell 10 / Pill, kTitleCard, kLabelField, kTextHint) et confirmation_dialog.dart + resource_picker.dart l'appliquent déjà. Ces deux écrans sont simplement antérieurs au système.

+ +

Premier pas conseillé : select_resource_modal.dart, pas les composants partagés. Un fichier, ~15 lignes, zéro ligne dans ResourcesScreen, et ça corrige un vrai bug — AlertDialog dispose de hauteur − 48 mais le contenu en demande 0,85 × hauteur plus titre plus actions, d'où un défilement dans un défilement. Ça sert de pilote avant de toucher aux composants qui irriguent 30 écrans. ⛔ Ne pas toucher à la largeur 0,85 : la règle du rail de facettes de v1-mediatheque-plan.md §3.3 est écrite sur cette hypothèse et déjà codée.

+ +

⚠️ Deux pièges qui ne sont pas du visuel. D'abord, manager-app n'a que 3 tests, aucun sur ces composants : vérification manuelle, et il reste des conteneurs à hauteur figée (height: 70, 100, SizedBox(height: 250)) autour — ce qui rentrait juste débordera. Ensuite un chemin d'écriture est câblé dans les composants : section_detail_screen.dart:270 déclenche un save(true, …) depuis MultiStringInputContainer.onGetResult, et la ligne 381 un save(false, …) depuis la case iBeacon. À ne pas perdre ni dédoubler.

+ +

⚠️ Le piège Quill (remonté de mémoire par Thomas, confirmé dans le code). Ça marche aujourd'hui parce que l'empilement vertical monte toutes les langues : _controllers est bâtie une fois dans initState (translation_input_container.dart:52) et libérée seulement dans dispose. Le rail de langues de la maquette casse cette propriété s'il reconstruit l'éditeur à chaque bascule. Règle : map construite une fois pour toutes les langues, bascule par IndexedStack/Offstage, jamais de TabBarView (construction paresseuse, enfants lâchés hors écran sans AutomaticKeepAliveClientMixin). Trace d'une tentative abandonnée : translation_tab.dart existe en TabBar, son appel est en commentaire dans multi_input_modal.dart:46-51. À retester en priorité : la traduction IA (ligne 142) et « appliquer à toutes les langues » (ligne 183), les deux chemins qui libèrent et recréent les contrôleurs.

+ +

Deux fusions et un ménage décidés dans la maquette : multi_input_modal.dart est le jumeau sans HTML du modal de traduction (même travail, deux implémentations) → une coquille, deux corps, Quill si isHTML et champ simple sinon ; les 8 showNewOrUpdate* et les 2 sélecteurs géo disparaissent dans les gabarits B et C ; showValues est mort en double (select_resource_modal.dart:64 et multi_input_modal.dart:186, appel commenté ligne 53).

+ +

⚠️ Conflit résolu avec l'auditsecurity/audit-manager-app.md recommandait d'extraire un FormDialogScaffold pour les 15 occurrences des showNewOrUpdate*. Or la refonte les supprime : les extraire d'abord, ce serait refactoriser du code à jeter. Ordre imposé : absorber d'abord (gabarits B et C), extraire ensuite sur ce qui survit — traduction, nouvelle section, nouvelle configuration, sélecteur de couleur, fichiers PDF, catégories. L'audit et STATUS.md ont été annotés en ce sens le 2026-09-04.

+ +

🐛 Trouvé en passant, à vérifier : pdf_file_input_container.dart et category_input_container.dart sont un copier-coller — mêmes dimensions, même structure — mais le second a ses libellés en dur (« Annuler », « Valider ») et son onGetResult(result) commenté. Une catégorie de carte modifiée ne remonte peut-être pas à la volée. Lecture de code, pas testé.

diff --git a/kanban/cards/5-planifie/325-test-faire-vivre-un-lieu-8-s-au-fourneau.md b/kanban/cards/5-planifie/325-test-faire-vivre-un-lieu-8-s-au-fourneau.md new file mode 100644 index 0000000..6cd5f83 --- /dev/null +++ b/kanban/cards/5-planifie/325-test-faire-vivre-un-lieu-8-s-au-fourneau.md @@ -0,0 +1,13 @@ +--- +title: Test « faire vivre un lieu » — 8 s au Fourneau +area: produit +horizon: v2 +tags: studio, video, IA +flag: good | quelques euros, une soirée — débloque une décision produit +src: v2/studio-plan.md §3.3 — décidé le 04/09, section conditionnelle +--- +

Une photo d'un bâtiment du Fourneau → sa version d'époque → un personnage en boucle de 8 s. Le but n'est pas de produire un asset, c'est de répondre à la seule question dont dépend tout le reste : est-ce que ça a l'air crédible, ou est-ce que ça décroche ? En patrimoine la crédibilité est la valeur — si le rendu est inquiétant, la fonctionnalité n'existe pas, quels que soient le prix et la facilité.

+

Le principe à valider : le text-to-video pur est écarté (il invente la géométrie du lieu). Le lieu réel reste la base, l'IA n'ajoute que le mouvement — c'est « Point de départ » du Studio transposé à la vidéo, donc aucun mécanisme nouveau en base : Kind = Video et sourceFidelity existent déjà, c'est un gabarit de plus.

+

Respecter les contraintes pendant le test, sinon il ne prouve rien : 6-10 s en boucle, une seule personne, mouvements simples, caméra calme, plan large ou moyen, jamais de gros plan sur les mains ou le visage.

+

⚠️ Deux choses déjà tranchées, à ne pas rejouer. Le motion transfer part en prestation Unov, pas dans le produit : il demande un tournage et un consentement écrit, ce qui échoue par construction le critère du §0 (« un conservateur produit sans intervention »). Et le cadrage de vente est resserré à « là où il ne reste rien » — bâtiment disparu, métier éteint, site réduit à ses fondations : partout où des archives existent, une vraie photo d'époque bat une boucle générée.

+

Ne bloque rien et n'est bloqué par rien — c'est justement l'intérêt de le faire tôt. Mais la fonctionnalité, elle, reste derrière le prérequis dur du Studio : IResourceBlobService n'expose toujours que IsConfigured et DeleteAsync, le backend ne sait pas écrire dans le bucket.

diff --git a/security/audit-manager-app.md b/security/audit-manager-app.md index 87a22ae..0904468 100644 --- a/security/audit-manager-app.md +++ b/security/audit-manager-app.md @@ -9,7 +9,7 @@ Audit du 2026-07-13. Trois axes analysés : architecture/state management, écra ## Haute -- **Duplication du shell de dialog** — tous les `showNewOrUpdate*.dart` (Parcours, Event, Map, Agenda, Quiz) répètent le même squelette : `Dialog` + `Container` + calcul `contentWidth/halfWidth/thirdWidth` recopié à l'identique + ligne boutons Annuler/Sauvegarder (15 occurrences). → extraire un `FormDialogScaffold({title, child, onSave, onCancel, widthFactor})`. +- **Duplication du shell de dialog** — tous les `showNewOrUpdate*.dart` (Parcours, Event, Map, Agenda, Quiz) répètent le même squelette : `Dialog` + `Container` + calcul `contentWidth/halfWidth/thirdWidth` recopié à l'identique + ligne boutons Annuler/Sauvegarder (15 occurrences). → extraire un `FormDialogScaffold({title, child, onSave, onCancel, widthFactor})`. ⚠️ **Mis à jour le 2026-09-04 — le périmètre de cette extraction a changé.** La refonte des écrans Configuration/Section (`DOCS/claude design/refonte-configuration-sections.html` §6) **supprime** les 8 `showNewOrUpdate*` : ils sont absorbés par le panneau d'édition en place de l'éditeur de collection. Extraire un scaffold pour eux serait refactoriser du code à jeter. Le scaffold reste utile, mais pour les dialogues qui **survivent** — traduction, nouvelle section, nouvelle configuration, sélecteur de couleur, fichiers PDF, catégories de carte. Et la coquille de référence existe déjà : `confirmation_dialog.dart`. **Ordre : absorber d'abord, extraire ensuite sur ce qui reste.** - **Composants quasi-copiés** — `translation_input_container.dart` (365 l.) vs `translation_input_and_resource_container.dart` (347 l.) : ~80% identiques, mêmes méthodes aux mêmes numéros de ligne, seule différence la gestion du `resourceId`. Même schéma pour `multi_string_input_container.dart` vs `..._and_resource_container.dart`. → fusionner avec un flag `withResource`. - **Méthodes/`build()` monolithiques** : - `section_detail_screen.dart:455` — `save(bool isTraduction, AppContext)` fait ~440 lignes (455→894), logique métier (appels API, traduction, notifications) mêlée à l'UI. diff --git a/test-plan-refonte-config-section.md b/test-plan-refonte-config-section.md new file mode 100644 index 0000000..22e1d6a --- /dev/null +++ b/test-plan-refonte-config-section.md @@ -0,0 +1,157 @@ +# Plan de test — refonte Configuration & Sections (2026-09-04) + +Périmètre : la refonte visuelle des écrans Configuration et Section, de leurs dialogues, +et le gabarit C de Map / Événement / Jeu. Maquettes : `DOCS/claude design/refonte-configuration-sections.html` +et `DOCS/claude design/refonte-gabarit-canevas.html`. + +**Rien n'a été vérifié à l'écran** — `flutter analyze` est propre et `flutter build web` passe, +c'est tout. `manager-app` n'a que 3 tests et aucun sur ces composants : la vérification est manuelle. + +Mode d'emploi : coche si le comportement attendu est constaté, laisse vide sinon et note l'écart +en bas. `→` = résultat attendu. + +--- + +## §0. Porte d'entrée + +- [ ] `flutter build web` passe +- [ ] `flutter analyze` ne remonte que les 3 problèmes connus de `test/widget_test.dart` +- [ ] L'app se lance, connexion OK → le champ e-mail et le mot de passe sont lisibles et espacés + +## §1. Le champ partagé — le plus gros risque de régression + +Ces composants sont importés par une trentaine d'écrans. **Ce qui rentrait juste peut déborder** : +un débordement Flutter est visible (rayures jaunes), pas silencieux. + +- [ ] Écran Configuration : les libellés sont **au-dessus** des champs, pas à gauche +- [ ] Le compteur de caractères est petit et discret sous le champ +- [ ] Aucune bande jaune de débordement sur : Ressources, Instances, Utilisateurs, Clés API, Devices +- [ ] Les cases à cocher (Hors ligne, iBeacon…) sont compactes et alignées à gauche de leur libellé +- [ ] Un champ de ressource vide affiche « Choisir » sur fond clair bordé, pas une pastille pleine + +## §2. Écran Configuration + +- [ ] Les actions (Supprimer, Annuler, Enregistrer, Export) sont **dans l'en-tête**, plus de barre en bas +- [ ] Le sous-titre indique date de création · nombre de sections · nombre de langues +- [ ] Le nombre de sections est juste **après le chargement** de la liste (et pas 0) +- [ ] Deux colonnes : Réglages + Sections à gauche, Habillage à droite ; empilées sous 900 px +- [ ] Enregistrer, puis rouvrir : les valeurs sont bien persistées + +## §3. La grille de sections + +- [ ] Les sections sont en grille responsive, plus de liste horizontale +- [ ] Tuiles 16/9 avec badge d'ordre, image si présente, icône de type sinon +- [ ] Une section inactive porte le badge « Masquée » et est estompée +- [ ] Glisser une tuile par sa poignée (coin haut droit) la réordonne → l'ordre est sauvé +- [ ] La tuile « Ajouter une section » en fin de grille ouvre le dialogue de création +- [ ] La recherche filtre bien la grille + +## §4. Dialogue « Nouvelle section » + +- [ ] Les 13 types sont visibles, groupés en 4 familles, avec une description par type +- [ ] Sur une **sous-section**, le type Menu n'est pas proposé +- [ ] L'avertissement « le type ne pourra plus être changé » est dans le pied +- [ ] Créer sans nom (< 3 caractères) → message d'erreur, pas de création + +## §5. Écran Section + +- [ ] En-tête : icône du type, pastille du type, actions ; plus de barre en bas +- [ ] Le panneau « Diffusion » est déplié dans le rail droit (QR, identifiant, iBeacon) +- [ ] Cliquer le QR le copie → notification de succès +- [ ] Cocher iBeacon **enregistre immédiatement** (chemin `save(false, …)`) +- [ ] Modifier le titre affiché **enregistre immédiatement** (chemin `save(true, …)`) + +## §6. Modal de traduction — ⚠️ le piège Quill + +Le point le plus fragile de la refonte. Les éditeurs sont montés en `IndexedStack` ; +deux chemins recréent les contrôleurs. + +- [ ] Le rail de gauche liste les langues avec une pastille pleine / vide +- [ ] Basculer de langue **conserve** ce qui a été tapé dans l'autre langue +- [ ] La langue source s'affiche en référence au-dessus de l'éditeur +- [ ] Le compteur de caractères suit la frappe +- [ ] **« Appliquer à toutes les langues »** : le texte de la langue affichée part dans les autres, et l'éditeur montre le résultat +- [ ] **« Traduire avec l'IA »** : les langues manquantes se remplissent, l'éditeur montre le résultat, le quota se met à jour +- [ ] Après une traduction IA, retaper dans une langue traduite fonctionne encore +- [ ] Un champ **sans HTML** (ex. libellé d'annotation) ouvre le même modal, sans barre de format +- [ ] Annuler ne conserve rien, Valider conserve + +## §7. Sélecteur de ressource + +- [ ] La modale n'a **plus de double défilement** : la grille défile, pas le dialogue +- [ ] Un clic sur une vignette sélectionne et referme +- [ ] Sa largeur est inchangée (≈ 85 % de l'écran) + +## §8. Éditeur de collection — Slider, Article, PDF + +- [ ] Slider : liste à gauche, contenu édité à droite, plus de modale +- [ ] Ajouter un contenu, choisir une ressource, un titre → Enregistrer → il est là au retour +- [ ] Un contenu **sans ressource** n'est pas envoyé au backend (comportement d'avant conservé) +- [ ] Réordonner par la poignée → l'ordre est conservé après Enregistrer +- [ ] Supprimer un contenu +- [ ] Article : contenu, audio et les deux cases fonctionnent, la liste d'images suit +- [ ] PDF : plus de bloc figé, ajouter / renommer / supprimer un fichier + +## §9. Quiz, Menu, Agenda + +- [ ] Quiz : ajouter une question → elle apparaît **et** existe au rechargement +- [ ] Quiz : modifier l'intitulé, l'image de fond, les réponses → persisté +- [ ] Quiz : **réordonner** → les rangs ne se marchent plus dessus (bug corrigé), l'ordre tient au rechargement +- [ ] Quiz : supprimer une question ; en cas d'erreur réseau, la liste revient à son état d'avant +- [ ] Quiz : les 4 messages de score s'ouvrent et s'enregistrent +- [ ] Menu : les sous-sections sont en grille, un clic ouvre l'écran de sous-section +- [ ] Menu : réordonner → ordre correct au rechargement +- [ ] Menu : créer une sous-section depuis la tuile « Ajouter » +- [ ] Agenda : plus de bloc de 600 px, la liste suit son contenu, recherche et filtres de date OK + +## §10. Le canevas — Map + +- [ ] Trois volets : liste, fiche, carte +- [ ] Les **autres points** apparaissent en gris sur la carte +- [ ] Cliquer la carte place le point ouvert dans la fiche ; le pied affiche ses coordonnées +- [ ] Les outils Point / Ligne / Zone et la couleur fonctionnent +- [ ] Le bouton « agrandir » élargit la carte et replie la fiche +- [ ] Recherche et catégories cachent les **mêmes** points dans la liste et sur la carte +- [ ] « Informations pratiques » se replie/déplie et le compteur est juste +- [ ] Créer un point, le placer, le renommer → rechargement : tout est là +- [ ] Supprimer un point passe par la confirmation rouge +- [ ] ⚠️ **À juger à l'usage** : chaque déplacement de point déclenche un appel API. Si c'est trop bavard, il faudra grouper en fin de geste. + +## §11. Le canevas — Événement, et Jeu + +- [ ] Événement : si aucun plan de base n'est choisi, **le premier plan disponible est pris par défaut** +- [ ] Événement : ajouter une annotation, lui donner un libellé, une icône, une position sur la carte +- [ ] Événement : la position est **conservée au rechargement** (elle ne l'était jamais avant : la modale ne remplissait pas `geometry`) +- [ ] Événement : supprimer une annotation +- [ ] Événement : les blocs de programme s'affichent sans hauteur figée, le parcours est toujours là +- [ ] Jeu : le choix Puzzle / Taquin est deux pastilles, plus d'onglets +- [ ] Jeu : le canevas montre le découpage lignes × colonnes sur l'image, et le compte de pièces suit + +## §12. Sélecteur de géométrie encore en modale + +Deux appelants n'ont pas été migrés — ils doivent continuer de marcher **à l'identique**. + +- [ ] Parcours → étape → géométrie : le dialogue s'ouvre, dessine, enregistre +- [ ] Agenda → événement → géométrie : idem + +## §13. Catégories de carte — 🐛 bug corrigé + +- [ ] Ouvrir les catégories d'une Map, **ne rien toucher**, valider → les catégories sont **toujours là** + (avant : la liste était effacée) +- [ ] Les boutons du dialogue sont traduits (ils étaient en dur en français) +- [ ] Ajouter / renommer / supprimer une catégorie fonctionne + +## §14. i18n + +- [ ] Passer le manager en EN puis NL : aucun texte français résiduel sur les écrans repris +- [ ] Les nouveaux libellés sont traduits : familles de types, descriptions de type, « Masquée », + « Informations pratiques », « Nouveau point », outils de géométrie + +--- + +## Écarts constatés + +| § | Ce qui a été observé | Gravité | +|---|---|---| +| | | | +| | | | diff --git a/v1-mediatheque-plan.md b/v1-mediatheque-plan.md index 4c6fb17..d102d53 100644 --- a/v1-mediatheque-plan.md +++ b/v1-mediatheque-plan.md @@ -173,7 +173,7 @@ Le libellé change, pas l'identifiant. | `Screens/Resources/resource_formatting.dart` | **Nouveau** — extension de téléchargement, poids, mois, dates | | `Screens/Resources/resource_download.dart` | **Nouveau** — `downloadResource()`, la correction du `.json` en dur | | `Helpers/ImageCompressor.dart` | `CompressedImage` expose `width`/`height` : c'est le seul endroit qui décode l'image avant l'upload | -| `Screens/Resources/select_resource_modal.dart` | Inchangé si possible — voir §3.3 | +| `Screens/Resources/select_resource_modal.dart` | **Contenu** inchangé ; **cadre** repris par la refonte Configuration/Sections — voir §3.3 | | `constants.dart` | Aucun token nouveau à créer : `kSurface*`, `kInk*`, `kLine*`, `kSpace*`, `kRadius*` couvrent tout | ### 3.3 ⚠️ Le piège central : un écran, deux vies @@ -199,6 +199,10 @@ Règles à respecter : n'ouvre pas le panneau de détail. - Le rail de facettes doit être **réductible ou masqué** dans la modale : elle fait déjà `size.width * 0.85`, et un rail de 194 px y est acceptable — mais le vérifier à 1280 px de large. +- ⚠️ **Cette règle dépend de la largeur `size.width * 0.85`.** La refonte des écrans + Configuration/Sections reprend le *cadre* de cette modale (voir plus bas) et **conserve + délibérément cette formule de largeur** pour ne pas invalider la règle ci-dessus. Si quelqu'un + veut un jour rétrécir la modale, c'est cette ligne-là qu'il faut rouvrir d'abord. - Le mode sélection multiple est **désactivé** quand `isSelect: true`. - `resourceTypes` filtre déjà en amont : la facette Type doit se limiter aux types reçus, comme aujourd'hui (`resource_body_grid.dart:42`). @@ -206,6 +210,30 @@ Règles à respecter : **Test de non-régression obligatoire** : ouvrir un champ image d'une section, choisir une ressource, enregistrer. C'est le chemin le plus emprunté de toute l'app. +#### Le cadre de la modale — repris par un autre lot + +Le *contenu* de `showSelectResourceModal` appartient à ce lot et n'est pas retouché ailleurs. Son +*cadre*, en revanche, est repris par la refonte des écrans Configuration/Sections — maquette : +`DOCS/claude design/refonte-configuration-sections.html`, section 06. + +Motif : ce n'est pas qu'une question de style. Un `AlertDialog` dispose de `hauteur − 48` (marge +verticale de 24 px), le contenu en demande `0,85 × hauteur`, plus un titre et une ligne d'actions de +70 px — d'où le `SingleChildScrollView` qui l'entoure, et un **défilement dans un défilement** : on +fait glisser le dialogue au lieu de la grille. + +Ce que l'autre lot change, et rien de plus : + +- coquille standard (`Dialog` + en-tête + pied) à la place de `AlertDialog` et de + `title: Center(Text(...))`, `kRadiusShell` au lieu du rayon 20 ; +- hauteur figée → corps souple sous un `maxHeight`, ce qui supprime le double défilement ; +- le bouton « Annuler » de 180 × 70 px descend dans le pied de dialogue ; +- `showValues` (lignes 64-78) supprimé : c'est du code mort. La copie qu'en garde + `multi_input_modal.dart` ligne 186 l'est aussi — son unique appel, ligne 53, est en commentaire. + Les deux peuvent partir. + +**La largeur `size.width * 0.85` n'est pas touchée**, exprès — voir l'avertissement des règles +ci-dessus. Aucune ligne de `ResourcesScreen` n'est modifiée. + ### 3.4 Le rail de facettes ``` diff --git a/v2/studio-plan.md b/v2/studio-plan.md index 22d1e15..b5e4061 100644 --- a/v2/studio-plan.md +++ b/v2/studio-plan.md @@ -442,6 +442,65 @@ pareil (badge, et proposition de régénérer). `AiProvenance.modelKey` le perme assumer côté scénario. Les échappatoires restent 2 vues par personnage, ou la composition en deux passes — générer A dans la scène, puis ajouter B en modification de cette image. +#### « Faire vivre un lieu » — la vidéo à partir du réel (décidé le 2026-09-04) + +Le text-to-video pur est écarté : il invente la géométrie du lieu, ce qui est disqualifiant en +patrimoine. **Le lieu réel reste la base, l'IA n'ajoute que le mouvement** — c'est exactement +« Point de départ » ci-dessus, transposé à la vidéo, avec `sourceFidelity: framing` : le modèle n'a +pas à inventer l'architecture, il l'a sous les yeux. + +**Rien de neuf en base.** `GenerationTemplate.Kind = Video` existe déjà, `sourceFidelity` aussi. Un +gabarit de plus, pas un mécanisme de plus. + +**Où ça vaut vraiment quelque chose, et où ça n'en vaut pas.** Pas « faire vivre un bâtiment » en +général : là où le lieu a des archives, **une vraie photo d'époque bat une boucle générée**, parce +qu'en patrimoine la crédibilité *est* la valeur. Le cas qui gagne est celui où **il ne reste rien** : +un bâtiment disparu, un métier éteint, un site réduit à ses fondations. Là, huit secondes plausibles +battent un paragraphe, et elles ne concurrencent aucune archive. Cadrer la vente là-dessus. + +**Deux voies techniques, et elles ne vont pas au même endroit :** + +| Voie | Ce que ça demande au client | Verdict | +|---|---|---| +| **Image-to-video** — photo du lieu (ou sa version d'époque déjà générée) + gabarit de mouvement | Trois champs | **Dans le produit** | +| **Motion transfer** — le médiateur mime le geste, le mouvement est transféré sur le personnage | Un tournage, un consentement écrit, son archivage | **En prestation Unov, pas dans le produit** | + +Le motion transfer donne un mouvement plus juste, et c'est la voie préférée qualitativement. Mais il +échoue le critère de réussite du §0 par construction — « installer un conservateur devant l'écran et +le laisser produire sans intervention » ne survit pas à « d'abord, filmez quelqu'un ». À 39-179 €/mois, +aucun client n'a l'équipe pour ça. Il se vend, il ne se livre pas en self-service. + +**Contraintes de production — la partie qui ne périmera pas** (au-delà, le résultat décroche) : + +- 6 à 10 secondes, pensées pour la boucle, fondu aux extrémités +- **une seule personne** à l'écran +- mouvements simples, caméra calme +- plan large ou moyen, **jamais de gros plan** sur les mains ou le visage + +Ces quatre lignes valent mieux qu'un nom de modèle : elles décrivent le domaine de validité de la +technique, pas d'un fournisseur. Le `ProviderModelId` vit en base (§3.6) et aura tourné plusieurs +fois d'ici la livraison. + +**Gabarits de mouvement, pas de prompt libre** — même règle que partout ailleurs dans Studio : le +conservateur **choisit**, il ne décrit pas. + +⚠️ **Consentement.** Le motion transfer part du corps d'une personne réelle : c'est un traitement de +ressemblance, au même titre que « Modifier une image » sur une personne (voir l'encadré ci-dessus). +Consentement écrit, tracé, et archivé avec l'asset — obligation qui pèse sur la prestation, ce qui +est une raison de plus de ne pas l'ouvrir en self-service. + +⚠️ **Mention IA plus visible qu'ailleurs.** Une figure humaine en mouvement dans un lieu réel n'est +pas une illustration décorative : le badge du §3.11 suffit juridiquement, mais certaines institutions +refuseront le principe même. À valider auprès d'un client réel, pas seulement techniquement. + +**Contenu 2D pour écrans.** Ne remet pas en cause la décision de rester sur des personnages stylisés +en VR ([v2/vr-quest-unity-plan.md](vr-quest-unity-plan.md)). + +> **Non tranché : est-ce que ça a l'air crédible ?** Tout le reste en dépend, et ça ne se décide pas +> sur le papier. Test à faire avant d'écrire la moindre ligne d'UI — une photo d'un bâtiment du +> Fourneau, sa version d'époque, un personnage en boucle de 8 s. Quelques euros, une soirée. +> Si ça décroche, cette section saute. + ### 3.4 Cycle de vie d'un asset ```