jimku233/workflow-visualizer

Transforme des processus métier en code source Mermaid + un diagramme de flux HTML interactif autonome (zéro dépendance, entièrement hors ligne, sans requête CDN).

Qu'est-ce que workflow-visualizer ?

Ce skill, nommé `workflow-visualizer`, permet de convertir des descriptions de processus métier — qu'elles proviennent de la parole d'un utilisateur, de documents, de SOP, de chaînes de production ou de chronologies de rétrospectives — en un diagramme de flux interactif prêt à l'emploi. Il génère simultanément deux artefacts : un diagramme Mermaid (code `.mmd` ou `.svg`) et une page HTML autonome et interactive (zéro dépendance, pas de CDN, entièrement hors ligne). Le HTML inclut des fonctionnalités avancées : clic sur les nœuds pour afficher les détails (temps, responsable, description), recherche et filtrage, mode clair/sombre, export SVG/PNG, et une version portrait/landscape automatiquement adaptée au mobile et au desktop. Le skill utilise ses propres scripts Node.js (`scripts/flow.mjs`) sans module externe ; il est donc utilisable même dans des environnements isolés ou sans accès réseau. Il est idéal pour les développeurs, les chefs de projet, ou toute personne voulant rapidement visualiser et partager des workflows de manière professionnelle et interactive, sur des plateformes comme Feishu, Yuque, GitHub, ou par simple fichier. Une validation visuelle automatisée est recommandée après génération pour s'assurer de la qualité du rendu.

Compatible avec✓Claude Code✓Codex CLI✓Cursor
npx skills add jimku233/workflow-visualizer

Installed? Explore more Productivité et collaboration skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

workflow-visualizer : Processus métier → Diagramme de flux interactif

Script de rendu : scripts/flow.mjs (relatif à ce répertoire de compétence) Chemin absolu : scripts\flow.mjs Manuel complet des champs et de la syntaxe : references/spec.md (lisez-le avant d'écrire une spécification)

I. Contraintes fondamentales (pourquoi cette approche)

  1. Zéro dépendance : utilise uniquement les modules internes node:. Pas besoin de npm install, pas d'installation de mermaid.
  2. Entièrement hors ligne : la mise en page et le rendu sont réalisés par ce script avec du SVG personnalisé, n'utilise aucun CDN. Le diagramme peut être généré même sans connexion réseau, en intranet ou sans accès à Internet (les web_fetch/docs.deepseek.com locaux ne fonctionnent pas, toute solution dépendante d'un CDN échouera).
  3. Produit autonome : le fichier .svg exporté contient <style>, il peut être correctement affiché même s'il est envoyé seul ; le fichier .html est un fichier unique, double-cliquez pour l'ouvrir.

II. Procédure en trois étapes

Étape 1 : Extraire le processus (cette étape est obligatoire) Extrayez à partir des documents réels de l'utilisateur, ne pas inventer. Ordre de priorité des sources :

  • Documents de planification/rétrospective dans l'espace de travail (*.md, contiennent généralement des sections "workflow/SOP/planning")
  • Exposé oral de l'utilisateur (dans ce cas, marquez les éléments incertains avec status: hold et précisez dans note ce qui doit être confirmé)
  • Historique de conversation ou logs

Lors de l'extraction, capturez toujours ces cinq éléments : étape (qui fait quoi), fenêtre de temps, dépendances de séquence, branches de décision, points de risque/nécessitant une validation humaine.

Étape 2 : Écrire la spécification

  • Diagramme simple (linéaire pur + une ou deux branches) → écrivez directement du Mermaid et enregistrez-le sous forme de fichier .mmd, c'est le plus simple.
  • Pour inclure le temps/le responsable/l'état/la description/le groupement → écrivez du JSON (densité d'information beaucoup plus élevée, le panneau a alors du contenu à afficher).
  • Exécutez d'abord sample pour obtenir une spécification prête à l'emploi comme modèle, c'est le plus rapide :
node "scripts\flow.mjs" sample --out ".\out\MonProcessus.json"

Étape 3 : Validation + rendu

$sk = "scripts\flow.mjs"
node $sk check  ".\out\MonProcessus.json"     # Vérification de structure : nœuds isolés/inaccessibles/sans fin/boucles
node $sk render ".\out\MonProcessus.json" --all --out ".\out\MonProcessus.html"

Le produit se trouve par défaut dans le même répertoire que le fichier d'entrée.

