Piloter la frise au clavier, à la vim

Les flèches décalaient déjà une phase, mais rien ne permettait de la choisir :
`selection` n'était posée qu'au clic, si bien qu'il fallait la souris pour
commencer. Elle devient `curseur`, désigne une ligne — celle d'un projet en est
une, d'où l'on plie, renomme et ajoute — et survit à Échap, sans quoi elle ne
pourrait servir de point de départ au déplacement suivant. `dessiner` le
normalise, la ligne du projet servant de refuge quand la sienne disparaît.

Deux couches qui ne se recouvrent pas : les flèches modifient, les lettres
déplacent et commandent. Aucun raccourci documenté ne change de sens. Les
touches se taisent dès que le clavier est à quelqu'un d'autre — une saisie, un
dialogue, le menu, un glisser —, faute de quoi `a` dans un nom créerait une
phase. AltGr est toléré : sur un clavier français `{` et `}` n'existent qu'avec
lui, et Windows le présente comme Ctrl+Alt.

Les phases étant triées par date, `o` et `O` insèrent dans le temps et non dans
un rang. `P` reste hors de cette famille : un projet naît sans position.

Le curseur se peint dans la colonne des libellés, seul endroit qui porte une
ligne pour chaque position atteignable — projet plié, projet masqué, ligne de
projet. Et `?` ouvre la table des raccourcis, qui ne se devinent pas.

Décision 26.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 10:36:07 +02:00
parent 7ff1329a86
commit 3092be86ae
7 changed files with 1060 additions and 38 deletions

View File

