fix: sélection multi-blocs fonctionnelle (Ctrl+C/X, drag souris, clic droit) + identité visuelle (v7.69.0)
FlowDeck CI / lint (push) Successful in 1m34s
FlowDeck CI / test (push) Failing after 17m31s
FlowDeck CI / docker (push) Skipped

Trois bugs d'éditeur, chacun avec sa cause racine prouvée par trace
d'événements — le mécanisme Clipboard livré en v7.68 était inutilisable.

1) Ctrl+C / Ctrl+X multi-blocs ne faisaient RIEN.
   Cause : le navigateur ne sait pas sérialiser une sélection qui traverse
   deux `contenteditable` (chaque bloc en a un). `getSelection().toString()`
   renvoie `""` et Chromium n'émet AUCUN événement `copy` — les handlers
   `ct.addEventListener('copy')` n'étaient donc jamais atteints.
   → le raccourci est intercepté au keydown (capture sur document, garde
   `closest('#_blocksCt')` pour ne pas voler Ctrl+C ailleurs) et le markdown
   est écrit dans le presse-papier par nos soins. Sélection mono-bloc =
   comportement natif inchangé.

2) Glisser la souris d'un bloc à l'autre ne sélectionnait qu'un bloc.
   Cause : `mouseup` (on reconstruit la plage) → `click` → `focusin` → le
   navigateur efface la sélection. La plage était reconstruite puis détruite
   dans la foulée.
   → la plage reconstruite est mise en attente (`_mdSelRange`) et restaurée
   au tour de boucle suivant si le focus l'a effectivement effacée. Chaque
   nouveau geste (mousedown) remet l'attente à zéro, donc un clic pour
   placer le curseur reste normal.

3) Le clic droit effaçait le surlignage.
   Cause : `.bm-search` prenait le focus 10 ms après l'ouverture du menu.
   → le menu ne vole plus le focus quand un texte est sélectionné. Les
   actions Clipboard utilisaient déjà la sélection figée à l'ouverture
   (`d._clipSt`) ; le `preventDefault` du clic droit est remonté sur
   `mousedown` (sur `contextmenu` il arrive trop tard).

Identité visuelle (bannière + logo du projet ajoutés à la racine) :
- assets optimisés servis : `static/img/banner.webp` 22,5 Ko (l'original
  fait 1,6 Mo) + JPEG de secours, `logo.webp` 5,3 Ko + `logo-512.jpg` ;
  originaux conservés dans `assets/` pour régénération.
- icônes PWA régénérées à partir du vrai logo (72 → 512 px, marge
  maskable sur 192/512) + apple-touch-icon.
- landing : logo dans la barre de navigation, bannière en <picture>
  (WebP + JPEG) avec dimensions déclarées (pas de décalage de mise en page).
- `og:image`, `twitter:card` et `meta description` ajoutés sur la landing
  ET dans base.html — la landing a son propre <head>.

Tests : `tests/test_v769_selection_persistence.py` (19 statiques) +
`e2e/v769_selection_persistence.spec.js` (5, vrais gestes : drag souris,
Ctrl+C/Ctrl+X au clavier, clic droit). `e2e/v768_columns_clipboard.spec.js`
corrigé : les gestes simulés (`execCommand`, plage fabriquée en evaluate)
sont remplacés par de vrais gestes, et le token statique obsolète
`si>=ei)return null;` aligné sur la garde `isCollapsed` réelle.

pytest 1413 passed / 0 failed · ruff OK · OpenAPI 526 chemins / 7.69.0
This commit is contained in:
2026-10-08 17:27:14 -04:00
parent 7cf922106f
commit 3d3b46b58b
28 changed files with 610 additions and 38 deletions
+36
View File
@@ -1545,6 +1545,42 @@ Détails livrés :
`v768_columns_clipboard.spec.js` (4) ; `pytest` **1394 passed / 0 failed**,
`ruff` OK ; `VERSION` + `app/main.py` = 7.68.0
### v7.69.0 — La sélection multi-blocs fonctionne enfin ✅ (2026-10-08)
**Contexte** : v7.68.0 livrait la mécanique Clipboard (plage multi-blocs, handlers
`copy`/`cut`, groupe Clipboard du menu contextuel) mais elle était **inutilisable en
pratique** — trois rapports d'usage : Ctrl+C ne copie rien, le glisser souris ne
sélectionne qu'un bloc, le clic droit efface le surlignage.
**Causes racines trouvées (trace d'événements à l'appui, pas de supposition)** :
| Symptôme | Cause réelle |
|----------|--------------|
| Ctrl+C/X muet | le navigateur **n'émet aucun événement `copy`** quand la sélection traverse deux `contenteditable` : `getSelection().toString()` renvoie `""`, donc nos handlers n'étaient jamais atteints |
| Glisser = 1 bloc | séquence `mouseup` → `click` → `focusin` → **collapse** : la plage reconstruite mourait dans la foulée |
| Clic droit efface | `.bm-search` prenait le focus 10 ms après l'ouverture du menu |
**Corrections** : interception du raccourci au `keydown` (capture `document`,
garde `closest('#_blocksCt')`) ; plage reconstruite mise en attente
(`_mdSelRange`) et restaurée au tour suivant, remise à zéro à chaque nouveau
geste ; le menu ne vole plus le focus quand un texte est sélectionné ;
`preventDefault` du clic droit remonté sur `mousedown` (sur `contextmenu` c'est
trop tard).
**Identité visuelle** : bannière + logo intégrés — WebP 22,5 Ko / 5,3 Ko (les
originaux faisaient 1,6 Mo / 1,1 Mo), JPEG de secours, icônes PWA régénérées à
partir du vrai logo, `<picture>` sur la landing, `og:image`/`twitter:card`.
**Vérification** : `tests/test_v769_selection_persistence.py` (19 statiques) +
`e2e/v769_selection_persistence.spec.js` (5, **vrais gestes**) ; le spec v768 a
été corrigé pour remplacer `execCommand` et les plages fabriquées par de vrais
gestes.
**Piège de test relevé au passage** : les `···` dans les échecs Playwright sont
le marqueur de troncature du message d'erreur, **pas** un bug produit — une heure
passée à chercher une fuite inexistante. Vérifier `JSON.stringify()` de la valeur
réelle avant de croire un affichage tronqué.
---
## ✅ Fonctionnalités livrées hors roadmap (bonus détectés dans le code)