Phase 1.2 : productivite de creation

Construire une grande carte demande beaucoup moins de gestes.

Ordre des freres (format v3)
`MindMapNode.order` devient la position parmi les freres. `parentId` reste la
seule source de verite de la relation hierarchique : une source par
preoccupation, jamais deux versions contradictoires. L'ordre est renumerote a
chaque modification structurelle dans `withEdges`, unique point de passage.
Le format passe en v3 : un lecteur v2 ignorerait `order` et reordonnerait
silencieusement la carte. Migration v2 -> v3 depuis la position dans le tableau,
v1 -> v2 -> v3 s'enchainent, et les noeuds sont ecrits en parcours de profondeur
pour rester corrects meme pour un lecteur anterieur.

Creation rapide
- Ajouter plusieurs enfants : une ligne = un noeud, avec apercu avant creation.
- Collage d'une liste multi-ligne sur un noeud : le presse-papiers interne reste
  prioritaire, l'edition de texte n'est jamais interceptee.
- Import d'une arborescence indentee : espaces ou tabulations, puces retirees,
  indentation incoherente signalee avec le numero de ligne plutot que devinee.
- Les nouveaux noeuds sont repartis autour du parent au lieu de se superposer.

Hierarchie
Monter, descendre, placer en premier ou en dernier, remonter d'un niveau,
"Deplacer vers..." avec recherche, suppression d'un noeud en conservant ses
enfants a la place qu'il occupait. Le re-parentage passe toujours par
`reparentNode` : la detection de cycle n'est ecrite qu'une fois.

Branches
Selectionner, dupliquer, copier, coller comme enfant, deplacer avec ses
descendants (reglage, ou Alt pour l'inverser le temps d'un glisser), replier et
developper une branche, tout replier, tout developper, replier au-dela d'un
niveau.

Clavier
Maj+Tab remonte d'un niveau, Maj+Entree ouvre la creation multiple, Alt+Haut et
Alt+Bas reordonnent. Tab, Entree et F2 gardent leur comportement.

Historique
Chaque operation logique reste une seule entree : creation multiple, import
d'outline, reordonnancement, deplacement de branche, duplication, collage,
suppression, tout replier.

Tests : 172 vitest (103 -> 172) et 36 pytest (31 -> 36).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 06:35:57 +02:00
parent a23a68629d
commit 2afbf637ce
22 changed files with 2557 additions and 112 deletions

View File

