jimku233/workflow-visualizer

Rendert Geschäftsprozesse als Mermaid-Quellcode + eigenständiges interaktives HTML-Flussdiagramm (null Abhängigkeiten, rein offline, keine CDN-Anfragen).

Was ist workflow-visualizer?

Dieses Skill verwandelt mündlich beschriebene oder in Dokumenten festgehaltene Arbeitsabläufe, SOPs, Produktionsketten oder Retrospektiven-Zeitlinien in interaktive Flussdiagramme. Es generiert sowohl Mermaid-Code (einfügbar in Feishu, Yuque oder GitHub) als auch eine vollständig eigenständige HTML-Datei, die ohne Internetverbindung oder zusätzliche Abhängigkeiten funktioniert. Der Skill unterstützt Such-/Filterfunktionen, Hell-/Dunkelmodi, Export als SVG/PNG, und bietet eine detaillierte interaktive Oberfläche mit Knotendetails (Zeit, Verantwortliche, Notizen). Entwickelt für Agenten wie Claude Code, Cursor oder Codex, die Prozesse visualisieren sollen, wenn ein Nutzer sagt 'Zeichne den Ablauf / Mach ein Flussdiagramm / Visualisiere den Workflow / Strukturiere den Prozess'. Die Ausgabe ist in zwei Layouts (Hoch- und Querformat) verfügbar, die sich automatisch an das Endgerät anpassen.

Funktioniert mit✓Claude Code✓Codex CLI✓Cursor
npx skills add jimku233/workflow-visualizer

Installed? Explore more Produktivität & Zusammenarbeit skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

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)

  1. Null Abhängigkeiten: Verwendet nur node: Built-in-Module. Kein npm install nötig, kein Mermaid installieren.
  2. 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).
  3. In sich geschlossene Ausgabe: Exportierte .svg enthalten <style> und können korrekt angezeigt werden, wenn sie separat verschickt werden; .html ist 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: hold und vermerke sie in note als „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 sample aus, 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):

LayoutAnordnungAnpassung
Hochformat portraitEinspaltig vertikal (wrap: 0)Handy: vertikales Scrollen, Breite füllt Bildschirm
Querformat landscapeLanger 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 / .png zugegriffen 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).

BereichFunktion
Obere LeisteBranding-Punkt + Titel, Suchfeld, „Alle Flussdiagramme“-Button (oben rechts), Status-Legenden-Kapseln (zum Filtern durch Klicken)
Rechte SpalteWenn nichts ausgewählt: Prozessübersicht; bei Suche: zuerst Trefferliste (siehe unten); nach Knotenauswahl: Detailkarte + „Zurück zur Übersicht“
FußzeileHoch-/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üsselWer schreibtBedeutung
flowviz-themeStartseite „Anzeigeeinstellungen“Globaleinstellung: System folgen / Immer dunkel / Immer hell
flowviz-theme:<Dateiname>Hell-/Dunkel-Button der FlussdiagrammseiteEinzelnes Diagramm separate Einstellung (Werte dark/light)
flowviz-lock:<Dateiname>„Farbe sperren“-Button der FlussdiagrammseiteSperrt 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):

  1. Gesperrt → Verwende die zum Zeitpunkt der Sperrung eingefrorene Farbe, globale und einzelne Einstellungen haben keine Wirkung
  2. Sonst vergleiche die Zeitstempel von globaler und einzelner Einstellung, die neuere gewinnt
  3. 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 setItem mit demselben Schlüssel überschreibt, es sammelt sich nicht an (getestet: nach 12-maligem Klicken auf Hell-/Dunkel-Button hat Object.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.json im Verzeichnis, wird bei jedem render automatisch neu erstellt (kann auch separat mit node 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“)

  1. 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“).
  2. Vorheriger / Nächster: Die Titelleiste des Ergebnisbereichs hat ↑ / ↓ und aktuell/gesamt Zählung; im Suchfeld Enter = nächster, Shift+Enter = vorheriger; Esc löscht.
  3. 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 .searching hinzu: Nicht-Treffer ausgeblendet auf 14% (Linien 10%, Gruppenbänder 35%), Trefferstrichstärke auf 3px erhöht. Programmatische Bewegungen (Fokus/Springen) fügen #vp die 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:

FallKriteriumAktion
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 unterschiedlichNicht löschen, nur „Umbenennen…“ anbieten (Standardvorschlag Originalname (AlteVersion Datum).html)
  • Fingerabdruck = Prozesssemantik, nimmt nur Titel/Untertitel/Richtung/Gruppierung/Knoten/Kanten, ignoriert Seitenfelder wie selfFile, 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) per rename hinein, 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 .html enden; 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)

