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>
340 lines
18 KiB
Markdown
340 lines
18 KiB
Markdown
# Frise multi-projets
|
|
|
|
Un outil de **planification macro** : voir sur une seule frise chronologique où en est chaque
|
|
projet, quelles sont ses grandes phases, et comment les projets se situent les uns par rapport aux
|
|
autres.
|
|
|
|
Ce n'est **pas** un outil de suivi quotidien : pas de sous-tâches, pas de tickets, pas de
|
|
checklists. Une phase représente plusieurs semaines ou plusieurs mois de travail.
|
|
|
|
## Démarrer
|
|
|
|
Récupérer le binaire de sa plateforme, le poser dans un dossier avec son planning, et le
|
|
lancer — par un double-clic ou en ligne de commande :
|
|
|
|
```sh
|
|
./frise
|
|
```
|
|
|
|
Le navigateur s'ouvre tout seul. **Rien à installer** : l'interface est embarquée dans
|
|
l'exécutable, qui ne dépend d'aucune bibliothèque système — pas même de la glibc. Il tourne tel
|
|
quel sur n'importe quelle distribution, et sur une machine sans Python ni Node.
|
|
|
|
Par défaut, le planning est le fichier `projets.json` **posé à côté de l'exécutable**. C'est ce
|
|
qui permet de déposer les deux dans un dossier synchronisé (Dropbox, Nextcloud, Syncthing) et de
|
|
retrouver son outil et ses données sur n'importe quelle machine.
|
|
|
|
```
|
|
📁 Mon dossier synchronisé
|
|
├── frise ← l'exécutable
|
|
└── projets.json ← le planning
|
|
```
|
|
|
|
Options :
|
|
|
|
```sh
|
|
./frise --port 9000 # imposer un port (sinon : le premier libre à partir de 8000)
|
|
./frise --data ~/plannings/2027.json # ouvrir un autre fichier
|
|
./frise --no-browser # ne pas ouvrir le navigateur
|
|
```
|
|
|
|
Au premier lancement, le planning est vide et aucun fichier n'est créé tant qu'on n'a rien
|
|
saisi. Pour découvrir l'outil sur des données réalistes, copier `data/exemple.json` en
|
|
`projets.json` à côté de l'exécutable.
|
|
|
|
### Compiler soi-même
|
|
|
|
```sh
|
|
./build.sh # les cinq plateformes, dans dist/
|
|
./build.sh local # seulement la machine courante
|
|
```
|
|
|
|
Il faut [Go](https://go.dev) 1.22 ou plus récent, et rien d'autre : aucune dépendance externe,
|
|
aucun `vendor/`. Les binaires des trois systèmes se produisent depuis une seule machine, sans
|
|
intégration continue ni SDK tiers.
|
|
|
|
### Développer
|
|
|
|
```sh
|
|
go run . --dev
|
|
```
|
|
|
|
Le mode `--dev` sert `index.html`, `css/` et `js/` **depuis le disque** plutôt que depuis le
|
|
binaire : un simple rafraîchissement suffit à voir ses modifications, sans recompiler. Il lit
|
|
alors `data/projets.json`, comme avant.
|
|
|
|
```sh
|
|
go test ./... # le serveur
|
|
node --test tests/ # le modèle métier
|
|
```
|
|
|
|
## Prise en main
|
|
|
|
**Nouveau projet** crée le projet aussitôt, sous un nom provisoire, et ouvre ses paramètres avec
|
|
ce nom présélectionné : on tape simplement par-dessus. Il n'y a aucun formulaire à valider — ni
|
|
ici, ni pour une phase. Tout s'applique au fil de la saisie, et un projet créé par erreur se
|
|
supprime depuis son propre panneau.
|
|
|
|
Dans la colonne de gauche, le **triangle** plie et déplie le couloir d'un projet, et un clic sur
|
|
son **nom** ouvre ses paramètres : nom, couleur, tags. Deux commandes apparaissent au survol de
|
|
la ligne, sur son bord droit :
|
|
|
|
- **`+`** ajoute une phase à la suite de la dernière, et ouvre son panneau de détail ;
|
|
- **`⋯`** ouvre le menu du projet : ajouter une phase, plier, masquer de la frise, renommer,
|
|
figer la référence, supprimer. Un clic droit sur le libellé ouvre le même menu.
|
|
|
|
Il n'y a pas de commande séparée pour créer un **jalon** : c'est une phase dont on coche la case
|
|
« Jalon » dans le panneau, ce qui ramène sa fin sur son début. La bascule joue dans les deux sens
|
|
à tout moment.
|
|
|
|
La colonne se **redimensionne** en glissant la ligne qui la sépare de la frise, en haut à
|
|
gauche : un double-clic revient à la largeur d'origine. Cette largeur est propre au navigateur,
|
|
elle n'est pas enregistrée dans le planning.
|
|
|
|
Une fois un projet en place :
|
|
|
|
| Geste | Effet |
|
|
|---|---|
|
|
| Glisser le corps d'une barre | Décale la phase, durée conservée |
|
|
| Glisser le bord d'une barre | Change la date de début ou de fin |
|
|
| Glisser le bord gauche du panneau | Change sa largeur (double-clic : d'origine) |
|
|
| Clic sur une barre ou son nom | Ouvre le panneau de détail |
|
|
| Clic sur une autre barre, panneau ouvert | Bascule le panneau sur cette phase |
|
|
| Clic sur un nom de phase hors écran | Ramène la frise sur elle |
|
|
| `←` `→` | Décale d'un jour la phase du curseur |
|
|
| `Maj` + `←` `→` | Allonge ou raccourcit d'un jour |
|
|
| `Échap` | Annule le glisser en cours, ou ferme le panneau |
|
|
| Clic dans une zone vide | Ferme le panneau ouvert |
|
|
| `Maj` + molette | Fait défiler la frise horizontalement |
|
|
| `↑` `↓` dans un menu | Parcourt les entrées |
|
|
|
|
Faire défiler jusqu'au bord droit élargit la frise vers le futur, et jusqu'au bord gauche
|
|
vers le passé. Le bouton **Aujourd'hui** ramène la vue sur la date du jour.
|
|
|
|
### Au clavier, à la vim
|
|
|
|
La frise a un **curseur** : la ligne sur laquelle portent les raccourcis. Il se pose en cliquant une
|
|
barre ou un nom, ou à la première frappe de `j`, et il se voit à la ligne bleutée dans la colonne de
|
|
gauche. Il tient aussi sur la ligne d'un **projet**, position d'où l'on plie, renomme et ajoute.
|
|
|
|
Le bouton **`?`** de la barre d'outils, ou la touche `?`, ouvre la liste complète. En résumé :
|
|
|
|
| Touche | Effet |
|
|
|---|---|
|
|
| `j` `k` | Ligne suivante, précédente — projets traversés |
|
|
| `h` `l` | Phase précédente, suivante **du même projet**, sans déborder sur le voisin |
|
|
| `{` `}` | Projet précédent, suivant |
|
|
| `gg` `G` | Première, dernière ligne |
|
|
| `Entrée` `e` | Ouvre le volet sur la ligne du curseur et saisit le nom |
|
|
| `Espace` | Statut suivant : à venir, en cours, terminé, bloqué |
|
|
| `A` `a` | Ajoute une phase à la fin, au début du projet |
|
|
| `o` `O` | En ajoute une juste après, juste avant celle du curseur |
|
|
| `P` | Ajoute un projet |
|
|
| `dd` | Supprime la phase — ou le projet depuis sa ligne |
|
|
| `za` | Plie ou déplie le projet |
|
|
| `zR` `zM` | Déplie tout, plie tout |
|
|
| `zz` | Recentre la frise sur le curseur |
|
|
| `w` `b` | Un mois plus tard, plus tôt |
|
|
| `W` `B` | Un an plus tard, plus tôt |
|
|
| `ga` | Revient à aujourd'hui |
|
|
| `gm` | Bascule entre la frise et la vue par mois |
|
|
|
|
Les phases étant rangées par date, `o` et `O` insèrent **dans le temps** et non dans une liste : la
|
|
nouvelle phase se pose bord à bord avec sa voisine. `P`, lui, ne dépend pas du curseur : un projet
|
|
naît hors de toute ligne, et la vue revient à la frise si on était dans la liste par mois.
|
|
|
|
Deux garde-fous. Ces touches **se taisent pendant une saisie** — dans un champ, une note, un
|
|
dialogue ou le menu contextuel, elles écrivent la lettre qu'elles portent. Et `dd` demande toujours
|
|
confirmation, comme le bouton **Supprimer**.
|
|
|
|
Le volet ouvert **suit le curseur** : `j` et `k` font alors défiler les phases une à une dans le
|
|
panneau de détail, ce qui est le moyen le plus rapide de relire tout un projet. Pour cela, ouvrir le
|
|
volet d'un **clic sur une barre** — `Entrée` et `e`, eux, placent la frappe dans le champ « Nom », où
|
|
les raccourcis se taisent.
|
|
|
|
`Échap` referme le volet sans déplacer le curseur, et la frappe suivante repart de là.
|
|
|
|
### Les notes
|
|
|
|
Un projet **et** une phase portent chacun des notes, en bas de leur panneau. Elles y prennent toute
|
|
la hauteur restante : les autres champs ont une taille dictée par leur contenu, une note fait ce
|
|
qu'on a à dire.
|
|
|
|
Le texte s'écrit en **markdown** — titres, `**gras**`, `*italique*`, listes, `- [ ] cases à
|
|
cocher`, liens, citations, blocs de code. Le bloc s'ouvre sur l'**aperçu** quand la note existe
|
|
déjà, sur la saisie quand elle est vide ; le crayon en haut à droite bascule d'un état à l'autre,
|
|
et une barre de six boutons paraît en saisie (`Ctrl`+`B` et `Ctrl`+`I` marchent aussi). Rien n'est
|
|
converti à l'enregistrement : le fichier contient le markdown tel quel, lisible et modifiable à la
|
|
main.
|
|
|
|
Les **cases à cocher se cliquent directement dans l'aperçu** — c'est le seul geste de l'aperçu qui
|
|
change les données. Cocher une ligne ne touche à rien d'autre : votre mise en forme n'est jamais
|
|
réécrite.
|
|
|
|
Les notes d'un projet sont ce que la frise ne sait pas dire — qui pilote, sur quel budget, quelle
|
|
décision à quelle date. Elles se lisent aussi au survol du nom du projet dans la colonne de gauche,
|
|
et celles d'une phase au survol de sa barre : l'infobulle les affiche sans les marques de mise en
|
|
forme, puces et cases dessinées comprises.
|
|
|
|
Le **panneau se redimensionne** en glissant son bord gauche, comme la colonne des projets ; un
|
|
double-clic revient à la largeur d'origine. Cette largeur est propre au navigateur, elle n'est pas
|
|
enregistrée dans le planning.
|
|
|
|
### La vue par mois
|
|
|
|
Les deux boutons **Frise** et **Par mois**, en haut à gauche, changent de regard sur les mêmes
|
|
données. La vue par mois déroule toutes les tâches et tous les jalons du planning en une seule
|
|
liste, projets mêlés, groupés par mois — celui où chaque phase **commence**. C'est la réponse à
|
|
« qu'est-ce qui arrive le mois prochain », que la frise donne mal parce qu'elle sépare les projets
|
|
en couloirs.
|
|
|
|
Chaque ligne se lit dans cet ordre : quand — la période, `31 → 13 nov.` quand la phase déborde du
|
|
mois —, chez qui — le projet et ses tags —, puis quoi — l'intitulé de la tâche ou du jalon — et
|
|
enfin son statut. Une phase longue n'est **pas** répétée dans les mois qu'elle traverse : elle
|
|
n'apparaît qu'à son mois de départ, avec sa date de fin écrite en clair.
|
|
|
|
Les phases qui portent des **notes** le signalent par un triangle en tête de ligne, le même que
|
|
celui qui plie un projet sur la frise. Un clic **n'importe où sur la ligne** déplie la note en
|
|
dessous, un second la referme. Elle s'y affiche rendue, comme dans le panneau. Rien n'est affiché
|
|
d'office — trois lignes de notes sous chaque intitulé étaleraient un mois sur deux écrans.
|
|
|
|
Cette liste se **lit** — elle ne s'édite pas. Déplacer une phase demande de voir ce qu'elle
|
|
chevauche, donc la frise ; c'est là que les dates et les notes se modifient. Seuls les tags et les
|
|
notes y sont cliquables, et ni filtrer ni déplier ne touche aux données — les cases à cocher d'une
|
|
note y sont d'ailleurs inertes, elles ne se cochent que depuis le panneau. Le bouton **Aujourd'hui**
|
|
ramène ici sur le mois en cours, et la vue affichée n'est pas mémorisée : l'outil s'ouvre toujours
|
|
sur la frise.
|
|
|
|
### Filtrer par tag
|
|
|
|
Un projet peut porter des **tags** — `client`, `interne`, `R&D` —, saisis séparés par des virgules
|
|
dans le dialogue « Renommer, changer la couleur… ». Il n'y a pas de liste à tenir : les tags
|
|
existants sont proposés en un clic, et un tag disparaît dès que plus aucun projet ne le porte.
|
|
|
|
Ils s'affichent en petites pastilles colorées dans la colonne de gauche, et une barre de filtres
|
|
apparaît sous la barre d'outils dès qu'un tag existe. Cliquer un tag — dans la barre de filtres ou
|
|
directement sur la pastille d'un projet — restreint la frise à ceux qui le portent ; recliquer le
|
|
retire. Le filtre vaut pour les **deux vues** : il ne change pas quand on passe de la frise à la
|
|
liste par mois.
|
|
|
|
Cocher plusieurs tags **cumule les conditions** : `client` + `urgent` ne montre que les projets
|
|
portant les deux. Le filtre n'est pas enregistré, il ne vaut que pour la session en cours. Le bouton
|
|
**Tout afficher** le vide d'un coup.
|
|
|
|
Les barres s'accrochent au lundi pendant le glisser. Pour une date au jour près, passer
|
|
par les champs du panneau de détail.
|
|
|
|
## Ce que fait l'outil
|
|
|
|
- **Plusieurs projets**, chacun décomposé en **phases** ayant un nom, des dates de début et de fin,
|
|
un statut, des notes. Une phase peut être un **jalon** (une date unique, rendue en losange).
|
|
- **Des notes en markdown**, sur un projet comme sur une phase : titres, listes, cases à cocher,
|
|
liens. Elles occupent toute la hauteur restante du panneau, et se cochent d'un clic dans l'aperçu.
|
|
- **Une frise commune**, les projets empilés en couloirs, pour les comparer d'un coup d'œil.
|
|
- **Une barre cumulative par projet**, segmentée en teintes selon le statut de chaque phase : la
|
|
forme d'ensemble, toujours visible sur la ligne du projet et toujours rendue pareil. Elle ne se
|
|
glisse pas.
|
|
- **Couloirs pliables** : déplié, chaque phase occupe sa ligne sous la barre cumulative ; plié, il
|
|
ne reste que celle-ci.
|
|
- **Manipulation directe** : glisser une barre pour la décaler, tirer ses bords pour changer sa
|
|
durée. Les barres s'accrochent au lundi ; pour une date exacte, on passe par le panneau de détail.
|
|
- **Planning de référence** : une fois le planning validé, on le fige. La frise affiche alors une
|
|
barre fantôme sous la barre réelle et la dérive en semaines — de quoi répondre à « ce projet a-t-il
|
|
glissé, et de combien ? ».
|
|
- **Un en-tête à trois niveaux** — année, mois, numéro de semaine ISO — qui donne le
|
|
repère temporel sans jamais descendre au jour : à cette échelle, la semaine suffit.
|
|
Il n'y a qu'une seule échelle, donc aucun réglage de zoom à comprendre.
|
|
- **Une frise sans fin** : elle s'élargit d'elle-même quand le défilement approche d'un
|
|
bord, ce qui permet d'aller planifier dans un futur qu'aucune phase n'occupe encore.
|
|
- **Une vue par mois** : toutes les tâches et tous les jalons en une liste, projets mêlés, groupés
|
|
par mois de début. Elle se lit, elle ne s'édite pas.
|
|
- **Des tags sur les projets**, et un filtre qui restreint les deux vues à ceux qui les portent
|
|
tous.
|
|
- **Repère « aujourd'hui »** et masquage d'un projet.
|
|
|
|
## Ce que l'outil ne fait pas
|
|
|
|
Volontairement : pas de dépendances entre phases, pas de sous-tâches, pas de collaboration temps
|
|
réel, pas d'authentification, pas de gestion de charge par personne, pas de notifications.
|
|
|
|
Les durées se comptent en **jours calendaires** : ni week-ends ni jours fériés ne sont gérés. À
|
|
l'échelle de phases de plusieurs semaines, la distinction n'apporte rien.
|
|
|
|
Les raisons derrière ces choix sont détaillées dans [docs/decisions.md](docs/decisions.md).
|
|
|
|
## Où vivent les données
|
|
|
|
Tout tient dans un fichier JSON, `data/projets.json`, décrit dans
|
|
[docs/modele-donnees.md](docs/modele-donnees.md). Il est indenté et utilise des identifiants
|
|
parlants (`site-web`, `cadrage`) plutôt que des UUID : il reste éditable à la main et lisible dans un
|
|
diff git.
|
|
|
|
**À l'ouverture**, le serveur copie le planning dans un dossier `backups/` voisin, horodaté : c'est
|
|
l'état d'avant la séance, celui qu'on veut retrouver si elle tourne mal. Si le serveur reste allumé
|
|
plusieurs jours, une copie est reprise au plus une fois par jour. Rouvrir l'outil sans avoir rien
|
|
modifié ne laisse pas de trace.
|
|
|
|
Cinquante sauvegardes sont conservées, les plus anciennes étant supprimées au fil de l'eau. Comme
|
|
elles valent une par séance et non une par modification, cela représente des mois d'historique
|
|
plutôt que la dernière demi-heure.
|
|
|
|
Pour restaurer, **arrêter `frise` et fermer l'onglet d'abord** : l'application garde son planning
|
|
en mémoire et sa prochaine sauvegarde écraserait ce qu'on vient de remettre en place.
|
|
|
|
```sh
|
|
ls -lt backups/ # repérer la version voulue
|
|
cp backups/projets-20260801-170352.json projets.json
|
|
```
|
|
|
|
Le planning n'est **pas versionné** : `.gitignore` exclut `projets.json`, ce dépôt ne contenant
|
|
que le code. Seul `data/exemple.json` y figure, comme jeu de démonstration.
|
|
|
|
**Pour synchroniser plusieurs machines**, déposer l'exécutable et son `projets.json` dans un
|
|
espace synchronisé. L'option `--data` accepte n'importe quel chemin, le planning peut donc aussi
|
|
vivre ailleurs que le binaire.
|
|
|
|
Deux frictions propres à ce mode de distribution, qui touchent tout exécutable et pas seulement
|
|
celui-ci : le bit d'exécution n'est pas toujours préservé par la synchronisation (`chmod +x frise`
|
|
une fois par machine), et un binaire non signé déclenche un avertissement au premier lancement —
|
|
SmartScreen sous Windows, Gatekeeper sous macOS.
|
|
|
|
L'outil ne gère pas les conflits — si deux machines modifient le planning en même temps, la dernière
|
|
écriture l'emporte. Les sauvegardes horodatées de `data/backups/` sont le filet de sécurité.
|
|
|
|
## Tests
|
|
|
|
```sh
|
|
node --test # la logique métier
|
|
go test ./... # le serveur
|
|
```
|
|
|
|
Côté navigateur : validation des dates, bornes d'un projet, calcul de dérive, génération des
|
|
identifiants, normalisation et filtrage des tags, regroupement des phases par mois. Côté serveur : lecture et écriture du planning,
|
|
refus des corps invalides, rotation des sauvegardes, écriture atomique, résolution du chemin de
|
|
données. Aucune dépendance de part ni d'autre, les lanceurs intégrés à Node et à Go suffisent.
|
|
|
|
## Organisation du code
|
|
|
|
| Fichier | Rôle |
|
|
|---|---|
|
|
| `main.go` | Sert l'interface embarquée, expose `GET`/`PUT` sur `/api/data`, écrit les sauvegardes |
|
|
| `build.sh` | Compile les binaires des cinq plateformes dans `dist/` |
|
|
| `js/model.js` | Données et règles métier : CRUD, validation, bornes, référence, dérive, tags, regroupement par mois |
|
|
| `js/storage.js` | Dialogue avec le serveur, sauvegarde debouncée, indicateur d'état |
|
|
| `js/timeline.js` | Rendu de la frise : échelle, couloirs, barres, jalons, pli/dépli, curseur |
|
|
| `js/mois.js` | Rendu de la vue par mois : groupes, lignes, ancrage sur le mois courant |
|
|
| `js/tags.js` | Pastilles de tags, partagées par les deux vues |
|
|
| `js/drag.js` | Glisser et redimensionner les barres |
|
|
| `js/clavier.js` | Table des raccourcis à la vim, préfixes `g` `z` `d`, garde-fou de saisie |
|
|
| `js/markdown.js` | Analyse du markdown des notes, aplatissement pour les infobulles. Sans DOM, donc testable sous Node |
|
|
| `js/notes.js` | Rendu du markdown en DOM et bloc de notes — aperçu, barre d'outils, saisie — partagé par les deux panneaux |
|
|
| `js/detail.js` | Panneau de détail d'une phase |
|
|
| `js/projet.js` | Panneau des paramètres d'un projet : nom, couleur, tags, notes |
|
|
| `js/menu.js` | Menu contextuel et dialogue de confirmation |
|
|
| `js/app.js` | Amorçage, état en mémoire, câblage des événements |
|
|
|
|
Le rendu utilise des éléments DOM positionnés en absolu, pas de SVG ni de bibliothèque Gantt.
|
|
|
|
Les identifiants de code et les clés JSON sont en anglais ; l'interface, les commentaires et la
|
|
documentation sont en français.
|