Commit Graph
71 Commits
Author SHA1 Message Date
william 6d80ed6958 Switch icons from a webfont to self-hosted SVG masks — the webfont was the actual problem
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).
2026-08-24 20:11:49 +02:00
william 15cad6c50f Self-host Font Awesome instead of loading it from a CDN
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.
2026-08-24 20:01:39 +02:00
william cc0442ef5f Add a Font Awesome icon gallery to the properties panel, draggable onto the screen
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).
2026-08-24 19:10:25 +02:00
william 850242d1d4 Make the left element-tree panel floating/draggable/closable too, same as the right one
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.
2026-08-24 15:33:47 +02:00
william 03ca2723c5 Make the screen editor's properties panel floating, draggable, and closable
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.
2026-08-24 15:12:50 +02:00
william bb01e9fdf1 Make Jauge, Onglets, and every form-field widget real native Bulma elements
- 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.
2026-08-24 14:31:55 +02:00
william 8da5715e5b Make the screen editor's properties panel use native Bulma elements, not just Bulma-styled buttons
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.
2026-08-24 13:30:17 +02:00
william 25a331830b Reskin the whole admin UI (not just game widgets) with Bulma
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.
2026-08-24 13:19:02 +02:00
william 26cf245ffe Truncate long cells in the data table and add a row-detail popup
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.
2026-08-24 11:33:29 +02:00
william d8a80b8a8a Show existing fields as a scrollable table on the object-edit page
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.
2026-08-24 10:28:03 +02:00
william 2bb0c253f9 Fix object-form breaking on repeat pjax visits, and stray autofill in game rename field
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.
2026-08-24 09:21:19 +02:00
william 4b05301e2e Add drag-and-drop reordering of sibling elements in the tree panel
Elements can now be reordered within the same container by dragging a row above or below another in the left-hand element tree.
2026-08-24 08:43:36 +02:00
williamandClaude Sonnet 5 17fa4cf087 Add an Onglets (Tabs) widget
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>
2026-08-23 21:42:39 +02:00
williamandClaude Sonnet 5 2b2619d1c4 Fix selection highlight getting clipped when flush against a container edge
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>
2026-08-23 20:53:48 +02:00
williamandClaude Sonnet 5 3228460f1c Give the selected-element canvas outline breathing room
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>
2026-08-23 20:34:48 +02:00
williamandClaude Sonnet 5 d8ab71ddeb Highlight the selected element on the canvas even when it's nested
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>
2026-08-23 20:25:05 +02:00
williamandClaude Sonnet 5 386e93d9c6 Move element tree above the add-element menus, make containers collapsible
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>
2026-08-23 20:17:21 +02:00
williamandClaude Sonnet 5 bb79f2f93d Redesign the screen editor's element tree and add duplication
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>
2026-08-23 19:53:38 +02:00
william 4a593d07e4 Consolidate the properties panel: Position & taille, no more Taille/Survol/duplicate Disposition
- 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.
2026-08-23 12:14:00 +02:00
william d86b1a60c7 Fix builder canvas resizing on element selection
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.
2026-08-23 11:00:09 +02:00
williamandwilliam 3f4ebc4527 first commit
Build and deploy / deploy (push) Successful in 10s
Build and deploy / build-and-push (push) Successful in 17s
2026-08-21 16:23:49 +02:00