MethodeWie zur StartseiteWoher kommt die Startseite
Lokal per Doppelklick (file://)🏠 im Flussdiagramm → ./index.htmlVon render geschriebene statische index.html (der native Browser-Directory-Listing zeigt nur Dateinamen, daher ist diese notwendig)
LAN / HTTP🏠 → ./Dynamisch von serve.mjs generiert
  • render erstellt bei jedem Lauf die index.html neu, sie teilt sich denselben Rendering-Code mit der HTTP-Startseite (buildHome(), exportiert von serve.mjs; serve.mjs startet 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.html geändert.
  • Die index.html erscheint 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 kein flow-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-recent gespeichert (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 sieht Ein-Finger-Ziehen · Zwei-Finger-Zoom
  • Gleichnamige Flussdiagramme (z.B. xxx - Kopie.html und xxx.html) sind optisch nicht unterscheidbar → Diese Karten zeigen unter dem Titel zusätzlich den Dateinamen
  • Thumbnails passen nach normalisiertem Namen: Entferne Leerzeichen und -quer/-hoch vor dem Vergleich, daher passt Beispielprozess - Kopie.html zu Beispielprozess-quer - Kopie.svg (Suffixe an unterschiedlichen Positionen werden auch erkannt).
  • Verzeichnisseite muss cache-control: no-store im 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 .svg tun 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.8 und 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

  1. 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.
  2. Status verwenden: done/doing/todo/risk/hold sind keine Dekoration. In realen Prozessen markiere Blockaden wie „warte auf Bestätigung/warte auf Material“ mit hold, „muss manuell geprüft werden“ mit risk – genau das macht Flussdiagramme besser als Dokumente.
  3. Entscheidungsknoten nur für echte Zweige: {...} für Entscheidungen, die zu unterschiedlichen Pfaden führen. Nur „überprüfen und weitermachen“ verwende milestone.
  4. Zeitfenster nach Möglichkeit aus Materialien, nicht erfinden. Wenn Material keine hat, leer lassen, nicht raten.
  5. Urheberrecht/Compliance/Rotlinien-Knoten explizit zeichnen und in note schreiben, sonst sieht das Diagramm gut aus, aber man läuft in Fallstricke.
  6. 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.
  7. Welches Layout wählen: Handy: Hochformat (eine Spalte nach unten, intuitiv); Computer: Querformat (Spalten, globaler Überblick). Der Schwellwert von autoVariant() ist Breite<760px oder Seitenverhältnis<1.08 → Hochformat.
  8. 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.
  9. 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 / ```mermaid Codeblöcke in .md, alle drei Pfade erfolgreich getestet.
  • Mermaid Hin- und Rückweg: Parsen → erneut als .mmd exportieren, Erkennung von Knoten {}, gestrichelten Pfeilen -.->, Kantenbeschriftungen -->|hat|, subgraph Gruppierungen 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; +/0 Tasten 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)

  1. Mermaid-Entscheidungsknoten nicht erkannt: touch() schrieb das shape-Feld, normalize() las das kind-Feld – Feldnamen nicht synchron, Diamanten wurden alle als Rechtecke gezeichnet.
  2. Anführungszeichen in Beschriftungen verschluckt: Beschriftung"Original" wurde zu Beschriftung"Original, weil unbedingt führende/schließende Anführungszeichen entfernt wurden; nur noch entfernen, wenn sie gleich sind.
  3. Zoom-Prozentsatz stimmt nicht mit der Realität überein: SVG viewBox führt selbst eine meet-Skalierung durch, das ursprüngliche transform wurde darübergelegt, sodass „100%“ tatsächlich 26% betrug. Jetzt im Pixelraum der Elemente einheitlich view{K,TX,TY} berechnen und dann umkehren.
  4. PowerShell Set-Content hat UTF-8 BOM hinzugefügt, was #!/usr/bin/env node mit SyntaxError zerstörte. Dieses Skript nicht mit Shell schreiben/dabei Dateien schreiben.
  5. b.members undefiniert: Gruppenband-Objekt hatte members nicht übergeben, Diagramm mit Bändern stürzte ab.
  6. 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“.
  7. Diamant drückt Text: Entscheidungsknoten mit innenliegendem Rechteck-Berechnungsverfahren neu berechnet, damit Ist das ein Syntax- oder Semantikproblem? in eine Zeile passt.
  8. ID-Konflikt bei gleichzeitigem Vorhandensein beider Layouts: renderSvg <g id="vp"> hatte keine Layout-Version als Suffix, Client getElementById('vp-landscape') erhielt null → paint() warf Ausnahme, gesamte Initialisierung unterbrochen, Übersichtspanel leer (Phänomen: aside nur noch 25px). Alle IDs (defs/filter/pattern/marker/vp) beider Layouts müssen -${vid} enthalten.
  9. wrap-Parameter-Semantik umgekehrt: Es bedeutet „Zeilen pro Spalte“, kein Umbruch sollte R = L sein, nicht R = 1 (R=1 ergab pro Ebene eine Spalte als horizontalen Streifen, Hochformat wurde einmal zu 3476×101).
  10. CDP-Ausnahme-Wiedergabe täuscht: Nach Runtime.enable werden auch Ausnahmen des vorherigen Dokuments gesendet. Vor der Beurteilung „hat die Seite wirklich einen Fehler“ zuerst Page.navigate zu about:blank und dann zur Zielseite, sonst werden Fehler der alten Seite der neuen Seite zugeschrieben.
  11. Bestätigung des Tippens auf Auswahl vorher anpassen: Nach Gestenoperationen kann der Zielknoten aus dem sichtbaren Bereich verschoben worden sein, elementFromPoint trifft ihn dann natürlich nicht – das ist ein Problem des Testskripts, nicht der Seite.
  12. 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 zu tools/cdp.mjs mit withOwnTab() – selbst Target.createTarget erstellt temporären Tab, fragt /json/list ab, schließt mit Target.closeTarget nach Gebrauch. Dennoch der neue Tab erobert den Fokus, also vorher fragen, wenn der Benutzer den Browser verwendet.
  13. Suche „zu viele“ Treffer sind kein Bug: Suche nach Mund trifft 2, weil die Suche auch Beschreibung/Anmerkungen durchsucht („Mundkalibrierung“) und ID (Knoten-ID j). Lösung ist nicht, die Zahl zu ändern, sondern unsichtbare Treffer zu markieren (orangefarbener gestrichelter Kreis + gestaffelte Zählung + Trefferliste).
  14. Hintergrund-Tabs können nicht gescreenshottet werden: Target.createTarget({background:true}) stiehlt nicht den Fokus, aber Hintergrund-Tabs erzeugen keine Frames, Page.captureScreenshot hä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 / getComputedStyle funktionieren normal). Außerdem werden CSS-Transitionen in Hintergrund-Tabs nicht ausgeführt – vor Geometriemessungen *{transition:none!important} injizieren, sonst wird der Animationsstart gemessen (ich hatte rect.x=398 des Drawers fälschlich als „nicht geöffnet“ interpretiert, dabei war es nur gedrosselt).
  15. 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.
  16. 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 von serve.mjs bediente Ausgabeverzeichnis.
  17. 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.
  18. Ä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).
  19. Fehlendes cache-control auf 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 muss no-store haben.
  20. Mit Start-Process separat 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.
  21. 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 bestehende ProzessA.html mit anderem Inhalt. Vor dem Rendern den title/die Knotenanzahl im JSON überprüfen und vorzugsweise die Eingabe mit demselben Namen wie die Ausgabe verwenden.
  22. 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 「」.
  23. 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 statische index.html erstellt werden, und der Startseiten-Button muss das Ziel je nach Protokoll umschalten.
  24. 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). Kann file:// 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“.
  25. Regex in Template-Strings benötigen doppelten Backslash: \d/\s werden in Template-Strings zu d/s degradiert, Regex im generierten Seiten-Code wird unbrauchbar (Zusammenfassung zeigte einmal „Insgesamt 0 Schritte“). Regex in Seiten-Skripten einheitlich mit \\d, \\s schreiben.
  26. 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).
  27. Gleichnamige Flussdiagramme im selben Verzeichnis führen zu identischen Karten: xxx - Kopie.html und xxx.html haben denselben Titel, Benutzer können sie nicht unterscheiden. Bei doppelten Titeln zusätzlich den Dateinamen anzeigen; Thumbnails nach normalisiertem Namen (Leerzeichen und -quer/-hoch entfernen) zuordnen.
  28. \n in Template-Strings wird zu echtem Zeilenumbruch: In homeScript() etc. geschriebenes Seiten-JS '...\n\n' + x enthält im Seitenkontext tatsächliche Zeilenumbrüche im String-Literal → Seiten-Skript-Syntaxfehler (node --check des serve.mjs erkennt das nicht). \\n verwenden. Validierungsmethode: Die beiden von buildHome() generierten <script>-Blöcke extrahieren und einzeln mit node --check prüfen (diesen Regressionstest hinzugefügt).
  29. Gleichheit des Inhalts nach Semantik, nicht nach Datei-Hash beurteilen: Der gesamte Seiten-Hash ist zwangsläufig unterschiedlich (Rendering-Zeit, ...)

Verwandte Skills