From 1051a72b522063a4bb50401d1fe55cbbb026913b Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Sat, 3 Oct 2026 00:15:41 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20flashs=20de=20navigation=20=E2=80=94=20l?= =?UTF-8?q?a=20zone=20swap=C3=A9e=20n'est=20plus=20peint=20non=20mont?= =?UTF-8?q?=C3=A9e=20(v7.45.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Symptôme : à chaque changement de page/section, une rafale de fenêtres / menus / états s'affiche une fraction de seconde avant la page voulue. - Cause : pendant la fenêtre x-ignore (swap fdLoad → Alpine.initTree, mise en place en v7.45.2), .main-wrapper était peint NON montée : tous ses [x-show] visibles à leur valeur brute, puis masqués d'un coup au montage. Mesure (e2e/probe_nav_perf.spec.js sur l'instance locale) : 19 éléments bruts visibles 87 ms sur /library (CLS 0.149, sources lib-loading/lib-empty/ lib-table), 80 éléments pendant 732 ms sur /settings, 7 sur /workspaces. - Fix (app.js, handler htmx:afterSwap) : opacity:0 + pointer-events:none posés dans la même task que le swap (aucun paint intermédiaire possible), retirés APRÈS Alpine.initTree sur les 3 chemins de démontage (scripts chargés, zéro script, filet 4 s). reveal() est appelé avant les early-returns de done() → impossible de rester bloqué invisible. - Après : 0 frame de contenu brut peint sur les 4 navigations du probe, 1–2 frames masquées, zone révélée (opacity:1, pointer-events:auto) et montée (0 [x-show] non montés, x-ignore absent) à chaque fois. - Gate : e2e/probe_nav_perf.spec.js — marques htmx, fenêtre x-ignore, échantillon par frame des [x-show] bruts peints, layout-shift + assertion (0 brut / zone révélée et montée), exécutable en CI. - Reste (non traité, voir ROADMAP) : CLS résiduel 0.057 sur /library (états loading → table), fenêtre x-ignore de ~250 ms sur /settings (coût des scripts de page), topbar incluse dans .main-wrapper re-swappée à chaque nav. Gates : probe_nav_perf + regression_partial_nav + regression_editor_mount + regression_logout_dnd + smoke · pytest 1094 passed · ruff OK · OpenAPI 7.45.3. --- CHANGELOG.md | 26 +++++++ ROADMAP.md | 1 + VERSION | 2 +- WORKLOAD.md | 2 +- app/main.py | 2 +- docs/openapi-v2.json | 2 +- e2e/probe_nav_perf.spec.js | 146 +++++++++++++++++++++++++++++++++++++ static/js/app.js | 14 ++++ 8 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 e2e/probe_nav_perf.spec.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 51cb71f..688a720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,31 @@ # Changelog - FlowDeck +## v7.45.3 (2026-10-03) — Navigation partielle : le contenu brut n'est plus peint + +### Fixed + +- **Flashs de fenêtres / frames à chaque changement de page ou de section** : + pendant la fenêtre `x-ignore` (swap → `Alpine.initTree`), la zone + `.main-wrapper` était peinte **non montée** — tous ses `[x-show]` visibles à + leur valeur brute (menus, panneaux, états loading/empty/table superposés), puis + masqués d'un coup au montage Alpine. Mesure (`e2e/probe_nav_perf.spec.js`, + instance locale) : **19 éléments bruts visibles sur 87 ms** en arrivant sur + /library (CLS 0.149, sources `lib-loading`/`lib-empty`/`lib-table`), **80 + éléments sur 732 ms** sur /settings, 7 sur /workspaces, 0 sur /trash. Fix : + `opacity: 0` + `pointer-events: none` posés **dans** le handler + `htmx:afterSwap` (même task que le swap → aucun paint intermédiaire possible) + et retirés **après** `Alpine.initTree` sur les 3 chemins de démontage (scripts + chargés, zéro script, filet 4 s). Seul le contenu monté est peint. + +### Added + +- **`e2e/probe_nav_perf.spec.js`** — probe de fluidité de navigation : marques + htmx (`beforeRequest`/`beforeSwap`/`afterSwap`/`afterSettle`), fenêtre + `x-ignore`, échantillon par frame du nombre d'`[x-show]` bruts encore + visibles et d'`[x-cloak]` masqués, `layout-shift` (CLS) avec sources. + `node node_modules/@playwright/test/cli.js test probe_nav_perf` (npx est cassé + sur ce poste). + ## v7.45.2 (2026-10-03) — Navigation partielle : montage Alpine sans course + scripts idempotents ### Fixed diff --git a/ROADMAP.md b/ROADMAP.md index 78161c3..4f6bb11 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1004,6 +1004,7 @@ Détails livrés : - [x] **Éditeur visuel** — **pipeline steps** dans Settings → Automations (v7.45.0) : cartes ordonnées (trigger/condition/delay/action) avec **config typée par type** (8 actions : webhook/set_property/create_page/notify/slack/email/forge_issue/agent_trigger + ops condition + datalist événements), ajout/édition/suppression/**↑↓** via l'API `/steps` + bouton **✨ Convertir le JSON en pipeline** (legacy → steps ordonnés). **Bonus trouvé par le gate** : **51 expressions multi-instructions** (`a=1; b()` — `;` = SEULE expression par directive interdit sous le parseur CSP, non couvert par le scan `tokens`) → converties en méthodes dans **11 fichiers** (nav settings ×8, menu section base ×7, partages/éditeur ×13, breadcrumb ×5, lib/local ×6, board ×3, ctx/agent/workspaces/card/gitea ×6). Nouveau scanner `scan_semi` ajouté au lot. - [x] **Fix SW + CSRF uploads** (v7.45.1) — logout ne sert plus la page « hors ligne » (le fetch event de navigation est en `redirect:'manual'` → 302 lue `opaqueredirect` → rejet ; rejoué en `redirect:'follow'` + **302 synthétique vers l'URL finale**, Chromium refusant les responses `redirected` servies à une navigation) et drag & drop de fichiers/dossiers réparé (`_doUpload` + `toggleFavorite` sans `X-CSRF-Token` = oublis A19, derniers appels mutants du front). `timeoutFetch` annule enfin ses requêtes. Gate `e2e/regression_logout_dnd.spec.js` (2 tests verts). - [x] **Navigation partielle Alpine** (v7.45.2) — les swaps fdLoad initialisaient `.main-wrapper` avant l'exécution des scripts de page (courses : « Undefined variable » sur l'éditeur, `reading 'has'` sur `$store.fdCtx`, `SyntaxError: 'LW' already declared` à la 2ᵉ visite). Fix maison dans `app.js` : `x-ignore` sur la zone swapée (nœud remplacé uniquement) → démontage unique à tous `