@@ -105,6 +105,7 @@ Choix structurants :
| Sujet | Choix | Pourquoi |
|---|---|---|
| Hierarchie | `parentId` fait autorite, les liens en sont derives | un seul invariant a maintenir, pas de desynchronisation |
| Ordre des freres | `node.order`, renumerote a chaque modification | une source par preoccupation : la relation d'un cote, la position de l'autre |
| Modele | types metier propres, adaptes vers React Flow a l'affichage | le format de fichier ne depend pas de la bibliotheque de rendu |
| Historique | instantanes `{nodes, edges}`, groupes par action | un glisser-deposer = une seule annulation |
| Auto-layout | dagre, declenche uniquement a la demande | les deplacements manuels ne sont jamais ecrases |
@@ -240,6 +241,19 @@ horizontal et vertical, retour a la ligne, largeur maximale du texte ; style,
epaisseur et couleur de la branche. Un reglage s'applique a toute la selection,
avec un etat *Mixte* quand les valeurs different.
**Creation rapide** : *Ajouter plusieurs enfants* (une ligne = un noeud, avec
apercu), collage d'une liste multi-ligne directement sur un noeud, import d'une
arborescence indentee (espaces ou tabulations, puces retirees, indentation
incoherente signalee), ajout avant / apres un frere.
**Hierarchie** : ordre explicite des freres (monter, descendre, placer en
premier / en dernier), remontee d'un niveau, *Deplacer vers...* avec recherche
du nouveau parent, suppression d'un noeud en conservant ses enfants.
**Branches** : selectionner, dupliquer, copier / coller comme enfant, deplacer
sur le canvas avec ses descendants, replier / developper une branche, tout
replier, tout developper, replier au-dela d'un niveau donne.
**Mise en page visuelle** : redimensionnement des noeuds a la souris (une seule
annulation par redimensionnement), taille automatique qui epouse le contenu,
alignement et distribution d'une multi-selection, aimantation sur la grille,
@@ -287,6 +301,11 @@ l'application chargee.
| `Ctrl+D` | Dupliquer |
| `Ctrl+Maj+C` | Copier le style du noeud (sans son icone) |
| `Ctrl+Maj+V` | Coller le style sur la selection |
| `Maj+Tab` | Remonter le noeud d'un niveau |
| `Maj+Entree` | Ajouter plusieurs enfants |
| `Alt+Haut` / `Alt+Bas` | Monter / descendre parmi les freres |
| `Alt` + glisser | Inverser le mode de deplacement de branche |
| `Ctrl+V` | Coller : noeuds OpenMind, sinon liste de texte en enfants |
| `Ctrl+A` | Tout selectionner |
| `Ctrl+S` | Sauvegarde explicite |
| `Ctrl+F` | Rechercher dans la carte |
@@ -300,11 +319,20 @@ l'application chargee.
Un `.mmap` est un fichier **JSON UTF-8** contenant le document complet de la
carte. Il ne contient jamais de jeton de partage.
Le format est en **version 2**. Les fichiers v1 restent lisibles : tous les
champs ajoutes en v2 (`layout`, `iconStyle`, `branch`, reglages d'espacement)
sont optionnels et un champ absent signifie simplement *herite du theme*. La
seule transformation appliquee a l'import d'un fichier v1 est le deplacement de
`style.width` vers `layout.width`.
Le format est en **version 3**.
| Version | Apport | Migration a l'import |
|---|---|---|
| v1 | format initial | `style.width` devient `layout.width` |
| v2 | mise en page des noeuds (`layout`, `iconStyle`, `branch`) | l'ordre des freres est materialise depuis leur position dans `nodes` |
| v3 | ordre explicite des freres (`node.order`) | aucune |
Les fichiers v1 et v2 restent lisibles, et les cartes deja enregistrees dans
IndexedDB s'ouvrent sans modification. Le passage en v3 est justifie : l'ordre
des freres fait desormais partie de la semantique du document, et un lecteur v2
l'ignorerait en reordonnant silencieusement la carte. Les noeuds sont malgre
tout ecrits en parcours de profondeur, freres dans l'ordre, ce qui donne le bon
resultat meme a un lecteur qui ignore `order`.
Toute carte qui entre dans l'editeur passe par le meme point d'entree,
`normalizeMapDocument()`, quelle que soit sa provenance :
@@ -573,6 +601,16 @@ de boucle, recherche, auto-layout, lecture seule), assainissement des URL.
- **Cercle** : un noeud circulaire garde toujours un rapport 1:1. Modifier sa
largeur ajuste sa hauteur et inversement ; convertir un noeud rectangulaire en
cercle le ramene a un carre. Il n'est donc pas possible d'obtenir un ovale.
- **Plusieurs racines** : le modele autorise depuis la v1 une carte a plusieurs
racines, et certaines cartes existantes en dependent ; ce n'est donc pas
interdit. Une carte mentale classique n'en a qu'une, et l'interface pousse
dans ce sens : *Detacher* est une action explicite, et un noeud detache
devient une racine a part entiere. Un noeud ne peut jamais devenir son propre
parent ni celui d'un de ses ancetres.
- **Reordonner ne deplace pas les noeuds sur le canvas** : l'ordre est logique.
Il se voit immediatement dans l'ordre des liens et dans le resultat de
*Reorganiser* ; les positions libres, elles, restent celles choisies par
l'utilisateur.
- **Deplacement sur une tres grande carte** : sur une carte de ~180 noeuds, une
frame de glisser-deposer coute environ 60 a 80 ms car React Flow recalcule
l'ensemble des noeuds controles. Le deplacement reste utilisable mais moins