@@ -718,3 +718,112 @@ reconstruit chaque projet champ par champ et laisse tomber ce qu'il ne connaît
incrémenter, un binaire antérieur ouvrirait un fichier plus riche que lui sans broncher et en
effacerait toutes les notes de projet à la première sauvegarde. Le numéro ne sert qu'à cela : faire
échouer bruyamment ce qui échouerait silencieusement.
## 26. Un curseur clavier, et des raccourcis à la vim
L'outil savait déjà décaler une phase aux flèches, mais pas la **choisir** : `selection` n'était
posée que par `ouvrirPhase`, appelée depuis un clic. Il fallait donc la souris pour commencer, et
tout raccourci ajouté par-dessus aurait hérité de cette dépendance.
### `selection` devient `curseur`, et survit à la fermeture
L'ancienne variable disait deux choses à la fois : « voici la phase que je manipule » et « le
panneau est ouvert dessus ». Les deux se séparent.
Le curseur désigne une **ligne de la frise**, et la ligne d'un projet en est une : c'est de là qu'on
plie, qu'on renomme, qu'on ajoute une phase et qu'on supprime le projet. D'où `{ projet, phase }`
avec `phase` à `null`, plutôt que deux curseurs concurrents dont l'un serait toujours à ignorer.
Il **survit à `Échap`**, alors que `selection` s'effaçait : un curseur qui disparaît à la fermeture
du volet ne peut pas servir de point de départ au déplacement suivant. En contrepartie, la marque
reste visible après un clic dans le vide — c'est le prix d'un curseur, et il se paie une fois.
Comme il peut désigner une ligne qu'un changement vient d'emporter — phase supprimée, projet replié,
filtre resserré —, `dessiner` commence par le **normaliser** : la ligne du projet sert de refuge,
puisqu'elle survit à ces trois cas, et l'effacement n'est que le dernier recours. Placer ce contrôle
dans `dessiner` plutôt qu'à chaque appelant garantit qu'aucun chemin ne l'oublie.
### Le volet suit le curseur
Ce n'est pas un agrément mais une nécessité : les raccourcis agissent sur le curseur, et un panneau
resté sur une autre phase donnerait deux cibles concurrentes à l'écran. On éditerait dans les champs
une phase que `Espace` ou `dd` n'atteindraient pas. Ouvert, le panneau est donc la vue détaillée du
curseur — ce qui fait de `j` et `k` le moyen le plus rapide de relire tout un projet.
Ce parcours passe par un **clic** sur une barre, qui ouvre le volet sans prendre le champ. Ouvrir au
clavier place la frappe dans le nom, où les raccourcis se taisent, et `Échap` referme tout : les deux
usages ne se rejoignent pas, et c'est un compromis assumé plutôt qu'un oubli. Le rendre continu
demanderait de faire d'`Échap` une sortie de champ avant d'être une fermeture — deux appuis là où il
en faut un aujourd'hui, y compris à la souris.
### Deux couches qui ne se recouvrent pas
Les **flèches modifient** la phase du curseur, les **lettres déplacent** et commandent. Aucun
raccourci documenté n'a changé de sens.
La décision 17 tient, mais son partage se déplace. Elle laissait le focus sur le panneau et non dans
un champ, pour que les flèches restent atteignables juste après avoir sélectionné une phase — au
clic, c'est toujours le cas. Ouvrir le volet **au clavier**, en revanche, est une demande explicite
d'écrire : `Entrée` et `e` saisissent donc le nom tous les deux. Ce qui protège les flèches n'est
plus le focus mais `Échap`, qui referme le volet sans déplacer le curseur — « `e`, taper, `Échap`,
flèches » enchaîne sans la souris.
Ce qui ne doit **jamais** prendre le focus, c'est le volet qui suit un déplacement du curseur : la
frappe suivante partirait dans le champ, où les raccourcis se taisent, et on ne saisirait plus jamais
qu'un nom. D'où le paramètre `saisir` de `ouvrirSousCurseur`, vrai sur commande, faux au passage.
`clavier.js` ne connaît rien du planning : il traduit des frappes en noms d'actions, que `app.js`
lui fournit. Les séquences à deux temps se déclarent dans la même table que les autres, et les
préfixes — `g` aller, `z` plis, `d` détruire — s'en déduisent au lieu d'être listés à part. Un
préfixe resté en attente s'oublie au bout de deux secondes : vim ne le fait pas, mais vim n'est pas
posé sur un écran qu'on quitte des yeux, et un `d` abandonné puis retrouvé cinq minutes plus tard
viserait une autre ligne.
Les touches **se taisent** dès que le clavier appartient à quelqu'un d'autre : une saisie en cours,
un dialogue modal, le menu contextuel — qui navigue déjà aux flèches — ou un glisser en cours. Sans
cela, `a` dans un nom de phase créerait une phase au lieu d'écrire un `a`.
`AltGr` est la seule combinaison tolérée avec `Maj`, et il n'y a pas le choix : sur un clavier
français, `{` et `}` ne s'obtiennent qu'avec lui. Windows le présente comme `Ctrl`+`Alt`, si bien
que refuser les deux ensemble rendrait ces deux touches inatteignables sur les binaires Windows.
### Insérer se dit en dates
`model.js` trie les phases par date de début. « Avant » et « après » ne peuvent donc pas s'exprimer
en rangs : `o` et `O` calculent une date et laissent le tri faire le reste. Les quatre points
d'insertion — `A` à la fin du projet, `a` au début, `o` après la phase du curseur, `O` avant — ne
diffèrent que par ce calcul, et passent tous par `ajouterUnePhase`, qui garde son comportement
d'origine quand aucune date ne lui est donnée.
`P` reste à part : un projet naît **hors de tout curseur**, sans date et sans ligne de référence.
L'agréger à la famille `a`/`o` aurait suggéré une position qu'il n'a pas.
### Le curseur se peint dans la colonne, pas seulement sur la barre
La barre portait déjà un contour. Il ne suffit pas : le curseur se pose sur des lignes qui n'ont
aucune barre en face — celle d'un projet, un projet plié, un projet masqué. La colonne des libellés
est le seul endroit qui porte une ligne pour *chaque* position atteignable, et c'est aussi ce qui
permet à `app.js` d'y compter les enfants pour amener le défilement au bon endroit. L'énumération de
`lignesFrise` et celle de `construireLibelles` doivent donc rester d'accord.
Sur la ligne d'un projet, le trait d'accent seul se noyait dans son fond coloré et son triangle de
pli : elle prend aussi une teinte, mélangée à sa couleur propre plutôt qu'au fond de la surface. La
teinte passe par une redéfinition de `--fond-libelle` et non par `background`, parce que le dégradé
qui donne un fond aux commandes du projet s'appuie sur cette même variable — un fond posé en direct
y laissait une couture.
### Le `?` n'est pas facultatif
Des raccourcis d'une lettre ne se devinent pas. Un bouton dans la barre d'outils et la touche `?`
ouvrent la même table, dans un `<dialog>` **modal** — contrairement aux deux panneaux : celui-ci ne
sert qu'à lire, et laisser `j` déplacer le curseur derrière la table qui explique `j` serait absurde.
La table est écrite à la main plutôt que bâtie depuis `SEQUENCES`, qui associe une touche à un nom
d'action : cela ne dit ni ce que l'action fait, ni dans quel ordre la présenter, ni quelles touches
vont par paires. La génération aurait produit une liste exacte et illisible.
### La vue par mois n'a pas de curseur
Elle se lit, elle ne s'édite pas (décision 24) : rien n'y aurait de sens à désigner. `j`, `k`, `gg`
et `G` y font donc ce qu'ils font dans un document — ils déroulent la liste. `gm` et `ga` marchent
dans les deux vues, le reste attend le retour à la frise.