Un rendu produit deux mises en page (dans le même HTML, sélection automatique basée sur l'écran, commutation manuelle en bas) :

Mise en pageDispositionAdaptation
Portrait portraitColonne unique verticale (wrap: 0)Téléphone : défilement vertical, largeur maximale
Paysage landscapeFlux long automatiquement divisé en plusieurs colonnes (wrap: -1, rapport hauteur/largeur cible 1.6)Ordinateur : tient sur un écran

Paramètres courants : --out <fichier.html>, --all (sortie supplémentaire <nom>-portrait.svg, <nom>-paysage.svg, .mmd), --wrap N (nombre fixe de lignes par colonne en mode paysage), --debug (affiche le nombre de colonnes/lignes des deux versions et les membres de chaque colonne).

Vérifiez les deux versions (recommandé) :

node $sk preview ".\out\MonProcessus.html"   # Génère un « Aperçu comparatif [pour ordinateur].html » : deux iframes côte à côte pour téléphone et ordinateur

Ouvrez-le dans un navigateur de débogage, vous pouvez voir d'un coup d'œil si les deux versions sont correctes. Pour une précision correspondant aux dimensions réelles de l'appareil, utilisez la simulation d'appareil CDP :

# Deux captures d'écran réelles pour téléphone + ordinateur, et imprime la hauteur réelle de canvas/aside/footer ainsi que la mise en page sélectionnée automatiquement
node $sk\..\tools\devshot.mjs "http://127.0.0.1:8765/MonProcessus.html" telephone.png ordinateur.png

# Pour exécuter un script JS dans la page et vérifier les interactions (par exemple, cliquer sur un nœud, saisir une recherche, ouvrir un tiroir) :
node $sk\..\tools\probe.mjs <url> sonde.js --w 390 --h 844 --mobile --touch

Ces deux outils créent leurs propres onglets temporaires, les ferment après utilisation, et ne touchent pas aux onglets que l'utilisateur est en train de consulter (les premières versions attrapaient directement la première cible de page, prenant la page que l'utilisateur regardait comme la page testée ; la leçon est notée ci-dessous). Mais attention : le nouvel onglet apparaîtra dans la fenêtre du navigateur de l'utilisateur et prendra le focus — demandez d'abord avant d'utiliser si l'utilisateur utilise ce navigateur de débogage, sinon cela pourrait entraîner des interruptions mutuelles.

III. Auto-vérification obligatoire après le rendu (ne vous fiez pas uniquement à "commande réussie")

Commande réussie ≠ diagramme correct. Effectuez au moins une vérification visuelle en utilisant browser-control :

# 1) Créez une copie avec un nom de fichier ASCII pour éviter les problèmes d'encodage des caractères chinois dans file://
Copy-Item ".\out\MonProcessus.html" ".\out\apercu.html" -Force
# 2) Utilisez le générateur d'URI système pour obtenir l'adresse file:// correcte (l'encodage manuel en pourcentage est très sujet aux erreurs)
$u = [System.Uri]::new((Resolve-Path ".\out\apercu.html").Path).AbsoluteUri
node browser-control\webctl.mjs shot $u "out\shot.png" 5000

