Relevé dans le code le 2026-09-16. Même cause pour les quatre : XR-2 a réutilisé AppConfigurationLinkScreen tel quel, et cet écran sert quatre canaux qui n'ont pas les mêmes réglages. Le sélecteur bento ne pilote rien en VR (les spans sont portés par le lien de configuration, pas par les sections), la notification annonce « application mobile », la popup d'édition d'un casque est celle du kiosk avec la moitié des champs morte, et un bento VR suppose des spans par section jusque dans Unity. Plan dans v2/vr-menu-bento-plan.md, carte kanban 340. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
11 KiB
Menu VR — bento, et le reste de l'onglet VR
Chantier V2, analysé dans le code le 2026-09-16. Suite de
vr-quest-unity-plan.md(lot XR-4, item E5 — le menu flottant existe et tourne). Ce document ne rouvre pas les décisions du plan VR : il traite quatre écarts entre ce que le manager laisse configurer et ce que le casque affiche réellement.
Le constat
L'onglet Main / VR du manager (vr_screen.dart)
réutilise AppConfigurationLinkScreen, l'écran de configuration partagé avec le mobile, le web
et le kiosk. Il hérite donc de tout son outillage — dont un sélecteur de forme bento
(les quatre petits boutons carré / large / haut / grand) qui, côté casque, ne pilote rien.
Vérifié :
| Fait | Preuve |
|---|---|
| Les spans sont portés par le lien de configuration, pas par les sections | AppConfigurationLink.cs:35 — commentaire d'origine : « Specific Mobile & Web » |
| Ils ne pilotent que l'écran « choisis ta configuration » | mymuseum-visitapp/lib/Screens/Home/home_3.0.dart:1179, visitapp-web/src/components/ConfigurationGrid.tsx:38 |
| Le casque ne les lit jamais | ConfigurationExport.cs n'a aucun champ span ; grep -r "Span" Assets/Scripts → rien |
| Un casque est appairé à une seule configuration | MenuBootstrap.cs:80 — pairing.ConfigurationId. Il n'y a pas d'écran de choix de configuration en VR |
| Le menu VR affiche les sections racines, toutes à la même taille | FloatingMenu.cs:106-125 — arc de 5 panneaux par rangée, 0,50 × 0,34 m fixes |
Autrement dit : en VR, le bento du manager porte sur un objet (la configuration) qui n'a pas d'écran de sélection, alors que l'objet qui a un écran (la section) n'a pas de spans.
Deux autres écarts, plus petits, de la même famille « écran partagé, canal différent » : la notification et la popup d'édition d'un casque.
Décisions actées
| Décision | Raison |
|---|---|
| Le bento VR porte sur les sections, pas sur les configurations | C'est le seul écran de choix qui existe dans le casque. Sans ça, les boutons resteront décoratifs quoi qu'on fasse |
| L'arc est conservé | Ce n'est pas un défaut de rendu : un mur plat oblige à regarder ses bords de biais. La grille bento est calculée en cellules, puis chaque cellule est mappée sur un angle + une hauteur |
| Grille cible : 6 colonnes × 2 rangées max | Le confort visuel en VR c'est ±60° horizontaux ; au-delà on encercle le visiteur. Format naturellement paysage, proche du rendu web |
| Une popup casque distincte de celle du kiosk | La moitié des champs de la popup partagée est morte en VR (voir lot 4) — les masquer un par un dans un widget commun coûte plus cher que d'en écrire un |
| Les spans de section ne sont pas exposés au mobile/web en V1 | Ils ont déjà leur bento, au niveau configuration. Ouvrir un second niveau de grille sur ces fronts est un chantier produit à part |
Lot 1 — La notification annonce la mauvaise app
~15 minutes, indépendant des autres.
Modifier un contenu VR affiche « application mobile mise à jour ». Le paramètre prévu pour ça existe déjà et n'est simplement pas renseigné :
app_configuration_link_screen.dart:144
final appUpdatedMsg = widget.appUpdatedLabel ?? AppLocalizations.of(context)!.appUpdatedSuccess;
↑ jamais passé par vr_screen.dart
À faire :
- Clé
vrAppUpdatedSuccessdansapp_fr.arb(template), puisapp_en.arbetapp_nl.arb. vr_screen.dart:38— passerappUpdatedLabel: l.vrAppUpdatedSuccessàAppConfigurationLinkScreen.vr_screen.dart:78(_backgroundCard) — même remplacement, c'est unshowNotificationséparé.vr_devices_tab.dart:280(_edit) — idem.
⚠️ Le template i18n est app_fr.arb ; les trois langues sont obligatoires (cf. manager-app/CLAUDE.md).
Lot 2 — Des spans par section (backend + manager)
Le socle du lot 3. Rien de visible dans le casque avant le lot 3.
Backend
Data/Section.cs + int? GridColSpan, int? GridRowSpan
DTOs/SectionDTO.cs + gridColSpan, gridRowSpan (après `order`, même famille)
→ mapping dans ToDTO / FromDTO des champs communs
Migrations/ AddSectionGridSpans
L'export est gratuit côté transport : /api/configuration/{id}/export
(ConfigurationController.cs:437) sérialise déjà les SectionDTO complets via
SectionFactory.ToDTO. Les nouveaux champs suivent.
⚠️ Section.ToDTO() n'est pas virtuelle — vérifier que la copie des champs communs se fait
bien au même endroit que order, sinon les spans sortiront nuls sur les sous-types.
⚠️ dotnet ef ignore appsettings.Development.json : passer par MIGRATIONS_CONNECTION.
Client API
manager-app/manager_api_new/ — éditer les fichiers générés à la main, ne jamais relancer
la génération OpenAPI (règle projet).
Manager
L'onglet « Contenu de l'application VR » ne doit plus lister des configurations avec des spans, mais les sections racines de la configuration, avec leurs spans et un aperçu.
Deux options, à trancher en début de conversation :
- a) Un écran VR dédié, qui reprend le sélecteur de forme et l'aperçu live de
AppConfigurationLinkScreenmais s'alimente en sections. Plus propre, plus de code. - b) Paramétrer
AppConfigurationLinkScreenpour qu'il accepte une source « sections ». Moins de code, mais l'écran sert déjà quatre canaux — il est le point de contact de tous ces écarts, l'élargir encore le rend plus difficile à corriger.
Recommandation : (a), c'est précisément l'accumulation de cas particuliers dans cet écran partagé qui produit les trois autres lots de ce document.
À réutiliser tel quel : le debounce de 400 ms sur l'enregistrement des spans
(app_configuration_link_screen.dart:89-98) — un slider de forme qui écrit à chaque frame
sature l'API.
Lot 3 — Le rendu bento dans le casque (Unity)
Ce qui change
FloatingMenu.Place(panel, index, total) positionne aujourd'hui par index brut :
row = index / 5, angle = pas fixe. Il faut passer par une grille.
1. Placement dense par spans → chaque section obtient (col, row, colSpan, rowSpan)
2. Mapping cellule → arc → angle = (col + colSpan/2 - cols/2) * StepDegrees
hauteur = HeightMeters - row * (cellH + gap)
3. Taille du quad → width = colSpan * cellW + (colSpan-1) * gap
height = rowSpan * cellH + (rowSpan-1) * gap
L'algorithme de placement dense existe en Dart dans manager-app/myinfomate_layout/
(le même que le mobile). Deux voies :
- Le porter en C# (~150 lignes) — le casque reste autonome, cohérent avec le cache-d'abord.
- Le faire calculer par le backend dans l'export — une seule implémentation, mais le casque devient dépendant d'un champ calculé, et le cache d'un vieux contenu porte un vieux placement.
Recommandation : le porter en C#, l'export reste une description, pas une mise en page.
Fichiers
Assets/Scripts/Net/ConfigurationExport.cs + GridColSpan / GridRowSpan sur SectionSummary
Assets/Scripts/Menu/FloatingMenu.cs Place() → grille ; MaxPerRow 5 → 6
Assets/Scripts/Menu/MenuItemPanel.cs Create() prend une taille au lieu des const
Assets/Scripts/Menu/BentoLayout.cs (neuf) le placement dense
⚠️ MenuItemPanel.WidthMeters / HeightMeters sont des const publiques, lues ailleurs
(FloatingMenu.Place, et à vérifier dans PagedView / SectionPages). Les rendre variables
d'instance sans casser ces usages — garder les const comme valeurs de cellule par défaut est
le chemin le plus court.
⚠️ La cascade d'apparition (PlayAppear(i * 0.04f)) doit suivre l'ordre de lecture de la
grille (rangée par rangée, gauche à droite), pas l'index de la liste : avec des spans, les
deux divergent.
⚠️ Un panneau colSpan = 2 couvre ~32° d'arc. Un quad plat reste acceptable à cette ouverture ;
au-delà (un hypothétique span 3) il faudrait le subdiviser. Ne pas le faire avant d'en avoir besoin.
Vérification
Sans casque, le viewer web (vr-app/viewer/) est le chemin court pour valider un placement.
Avec casque : adb push d'un scene.json, l'app le préfère à celui embarqué.
Lot 4 — La popup d'édition d'un casque
Indépendant. ~une demi-journée.
showChangeInfo (Kiosk_devices/change_device_info_modal.dart:16)
est partagée avec le kiosk : largeur figée à 580, une colonne scrollée, et deux boutons
RoundedButton dont les libellés « Annuler » / « Changer » sont en dur (violation i18n).
Le vrai problème n'est pas la mise en page : la moitié des champs ne sert à rien en VR.
Aucun script Unity ne lit ces réglages — grep -r sur Assets/Scripts : zéro occurrence.
| Champ | En VR |
|---|---|
name, configurationId |
gardés |
primaryColor, secondaryColor |
morts — le menu a sa propre palette de panneaux |
loaderImageId |
mort — un casque n'a pas d'écran de chargement 2D |
screenPercentageSectionsMainPage, roundedValue |
morts — ce sont des réglages d'écran plat |
isSectionImageBackground, isHour, isDate |
morts |
À faire : Vr_devices/change_headset_modal.dart, paysage deux colonnes (~940 px), avec
identité + configuration à gauche, fond immersif à droite (il vit aujourd'hui dans le panneau
latéral de l'onglet contenu — à décider : déplacé ou dupliqué). Boutons repris du langage
visuel de la Médiathèque / du Studio IA plutôt que des RoundedButton historiques.
Tout le texte passe par AppLocalizations, y compris les deux boutons.
Ordre
Lot 1 ────────────────────────────── indépendant, à faire en premier (15 min)
Lot 4 ────────────────────────────── indépendant
Lot 2 ───→ Lot 3 le 3 n'affiche rien sans le 2
Mini-prompts de lancement
Un lot = une conversation. Chaque prompt suppose que ce fichier est lisible.
Lot 1
Lis
DOCS/v2/vr-menu-bento-plan.md§ Lot 1 et applique-le : la notification de l'onglet VR du manager annonce « application mobile ». Ajoute la clé i18n dans les trois langues et passeappUpdatedLabelaux quatre endroits listés.
Lot 2
Lis
DOCS/v2/vr-menu-bento-plan.md§ Lot 2. AjoutegridColSpan/gridRowSpansurSectioncôtémanager-service(entité, DTO, migration, client API édité à la main), puis fais que l'onglet « Contenu de l'application VR » du manager édite les spans des sections et non des configurations. Commence par me dire si tu pars sur l'option (a) ou (b) du plan.
Lot 3
Lis
DOCS/v2/vr-menu-bento-plan.md§ Lot 3. Dansvr-app/unity, fais que le menu flottant place ses panneaux sur une grille bento par spans mappée sur l'arc, au lieu de l'index brut actuel. Le lot 2 doit être fait — vérifie que l'export porte bien les spans avant de commencer.
Lot 4
Lis
DOCS/v2/vr-menu-bento-plan.md§ Lot 4. Écris une popup d'édition de casque dédiée en paysage deux colonnes, sans les champs morts en VR, avec les boutons du langage visuel de la Médiathèque, et tout le texte en i18n FR/EN/NL.