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)
- Zéro dépendance : utilise uniquement les modules internes
node:. Pas besoin denpm install, pas d'installation de mermaid. - 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). - Produit autonome : le fichier
.svgexporté contient<style>, il peut être correctement affiché même s'il est envoyé seul ; le fichier.htmlest 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: holdet précisez dansnotece 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
samplepour 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 page | Disposition | Adaptation |
|---|---|---|
Portrait portrait | Colonne unique verticale (wrap: 0) | Téléphone : défilement vertical, largeur maximale |
Paysage landscape | Flux 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).
| Zone | Capacité |
|---|---|
| Barre supérieure | Point de marque + titre, champ de recherche, bouton "Tous les diagrammes" (en haut à droite), légende des états (capsules, cliquer pour filtrer) |
| Colonne de droite | Lorsque 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érieure | Commutation 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 par | Signification |
|---|---|---|
flowviz-theme | Paramètres d'affichage de la page d'accueil | Paramètre général : Suivre le système / Toujours sombre / Toujours clair |
flowviz-theme:<nom de fichier> | Touche clair/sombre de la page du diagramme | Réglage individuel pour un seul diagramme (valeurs dark/light) |
flowviz-lock:<nom de fichier> | Verrouillage de couleur de la page du diagramme | Verrouille 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) :
- 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
- Sinon, compare les horodatages du paramètre général et du réglage individuel, le plus récent prévaut
- 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 :
setItemré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.jsondans le répertoire, recréé automatiquement à chaquerender(peut également être recréé individuellement avecnode flow.mjs index <répertoire>) - Lors de l'ouverture via HTTP, le tiroir
fetchla 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")
- 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") - Précédent / Suivant : la barre de titre de la zone de résultats a des boutons
↑ / ↓et un compteuractuel/total; dans le champ de recherche, Entrée = suivant, Shift+Entrée = précédent ;Escefface - 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"
- Pendant la recherche, ajouter
.searchingau svg : les non-correspondances s'estompent à 14% (lignes à 10%, bandes de groupe à 35%), le contour des correspondances est épaissi à 3px - 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 :
| Cas | Critère | Action 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érente | Ne 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, ignoreselfFile,flows,sluget 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)
| Mode | Comment accéder à la page d'accueil | D'où vient la page d'accueil |
|---|---|---|
Double-clic local (file://) | 🏠 en haut à droite du diagramme → ./index.html | index.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 |
renderreconstruitindex.htmlà chaque fois, il partage le même code de rendu avec la page d'accueil HTTP (lebuildHome()exporté parserve.mjs;serve.mjsne 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.htmllui-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 contiennentflow-statssont 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 voitGlisser d'un doigt · Pincer pour zoomer
- Ordinateur (
- Diagrammes homonymes (par exemple
xxx - copie.htmletxxx.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/-portraitavant la comparaison, doncExemple_de_processus - copie.htmlpeut 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
.svgexporté 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.8et 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
- 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.
- Utilisez les états :
done/doing/todo/risk/holdne sont pas décoratifs. Dans un processus réel, les blocages comme "en attente de confirmation/en attente de matériel" doivent être marquéshold, "nécessite une validation humaine" doit être marquérisk— c'est là que le diagramme de flux est plus puissant qu'un document. - 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 », utilisezmilestone. - 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.
- 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. - 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.
- 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()estlargeur<760px ou rapport largeur/hauteur<1.08 → portrait. - 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.
- 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
```mermaiddans 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-groupessubgraphsont 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+/0et 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)
- Les nœuds de décision Mermaid ne fonctionnent pas :
touch()écrivait le champshape,normalize()lisaitkind— noms de champs incompatibles, tous les losanges devenaient des rectangles. - 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. - Le pourcentage de zoom ne correspondait pas à la réalité : le
viewBoxdu SVG effectuait lui-même un zoommeet, letransformoriginal se superposait, donc « 100% » était en réalité affiché à 26%. Corrigé en calculantview{K,TX,TY}dans l'espace de pixels de l'élément, puis en le résolvant. Set-Contentde PowerShell ajoutait un BOM UTF-8, brisant#!/usr/bin/env nodedirectement avec uneSyntaxError. N'utilisez pas le shell pour écrire ce script.b.membersnon défini : l'objet de bande de groupe n'avait pas de champmembers, le diagramme avec étiquettes plantait directement.- 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".
- 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. - Conflit d'id lors de l'existence des deux versions : le
<g id="vp">dansrenderSvgn'avait pas de suffixe de version, le clientgetElementById('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 queasidene faisait que 25px). Tous les id (defs/filter/pattern/marker/vp) des deux versions doivent porter le suffixe-${vid}. - La sémantique du paramètre
wrapétait inversée : il signifie "nombre de lignes par colonne", pour ne pas wrapper, il fautR = Let nonR = 1(R=1 produisait une bande horizontale d'une colonne par niveau, le portrait a été une fois rendu en 3476×101). - 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 versabout:blankviaPage.navigate, puis vers la page cible, sinon les erreurs de l'ancienne page seront comptées pour la nouvelle. - 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
elementFromPointne peut pas l'atteindre — c'est un problème du script de test, pas de la page. - 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,locationdevenait un site de stage toutes les quelques dizaines de secondes). Corrigé en utilisanttools/cdp.mjsavecwithOwnTab()— crée son propre onglet temporaire viaTarget.createTarget, interroge jusqu'à ce qu'il apparaisse dans/json/list, le ferme viaTarget.closeTargetaprès utilisation. Cependant, le nouvel onglet prend toujours le focus ; si l'utilisateur utilise le navigateur, demandez d'abord. - Les correspondances "supplémentaires" de la recherche ne sont pas un bug : chercher
bouchedonne 2 correspondances, car la recherche vérifie également la description/remarque ("calibrage du diamètre") et l'id (nœudj). La correction n'est pas de modifier le nombre, mais de marquer les correspondances invisibles (cercle pointillé orange + comptage par niveau + liste des correspondances). - 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.captureScreenshotse 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/getComputedStylefonctionnent 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é à tortrect.x=398d'un tiroir comme "pas ouvert", alors qu'il était en fait limité). - 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. - Ne placez pas les produits de validation dans le répertoire servi : les fichiers
shot-*.pnget les scripts de sonde apparaissent dans la liste de fichiers du téléphone. Placez-les dans un répertoire non servi (par exempleout/_shots/), pas dans le répertoire des produits servi parserve.mjs. - 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. - 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"). - L'absence de
cache-controlsur 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 avoirno-store. - Le service démarré séparément avec
Start-Processne 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. - 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'airendersans vérifier, écrasant directementProcessusA.htmlavec un contenu différent. Avant de rendre, vérifiez letitle/nombre de nœuds dans le JSON, et privilégiez l'entrée portant le même nom que le produit. - 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 « ». - 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 deindex.htmlstatique, et le bouton d'accueil doit basculer la cible en fonction du protocole. - 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 localesfile://, 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". - Dans les chaînes de modèle, les regex nécessitent des doubles barres obliques inverses :
\d/\sdans les chaînes de modèle se dégradent end/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. - 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). - Lorsque des diagrammes homonymes apparaissent dans le même répertoire, les cartes sont identiques :
xxx - copie.htmletxxx.htmlont 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). - Les
\ndans les chaînes de modèle deviennent de vraies nouvelles lignes : dans le JS de la page écrit dans des modèles commehomeScript(),'...\n\n' + xdevient sur la page une nouvelle ligne littérale dans la chaîne → erreur de syntaxe du script de la page (node --checksur serve.mjs ne la détecte pas). Utilisez\\n. Méthode de validation : extrayez les deux<script>générés parbuildHome()et exécuteznode --checkindividuellement (j'ai ajouté cette étape de régression). - 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]