Ensuite, regardez l'image pour confirmer : le texte n'est pas rogné, les branches ne sont pas entremêlées, les bandes de groupe ne couvrent pas les nœuds. Les interactions par clic peuvent également être vérifiées par script (webctl.mjs evalfile, attention, l'expression doit être (function(){...})(), un return nu provoquera une Illegal return statement).

IV. Pour les humains : deux façons d'ouvrir sur ordinateur / téléphone

Ordinateur : double-cliquez directement sur le fichier .html (fichier unique, utilisable hors ligne) ; pour l'intégrer dans un document, faites glisser le fichier .svg.

Téléphone (le HTML est sur l'ordinateur, le navigateur du téléphone n'a pas de "ouvrir un fichier local") — utilisez l'accès au réseau local, pas besoin de transférer le fichier :

node "scripts\serve.mjs" ".\out" 8765

Le script affichera directement l'adresse disponible pour le téléphone, sous une forme comme : Téléphone → http://<VotreIPLocale>:8765/ ✔ Utilisez ceci (WLAN) Connectez le téléphone au même Wi-Fi, ouvrez cette adresse dans le navigateur, et cliquez sur le nom du fichier. Points importants :

  • Exposez uniquement le répertoire des produits, ne pointez pas vers la racine de l'espace de travail.
  • Sur Windows, la première fois, le pare-feu demandera une confirmation ; cochez "Réseau privé" pour autoriser l'accès entrant. Si la fenêtre ne s'affiche pas et que la connexion échoue, c'est que le pare-feu bloque l'accès.
  • La page du répertoire est une liste de fichiers ; sur le téléphone, vous pouvez cliquer sur n'importe quel fichier .html / .svg / .png.
  • Une fois terminé, vous pouvez me demander d'arrêter ce service (c'est une tâche d'arrière-plan), ou simplement fermer la fenêtre du terminal.

Pas sur le même réseau / à envoyer à quelqu'un d'autre : envoyez directement le fichier HTML lui-même — il est autonome, ouvrable hors ligne, peut être envoyé via WeChat/QQ ; si vous voulez seulement une image, cliquez sur "Exporter en PNG" dans la page et envoyez l'image, ou utilisez Ctrl+P dans le navigateur pour enregistrer en PDF.

Opérations sur le téléphone : glisser pour déplacer (un doigt), pincer pour zoomer (deux doigts), taper légèrement sur un nœud pour voir les détails, "Adapter" en bas pour revenir à la vue d'ensemble ; les boutons portrait/paysage se trouvent à gauche du pied de page (sur le téléphone, seules les icônes sont affichées).

ZoneCapacité
Barre supérieurePoint de marque + titre, champ de recherche, bouton "Tous les diagrammes" (en haut à droite), légende des états (capsules, cliquer pour filtrer)
Colonne de droiteLorsque rien n'est sélectionné, c'est un aperçu du flux ; lors de la recherche, la liste des correspondances est d'abord affichée (voir ci-dessous) ; après avoir sélectionné un nœud, une carte de détails + "Retour à l'aperçu"
Barre inférieureCommutation portrait/paysage, aperçu/détails (téléphone), zoom, adaptation, exportation SVG/PNG, thème clair/sombre, code source Mermaid

Les trois niveaux de correspondance de la recherche (important, ne le prenez pas pour un bug)

La recherche vérifie simultanément le titre / id / temps / responsable / description / remarque, mais sur le canvas, seul le titre est visible, donc les correspondances sont divisées en deux catégories :

  • Correspondances visibles (titre/id/temps/responsable) → le nœud est entouré d'un bord bleu, compté directement comme une correspondance
  • Correspondances uniquement dans la description/remarque → le nœud est entouré d'une ligne pointillée orange, le comptage est écrit comme "2 correspondances (1 uniquement dans la description/remarque)", et la liste des correspondances est affichée à droite (cliquez pour vous y rendre)

La couleur de correspondance utilise la variable dédiée --hit (couleur foncée #4a9dff, thème beige #2b7fe0), ne peut pas emprunter --accent — dans le thème beige, la couleur d'accent est le terre cuite #bf6b3a ; l'emprunter ferait apparaître les correspondances avec un "bord rouge", ce qui ne correspondrait pas à la description du "bord bleu" (déjà expérimenté).

Exemple : dans le flux d'exemple, chercher tasse correspondra à 2 éléments — Ouvrir un deuxième poste de service (titre) et Réapprovisionnement de l'après-midi (dans la description, "tasse" ustensile) ; chercher f correspondra aux nœuds avec l'id f, ainsi qu'à tout nœud dont la description ou la remarque contient la lettre f. Un même nœud n'est compté qu'une seule fois, ce n'est pas un double comptage.

Thème clair/sombre : Paramètre général / Diagramme individuel / Verrouillage (le dernier modifié prévaut)

Les trois clés sont stockées dans le localStorage du navigateur (toutes stockées au format JSON {mode, ts}, comparées par horodatage, le dernier modifié est appliqué) :

CléÉcrit parSignification
flowviz-themeParamètres d'affichage de la page d'accueilParamètre général : Suivre le système / Toujours sombre / Toujours clair
flowviz-theme:<nom de fichier>Touche clair/sombre de la page du diagrammeRéglage individuel pour un seul diagramme (valeurs dark/light)
flowviz-lock:<nom de fichier>Verrouillage de couleur de la page du diagrammeVerrouille la couleur de ce diagramme après verrouillage ({locked:true, mode})

Règles d'application (le script de démarrage dans le <head> de la page et le JS de la page sont parfaitement cohérents, pour éviter les flashs de couleur) :

  1. Verrouillé → Utilise la couleur figée au moment du verrouillage, le paramètre général et le réglage individuel n'ont aucun effet sur elle
  2. Sinon, compare les horodatages du paramètre général et du réglage individuel, le plus récent prévaut
  3. Aucun des deux → Suit le système (prefers-color-scheme), suit en temps réel lors des changements de système

Donc : si vous modifiez d'abord un diagramme individuel, puis le paramètre général → tous les diagrammes suivent le paramètre général ; si vous modifiez d'abord le paramètre général, puis un diagramme individuel → seul ce diagramme change. Une fois verrouillé, il faut d'abord déverrouiller pour changer la couleur (cliquer sur la touche clair/sombre en mode verrouillé est bloqué et une invite s'affiche).

Matrice de tests réels (8 étapes toutes réussies, 0 anomalie) : Effacer → Général=clair → Diagramme=clair ; Cliquer sur clair/sombre d'un diagramme individuel → Diagramme=sombre (écrit la clé individuelle) ; Modifier Général=clair → Diagramme=clair (mise à jour du paramètre général, écrase le réglage individuel) ; Cliquer à nouveau sur un diagramme individuel → Seulement ce diagramme devient sombre ; Verrouiller → Modifier le paramètre général reste la couleur sombre verrouillée ; Cliquer sur la touche clair/sombre en mode verrouillé → Bloqué avec l'invite "Déverrouillez d'abord" ; Déverrouiller → Revient au paramètre général clair ; La carte sur la page d'accueil affiche le badge « 🔒 Verrouillé Clair ».

Autres points importants :

  • setItem répété sur la même clé est un écrasement, l'utilisation n'augmente pas (testé : après 12 clics sur la touche clair/sombre, Object.keys(localStorage) ne contient toujours qu'un seul élément, total de 18 octets), pas besoin de "supprimer la précédente"
  • Les préférences sont stockées dans le navigateur de l'appareil, le redémarrage du service ne les affecte pas ; changer d'IP/port (changement d'origine), effacer les données du navigateur, changer de navigateur est nécessaire pour les réinitialiser
  • Le mode clair est un style beige millénaire, pas du blanc pur : fond #f2ebdd, panneau #fdfaf3, texte #33302a, accent terre cuite #bf6b3a, texture de fond du diagramme #e6dcc8 (le thème sombre conserve l'ensemble original)

Bouton supérieur

🏠 Accueil retour à la page de liste des fichiers (href="./") | Tous les diagrammes ouvre le tiroir droit | Champ de recherche | Légende des états | Clair/Sombre pour basculer ce diagramme | Verrouiller la couleur pour figer la couleur de ce diagramme. Dans le tiroir, la page actuelle est rendue sous forme de lien non cliquable (cliquer n'aura aucun effet, pour éviter "Je pensais que c'était cassé"), la liste est triée par nom de fichier de manière fixe, donc lors du rafraîchissement de la liste via fetch, aucun réordonnancement n'aura lieu.

Le bouton en haut à droite ouvre un tiroir droit listant tous les diagrammes générés par cette compétence dans le même répertoire (triés par nom de fichier de manière fixe, la page actuelle est marquée « actuelle » et rendue comme un lien non cliquable), cliquez sur une entrée pour basculer. La méthode d'identification est la marque data-flow-viz="1" dans la page, donc les autres HTML ne seront pas inclus.

  • Le fichier de liste est index_flux.json dans le répertoire, recréé automatiquement à chaque render (peut également être recréé individuellement avec node flow.mjs index <répertoire>)
  • Lors de l'ouverture via HTTP, le tiroir fetch la liste en temps réel pour se rafraîchir ; lors d'un double-clic sur un fichier local, un snapshot intégré à la page sert de solution de repli (chaque page en contient une copie, donc la commutation fonctionne même en local)

Interactions de la recherche (trois correctifs pour "correspondance peu évidente")

  1. Panneau de résultats automatiquement déplié : lorsqu'une correspondance est trouvée et qu'aucun nœud n'est actuellement sélectionné, setPanel(true) est appelé — sur un téléphone, la liste des résultats est cachée par défaut dans un tiroir en bas replié ; ne pas la déplier équivaut à ne pas avoir de notification (c'est la cause racine de "la recherche semble n'avoir donné aucun indice")
  2. Précédent / Suivant : la barre de titre de la zone de résultats a des boutons ↑ / ↓ et un compteur actuel/total ; dans le champ de recherche, Entrée = suivant, Shift+Entrée = précédent ; Esc efface
  3. Mise au point automatique (désactivable) : la zone de résultats a un interrupteur « Mise au point automatique sur la correspondance ». Lorsqu'il est activé —
    • Le cadre englobant de la correspondance peut contenir le tout, et le zoom nécessaire est ≥ zoom actuel × 0.6 → cadrer toutes les correspondances (plusieurs correspondances visibles sur le même écran)
    • Sinon (la correspondance est trop éloignée) → ne pas réduire, garder le zoom actuel centré sur la "correspondance actuelle", naviguer individuellement avec ↑/↓ Ainsi, le texte ne sera jamais réduit à néant pour "contenir deux correspondances éloignées"
  4. Pendant la recherche, ajouter .searching au svg : les non-correspondances s'estompent à 14% (lignes à 10%, bandes de groupe à 35%), le contour des correspondances est épaissi à 3px
  5. Les déplacements programmatiques (mise au point/saut) ajoutent .vp-anim à #vp (transition de 0.28 s), le glissement manuel n'est pas affecté

Diagrammes homonymes : distinguez d'abord « contenu identique » ou « simplement homonymes »

Lorsque plusieurs fichiers portent le même titre apparaissent dans le même répertoire (source typique : fichier supprimé puis restauré, le système génère xxx - copie.html), une boîte d'information s'affiche en haut de la page d'accueil, traitée en deux catégories selon le contenu :

CasCritèreAction proposée
Contenu identique (deux rendus du même processus)Empreinte sémantique du processus identique« Conserver uniquement la version la plus récente, déplacer les autres dans le dossier de corbeille »
Contenu différent (même nom mais versions différentes)Empreinte différenteNe pas supprimer, proposer seulement « Renommer… » (suggestion par défaut : NomOriginal (AncienneVersion Date).html)
  • Empreinte = sémantique du processus, ne prend que titre/sous-titre/orientation/groupement/nœuds/arcs, ignore selfFile, flows, slug et autres champs au niveau de la page. Ainsi, « deux fichiers rendus à des moments différents du même processus » ont la même empreinte, tandis que « seule la métadonnée de la page a été modifiée » ne sera pas considéré à tort comme un contenu différent.
  • La suppression est toujours un "déplacement dans le dossier de corbeille" : créez un dossier .trash-YYYY-MM-DD/ dans le même répertoire, renommez le HTML ainsi que ses SVG / MMD (identifiés par un nom normalisé) dedans, récupérable à tout moment.
  • Les actions de traitement sont envoyées via le point de terminaison POST /__flow/act ({op:'trash', remove:[...]} / {op:'rename', file, to}) :
    • Autorisé uniquement pour 127.0.0.1 (téléphone, réseau local toujours 403) — sinon, la capacité de "supprimer vos fichiers" serait exposée sur le Wi-Fi
    • Les noms de fichiers sont passés au peigne fin par une liste blanche : sans séparateur de chemin, longueur ≤ 200, doit se terminer par .html ; en cas de conflit de noms, retour 409
    • Le bouton n'est pas affiché en mode téléphone / fichier local file://, seul un message d'information est affiché (ces deux scénarios n'ont pas de backend accessible en écriture)
  • Le nom du fichier est toujours conservé sur la carte (lorsque les titres sont identiques), pour faciliter l'identification manuelle

Deux modes d'ouverture (les deux doivent permettre d'accéder à la page d'accueil)

ModeComment accéder à la page d'accueilD'où vient la page d'accueil
Double-clic local (file://)🏠 en haut à droite du diagramme → ./index.htmlindex.html statique écrit par render (la liste de fichiers par défaut du navigateur n'affiche que les noms de fichiers, donc celle-ci est nécessaire)
Réseau local / HTTP🏠 → ./Généré dynamiquement par serve.mjs
  • render reconstruit index.html à chaque fois, il partage le même code de rendu avec la page d'accueil HTTP (le buildHome() exporté par serve.mjs ; serve.mjs ne démarre le service que lorsqu'il est exécuté directement, lorsqu'il est importé, il ne fournit que les fonctions)
  • Différences entre la version statique et la version HTTP : le fil d'Ariane affiche 📁 Nom du répertoire (dossier local), le badge indique « Page de diagramme de flux », le pied de page a un texte supplémentaire « Mode fichier local : pour accéder depuis un téléphone, vous devez d'abord démarrer le service »
  • Le bouton 🏠 est conscient du protocole : lorsque location.protocol==='file:', il change le href en ./index.html
  • index.html lui-même n'apparaît pas dans la liste des cartes (dans les premières versions, la première carte s'appelait « index ») ; les HTML qui ne sont ni dans la liste ni ne contiennent flow-stats sont classés dans « Autres fichiers »

Page d'accueil (une seule page pour ordinateur/téléphone)

  • Fil d'Ariane : 🏠 Diagrammes / Sous-répertoire (actuel), chaque niveau cliquable
  • Bloc de chemin disque : affiche directement .\out, avec un bouton « Copier le chemin » — pour envoyer le fichier source ou le retrouver, plus besoin de deviner
  • Recherche + Tri + Résumé : filtre par titre/nom de fichier ; tri basculable entre « Récents d'abord / Par nom / Par date de modification » ; résumé affiche 3 diagrammes · 36 étapes / 47 connexions · 4 points d'attention (2 diagrammes)
  • Récents : lorsqu'on clique sur une carte, on enregistre dans flowviz-recent (8 maximum), par défaut épinglé en haut et avec un badge « Récemment ouvert »
  • Les cartes affichent également des badges « 🔒 Verrouillé X », « Défini individuellement X » (lecture du localStorage local)
  • Texte adapté à l'appareil : deux ensembles de conseils écrits dans le même HTML, basculés avec @media (hover:none),(pointer:coarse) (plus précis que la vérification de la largeur, fonctionne aussi pour les téléphones en mode paysage) —
    • Ordinateur (.only-pc) : 🖱️ Cliquez sur la carte pour ouvrir. Dans le diagramme, molette pour zoomer, maintenir enfoncé pour faire glisser ; en haut à droite « Paramètres d'affichage » pour basculer entre clair/sombre
    • Téléphone (.only-touch) : 📱 Cliquez sur la carte pour ouvrir : glisser d'un doigt, pincer pour zoomer, appuyer sur une étape pour voir les détails…
    • Le badge de la carte « Cliquez ici sur le téléphone » a une classe .touchonly, non affiché sur l'ordinateur ; le badge de la page de comparaison devient le neutre « Page de comparaison côte à côte · pour ordinateur » (compréhensible des deux côtés)
    • Les conseils dans le pied de page/la barre latérale de la page du diagramme sont similaires : ordinateur voit Molette pour zoomer · Glisser pour déplacer · Esc, téléphone voit Glisser d'un doigt · Pincer pour zoomer
  • Diagrammes homonymes (par exemple xxx - copie.html et xxx.html) impossibles à distinguer par le titre seul → ces cartes affichent le nom du fichier sous le titre
  • Miniatures appariées par nom normalisé : suppression des espaces, suppression de -paysage/-portrait avant la comparaison, donc Exemple_de_processus - copie.html peut correspondre à Exemple_de_processus-paysage - copie.svg (même si la position du suffixe diffère)
  • La réponse de la page du répertoire doit inclure cache-control: no-store : elle est générée dynamiquement, une fois mise en cache par le navigateur, la nouvelle version n'est pas visible (déjà expérimenté : l'utilisateur a vu une liste brute ancienne pendant longtemps, pensant à tort que "la page d'accueil est très basique, sans miniatures")

Opérations sur le téléphone

Glisser d'un doigt pour déplacer, pincer pour zoomer, appuyer sur un nœud pour voir les détails, « Adapter » en bas pour revenir à la vue d'ensemble. Sur le téléphone, l'aperçu/les détails sont un tiroir couvrant en bas (position:fixed) : appuyer sur un nœud le fait glisser, appuyer sur un espace vide le fait disparaître, le bouton « Aperçu » dans la barre inférieure l'ouvre manuellement. Il s'agit d'une couche de recouvrement, pas d'un nœud frère flex — donc lorsque le panneau s'ouvre ou se ferme, la hauteur du canvas reste inchangée (testé : 607→607), sans déclencher la réaction en chaîne "canvas rétrécit → réadaptation → diagramme rogné".

Deux détails d'implémentation liés au comportement après zoom :

  • La texture de fond en points est à l'intérieur de #vp, sa zone est 3 fois celle du canvas et centrée sur le contenu (x=-W, y=-H, w=3W, h=3H). Ainsi, la texture suit le zoom et le déplacement, sans laisser apparaître le bord dur du rectangle de texture dans le champ de vision (dans les premières versions, la texture avait la taille du canvas uniquement, zoomer à 150% puis se déplacer faisait apparaître une bordure).
  • La version HTML ne dessine pas de bloc de titre SVG, seul le fichier .svg exporté le fait (la barre supérieure de la page affiche déjà le titre). Sinon, le titre écraserait les nœuds après zoom.
  • Sur écran étroit, passer en mode paysage adapte automatiquement à la vue d'ensemble : le critère est largeur du canvas / largeur du contenu > fit×1.8 et rapport hauteur/largeur du contenu ≤ 1.2 pour utiliser "largeur maximale". Le mode paysage est un diagramme plat avec un rapport de 1.58 ; sur un écran de 390px, si la largeur est maximale, il serait zoomé à 150% (seul un coin serait visible) ; maintenant, il reste stable à 100%.

V. Règles d'expérience pour dessiner des diagrammes de processus métier

  1. Granularité des nœuds : un nœud = une action livrable ("Écrire un script vidéo" est un nœud, "Réfléchir sérieusement" n'en est pas un). 8 à 16 nœuds par diagramme est le plus confortable, au-delà de 20, divisez en diagramme principal + sous-processus.
  2. Utilisez les états : done/doing/todo/risk/hold ne sont pas décoratifs. Dans un processus réel, les blocages comme "en attente de confirmation/en attente de matériel" doivent être marqués hold, "nécessite une validation humaine" doit être marqué risk — c'est là que le diagramme de flux est plus puissant qu'un document.
  3. Les nœuds de décision ne placent que les vraies branches : {...} est utilisé pour les décisions qui empruntent des chemins différents. Pour un simple « vérifier puis continuer », utilisez milestone.
  4. Les fenêtres de temps doivent autant que possible provenir des documents, ne les inventez pas. Si les documents ne les contiennent pas, laissez vide, ne devinez pas.
  5. Les nœuds liés aux droits d'auteur/conformité/lignes rouges doivent être dessinés explicitement et notés dans note, sinon le diagramme semble correct mais peut en réalité causer des problèmes.
  6. Les longues chaînes n'utilisent pas LR horizontal : une chaîne de 13 étapes en LR donne un ruban de 1980×489 (rapport 4:1), dans un canvas de 1100px de large, l'adaptation donne seulement 0.55x, le texte est illisible ; alors que le mode multicollonnes (paysage) peut compresser le même diagramme à environ 1.6:1, tient sur un écran sans réduire la police. Expérience : pour un flux en chaîne horizontal → utilisez le mode multicollonnes, pas LR.
  7. Comment choisir entre les deux versions : téléphone → portrait (une colonne qui défile, intuitif pour l'écran vertical) ; ordinateur → paysage (multicolonnes, vue d'ensemble sur un écran). Le seuil de autoVariant() est largeur<760px ou rapport largeur/hauteur<1.08 → portrait.
  8. Le sens de lecture en mode multicollonnes reste descendant : pas de serpentin (alternance haut-bas), car les colonnes inversées rendraient la direction des flèches confuse. Les lignes de connexion entre colonnes passent par l'espace entre les colonnes (lorsqu'il n'y a qu'un seul nœud dans une ligne) ou le couloir inférieur (lorsqu'il y a plusieurs nœuds dans une ligne), sans traverser les nœuds.
  9. Lorsqu'un groupe traverse des colonnes, il est automatiquement coupé en plusieurs segments et marqué avec « (suite) », pour éviter qu'un rectangle n'englobe des nœuds sans rapport.

VI. Enregistrement des validations (2026-10-04)

  • Format d'entrée : JSON / sous-ensemble Mermaid / blocs de code ```mermaid dans les fichiers .md, les trois chemins ont été testés avec succès.
  • Aller-retour Mermaid : analyse → réexportation du fichier .mmd, les nœuds de décision {}, les lignes pointillées -.->, les étiquettes de bord -->|a|, les sous-groupes subgraph sont correctement conservés.
  • Mises en page des deux versions (flux d'exemple avec 13 nœuds / 13 connexions / 3 groupes) : portrait 533×1936 (12 lignes sur une colonne) ; paysage 1119×710 (division automatique en 3 colonnes × 4 lignes, rapport 1.58, tient sur un écran). Flux d'exemple avec 7 nœuds : portrait 533×890, paysage 806×459 (2 colonnes automatiques).
  • Sélection automatique de la version (testé avec simulation d'appareil CDP) : 390×844 → show=portrait ; 1440×820 → show=landscape.
  • Mise en page mobile (hauteurs mesurées sur 390×844) : canvas 364 / panneau d'aperçu 253 / pied de page 81, contenu de l'aperçu entièrement rendu (panelLen 1533).
  • Gestes tactiles (testés avec TouchEvent synthétique CDP) : glisser d'un doigt pour déplacer ✓, pincer pour zoomer ✓, appui sur un nœud pour sélectionner et afficher « ← Retour à l'aperçu | s | T à 12:00 début | Début | Terminé » ✓. Chemin de la souris également réussi pour les trois.
  • Interactions : clic sur un nœud → carte de détails + mise en évidence des prédécesseurs/successeurs + assombrissement du reste ; filtrage par légende écrit dans data-hide-status ; comptage des correspondances de recherche ; zoom avec les touches +/0 et adaptation pour revenir à 100% ; basculement du thème clair/sombre ; les deux versions conservent simultanément l'état de sélection/recherche/filtrage.
  • Vérification des exceptions pendant le chargement (Runtime.exceptionThrown) : 0 exception pour les deux pages d'exemple.
  • Version embellie : nœuds de type carte (barre verticale de couleur d'état + ombre portée douce), badge de type, pilule de temps, contour orthogonal arrondi, étiquette de bord en capsule, étiquette de capsule de bande de groupe, aperçu du flux dans la colonne de droite (comptage + filtre d'état + liste des points d'attention), texture de fond en points, barre d'outils d'icônes.

Pièges déjà rencontrés (tous corrigés dans le code, notés ici pour gagner du temps la prochaine fois)

  1. Les nœuds de décision Mermaid ne fonctionnent pas : touch() écrivait le champ shape, normalize() lisait kind — noms de champs incompatibles, tous les losanges devenaient des rectangles.
  2. Les guillemets des étiquettes étaient supprimés : Étiquette "Adaptation aux examens réels" devenait Étiquette "Adaptation aux examens réels, car les guillemets de début et de fin étaient supprimés sans condition ; corrigé pour ne les supprimer que lorsque les guillemets de début et de fin sont identiques.
  3. Le pourcentage de zoom ne correspondait pas à la réalité : le viewBox du SVG effectuait lui-même un zoom meet, le transform original se superposait, donc « 100% » était en réalité affiché à 26%. Corrigé en calculant view{K,TX,TY} dans l'espace de pixels de l'élément, puis en le résolvant.
  4. Set-Content de PowerShell ajoutait un BOM UTF-8, brisant #!/usr/bin/env node directement avec une SyntaxError. N'utilisez pas le shell pour écrire ce script.
  5. b.members non défini : l'objet de bande de groupe n'avait pas de champ members, le diagramme avec étiquettes plantait directement.
  6. L'adaptation complète réduisait un diagramme long à 26% : corrigé pour utiliser "adaptation complète" si la différence avec "largeur maximale" est inférieure à 1.8 fois, sinon "largeur maximale + alignement en haut".
  7. Texte écrasé dans les losanges : après recalcul de la taille du nœud de décision selon la condition du rectangle inscrit, Est-ce un problème de syntaxe ou de sémantique ? pouvait tenir sur une ligne.
  8. Conflit d'id lors de l'existence des deux versions : le <g id="vp"> dans renderSvg n'avait pas de suffixe de version, le client getElementById('vp-landscape') obtenait null → paint() levait une exception, l'initialisation entière était interrompue, le panneau d'aperçu était vide (le phénomène était que aside ne faisait que 25px). Tous les id (defs/filter/pattern/marker/vp) des deux versions doivent porter le suffixe -${vid}.
  9. La sémantique du paramètre wrap était inversée : il signifie "nombre de lignes par colonne", pour ne pas wrapper, il faut R = L et non R = 1 (R=1 produisait une bande horizontale d'une colonne par niveau, le portrait a été une fois rendu en 3476×101).
  10. La relecture des exceptions CDP peut tromper : après Runtime.enable, les exceptions du document précédent sont également envoyées. Avant de juger "la page a-t-elle vraiment une erreur", naviguez d'abord vers about:blank via Page.navigate, puis vers la page cible, sinon les erreurs de l'ancienne page seront comptées pour la nouvelle.
  11. La validation d'une sélection par appui nécessite d'abord une adaptation : après une interaction gestuelle, le nœud cible peut avoir été déplacé hors de la zone visible, donc elementFromPoint ne peut pas l'atteindre — c'est un problème du script de test, pas de la page.
  12. La validation CDP usurpait l'onglet de l'utilisateur : webctl shot / la capture directe de la première cible de page naviguait dans l'onglet que l'utilisateur était en train de regarder ; si l'utilisateur utilisait le navigateur en même temps, il y avait des interruptions mutuelles (testé : je naviguais, l'utilisateur faisait une recherche et le ramenait, location devenait un site de stage toutes les quelques dizaines de secondes). Corrigé en utilisant tools/cdp.mjs avec withOwnTab() — crée son propre onglet temporaire via Target.createTarget, interroge jusqu'à ce qu'il apparaisse dans /json/list, le ferme via Target.closeTarget après utilisation. Cependant, le nouvel onglet prend toujours le focus ; si l'utilisateur utilise le navigateur, demandez d'abord.
  13. Les correspondances "supplémentaires" de la recherche ne sont pas un bug : chercher bouche donne 2 correspondances, car la recherche vérifie également la description/remarque ("calibrage du diamètre") et l'id (nœud j). La correction n'est pas de modifier le nombre, mais de marquer les correspondances invisibles (cercle pointillé orange + comptage par niveau + liste des correspondances).
  14. Les onglets en arrière-plan ne peuvent pas être capturés : Target.createTarget({background:true}) ne vole pas le focus, mais les onglets en arrière-plan ne génèrent pas de trames, Page.captureScreenshot se bloque indéfiniment (testé, timeout de 120s). Pour une capture d'écran, un onglet au premier plan est nécessaire (vole le focus) ; les onglets en arrière-plan ne conviennent que pour des mesures géométriques pures (getBoundingClientRect / getComputedStyle fonctionnent normalement). De plus, les transitions CSS ne progressent pas dans les onglets en arrière-plan — avant de mesurer la géométrie, injectez *{transition:none!important}, sinon vous mesurerez le point de départ de l'animation (j'ai interprété à tort rect.x=398 d'un tiroir comme "pas ouvert", alors qu'il était en fait limité).
  15. Les produits destinés à "l'ordinateur" recevaient également le badge "Cliquez ici sur le téléphone" : le badge de la liste des fichiers ne regardait que l'extension. Maintenant, les fichiers commençant par 【/_ sont marqués individuellement "Pour ordinateur, ne cliquez pas sur le téléphone" et grisés — la condition de jugement doit être modifiée en même temps que le texte d'affichage.
  16. Ne placez pas les produits de validation dans le répertoire servi : les fichiers shot-*.png et les scripts de sonde apparaissent dans la liste de fichiers du téléphone. Placez-les dans un répertoire non servi (par exemple out/_shots/), pas dans le répertoire des produits servi par serve.mjs.
  17. Le réordonnancement de la liste du tiroir sous les doigts = cliquer sur la mauvaise entrée : le snapshot intégré à la page mettait initialement "la page actuelle en premier", tandis que le fetch('index_flux.json') renvoyait trié par mtime — environ 100 ms après l'ouverture du tiroir, la liste sautait, et l'utilisateur cliquait sur autre chose que ce qu'il voulait, donnant l'impression de "cliquer sans réaction / ne pas pouvoir revenir". Corrigé par deux moyens : la liste est désormais triée fixement par nom de fichier (le snapshot et la liste ont toujours le même ordre), la page actuelle est rendue comme un lien non cliquable. Leçon : toute liste qui se "rafraîchit de manière asynchrone après ouverture" doit garantir un ordre stable.
  18. Après avoir modifié serve.mjs, il faut redémarrer le service : le modèle de page est en mémoire du processus, sans redémarrage, l'ancienne version persiste (j'ai passé un tour à chercher "pourquoi il n'y a pas de h1 sur la page d'accueil").
  19. L'absence de cache-control sur la page du répertoire faisait que l'utilisateur voyait toujours l'ancienne page d'accueil : l'utilisateur a dit deux fois "la page d'accueil de l'ordinateur est-elle trop basique / sans miniatures", alors que j'avais déjà ajouté des cartes + miniatures, mais le navigateur utilisait l'ancienne réponse mise en cache. Les pages HTML générées dynamiquement doivent avoir no-store.
  20. Le service démarré séparément avec Start-Process ne survivait pas à la fin de la commande : le bac à sable nettoyait l'arbre des processus à la fin de la commande, le service disparaissait silencieusement (se manifestant par "impossible de se connecter au serveur distant" à la requête suivante). Utilisez une tâche d'arrière-plan, ou laissez l'utilisateur exécuter la commande dans son terminal.
  21. N'utilisez pas de JSON de nom similaire/identique dans le répertoire comme entrée : il y avait un fichier ProcessusA.json, dont le nom ressemblait à ProcessusA, mais le contenu était en fait la spécification du ProcessusB ; je l'ai render sans vérifier, écrasant directement ProcessusA.html avec un contenu différent. Avant de rendre, vérifiez le title/nombre de nœuds dans le JSON, et privilégiez l'entrée portant le même nom que le produit.
  22. Les guillemets droits dans les commentaires chinois coupent les chaînes JS avec guillemets doubles (" // parle de la liste "uniquement des noms de fichiers" → se termine prématurément). Je suis tombé trois fois dans le même piège : dans les commentaires chinois, soit n'utilisez pas de guillemets, soit utilisez « ».
  23. Sous file://, il n'y a pas de "page d'accueil du répertoire" : href="./" en local mène à la liste brute des noms de fichiers du navigateur, l'utilisateur voit "uniquement les noms de fichiers". La page de répertoire générée dynamiquement doit être écrite séparément sous forme de index.html statique, et le bouton d'accueil doit basculer la cible en fonction du protocole.
  24. Méthode de capture d'écran sans navigateur de débogage : Edge ne peut pas être démarré dans le bac à sable (refus du tube nommé mojo), mais msedge --headless=new --screenshot=... --window-size=W,H <url> pour un rendu ponctuel en fichier est possible (nécessite une autorisation unique). Il peut capturer des pages locales file://, et simuler téléphone/ordinateur en fonction de la taille. A été clé pour localiser "la première page de l'écran du téléphone ne voit pas les cartes" et "le résumé calcule 0".
  25. Dans les chaînes de modèle, les regex nécessitent des doubles barres obliques inverses : \d/\s dans les chaînes de modèle se dégradent en d/s, rendant la regex générée sur la page inutilisable (le résumé a un moment affiché "0 étapes au total"). Pour les regex écrites dans le script de la page, utilisez systématiquement \\d, \\s.
  26. Le texte ne peut pas être codé en dur par appareil : écrire "Cliquez ici sur le téléphone" sur la page d'accueil de l'ordinateur, ou enseigner "Molette pour zoomer/Double-clic/Esc" sur le diagramme du téléphone, sont des "mauvais appariements d'appareils". La solution est d'écrire deux ensembles au même endroit (.only-pc / .only-touch) + @media (hover:none),(pointer:coarse) pour basculer — utiliser le mode de saisie pour juger est plus précis que la largeur d'écran (ne se trompe pas pour un téléphone en paysage ou un ordinateur avec une fenêtre étroite).
  27. Lorsque des diagrammes homonymes apparaissent dans le même répertoire, les cartes sont identiques : xxx - copie.html et xxx.html ont le même titre, l'utilisateur ne peut pas les distinguer. En cas de titre identique, ajoutez le nom du fichier sur une ligne ; les miniatures doivent également être appariées par un nom normalisé (suppression des espaces et de -paysage/-portrait).
  28. Les \n dans les chaînes de modèle deviennent de vraies nouvelles lignes : dans le JS de la page écrit dans des modèles comme homeScript(), '...\n\n' + x devient sur la page une nouvelle ligne littérale dans la chaîne → erreur de syntaxe du script de la page (node --check sur serve.mjs ne la détecte pas). Utilisez \\n. Méthode de validation : extrayez les deux <script> générés par buildHome() et exécutez node --check individuellement (j'ai ajouté cette étape de régression).
  29. Le jugement "le contenu est-il identique" doit être basé sur la sémantique, pas sur le hachage du fichier : le hachage de la page entière sera nécessairement différent (temps de rendu)

…

[tronqué pour la traduction]

Skills associés