blog.vfg.lol

Essai UI in-game 2a — design complet : paquets, rivière SVG, panneaux latéraux

Toutes les sections du handoff 2a sont en code ; la rivière est un overlay SVG, pas un rewrite des axes 3D

Essai UI in-game 2a — design complet : paquets, rivière SVG, panneaux latéraux

Le billet précédent s’arrêtait à T3 (suggestions ancrées). Cette vague ferme la promesse : tout ce que le handoff design 2a décrit — paquets par degré, rivière qui remonte, panneaux latéraux — est en code sur la branche essai/ui-ingame-2a. Le monde 3D n’a pas été touché.

Ce qui manquait au handoff après T3

Capture design UI in-game

Trois sections entières du design restaient à implémenter :

  • §2 Paquets (besaces par degré, jauges, respiration du plus rempli).
  • §3 Rivière (rubans qui montent de y=800 à y=150, deux passes par couloir).
  • §6 Panneaux latéraux (DERNIERE NOTE à gauche, JOURNAL à droite, accents #52d6d1 / #7cf59a).

Le runtime avait déjà D-STATE (compteurs par degré, pastilles en vol, dernière note, journal) — la moitié du travail côté données. Il restait à peindre la peau.

Vague design 2a complète — sept chantiers

Plan : PLAN-ORCH-DESIGN-2a-COMPLET.md. Sept chantiers en trois vagues :

VagueChantiers
1 (parallèle)D-POLISH, D-STATE
2 (parallèle)D-POUCH, D-RIVER, D-PANELS
3 (séquentiel)D-MOUNT, D-VERIFY

D-POLISH a refermé les fuites de la fidélité : Cinzel 500 chargé, regroupement gap:14px du bloc droit de la topbar, transition de teinte des couloirs via @property --lane-color.

D-STATE a exposé runtime.pouchCounts, runtime.climbers, runtime.lastNote, runtime.journal — quatre getters purs, sans réécriture du routage MIDI. La causalité « la pastille arrive → le paquet grossit » est portée par settleClimbers (appelé dans le tick()).

D-POUCH — PouchRow.tsx

Une pochette par degré actif, centrée sur x(i). Pas un compteur abstrait : une forme (84×74, border-radius organique 42%/30%), un col, une jauge interne qui passe de 18px à 78px selon la part de session, un compte Space Grotesk 700 15 avec text-shadow: 0 0 12px #000. Le paquet le plus rempli respire via rjBreathe 3.4s ease-in-out.

Positionnement : le composant s’ancre lui-même dans la bande utile left:120 width:1040 et appelle laneCenterX — la même géométrie que les couloirs et les suggestions. Aucun risque de désynchronisation.

const centerXPct = ((laneCenterX(index, n) - BAND_LEFT) / BAND_WIDTH) * 100

D-RIVER — RiverOverlay.tsx

Une décision structurante au passage : pas de rewrite 3D pour la rivière qui monte. Le canon produit (profondeur = temps) tient debout ; les couloirs 2a doivent se lire à plat, pas en projection 3D.

Donc : un SVG plein cadre (viewBox 0 0 1280 800), plein position: absolute; inset: 0; z-index: 1, avec un ruban par couloir. Deux passes :

  • halo : stroke-opacity: .3, stroke-width: 34, filtre feGaussianBlur stdDeviation=6 ;
  • courant : stroke-opacity: .55, stroke-width: 2, stroke-dasharray: 12 16, rjFlow 7…13s linéaire.

Le chemin part et arrive sur cx :

M cx 800 C cx-16 660 cx+16 520 cx 400 C cx-12 320 cx+12 250 cx 150

Le balancement gauche/droite est léger — il donne du courant sans déplacer la moyenne. Si la position du ruban dérivait, le X de la note ne tomberait plus sur le centre du couloir, et tout le reste se casserait. Le test riverPath vérifie que M cx 800 ouvre et cx 150 ferme.

prefers-reduced-motion coupe rjFlow, le SVG reste statique. Le monde 3D en arrière-plan n’est pas modifié.

D-PANELS — LastNotePanel + JournalPanel

Deux FloatingPanel 214px de large, top: 470px, à 18px des bords gauche / droit. Accents 2px :

  • gauche DERNIERE NOTE : bordure #52d6d1, nom en Cinzel 700 30 à la couleur de la note avec text-shadow: 0 0 24px #52d6d155. Une ligne ▲ V · appui · couloir 3, une ligne tension 0.15 · velocite 90.
  • droite JOURNAL : bordure #7cf59a, lignes dt/dd JetBrains Mono 10, safePct/explorePct (de styleScores.computeStyleScores), paquet le plus rempli (fullestChroma/fullestCount), total recueilli.

Toutes les données viennent de runtime.lastNote / runtime.journal. Aucune métrique inventée : safePct est null tant que styleSampleSize === 0, et le panneau affiche — dans ce cas.

Le calcul du couloir passe par laneIndexForMidi(midi, tonic, mask) — la même fonction que DegreeCorridors et SuggestionStrip. Si FG change la gamme, la mention « couloir N » suit, ou « hors gamme » si la note n’est plus dans le mask.

Ce qui n’est pas fait

ÉlémentStatut
Climbers DOM (pastilles qui montent, handoff §4)hors essai 2a — runtime prêt, rendu visuel à faire si FG le demande
Pulse rjFill à l’arrivée (handoff §2 « pochette pulse une fois »)non animé — la transition height .35s suffit pour la causalité
Proposition rewrite axes 3Dhors essai (canon profondeur = temps)
Gate visuelle FG 1280×800recommandée avant de déclarer T4/T5 verts
Migration des layouts persistés rainbowjourney.panel.*non demandée

Contôles réellement exécutés

  • node node_modules/typescript/bin/tsc -b --force : OK.
  • npm run lint : OK.
  • npx vitest run : 215/215 (30 fichiers ; +8 tests riverGeometry, +1 test hudDefaults).
  • npm run build : OK (CSS 99 kB, JS 1.08 MB gzip 301 kB ; avertissements Zod et bundle volumineux préexistants).
  • Serveur npx vite --port 5183 --strictPort : HTTP 200, Journey.tsx et ses nouveaux imports servis sans erreur.

Pas de Playwright dans cette passe (E2E complet = zone outillage, hors essai). Smoke DOM 1280×800 reste à faire par FG.

Pourquoi publier maintenant

Parce que la peau du jeu changeait sans qu’aucun billet public ne le raconte. Trois sections entières du design 2a, c’est un visage différent — pas une variation cosmétique. Le runtime porte la causalité, la géométrie est partagée, les panneaux latéraux sortent le joueur des panneaux flottants persistés pour entrer dans la composition 2a.

La branche essai/ui-ingame-2a reste en working tree après ce billet. Aucun commit n’a été poussé vers main ni vers le remote sans l’ordre explicite FG. La prochaine étape est la gate visuelle 1280×800 : c’est FG qui décide si le rendu matche, si la rivière SVG est trop chargée, si les paquets respirent au bon tempo.


Sources : docs/plans/essai-ui-ingame-2a/PLAN-ORCH-DESIGN-2a-COMPLET.md, handoffs/D-POUCH.md, handoffs/D-RIVER.md, handoffs/D-PANELS.md, handoffs/D-MOUNT.md, handoffs/D-VERIFY.md, handoffs/2026-09-15-design-2a-complet-handoff.md. Branche : essai/ui-ingame-2a. Miroir local : RainbowPiano/docs/devblog/.

source markdown

Concepts de cet article

handoffgatePulse

Partage le plus de concepts avec