Trois demandes distinctes de l'utilisateur, regroupées car elles touchent
toutes à la navigation d'un jeu :
1. Fil d'Ariane retiré (devenu redondant avec la barre de navigation du
jeu ajoutée au commit précédent) : bloc breadcrumb_wrap retiré de
base.html, et son override ({% block breadcrumb %}) retiré des 9
templates qui le définissaient encore. .breadcrumbBar (CSS) retiré,
y compris des règles flex:0 0 auto de body.objectEditBody/builderBody.
2. Liens de .gameNavBar centrés (justify-content:center).
3. "Jouer" déplacé en dernier lien (c'est une action à part — ouvre
l'aperçu jouable dans un nouvel onglet — pas un éditeur de plus comme
les 4 autres).
4. game_dashboard.html devient un vrai tableau de bord : une grille de
cartes (Écrans/Objets/Éléments de jeu/Variables), chacune listant les
entrées existantes avec un accès direct (clic = éditeur concerné) et
un lien "Gérer" vers la page dédiée pour créer/réorganiser. Remplace
l'ancien panneau "Créer" (redondant avec la barre de navigation
persistante) et la simple table "Objets définis". routes/games/
game_dashboard.py alimente maintenant aussi screen_list, element_types
(+ usage) et variables, en réutilisant list_screens/
list_element_types/element_type_usage_count/list_global_variables déjà
utilisés ailleurs.
Vérifié en rendant toutes les pages concernées via le client de test
Flask : barre de nav présente partout où un `game` est dans le contexte
(absente sur l'accueil), fil d'Ariane absent partout, ordre des liens
avec "Jouer" en dernier.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Jusqu'ici, les liens vers les différents éditeurs d'un jeu (Écrans,
Éléments de jeu, Variables, Jouer, Objet) n'existaient que dans le
panneau "Créer" du tableau de bord (game_dashboard.html) — changer
d'éditeur obligeait à revenir sur cette page à chaque fois.
base.html expose désormais une 2e barre (.gameNavBar), sous la barre
"Forge Engine" et au-dessus du fil d'Ariane, reprenant ces mêmes 5
liens — visible sur TOUTE page qui met un `game` dans le contexte du
template (déjà fait par chaque route pour le fil d'Ariane, donc aucun
changement de route nécessaire), absente sur l'accueil (liste des jeux,
pas de jeu courant). L'onglet correspondant à la section actuelle est
mis en évidence via un simple préfixe sur request.path.
static/style.css : nouvelle barre en ligne (contrairement à .navList/
.navRow, empilés verticalement dans le panneau "Créer" du tableau de
bord, réutilisés tels quels ailleurs). Ajoutée aux règles flex:0 0 auto
de body.objectEditBody/body.builderBody (mise en page plein-écran des
éditeurs) aux côtés de .topbar/.breadcrumbBar, sans quoi elle aurait
cassé la répartition de hauteur figée de ces pages.
Vérifié en rendant plusieurs routes via le client de test Flask : barre
absente sur l'accueil, présente partout ailleurs (tableau de bord, liste
des écrans, éditeur d'écran normal ET d'écran-modèle, éléments de jeu,
variables, nouvel objet).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le calcul JS "object-fit:contain" du tour précédent gardait l'aspect-ratio
(Portrait/Paysage/Carré) au prix de marges vides sur les côtés dès que la
fenêtre n'avait pas exactement ce ratio - "prendre toute la place
disponible" et "garder l'aspect-ratio" sont deux exigences contradictoires
dans ce cas, et c'est la première qui doit l'emporter dans l'éditeur.
Le canevas (#canvas) remplit donc maintenant .canvasFrame à 100% x 100%,
sans plus tenir compte de l'aspect-ratio choisi dans l'éditeur - ce
réglage continue de s'appliquer normalement à l'aperçu jouable ("Jouer",
voir screen_set_aspect.py/play.html), qui reste la référence pour le
rendu final. Padding de .canvasFrame réduit au minimum. fitCanvasToFrame()
et son calcul en pixels n'ont plus lieu d'être - retirés entièrement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le calcul purement CSS du tour précédent (aspect-ratio + height:100% +
max-width:100%, pour que le canevas garde ses proportions tout en tenant
dans la zone visible) laissait en pratique le canevas bien plus petit que
l'espace réellement disponible - le calcul de taille "auto" d'un élément
non remplacé dans ce contexte flex n'est pas fiable.
Remplacé par un calcul en JavaScript (fitCanvasToFrame()) : mesure la
taille réelle de .canvasFrame et calcule la plus grande taille en pixels
qui tient à la fois en largeur ET en hauteur pour l'aspect-ratio courant
(l'équivalent d'un "object-fit:contain"), posée directement en style
inline sur #canvas. Recalculé à l'ouverture de l'écran, au changement de
format (Portrait/Paysage/Carré), au redimensionnement de la fenêtre, et à
chaque rafraîchissement du panneau (#canvas étant recréé à chaque
sélection d'élément, sa taille calculée était perdue à chaque fois).
Sous 1300px (mise en page empilée), le style inline est explicitement
effacé pour laisser la règle CSS de repli (pleine largeur, page qui
défile normalement) reprendre la main.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Le fil d'Ariane disparaît entièrement sur cette page (breadcrumb_wrap vide)
- le nom de l'écran est de toute façon déjà éditable juste au-dessus, dans
le panneau "Éléments" (voir le tour précédent) - et .content-builder perd
son padding par défaut hérité de .content, pour que le canevas commence le
plus haut possible.
Change aussi la façon dont le canevas est dimensionné : il était
jusqu'ici contraint par la LARGEUR (width:100%), ce qui pouvait le rendre
bien plus haut que la fenêtre pour un format Portrait - obligeant à
défiler .builderCanvasArea (toolbar/onglets compris) pour voir le bas de
l'écran. Il est maintenant contraint par la HAUTEUR disponible
(height:100%, la largeur se déduisant de l'aspect-ratio), avec
max-width:100% en secours si c'est la largeur qui manque en premier -
l'écran entier reste donc visible sans défiler. Le défilement, s'il reste
nécessaire (fenêtre très basse), se fait maintenant sur .canvasFrame
lui-même, jamais sur .builderCanvasArea (repassé à overflow:hidden) : la
barre d'onglets et la barre d'outils restent toujours fixes en haut.
Ajoute le pendant pour le repli en page empilée (< 1300px, une seule
colonne) : le "letterboxing" par hauteur suppose une chaîne de hauteurs
définies qui n'existe plus une fois empilé - revient alors à un
dimensionnement par largeur, cohérent avec une page qui défile normalement.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Remplace l'ancien panneau du bas rétractable/redimensionnable à la souris
(Logique de la scène, Timeline d'animation) par un système d'onglets au
centre de l'éditeur - Écran / Logique de la scène / Timeline d'animation
- un seul visible à la fois, occupant systématiquement tout l'espace
disponible (switchBuilderTab() dans screen_edit.html). Changer d'onglet
équivaut à "fermer" celui qu'on quitte ; plus besoin d'une poignée de
redimensionnement séparée puisque l'onglet actif prend déjà toute la
place. Supprime au passage tout l'ancien mécanisme (toggleFlowPanel/
toggleAnimPanel, poignées flowResizeHandle/animResizeHandle, classe CSS
.flowPanel) devenu inutile.
Déplace aussi le renommage de l'écran, le bouton "Jouer depuis le début"
et le choix du format d'aperçu (Portrait/Paysage/Carré) - jusqu'ici
au-dessus du canevas - dans le panneau flottant "Éléments" (celui qui
porte déjà ce nom, à gauche) : des réglages qu'on touche rarement une
fois l'écran en construction, qui n'ont plus besoin de rester en
permanence visibles au-dessus de la zone de travail.
Corrige au passage un bug découvert pendant ce tour : sur la page "Nouvel
objet" (2 colonnes), le bouton "+ Ajouter un champ" avait disparu -
placé APRÈS la zone de liste à défilement (flex-grow:1) dans la colonne
de droite, un flex-grow imprévisible dans ce contexte le poussait hors de
vue. Déplacé avant la liste (statique, toujours visible), pattern déjà
éprouvé ailleurs sur cette même page.
Deux tests mis à jour pour refléter intentionnellement la nouvelle
structure : la présence de "animTabPanel" (au lieu de l'ancien
"animPanel"), et un marqueur plus précis pour distinguer le bloc de
propriétés "Onglets" d'une simple occurrence du même texte dans une liste
déroulante de la Logique de la scène (qui apparaît désormais plus tôt
dans le document, cet éditeur de flow étant maintenant un onglet du
centre plutôt qu'un panneau tout en bas de page).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Généralise le principe déjà utilisé pour le tableau de bord et l'édition
d'objet à toutes les pages restantes : liste des jeux, écrans, éléments de
jeu, variables, nouvel objet, formulaire de données, tableau des données
d'un objet - colonne de gauche pour créer/agir, colonne de droite pour ce
qui existe déjà, chaque colonne défilant pour son propre compte.
Pour un formulaire qui doit rester UN SEUL <form> à cheval sur les deux
colonnes (nouvel objet : nom à gauche, champs à droite ; formulaire de
données : bouton Enregistrer à gauche, champs à droite), nouvelle classe
.formPassthrough ("display:contents") : le <form> ne devient pas lui-même
une boîte dans la mise en page flex, seul .twoCol à l'intérieur compte.
Supprime au passage .content-page/.formScroll/body.pageBody (le gabarit à
une colonne introduit au tour précédent, plus utilisé nulle part) et
.list/.listRow/.listRowFlex/.rowTitle/.rowSub/.rowActions/.addBtn (les
cartes à deux lignes remplacées par les tableaux denses et la nav
compacte) - du CSS mort plutôt que deux systèmes qui se chevauchent.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Généralise le principe déjà en place pour l'édition d'objet (la page
n'occupe jamais plus que la hauteur de la fenêtre, seules ses zones
internes défilent) à toutes les pages restantes : liste des jeux, tableau
de bord d'un jeu, écrans, éléments de jeu, variables, nouvel objet,
formulaire de données, tableau des données d'un objet. Seuls l'éditeur
d'écran/d'élément et l'aperçu jouable restent en dehors (déjà exclus par
leur propre gabarit plein écran, ou pas concernés).
Nouveau gabarit générique à une colonne (body.pageBody + .content-page +
.scrollArea) sur le même principe que .content-objectEdit, plus une
variante .formScroll pour un formulaire long à défilement interne (liste
de champs d'un nouvel objet, formulaire de données) tout en gardant les
boutons d'action toujours visibles. Les listes/tableaux remplacés par le
format dense .fieldsTable (déjà utilisé pour le tableau de bord) pour
rester cohérent et afficher plus de lignes à l'écran.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Réutilise le système déjà en place pour l'édition d'objet (body.objectEditBody
+ .content-objectEdit) : la page occupe exactement la hauteur de la fenêtre,
seules les deux colonnes défilent chacune de leur côté si besoin - plus la
page elle-même, qui ne doit jamais défiler.
Colonne de gauche ("Créer") : nouvelle nav à une ligne (.navRow/.navList,
icône + libellé, sous-titre en info-bulle) plutôt que des cartes à deux
lignes - plus dense, plus sobre.
Colonne de droite ("Objets définis") : remplace les cartes par le tableau
compact à en-tête collant déjà utilisé pour les champs d'un objet
(.fieldsTable) - format nettement plus adapté à beaucoup de lignes.
Réduit aussi le padding par défaut de .listRow/.dangerZone (encore utilisés
par la page Variables), dans le même esprit.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Redundant since dragging an element onto a container row in the tree now does the same thing (see move_element_to_container.py). The element_add_child route itself stays (still used by tests and available as an API), only the UI entry point is gone. Removed the now-dead .containerContentGroup CSS along with it.
- The "icone" widget no longer appears as a generic tile in "Ajouter un élément sur l'écran" / "Ajouter DANS ce conteneur" — the icon gallery (now living under "Ajouter un élément sur l'écran" in the left panel instead of the right one) is the only way to add one, always pre-set to the icon you picked.
- The element tree now supports dragging an element onto a container row to move it inside (last child), from anywhere in the tree — not just reordering within the same parent. Hovering a container row splits it into before/after/inside zones (thirds) when dragging a sibling, or "inside only" when dragging from elsewhere in the tree. New move_element_to_container() rejects non-container targets and cycles (dropping a container into itself or one of its own descendants) silently, mirroring reorder_element's existing safety pattern.
Verified end to end: gallery renders in the left panel with no icone tile in the widget grids, and the move endpoint correctly reparents, rejects a cycle, and rejects a non-container target. Full suite green (89).
Self-hosting Font Awesome's font files (previous commit) didn't fix it either: every icon in the picker still failed identically. That rules out CDN/network blocking specifically and points to something in the browser blocking custom webfont loading altogether regardless of origin (common with strict anti-fingerprinting protection, e.g. Brave's font blocking or certain privacy extensions — @font-face loading is a well-known fingerprinting vector).
Replaced the whole mechanism: each of the 258 curated icons is now a real downloaded SVG file (static/icons/*.svg, sourced from Font Awesome 5 Free's official SVG package) displayed via CSS mask-image (.icon-svg in style.css) instead of a font glyph. A masked SVG isn't a font resource at all, so it isn't subject to webfont-blocking — and it still colors via the existing "color" style property (background-color:currentColor) and sizes via font-size (1em), so no change to how the widget's other controls work.
The "Icône" widget now stores just the icon's slug (e.g. "trophy") in a dedicated attribute instead of a full CSS class string, rendered by a new special_render (render_icone.py). Updated the add-gallery and the properties-panel picker modal to use the same mask technique for their previews, and element_add.py to validate the slug against the curated list. Removed the now-unused self-hosted Font Awesome CSS/webfont files and the <link> tags — no font dependency left for icons at all.
Verified end to end (gallery renders with real SVG previews, simulated add creates a correctly-attributed element, the SVG file is actually served, the per-element picker reflects the current icon). Full suite green (89).
Switching CDN provider (cdnjs -> jsdelivr) didn't fix icons rendering as fallback glyph-code text — every icon in the picker still failed the same way, which points to something in the user's browser/network blocking cross-origin webfont loading specifically (common with strict tracking/fingerprinting protection, e.g. Brave's font-blocking or an ad-blocker's remote-font rule), not the CDN itself.
Downloaded Font Awesome 5.15.4's CSS and the solid-weight webfont files (the only style this app's icon classes actually use) into static/fontawesome/, served same-origin like style.css and pjax.js already are. Removes the CDN dependency entirely for icons rather than betting on a second provider.
New "Icône" widget (<i class="...">, color/size customizable exactly like any other widget) plus a curated set of ~250 verified Font Awesome 5 Free solid icon names (fontawesome_icons.py) — not the full ~1500-icon catalog, since an embedded list needs to be guaranteed accurate (a wrong class name silently renders as a blank glyph); any other valid FA5 class still works by typing it directly into the widget's "Icône" setting.
The gallery lives in the (now always-visible) top of the right floating panel, searchable, with each tile both clickable and HTML5-draggable onto the canvas — either action posts to element_add with the chosen icon_class, which now seeds the new element's class instead of leaving it on the generic default. Available in the element-type template editor too, since it reuses screen_edit.html.
Loads Font Awesome 5.15.4 (cdnjs) alongside the existing animate.css/Bulma links, in both the editor and /play.
Verified end to end: gallery renders with real icon glyphs, clicking/simulated-drop creates a correctly-classed <i> element, and the actual /play route renders it with Font Awesome loaded. Full suite green (89).
Generalized the floating-panel mechanism from the properties panel (props/propsPanelFloat/propsPanelReopenBtn) into reusable functions keyed by a short id, and applied it to the left "Éléments de cet écran" panel (key "left", panelLeftFloat, panelLeftReopenBtn). Both panels can now be closed independently, letting the canvas take the screen's full width for the most faithful possible preview against the actual /play rendering. Shared CSS (.floatPanel/.floatPanelHeader/.floatPanelBody/.floatPanelReopenBtn with --left/--right position variants) replaces the props-panel-only rules from the previous commit.
Fixed the two querySelector('.builderPanel .elementTree') call sites (used to patch the tree preview into the DOM after an unrelated canvas-only refresh) that would have silently stopped finding the tree once its wrapper's class changed.
The right-hand properties panel was a fixed 320px flex column, permanently shrinking the canvas — on a screen with real content (tabs, gauges), this made the editor's preview visibly narrower than the actual /play rendering, to the point of wrapping tab labels differently. It's now position:fixed with a drag handle header, moved out of .builder3's flex flow so .builderCanvasArea automatically reclaims the space; closing it (✕, replaced by a small "⚙️ Propriétés" reopen button) frees the full width for a more faithful preview. Position and open/closed state persist per screen in localStorage and get reapplied after every selection change (which replaces #builder3 wholesale). Falls back to a normal stacked column below 1300px width, where a floating panel wouldn't fit usefully.
- Jauge now renders a real <progress class="progress"> instead of a hand-built pair of absolutely-positioned divs. The bas/haut color interpolation still works, set via Bulma's own --bulma-progress-value-background-color CSS custom property rather than fighting the class.
- Onglets' tab strip is now genuine Bulma tabs markup (tabs > ul > li, is-active on the li) instead of custom forgeTabBar/forgeTabBtn classes; forgeShowTab (duplicated in screen_edit.html and play.html) now toggles is-active to match.
- Champ texte/email/mot de passe get class="input", Zone de texte gets class="textarea", Case à cocher/Bouton radio's existing <label> wrapper gets class="checkbox"/"radio" (Bulma's own convention — the structure already matched, just needed the class), Liste déroulante is wrapped in Bulma's required <div class="select"> (a bare class on the <select> itself has zero effect in Bulma), Tableau gets class="table is-bordered is-fullwidth" with the per-cell inline borders removed so Bulma's own table styling applies.
- Removed the now-dead .forgeTabBar/.forgeTabBtn CSS.
Updated tests/test_jauge.py and tests/test_onglets_widget.py assertions to match the new markup (value="X" attribute instead of width:X% inline style, --bulma-progress-value-background-color instead of background-color, forgeShowTab(this) marker instead of the removed forgeTabBtn class) — same behavior, different rendering mechanism. Full suite green (89 passed), verified end to end against the real "test" project via the actual /play route.
The element header/rename/id row, tabs list, position & size grid, and — most importantly — the generic control-rendering loop (used by every widget's properties panel: text, color, slider, checkbox, align, select...) now emit real field/control/label/select/checkbox/buttons-has-addons Bulma markup instead of the old custom controlRow/sliderRow/alignGroup/posGrid CSS. Removed the now-redundant custom CSS those classes used to carry.
Verified structurally safe: bindPropsAutosave/submitPropsForm already used querySelectorAll/FormData (structure-agnostic), so wrapping inputs in field/control divs doesn't affect autosave; the one sibling-dependent bit (slider oninput reading nextElementSibling) was kept adjacent inside its wrapper. Confirmed end-to-end with a live save round-trip through the actual route (Bulma modifier + free-style values both persist correctly) and every widget type's panel still renders 200 OK.
The flow-graph node editor (bottom panel) is not converted yet — part of its markup is built dynamically in JS (renderFlowConditionClauses), so reskinning it means updating the JS templates in lockstep with the HTML, which is more work/risk than this pass; noting it as the next piece.
Loads Bulma 1.0.2 in base.html (data-theme="dark" for its native dark palette) for every page that extends it, plus play.html directly. Converts every admin template — index, game dashboard, screens list, element types, object new/edit, data form/list — to real Bulma markup: navbar, breadcrumb component, box/card, field/control/input/select, table, notification, buttons, and a native Bulma modal for the data-row detail popup. Forge's own style.css keeps only what Bulma doesn't cover (the fixed-viewport builder/object-edit layouts, the element tree, canvas, flow-graph editor) and now acts as a secondary/override layer rather than a competing design system, matching how per-element inline customization already overrides widget defaults.
screen_edit.html (the 3-panel screen builder) gets the same navbar/breadcrumb/button treatment plus its top rename form, but its flow-graph node editor, canvas and element tree keep their existing custom styling — several of their inputs have JS relying on exact DOM sibling structure (e.g. slider oninput reading nextElementSibling) or Bulma's own select/wrapper requirement, and reskinning them without a browser to verify against risked silently breaking the app's most complex feature. Buttons and headings there are still fully converted (safe, purely additive class changes).
Verified: full test suite green, every route smoke-rendered 200 OK via the test client after the change.
Cells now clip with an ellipsis instead of wrapping and inflating row height. A new eye button before the pencil opens a popup showing every field of that row in full, label above value, for comfortable reading.
Replaces the stacked per-field cards with a compact table (one row per field) for a clearer overview when an object has many fields. The page itself no longer scrolls — only the table body and the left column do, mirroring the screen editor's fixed-viewport layout. Each row stays independently editable/deletable via hidden forms targeted with the HTML form= attribute.
object_form.js declared top-level const bindings, which pjax replays verbatim on every visit — the second visit threw "already declared" and silently broke "Ajouter un champ"/"Créer l'objet". Wrapped it in an IIFE. Also renamed the generic name="name" rename-game field to name="game_name" with autocomplete off, since browsers were autofilling it with unrelated previously-typed values.
New widget where each tab is a real "conteneur" element posed as a child
(see screens/elements/add_tab.py) — this reuses everything that already
exists for a normal container (adding a Répéteur/Conteneur/etc. inside via
"Ajouter DANS ce conteneur", renaming to change the tab's visible label,
deleting via the standard trash icon) instead of inventing a separate
storage format for tabs.
The widget's own properties panel gets a dedicated "Onglets" section to
add a tab, rename one, jump to its content, or delete it. Rendering
(render_onglets.py) builds a tab bar + one panel per tab, switched
client-side (forgeShowTab, in both screen_edit.html and play.html) with
only one panel visible at a time.
Distinct from the existing "activer_onglet" flow action (2.3, manual
show-one/hide-siblings) — that stays available for custom show/hide
wiring; this widget is the turnkey version with tab management built in.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The positive outline-offset from the previous fix pushed the highlight
outside the element's own box — which gets clipped by the first
overflow:hidden ancestor (the canvas itself, or a rounded-corner
container) on whichever sides touch that ancestor's edge. A row of
gauges flush against the top/left of its container showed only a
stray line on the untouched side instead of a full frame.
Switch to an inset box-shadow: it always stays inside the element's own
box, so it can never be clipped by an ancestor, and it naturally follows
the element's own border-radius.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A compact element like a Jauge (name label + thin bar, no padding) got
an outline pulled 1px INSIDE its box (outline-offset:-1px), which hugged
the content so tightly it looked like it was slicing through the bar
instead of framing it. Push the outline outward instead (+3px) with a
soft glow, so the selection frame reads clearly regardless of how
tightly the underlying element is sized.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A top-level element already got a selection border via its
.canvasElement wrapper, but a child posed inside a container/répéteur/
groupe de champs has no such separate frame, so selecting it from the
tree gave no visual feedback on the canvas at all.
applySelectionHighlight() now targets the element's own tag directly via
data-element-id (present on every rendered element, nested or not) and
outlines it, re-run after every canvas refresh path (initial load, full
pjax navigation, partial panel refresh, and the autosave-only canvas
refresh).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "Éléments de cet écran" tree now sits above "Ajouter un élément sur
l'écran" / "Mes éléments de jeu" so it's the first thing visible in the
left panel.
Each container row gets a disclosure triangle that collapses/expands its
children, independent from the row's link (which still selects the
element and shows its properties whether collapsed or expanded). The
collapsed state is remembered per element in localStorage.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "Éléments de cet écran" tree now renders every level of nesting
(previously stopped after one level of children) as a compact single-line
list, and right-clicking a row opens a context menu to duplicate the
element (and its full subtree) in place, in its current container.
Also: all property panels start collapsed instead of some being open by
default, the redundant nested element list inside "Ajouter DANS ce
conteneur" is removed (it only needs the widget picker), and the
now-unneeded "a container is selected" warning banner is gone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Merge "Taille dans le conteneur" into "Position & taille": a child
element (posé dans un conteneur/répéteur/groupe de champs) now shows
greyed-out X/Y (not applicable, it follows its parent's layout) and
a single Largeur/Hauteur field per axis with a unit selector (%/px)
instead of the old fixed-px-only sliders capped at 1000 — which was
the root cause of a landscape-mode bug where a child couldn't be
made wider than 1000px even though the actual screen was wider.
New c_size control type (one style key per axis, not two competing
sliders — an earlier px+% two-slider attempt let the untouched
slider silently clobber the other's value on every autosave).
- Merge the two "Disposition" groups (visibility + scale, previously
split apart in UNIVERSAL_CONTROLS by unrelated groups) into one.
- Remove the "Survol" panel: hovering is conceptually a flow trigger,
not a static element property — to be reintroduced there. The
underlying data-hover-text/bindHoverTexts runtime is untouched.
main.content (.content-builder) inherited "margin:0 auto" from the
base .content class. On this page body is a column flex container, so
that auto margin disabled cross-axis stretch and made the element
shrink-to-fit instead — sized via an algorithm that runs back down
through the flex/%/aspect-ratio chain to the canvas, and shifts
slightly whenever sibling panel content changes (e.g. selecting an
element). Give it an explicit width:100% so max-width/auto-margin
centering still work but sizing is stable and fills the available
space. Also add min-width:0 to .builderPanelRight, matching its
siblings, as defense against the same class of bug.
Verified with Playwright: canvas box was previously not filling
available width (918px expected vs 379px actual at a 1920px viewport)
and shifted between steps; both are fixed.