workflow-visualizer: Geschäftsprozesse → interaktive Flussdiagramme
Rendering-Skript: scripts/flow.mjs (relativ zum Skill-Verzeichnis)
Absoluter Pfad: scripts\flow.mjs
Vollständiges Feld- und Syntaxhandbuch: references/spec.md (Vor dem Schreiben einer Spezifikation zuerst lesen)
1. Kernbeschränkungen (warum dieser Ansatz)
- Null Abhängigkeiten: Verwendet nur
node:Built-in-Module. Keinnpm installnötig, kein Mermaid installieren. - Rein offline: Layout und Rendering sind eigenständig in SVG implementiert, keine CDN-Anfragen. Funktioniert bei Netzwerkausfall, Intranet, ohne Internet (lokale
web_fetch/docs.deepseek.com sind nicht erreichbar; jeder Ansatz, der auf ein CDN angewiesen ist, schlägt fehl). - In sich geschlossene Ausgabe: Exportierte
.svgenthalten<style>und können korrekt angezeigt werden, wenn sie separat verschickt werden;.htmlist eine einzelne Datei, die per Doppelklick geöffnet werden kann.
2. Drei Schritte zur Nutzung
Schritt 1: Prozess extrahieren (dieser Schritt ist obligatorisch) Extrahiere den Prozess aus den tatsächlichen Materialien des Benutzers, erfinde nichts. Priorität der Quellen:
- Pläne/Retrospektiven-Dokumente im Arbeitsbereich (
*.md, enthalten normalerweise Abschnitte wie "Workflow/SOP/Zeitplan") - Mündliche Beschreibungen des Benutzers (markiere unsichere Stellen mit
status: holdund vermerke sie innoteals „zu bestätigen“) - Konversationsverlauf oder Logs
Extrahiere fest diese fünf Dinge: Schritte (wer macht was), Zeitfenster, Abhängigkeiten in der Reihenfolge, Entscheidungszweige, Risiken/Manuelle Prüfpunkte.
Schritt 2: Spezifikation schreiben
- Einfaches Diagramm (rein linear + ein oder zwei Verzweigungen) → Schreibe direkt Mermaid und speichere es als
.mmd, das ist am einfachsten. - Mit Zeit/Verantwortlichem/Status/Beschreibung/Gruppierung → Schreibe JSON (viel höhere Informationsdichte, das Panel hat dann Inhalt zum Anzeigen).
- Führe zuerst
sampleaus, um eine fertige Spezifikation als Vorlage zu erhalten:
node "scripts\flow.mjs" sample --out ".\out\MeinProzess.json"
Schritt 3: Validieren + Rendern
$sk = "scripts\flow.mjs"
node $sk check ".\out\MeinProzess.json" # Strukturprüfung: isolierte Knoten/unerreichbar/kein Endpunkt/Kreis
node $sk render ".\out\MeinProzess.json" --all --out ".\out\MeinProzess.html"
Die Ausgabe landet standardmäßig im selben Verzeichnis wie die Eingabedatei.
Ein render erzeugt zwei Layouts (im selben HTML, automatisch nach Bildschirm ausgewählt, unten manuell umschaltbar):
| Layout | Anordnung | Anpassung |
|---|---|---|
Hochformat portrait | Einspaltig vertikal (wrap: 0) | Handy: vertikales Scrollen, Breite füllt Bildschirm |
Querformat landscape | Langer Prozess automatisch in mehrere Spalten aufgeteilt (wrap: -1, Ziel-Seitenverhältnis 1,6) | Computer: auf einen Bildschirm passend |
Nützliche Parameter: --out <datei.html>, --all (zusätzlich <name>-hoch.svg, <name>-quer.svg, .mmd), --wrap N (feste Zeilen pro Spalte im Querformat), --debug (druckt Spalten-/Zeilenanzahl und Mitglieder beider Layouts).
Beide Layouts validieren (empfohlen):
node $sk preview ".\out\MeinProzess.html" # Erzeugt „[Für Computer] Vergleichsvorschau.html“: Handy/Computer zwei iframes nebeneinander
Öffne es im Debug-Browser, um auf einen Blick zu sehen, ob beide Versionen korrekt sind. Für reale Gerätegrößen verwende CDP-Gerätesimulation:
# Handy + Computer zwei Screenshots, druckt tatsächliche Höhen von Canvas/Aside/Footer und automatisch ausgewähltes Layout
node $sk\..\tools\devshot.mjs "http://127.0.0.1:8765/MeinProzess.html" Handy.png Computer.png
# Um JS im Seitenkontext zur Validierung von Interaktionen auszuführen (z.B. Knoten klicken, Suche eingeben, Drawer öffnen):
node $sk\..\tools\probe.mjs <url> probe.js --w 390 --h 844 --mobile --touch
Diese beiden Tools erstellen ihre eigenen temporären Tabs, schließen sie nach Gebrauch und greifen nicht auf die vom Benutzer durchsuchten Tabs zu (Frühere Versionen haben direkt das erste Page-Target genommen, was den gerade betrachteten Tab des Benutzers überschrieben hat – die Lektion ist unten dokumentiert). Beachte jedoch: Der neue Tab erscheint im Browserfenster des Benutzers und erobert den Fokus – wenn der Benutzer diesen Debug-Browser gerade verwendet, frage vorher, sonst unterbrecht ihr euch gegenseitig.
3. Nach dem Rendern: Selbstprüfung (verlasse dich nicht auf „Befehl erfolgreich“)
Befehl erfolgreich ≠ Diagramm ist gut. Führe mindestens eine visuelle Überprüfung mit browser-control durch:
# 1) Erstelle eine Kopie mit ASCII-Dateinamen, um Probleme mit chinesischen Zeichen in file:// zu vermeiden
Copy-Item ".\out\MeinProzess.html" ".\out\preview.html" -Force
# 2) Verwende den System-URI-Generator, um die korrekte file://-Adresse zu erhalten (manuelle Prozentkodierung ist fehleranfällig)
$u = [System.Uri]::new((Resolve-Path ".\out\preview.html").Path).AbsoluteUri
node browser-control\webctl.mjs shot $u "out\shot.png" 5000
Dann das Bild ansehen und bestätigen: Text nicht abgeschnitten, Zweige nicht überkreuzt, Gruppenbänder überdecken keine Knoten.
Klick-Interaktionen können auch per Skript validiert werden (webctl.mjs evalfile, beachte, dass es ein (function(){...})() Ausdruck sein muss; nacktes return führt zu „Illegal return statement“).
4. Für Menschen: Computer / Handy – zwei Öffnungsmethoden
Computer: Einfach die .html (Einzeldatei, offline nutzbar) doppelklicken. Zum Einfügen in Dokumente die .svg ziehen.
Handy (HTML auf dem Computer, der Handy-Browser hat keine „lokale Datei öffnen“ Funktion) – verwende LAN-Zugriff, keine Dateiübertragung nötig:
node "scripts\serve.mjs" ".\out" 8765
Das Skript gibt direkt die verfügbare Handy-Adresse aus (VPN/virtuelle Netzwerkkarten/169.254 werden automatisch als „ignorieren“ markiert), etwa so:
Handy → http://<DeineLAN-IP>:8765/ ✔ Diesen verwenden (WLAN)
Das Handy verbindet sich mit demselben Wi-Fi, öffnet die Adresse im Browser und klickt auf den Dateinamen. Wichtig:
- Gib nur das Ausgabeverzeichnis frei, nicht den Arbeitsbereichsroot.
- Windows fragt beim ersten Mal nach der Firewall: Wähle „Privates Netzwerk“ und erlaube eingehende Verbindungen; falls kein Dialog erscheint und es nicht funktioniert, blockiert die Firewall.
- Die Verzeichnisseite ist eine Dateiliste, über die auf dem Handy auf jede
.html/.svg/.pngzugegriffen werden kann. - Nach der Nutzung kann ich den Dienst stoppen (er läuft im Hintergrund) oder das Terminalfenster einfach schließen.
Nicht im selben Netzwerk / Senden an andere: Sende die HTML-Datei selbst – sie ist in sich geschlossen, offline nutzbar, kompatibel mit WeChat/QQ zum Dateiversand; für reine Bilder klicke auf „Export PNG“ in der Seite oder verwende Strg+P für PDF.
Bedienung auf dem Handy: Ein-Finger-Ziehen zum Verschieben, Zwei-Finger-Pinch zum Zoomen, Tippen auf Knoten für Details, unten „Anpassen“ zum Zurücksetzen auf Gesamtansicht; Hoch-/Querformat-Schalter links in der Fußzeile (auf dem Handy nur als Symbole).
| Bereich | Funktion |
|---|---|
| Obere Leiste | Branding-Punkt + Titel, Suchfeld, „Alle Flussdiagramme“-Button (oben rechts), Status-Legenden-Kapseln (zum Filtern durch Klicken) |
| Rechte Spalte | Wenn nichts ausgewählt: Prozessübersicht; bei Suche: zuerst Trefferliste (siehe unten); nach Knotenauswahl: Detailkarte + „Zurück zur Übersicht“ |
| Fußzeile | Hoch-/Querformat-Umschalter, Übersicht/Detail (Handy), Zoom, Anpassen, Export SVG/PNG, Hell-/Dunkelmodus, Mermaid-Quellcode |
Die drei Stufen der Suchergebnisse (wichtig, nicht als Bug betrachten)
Die Suche durchsucht gleichzeitig Titel / ID / Zeit / Verantwortlichen / Beschreibung / Anmerkungen, aber auf der Zeichenfläche sind nur Titel sichtbar, daher gibt es zwei Arten von Treffern:
- Sichtbare Treffer (Titel/ID/Zeit/Verantwortlicher) → Knoten haben blauen Rand, Zählung zählt einen
- Treffer nur in Beschreibung/Anmerkungen → Knoten haben orangefarbenen gestrichelten Rand, Zählung als
2 Treffer (1 nur in Beschreibung/Anmerkungen), rechts erscheint gleichzeitig eine Trefferliste (Klick zum Navigieren)
Die Trefferfarbe verwendet die dedizierte Variable
--hit(dunkles Thema#4a9dff, Beige-Thema#2b7fe0), nicht--accent– die Akzentfarbe im Beige-Thema ist Terrakotta#bf6b3a, das würde die Treffer „rot“ machen, was nicht zur Beschreibung „blau“ passt (schon erlebt).
Beispiel: Suche im Beispielprozess nach Tasse trifft 2 – Zweite Ausgabestation öffnen (Titel) und Nachmittagsauffüllung (Beschreibung „Tassen“); Suche nach f trifft jeden Knoten mit ID f und jeden Knoten mit Buchstabe f in Beschreibung/Anmerkungen. Derselbe Knoten wird nur einmal gezählt, das ist keine Doppelzählung.
Hell-/Dunkelmodus: Globaleinstellung / Einzelnes Diagramm / Sperren (wer zuletzt ändert, gewinnt)
Drei Schlüssel existieren im lokalen Speicher des Browsers (alle speichern JSON {mode, ts}, Vergleich per Zeitstempel, wer später ändert, gewinnt):
| Schlüssel | Wer schreibt | Bedeutung |
|---|---|---|
flowviz-theme | Startseite „Anzeigeeinstellungen“ | Globaleinstellung: System folgen / Immer dunkel / Immer hell |
flowviz-theme:<Dateiname> | Hell-/Dunkel-Button der Flussdiagrammseite | Einzelnes Diagramm separate Einstellung (Werte dark/light) |
flowviz-lock:<Dateiname> | „Farbe sperren“-Button der Flussdiagrammseite | Sperrt die Farbe des Diagramms nach dem Sperren ({locked:true, mode}) |
Aktivierungsregeln (das Bootstrap-Script im <head> der Seite und das Seiten-JS sind identisch, um Farbflackern zu vermeiden):
- Gesperrt → Verwende die zum Zeitpunkt der Sperrung eingefrorene Farbe, globale und einzelne Einstellungen haben keine Wirkung
- Sonst vergleiche die Zeitstempel von globaler und einzelner Einstellung, die neuere gewinnt
- Weder noch → System folgen (
prefers-color-scheme), Echtzeitverfolgung bei Systemwechsel
Also: Erst einzelnes Diagramm ändern, dann globale Einstellung → alle folgen der globalen; erst global ändern, dann einzelnes → nur dieses eine ändert sich. Nach dem Sperren muss zuerst entsperrt werden, um die Farbe zu ändern (Klick auf Hell-/Dunkel-Button im gesperrten Zustand wird abgefangen und es erscheint ein Hinweis).
Getestete Matrix (8 Schritte alle bestanden, 0 Ausnahmen): Leeren → global=hell → diagramm=hell; Einzelnen Hell-/Dunkel-Button klicken → diagramm=dunkel (schreibt Einzelschlüssel); global wieder auf hell → diagramm=hell (globale Einstellung aktualisiert, überschreibt Einzel); erneut einzelne Einstellung → nur dieses eine wird dunkel; sperren → globale Einstellung ändern → bleibt gesperrt im dunklen Modus; im gesperrten Zustand auf Hell-/Dunkel-Button klicken → blockiert mit Hinweis „Zuerst entsperren“; entsperren → zurück zur globalen Helleinstellung; Startseitenkarte zeigt Abzeichen „🔒 Gesperrt Hell“.
Weitere Punkte:
- Wiederholtes
setItemmit demselben Schlüssel überschreibt, es sammelt sich nicht an (getestet: nach 12-maligem Klicken auf Hell-/Dunkel-Button hatObject.keys(localStorage)immer noch nur 1 Eintrag, insgesamt 18 Bytes), kein „vorherigen löschen“ nötig - Die Präferenz wird auf dem Gerät im Browser gespeichert, ein Neustart des Dienstes hat keinen Einfluss; nur bei IP-/Port-Wechsel (geänderter Origin), Löschen der Browserdaten oder Browserwechsel wird zurückgesetzt
- Hell ist im Beige-Millennium-Stil, nicht reines Weiß: Hintergrund
#f2ebdd, Panels#fdfaf3, Text#33302a, Akzentfarbe Terrakotta#bf6b3a, Diagrammhintergrund#e6dcc8(dunkles Thema bleibt beim bisherigen Satz)
Obere Buttons
🏠 Startseite zurück zur Dateiliste (href="./") | Alle Flussdiagramme öffnet den rechten Drawer | Suchfeld | Statuslegende | Hell/Dunkel für dieses Diagramm | Farbe sperren friert die Farbe dieses Diagramms ein.
Im Drawer wird die aktuelle Seite als nicht klickbarer Link dargestellt (Klick hat keine Wirkung, um Verwirrung zu vermeiden), die Liste ist fest nach Dateinamen sortiert, daher wird beim Fetch-Refresh der Liste keine Neuordnung vorgenommen.
Der Button oben rechts öffnet den rechten Drawer, der alle vom Skill im selben Verzeichnis generierten Flussdiagramme auflistet (fest nach Dateinamen sortiert, aktuelle Seite mit „Aktuell“ markiert und als nicht klickbarer Link dargestellt), ein Klick wechselt zu einem anderen. Die Erkennung erfolgt über das data-flow-viz="1" Attribut der Seite, daher werden andere HTML-Dateien nicht aufgenommen.
- Die Listendatei ist
Flussdiagramm-Index.jsonim Verzeichnis, wird bei jedemrenderautomatisch neu erstellt (kann auch separat mitnode flow.mjs index <Verzeichnis>neu erstellt werden) - Bei HTTP-Zugriff aktualisiert der Drawer die Liste in Echtzeit per
fetch; beim Doppelklick auf eine lokale Datei wird die eingebettete Momentaufnahme verwendet (jede Seite enthält eine, daher ist auch lokal ein Wechsel möglich)
Interaktion der Suche (drei Patches für „Treffer nicht deutlich genug“)
- Ergebnispanel automatisch öffnen: Wenn Treffer gefunden und aktuell kein Knoten ausgewählt ist, wird
setPanel(true)aufgerufen – auf dem Handy ist die Ergebnisliste standardmäßig im eingefahrenen unteren Drawer versteckt, ohne Öffnen gibt es keinen Hinweis (das ist die Grundursache für „Suche hat nichts gezeigt“). - Vorheriger / Nächster: Die Titelleiste des Ergebnisbereichs hat
↑ / ↓undaktuell/gesamtZählung; im Suchfeld Enter = nächster, Shift+Enter = vorheriger;Esclöscht. - Autofokus (abschaltbar): Der Ergebnisbereich hat einen Schalter „Autofokus Treffer“. Wenn aktiv:
- Der Bounding Box der Treffer passt und der benötigte Zoom ≥ aktueller Zoom × 0,6 → alle Treffer einrahmen (mehrere Treffer auf einem Bildschirm sichtbar)
- Sonst (Treffer zu weit entfernt) → nicht verkleinern, aktuellen Zoom beibehalten und auf „aktuellen Treffer“ zentrieren, mit ↑/↓ einzeln springen
So wird nie der Text zu klein, um „zwei weit entfernte Treffer unterzubringen“. Die Suche fügt dem SVG die Klasse
.searchinghinzu: Nicht-Treffer ausgeblendet auf 14% (Linien 10%, Gruppenbänder 35%), Trefferstrichstärke auf 3px erhöht. Programmatische Bewegungen (Fokus/Springen) fügen#vpdie Klasse.vp-anim(0.28s Übergang) hinzu, manuelles Ziehen bleibt unbeeinflusst.
Doppelte Flussdiagrammnamen: Zuerst prüfen, ob „Inhalte gleich“ oder „nur Name gleich“
Wenn mehrere Dateien mit demselben Titel auftauchen (typische Quelle: Datei gelöscht und wiederhergestellt, System erzeugt xxx - Kopie.html), erscheint oben auf der Startseite ein Hinweisfeld, das nach Inhalt zwei Fälle behandelt:
| Fall | Kriterium | Aktion |
|---|---|---|
| Inhalte gleich (gleicher Prozess zweimal gerendert) | Prozesssemantischer Fingerabdruck gleich | „Nur die aktuellste behalten, Rest in den Papierkorb verschieben“ |
| Inhalte unterschiedlich (gleicher Name, aber verschiedene Versionen) | Fingerabdruck unterschiedlich | Nicht löschen, nur „Umbenennen…“ anbieten (Standardvorschlag Originalname (AlteVersion Datum).html) |
- Fingerabdruck = Prozesssemantik, nimmt nur
Titel/Untertitel/Richtung/Gruppierung/Knoten/Kanten, ignoriert Seitenfelder wieselfFile,flows,slug. Daher haben „zwei zu unterschiedlichen Zeiten gerenderte Dateien desselben Prozesses“ denselben Fingerabdruck, während „nur Seitenmetadaten geändert“ nicht fälschlich als unterschiedlicher Inhalt gewertet wird. - Löschen bedeutet immer „in Papierkorb verschieben“: Erstelle
.trash-JJJJ-MM-TT/im selben Verzeichnis, verschiebe die HTML zusammen mit ihren SVG / MMD (anhand des normalisierten Namens zugeordnet) perrenamehinein, jederzeit wiederherstellbar. - Die Aktion erfolgt über den Backend-Endpunkt
POST /__flow/act({op:'trash', remove:[...]}/{op:'rename', file, to}):- Nur von 127.0.0.1 erlaubt (Handy, LAN immer 403) – sonst wäre die Fähigkeit „lösche deine Dateien“ über WLAN verfügbar
- Dateiname durch Whitelist: kein Pfadtrennzeichen, Länge ≤ 200, muss auf
.htmlenden; Namenskonflikt gibt 409 zurück - Im Handy-/lokalen file:// Modus werden keine Buttons, sondern nur Hinweise angezeigt (in diesen Szenarien gibt es kein beschreibbares Backend)
- Auf der Karte wird weiterhin der Dateiname angezeigt (wenn der Titel doppelt ist), zur manuellen Unterscheidung
Zwei Öffnungsmethoden (beide müssen zur Startseite führen)
| Methode | Wie zur Startseite | Woher kommt die Startseite |
|---|---|---|
Lokal per Doppelklick (file://) | 🏠 im Flussdiagramm → ./index.html | Von render geschriebene statische index.html (der native Browser-Directory-Listing zeigt nur Dateinamen, daher ist diese notwendig) |
| LAN / HTTP | 🏠 → ./ | Dynamisch von serve.mjs generiert |
rendererstellt bei jedem Lauf dieindex.htmlneu, sie teilt sich denselben Rendering-Code mit der HTTP-Startseite (buildHome(), exportiert vonserve.mjs;serve.mjsstartet nur bei direktem Aufruf den Dienst, bei Import stellt es nur Funktionen bereit)- Unterschiede zwischen statischer und HTTP-Version: Breadcrumb zeigt
📁 Verzeichnisname (Lokaler Ordner), Abzeichen zeigt „Flussdiagramm-Seite“, Fußzeile enthält einen Hinweis „Lokaler Dateimodus: Für Handyzugriff muss zuerst der Dienst gestartet werden“ - Der 🏠 Button ist protokollbewusst: Bei
location.protocol==='file:'wird href auf./index.htmlgeändert. - Die
index.htmlerscheint nicht selbst in der Kartenliste (frühe Versionen taten das, die erste Karte hieß „index“); HTML-Dateien, die nicht in der Liste sind und auch keinflow-stats-Attribut enthalten, werden unter „Andere Dateien“ einsortiert.
Startseite (gleiche Seite für Computer und Handy)
- Breadcrumb:
🏠 Flussdiagramme / Unterverzeichnis (aktuell), jede Ebene anklickbar - Festplattenpfadblock: Zeigt direkt
\out\an, mit „Pfad kopieren“-Button – zum Senden der Originaldateien oder Finden der Quelldatei ohne Raten. - Suche + Sortierung + Zusammenfassung: Filtere nach Titel/Dateinamen; Sortierung umschaltbar zwischen „Zuletzt geöffnet zuerst / Nach Name / Nach Änderungsdatum“; Zusammenfassung zeigt
3 Diagramme · Insgesamt 36 Schritte / 47 Verbindungen · 4 mit Aufmerksamkeit (2 Diagramme) - Zuletzt geöffnet: Beim Klick auf eine Karte wird sie in
flowviz-recentgespeichert (max. 8 Einträge), standardmäßig oben angeheftet mit Abzeichen „Zuletzt geöffnet“ - Karten zeigen auch Abzeichen „🔒 Gesperrt X“ „Separat auf X gesetzt“ (liest lokalen localStorage des Geräts)
- Texte nach Gerät: Gleiches HTML enthält zwei Sätze von Hinweisen, umgeschaltet mit
@media (hover:none),(pointer:coarse)(genauer als Breitenabfrage, auch Querformat-Handys korrekt):- Computer (
.only-pc):🖱️ Karte klicken zum Öffnen. Im Diagramm: Mausrad Zoom, Ziehen zum Verschieben; oben rechts „Anzeigeeinstellungen“ für Hell/Dunkel - Handy (
.only-touch):📱 Karte klicken zum Öffnen: Ein-Finger-Ziehen auf dem Diagramm, Zwei-Finger-Zoom, Schritt antippen für Details… - Kartenabzeichen „Auf dem Handy tippen“ mit Klasse
.touchonly, auf dem Computer nicht sichtbar; Abzeichen der Vergleichsseite neutral „Nebeneinander-Vergleichsseite · Für Computer“ (von beiden verständlich). - Hinweise in Fuß-/Seitenleisten der Flussdiagrammseite ebenso: Computer sieht
Mausrad Zoom · Ziehen Verschieben · Esc, Handy siehtEin-Finger-Ziehen · Zwei-Finger-Zoom
- Computer (
- Gleichnamige Flussdiagramme (z.B.
xxx - Kopie.htmlundxxx.html) sind optisch nicht unterscheidbar → Diese Karten zeigen unter dem Titel zusätzlich den Dateinamen - Thumbnails passen nach normalisiertem Namen: Entferne Leerzeichen und
-quer/-hochvor dem Vergleich, daher passtBeispielprozess - Kopie.htmlzuBeispielprozess-quer - Kopie.svg(Suffixe an unterschiedlichen Positionen werden auch erkannt). - Verzeichnisseite muss
cache-control: no-storeim Header haben: Sie wird dynamisch generiert; sobald der Browser sie cached, sieht er die neue Version nicht (einmal erlebt: Nutzer sah ständig die alte reine Liste und dachte fälschlich „Startseite ist sehr spartanisch, keine Thumbnails“).
Bedienung auf dem Handy
Ein-Finger-Ziehen zum Verschieben, Zwei-Finger-Pinch zum Zoomen, Tippen auf Knoten für Details, unten „Anpassen“ zum Zurücksetzen auf Gesamtansicht.
Auf dem Handy sind ** Übersicht/Detail als überlagernder unterer Drawer (position:fixed)**: Tippen auf Knoten schiebt ihn heraus, Tippen auf leeren Raum schließt ihn, Button „Übersicht“ in der Fußzeile öffnet ihn manuell. Er ist eine Überlagerung und kein Flex-Geschwister – daher ändert sich die Zeichenflächenhöhe beim Öffnen/Schließen nicht (getestet 607→607), wodurch die Kette „Canvas wird kürzer → Neu anpassen → Diagramm wird abgeschnitten“ vermieden wird.
Zwei Implementierungsdetails zu „sieht nach Zoom nicht richtig aus“:
- Punktmuster-Hintergrund innerhalb von
#vp, 3× die Zeichenfläche und um den Inhalt zentriert (x=-W, y=-H, w=3W, h=3H). So folgt der Hintergrund dem Zoom/Pan und die harte Kante des Hintergrundrechtecks wird nicht sichtbar (frühere Versionen hatten den Hintergrund nur in Zeichenflächengröße, bei 150% Zoom und Pan war eine Kante sichtbar). - HTML-Version zeichnet keinen SVG-Titelblock, nur exportierte
.svgtun das (die Seitenkopfzeile hat bereits den Titel). Sonst würde der Titel bei Zoom die Knoten überdecken. - Schmalbildschirm-Querformat immer im Gesamtansichts-Modus: Kriterium ist
Canvas-Breite/Inhaltsbreite > fit×1.8und Inhalts-Seitenverhältnis ≤ 1,2 nur dann „Breite füllen“. Querformat ist ein flaches Diagramm mit 1,58; auf einem 390px Bildschirm würde „Breite füllen“ auf 150% zoomen (nur eine Ecke sichtbar), bleibt nun stabil bei 100%.
5. Erfahrungsregeln für Geschäftsprozessdiagramme
- Knotengranularität: Ein Knoten = eine auslieferbare Aktion („Skript schreiben“ ist ein Knoten, „Nachdenken“ nicht). 8–16 Knoten sind am angenehmsten, über 20 in Hauptdiagramm + Unterprozesse aufteilen.
- Status verwenden:
done/doing/todo/risk/holdsind keine Dekoration. In realen Prozessen markiere Blockaden wie „warte auf Bestätigung/warte auf Material“ mithold, „muss manuell geprüft werden“ mitrisk– genau das macht Flussdiagramme besser als Dokumente. - Entscheidungsknoten nur für echte Zweige:
{...}für Entscheidungen, die zu unterschiedlichen Pfaden führen. Nur „überprüfen und weitermachen“ verwendemilestone. - Zeitfenster nach Möglichkeit aus Materialien, nicht erfinden. Wenn Material keine hat, leer lassen, nicht raten.
- Urheberrecht/Compliance/Rotlinien-Knoten explizit zeichnen und in
noteschreiben, sonst sieht das Diagramm gut aus, aber man läuft in Fallstricke. - Lange Ketten nicht horizontal mit
LR: 13 Schritte als LR ergeben 1980×489 (4:1), bei 1100px Canvas fit nur 0,55×, Text verschwommen; Spaltenlayout (Querformat) reduziert dasselbe auf ca. 1,6:1, auf einen Bildschirm passend ohne Schrumpfen. Erfahrung: Kettenförmige Prozesse horizontal → Spaltenlayout verwenden, nicht LR. - Welches Layout wählen: Handy: Hochformat (eine Spalte nach unten, intuitiv); Computer: Querformat (Spalten, globaler Überblick). Der Schwellwert von
autoVariant()istBreite<760px oder Seitenverhältnis<1.08 → Hochformat. - Leserichtung der Spalten ist weiterhin nach unten: Kein Schlangenlinien (abwechselnd oben/unten), da umgekehrte Spalten die Pfeilrichtung verwirren würden. Spaltenübergreifende Verbindungen laufen durch Zwischenräume zwischen den Spalten (bei nur einem Knoten pro Zeile) oder unteren Korridor (bei mehreren Knoten pro Zeile), nicht durch Knoten.
- Gruppen teilen sich beim Spaltenüberschreiten automatisch in mehrere Segmente auf und werden mit „(Fortsetzung)“ markiert, um zu vermeiden, dass ein Rechteck unzusammenhängende Knoten umschließt.
6. Bestätigte Aufzeichnungen (2026-10-04)
- Eingabeformate: JSON / Mermaid-Teilmenge /
```mermaidCodeblöcke in.md, alle drei Pfade erfolgreich getestet. - Mermaid Hin- und Rückweg: Parsen → erneut als
.mmdexportieren, Erkennung von Knoten{}, gestrichelten Pfeilen-.->, Kantenbeschriftungen-->|hat|,subgraphGruppierungen korrekt erhalten. - Zwei Layouts (13 Knoten / 13 Verbindungen / 3 Gruppen Beispielprozess): Hochformat 533×1936 (eine Spalte 12 Zeilen); Querformat 1119×710 (automatisch aufgeteilt in 3 Spalten × 4 Zeilen, Seitenverhältnis 1,58, ein Bildschirm). 7-Knoten Beispielprozess: Hochformat 533×890, Querformat 806×459 (automatisch 2 Spalten).
- Automatische Layoutauswahl (CDP-Gerätesimulation getestet): 390×844 →
show=portrait; 1440×820 →show=landscape. - Handy-Layout (390×844 gemessene Höhen): Canvas 364 / Übersichtspanel 253 / Fußzeile 81, Übersichtsinhalt vollständig gerendert (panelLen 1533).
- Touch-Gesten (CDP synthetisierte TouchEvent getestet): Ein-Finger-Ziehen Verschieben ✓, Zwei-Finger-Pinch Zoomen ✓, Tippen auf Knoten auswählen und Popup „← Zurück zur Übersicht | s | T 12:00 Start | Start | Abgeschlossen“ ✓. Mauspfad ebenfalls alle drei bestanden.
- Interaktion: Klick auf Knoten → Detailkarte + Hervorhebung von Vorgänger/Nachfolger + Rest abgedunkelt; Legendenschreiben schreibt
data-hide-status; Suche Trefferzählung;+/0Tasten Zoom und Anpassen auf 100%; Hell-/Dunkelmodus Umschalten; Beide Layouts behalten Auswahl/Suche/Filter Status. - Ausnahme während des Ladens (
Runtime.exceptionThrown): Beide Beispielseiten 0 Ausnahmen. - Optimierte Version: Kartenförmige Knoten (Statusfarbstreifen + weicher Schatten), Typ-Abzeichen, Zeit-Pille, abgerundete orthogonale Bypässe, Kapsel-Kantenbeschriftungen, Gruppenband-Kapsel-Abzeichen, rechte Spalte Prozessübersicht (Zählung + Statusfilter + Liste der zu beachtenden Punkte), Punktmuster-Hintergrund, Symbolleiste.
Fallstricke, die gelöst wurden (bereits im Code behoben, notiert für zukünftige Zeitersparnis)
- Mermaid-Entscheidungsknoten nicht erkannt:
touch()schrieb dasshape-Feld,normalize()las daskind-Feld – Feldnamen nicht synchron, Diamanten wurden alle als Rechtecke gezeichnet. - Anführungszeichen in Beschriftungen verschluckt:
Beschriftung"Original"wurde zuBeschriftung"Original, weil unbedingt führende/schließende Anführungszeichen entfernt wurden; nur noch entfernen, wenn sie gleich sind. - Zoom-Prozentsatz stimmt nicht mit der Realität überein: SVG
viewBoxführt selbst einemeet-Skalierung durch, das ursprünglichetransformwurde darübergelegt, sodass „100%“ tatsächlich 26% betrug. Jetzt im Pixelraum der Elemente einheitlichview{K,TX,TY}berechnen und dann umkehren. - PowerShell
Set-Contenthat UTF-8 BOM hinzugefügt, was#!/usr/bin/env nodemitSyntaxErrorzerstörte. Dieses Skript nicht mit Shell schreiben/dabei Dateien schreiben. b.membersundefiniert: Gruppenband-Objekt hattemembersnicht übergeben, Diagramm mit Bändern stürzte ab.- Fit gesamt ließ langes Flussdiagramm auf 26% schrumpfen: Geändert zu „Wenn Gesamtansicht und Breitenfüllung weniger als 1,8× auseinander liegen, dann Gesamtansicht, sonst Breitenfüllung + oben ausrichten“.
- Diamant drückt Text: Entscheidungsknoten mit innenliegendem Rechteck-Berechnungsverfahren neu berechnet, damit
Ist das ein Syntax- oder Semantikproblem?in eine Zeile passt. - ID-Konflikt bei gleichzeitigem Vorhandensein beider Layouts:
renderSvg<g id="vp">hatte keine Layout-Version als Suffix, ClientgetElementById('vp-landscape')erhielt null →paint()warf Ausnahme, gesamte Initialisierung unterbrochen, Übersichtspanel leer (Phänomen:asidenur noch 25px). Alle IDs (defs/filter/pattern/marker/vp) beider Layouts müssen-${vid}enthalten. wrap-Parameter-Semantik umgekehrt: Es bedeutet „Zeilen pro Spalte“, kein Umbruch sollteR = Lsein, nichtR = 1(R=1 ergab pro Ebene eine Spalte als horizontalen Streifen, Hochformat wurde einmal zu 3476×101).- CDP-Ausnahme-Wiedergabe täuscht: Nach
Runtime.enablewerden auch Ausnahmen des vorherigen Dokuments gesendet. Vor der Beurteilung „hat die Seite wirklich einen Fehler“ zuerstPage.navigatezuabout:blankund dann zur Zielseite, sonst werden Fehler der alten Seite der neuen Seite zugeschrieben. - Bestätigung des Tippens auf Auswahl vorher anpassen: Nach Gestenoperationen kann der Zielknoten aus dem sichtbaren Bereich verschoben worden sein,
elementFromPointtrifft ihn dann natürlich nicht – das ist ein Problem des Testskripts, nicht der Seite. - CDP-Validierung hat den Tab des Benutzers übernommen:
webctl shot/ direktes Greifen des ersten Page-Targets navigiert den Tab, den der Benutzer gerade ansieht; wenn der Benutzer gleichzeitig den Browser verwendet, unterbrechen sie sich gegenseitig (getestet: Ich navigierte hin, der Benutzer suchte und navigierte zurück,locationänderte sich alle paar Dutzend Sekunden zur Praktikumsseite). Geändert zutools/cdp.mjsmitwithOwnTab()– selbstTarget.createTargeterstellt temporären Tab, fragt/json/listab, schließt mitTarget.closeTargetnach Gebrauch. Dennoch der neue Tab erobert den Fokus, also vorher fragen, wenn der Benutzer den Browser verwendet. - Suche „zu viele“ Treffer sind kein Bug: Suche nach
Mundtrifft 2, weil die Suche auch Beschreibung/Anmerkungen durchsucht („Mundkalibrierung“) und ID (Knoten-IDj). Lösung ist nicht, die Zahl zu ändern, sondern unsichtbare Treffer zu markieren (orangefarbener gestrichelter Kreis + gestaffelte Zählung + Trefferliste). - Hintergrund-Tabs können nicht gescreenshottet werden:
Target.createTarget({background:true})stiehlt nicht den Fokus, aber Hintergrund-Tabs erzeugen keine Frames,Page.captureScreenshothängt für immer (getestet 120s Timeout). Für Screenshots muss ein Vordergrund-Tab verwendet werden (stiehlt Fokus); Hintergrund-Tabs sind nur für reine Geometriemessungen geeignet (getBoundingClientRect/getComputedStylefunktionieren normal). Außerdem werden CSS-Transitionen in Hintergrund-Tabs nicht ausgeführt – vor Geometriemessungen*{transition:none!important}injizieren, sonst wird der Animationsstart gemessen (ich hatterect.x=398des Drawers fälschlich als „nicht geöffnet“ interpretiert, dabei war es nur gedrosselt). - FÜR COMPUTER bestimmte Ausgaben erhalten auch das Label „Auf dem Handy tippen“: Die Abzeichen der Dateiliste basieren nur auf der Dateierweiterung. Jetzt erhalten Dateien, die mit
【oder_beginnen, ein separates Label „Nur für Computer, auf dem Handy nicht öffnen“ und werden abgeblendet – die Bedingung muss mit dem angezeigten Text zusammen geändert werden. - Validierungsausgabe nicht im bedienten Verzeichnis platzieren:
shot-*.png, Probeskripte erscheinen in der Handy-Dateiliste. Einheitlich in ein nicht bedientes Verzeichnis legen (z.B.out/_shots/), nicht in das vonserve.mjsbediente Ausgabeverzeichnis. - Neuanordnung der Drawer-Liste unter dem Finger = Klick auf falschen Eintrag: Die eingebettete Momentaufnahme der Seite hatte „aktuelle Seite zuerst“, während
fetch('Flussdiagramm-Index.json')nach mtime sortiert zurückkam – etwa 100ms nach Öffnen des Drawers sprang die Liste, der Benutzer klickte nicht mehr auf den gewünschten Eintrag, was sich als „Klick hat keine Wirkung / kann nicht zurück“ äußerte. Lösung in zwei Teilen: Liste fest nach Dateinamen sortieren (Reihenfolge von Momentaufnahme und Live-Liste immer identisch), aktuelle Seite als nicht klickbar darstellen. Lektion: Jede Liste, die sich nach dem Öffnen asynchron aktualisiert, muss stabile Reihenfolge gewährleisten. - Änderungen an serve.mjs erfordern einen Neustart des Dienstes: Das Seiten-Template befindet sich im Prozessspeicher, ohne Neustart bleibt es die alte Version (ich habe deshalb vergeblich nach einem fehlenden h1 auf der Startseite gesucht).
- Fehlendes
cache-controlauf der Verzeichnisseite ließ Benutzer immer die alte Startseite sehen: Der Benutzer sagte zweimal „Die Computer-Startseite ist zu spartanisch/hat keine Thumbnails“, aber ich hatte sie längst auf Karten + Thumbnails umgestellt, der Browser lieferte nur die alte gecachte Antwort. Dynamisch generiertes HTML mussno-storehaben. - Mit
Start-Processseparat gestartete Dienste überleben das Ende des Befehls nicht: Die Sandbox räumt beim Ende des Befehls die Prozessbaum mit auf, der Dienst verschwindet leise (äußert sich bei der nächsten Anfrage als „kann keine Verbindung zum Remote-Server herstellen“). Verwende einen Harness-Hintergrundprozess oder lasse den Benutzer den Dienst selbst im Terminal ausführen. - Nimm keine gleichnamigen/ähnlichen JSONs im Verzeichnis als Eingabe: Es gab eine
ProzessA.json, deren Name nach Prozess A klang, aber deren Inhalt die Spezifikation von Prozess B war;renderüberschrieb die bestehendeProzessA.htmlmit anderem Inhalt. Vor dem Rendern dentitle/die Knotenanzahl im JSON überprüfen und vorzugsweise die Eingabe mit demselben Namen wie die Ausgabe verwenden. - Anführungszeichen in chinesischen Kommentaren können JS-Strings mit doppelten Anführungszeichen abschneiden (
" // Gemeint ist die Liste der "reinen Dateinamen""→ vorzeitiges Ende). In denselben Fall dreimal getappt: In chinesischen Kommentaren entweder keine Anführungszeichen verwenden oder 「」. - Unter
file://gibt es keine „Verzeichnis-Startseite“:href="./"führt lokal zum nativen Dateinamen-Listing des Browsers, der Benutzer sieht nur „Dateinamen“. Daher muss eine separate statischeindex.htmlerstellt werden, und der Startseiten-Button muss das Ziel je nach Protokoll umschalten. - Screenshot-Methode ohne Debug-Browser: In der Sandbox kann Edge nicht starten (Mojo Named Pipe verweigert), aber
msedge --headless=new --screenshot=... --window-size=W,H <url>ist als einmaliger Render und Dateischreibvorgang machbar (benötigt einmalig uneingeschränkte Berechtigung). Kannfile://lokale Seiten und auch gerätespezifische Größen für Handy/Computer erfassen – war entscheidend für die Lokalisierung „Handy-Startseite sieht keine Karten“ und „Zusammenfassung rechnet 0“. - Regex in Template-Strings benötigen doppelten Backslash:
\d/\swerden in Template-Strings zud/sdegradiert, Regex im generierten Seiten-Code wird unbrauchbar (Zusammenfassung zeigte einmal „Insgesamt 0 Schritte“). Regex in Seiten-Skripten einheitlich mit\\d,\\sschreiben. - Texte nicht geräteunabhängig hartcodieren: Auf der Computer-Startseite „Auf dem Handy tippen“ und im Handy-Flussdiagramm „Mausrad Zoom/Doppelklick/Esc“ zu schreiben ist „Gerät falsch zugeordnet“. Lösung: Zwei Versionen pro Stelle (
.only-pc/.only-touch) +@media (hover:none),(pointer:coarse)Umschaltung – Eingabemethode ist genauer als Bildschirmbreite (Querformat-Handys und schmale Computerfenster werden korrekt erkannt). - Gleichnamige Flussdiagramme im selben Verzeichnis führen zu identischen Karten:
xxx - Kopie.htmlundxxx.htmlhaben denselben Titel, Benutzer können sie nicht unterscheiden. Bei doppelten Titeln zusätzlich den Dateinamen anzeigen; Thumbnails nach normalisiertem Namen (Leerzeichen und-quer/-hochentfernen) zuordnen. \nin Template-Strings wird zu echtem Zeilenumbruch: InhomeScript()etc. geschriebenes Seiten-JS'...\n\n' + xenthält im Seitenkontext tatsächliche Zeilenumbrüche im String-Literal → Seiten-Skript-Syntaxfehler (node --checkdes serve.mjs erkennt das nicht).\\nverwenden. Validierungsmethode: Die beiden vonbuildHome()generierten<script>-Blöcke extrahieren und einzeln mitnode --checkprüfen (diesen Regressionstest hinzugefügt).- Gleichheit des Inhalts nach Semantik, nicht nach Datei-Hash beurteilen: Der gesamte Seiten-Hash ist zwangsläufig unterschiedlich (Rendering-Zeit, ...)