ResourcesScreen, il appartient au lot Médiathèque.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.
constants.dart porte déjà un système complet — kSurface2, kInk2, kLine, kSpace1…kSpace8, 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é.
Quatre causes, toutes dans des composants réutilisés par tous les écrans d'édition.
11/50 se retrouve à un mètre du texte.Row
string_input_container.dart · resource_input_container.dartauto-fill minmax(146px, 1fr), hauteur pilotée par le contenu, réordonnancement conservé.C'est la brique qui décide de la densité de tous les écrans. Elle se corrige dans trois fichiers.
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.
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.
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.
Même squelette, même grille, même rail. Seul le contenu de la carte « Configuration <type> » change d'un type à l'autre.
a17c9b…Réponse à ta question : non. Les 13 types se rangent dans trois formes, et c'est la forme qui se dessine.
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.
Un à quatre champs dans la carte de type. Rien à dessiner de plus : la grille de 12 colonnes de l'écran suffit.
Liste ordonnée d'éléments + édition en place. Maquetté ci-dessus avec le Quiz ; l'élément édité change, la mécanique non.
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.
| Type | Gabarit | Contenu de la carte de type | Dette actuelle |
|---|---|---|---|
| Web | A | URL | — |
| Météo | A | Ville | Champ marqué isUrl à tort |
| Vidéo | A | Source (YouTube / Vimeo / ressource) + URL | Déjà refait, sert de référence |
| B | Liste de fichiers traduits | Bloc figé 70 × 440 px centré | |
| Slider | B | Contenus ordonnés : image, titre, description | Liste horizontale height: 300 |
| Menu | B | Sous-sections ordonnées | Liste horizontale 0.35 × hauteur |
| Article | B | Contenus + audio + lecture auto | Liste horizontale height: 250 |
| Quiz | B | Questions, réponses, points, scores | Le plus dense : 4 zones à hauteur fixe |
| Agenda | B | Événements datés | Modale par événement |
| Parcours | B | Étapes, mode de progression | Éditeur dédié de 785 lignes à raccorder |
| Map | C | Points géo, catégories, images par point | 753 lignes, pas d'aperçu carte à l'édition |
| Événement | C | Blocs de programme, annotations sur plan | Trois modales imbriquées |
| Jeu | C | Image, messages, lignes × colonnes | TabBar dans un SizedBox(height: 650) |
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.
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.
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.
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.
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.
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.
| Dialogue | Sort | Pourquoi |
|---|---|---|
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. |
| Traduction | maquetté | L'interface la plus utilisée de l'app. Ci-dessus. |
| Nouvelle section | maquetté | Première impression client. Ci-dessus. |
| Nouvelle configuration | aligné | Formulaire court : la coquille M et la grille de champs suffisent, rien à décider. |
| Confirmation | fait | Déjà repris, et c'est lui la référence. |
| Ajouter une ressource | fait | Maquette dédiée du 03/09, déjà publiée à part. |
| Sélecteur de ressource | cadre seul | Il 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 couleur | aligné | 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 | à part | 785 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, notifications | aligné | Hors périmètre de cette refonte, mais héritent de la coquille et des boutons dès qu'on y touche. |
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.
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.
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.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._buildFooter. Le _cardQR devient le panneau « Diffusion » du rail droit.showNewOrUpdate*. C'est le plus gros morceau, et le seul qui puisse s'étaler dans le temps : chaque type migré est indépendant.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.