Files
posts/uebersicht.html
T
dejanandClaude Opus 5 bae57e64ce Post 4: die Workflow-Engine, drei Ansichten derselben Oberflaeche
Nummer 3 im Redaktionsplan, erscheint Mi 19.08. Carousel, vier Slides,
Theme modern, Variante hell.

DER BEITRAG
Ein Ablauf, die Bausteine, aus denen er entsteht, und der Hinweis, dass man
nicht bei null anfaengt:

  1  Automatisiere deinen Betrieb.   ein Ablauf auf dem Canvas
  2  Erst der Ausloeser.             die Ausloeser-Auswahl
  3  Dann die Aktionen.              die Aktionen-Auswahl
  4  Fang nicht bei null an.         drei fertige Vorlagen

Slide 2 und 3 teilen sich eine Funktion mit zwei Rubriken, weil es im Produkt
eine Auswahl ist: "Ausloeser" und "Aktionen" stehen dort untereinander im
selben Feld. Getrennt gezeichnet waeren es zwei Bilder, die zufaellig gleich
aussehen.

Vier Slides statt der fuenf bis sieben aus dem Redaktionsplan. Das ist eine
Spanne und kein Soll: eine Slide, die nichts hinzufuegt, kostet einen Wisch
und gibt nichts zurueck.

WAS AUS DEM PRODUKT KOMMT UND WAS NICHT
Der Knoten ist NodeItem aus ui/components/NodeWorkflowEngine des Pitch-Pakets:
quadratisch 128 x 128, rounded-lg, 6 px Rand, Titel und Beschreibung unter dem
Feld, beim Ausloeser die linke Flanke voll gerundet. Genau so rendert es auch
assets/screens/workflow.png.

Ein Zwischenstand zeichnete stattdessen breite Karten mit farbiger Kante links
und einem Block "BESCHREIBUNG" -- abgemalt vom Pitch-Bild 02_Praesentation/03.
Im Code des Projekts gibt es das nirgends: weder "BESCHREIBUNG" als Label noch
eine linke Farbkante, beides null Treffer. Der Kommentar behauptete trotzdem,
es sei ein Nachbau der Komponente. Das ist berichtigt.

Die Farben sind die des Produkts, aufgeloest aus ui/theme.css ueber die ganze
Token-Kette und nicht aus dem Bild geschaetzt. Sie tragen Bedeutung: gruen
faengt an, rot aendert Daten, blau meldet, amber macht eine Aufgabe daraus,
violett entscheidet. Ein Zwischenstand hatte sie durch Markenfarben ersetzt,
weil rules.md 2 diese Toene nicht kennt -- zurueckgenommen, der Beitrag zeigt
die Oberflaeche, und umgefaerbt waere es eine Nacherzaehlung.

Alle Produkttexte stehen woertlich in der Quelle. Wo ein Name nicht in eine
Zeile passte, steht er zweizeilig: aus "Daten vereinheitlichen" ein "Pruefen"
zu machen ist keine Kuerzung, sondern ein anderer Name.

DREI STELLEN, AN DENEN BEWUSST ABGEWICHEN WIRD
- Slide 1 gabelt nach dem zweiten Knoten. Alle drei echten Ablaeufe verzweigen
  erst an Position 3 oder 4, und das fuellt ein hochkantes Format schlecht --
  solange der Stamm laeuft, steht ueber und unter ihm nichts. Dafuer fehlt
  zwischen "Formular eingegangen" und "Passender Lead?" der Schritt "Daten
  vereinheitlichen". Ein Ablauf, den es genau so gibt, ist es damit nicht mehr;
  einer, den es geben koennte, schon.
- Slide 4 nennt die Demo-Ablaeufe "Vorlagen". Dass es eine Vorlagen-Bibliothek
  gibt, steht als Merkmal auf der Website; dass sie diese drei enthaelt, steht
  nirgends. Im Werkzeug werden sie ueber workflowId geladen, nicht als Galerie
  gezeigt, und das Wort "Vorlage" kommt in der Komponente nicht vor.
- Kein PDS im Beitrag. Von den sechs Vorlagen nennen drei das Branchenprogramm.
  Uebrig bleibt genau die Gruppe GENERAL_WORKFLOW_PRESETS, in der es kein
  einziges Mal vorkommt -- also keine Auswahl nach Geschmack, sondern die
  vollstaendige zweite Gruppe. Gekuerzt wurde nichts.

Erfundene Vorgaenge sind raus. Ein Zwischenstand hatte eine Slide mit einem
Aktivitaetenverlauf samt Uhrzeiten. Nichts davon war gemessen, und bei einem
Produkt vor dem Start ist eine erfundene Uhrzeit kein Beispiel, sondern ein
Beleg, den es nicht gibt.

DER PUNKTGRUND
Die Arbeitsflaeche des Workflow-Editors, Werte aus WorkflowEditor.tsx der
Website. Zwei begruendete Abweichungen: die Teilung ist verdoppelt, weil ein
24er Raster im Feed auf 9 px zusammenfaellt und zu grauem Schleier wird, und
der Grund bleibt weiss statt getoent, weil rules.md 1a das verbietet.

EIN FEHLER, DER FAST DURCHGERUTSCHT WAERE
Beim Entfernen der Karten-Regeln von Post 2 blieben rund neunzig Zeilen
Deklarationen ohne Selektor stehen, dazu ein Kommentar ohne Ende: nur die
Selektorzeilen waren weggeschnitten. CSS faengt sich davon nicht wieder, der
Parser ueberspringt bis zur naechsten Marke -- und dabei verschwand die Regel
[data-edit] darunter. Damit war das Verschieben von Elementen im Canvas tot,
ohne dass die Seite einen Fehler zeigte. Sichtbar wurde es erst, als ein
gespeicherter Versatz von 3 px nicht mehr ankam.

Behoben, und an der Stelle steht jetzt eine Warnung: Regeln als ganzen Block
entfernen und danach nachsehen, ob document.styleSheets sie noch alle kennt.

NEBENBEI
- Die Ueberschriften stehen auf denselben Werten wie post3.html, bis auf den
  Pixel nachgemessen: 76 px, -0,032em, nowrap und der Versatz dy 3, den auch
  post2, post2-alt und post3 auf jeder Slide tragen.
- Die Projektliste steht sechsmal im Repo, einmal je Post-Seite plus
  uebersicht.html. Post 4 ist ueberall eingetragen. Wer einen siebten Beitrag
  anlegt, muss an alle sechs Stellen denken.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 23:52:19 +02:00

233 lines
9.5 KiB
HTML

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PostGrid · Alle Projekte</title>
<link rel="stylesheet" href="./assets/fonts/manrope.css">
<style>
/* ─── Projektübersicht ────────────────────────────────────────────────────
Alle Streifen untereinander auf einer Fläche. Die Seite baut die Slides
nicht selbst: jedes Projekt kommt als iframe im Modus ?nur=streifen, in
dem die Post-Seite ihre Bedienoberfläche ausblendet und nur den Streifen
in natürlicher Größe zeigt. Skaliert wird hier außen.
Warum nicht nachbauen: die Streifen müssen genau das zeigen, was der
Generator zeigt — bis auf den letzten gespeicherten Versatz. Jede zweite
Implementierung würde irgendwann abweichen, und dann wäre die Übersicht
schlimmer als keine. */
* { box-sizing: border-box; }
:root { color-scheme: dark; }
html, body { height: 100%; }
body {
margin: 0; font-family: Manrope, system-ui, sans-serif;
background: #101116; color: #C7CCD6; overflow: hidden;
}
.menubar {
position: relative; z-index: 10; display: flex; align-items: center; gap: 2px;
height: 34px; padding: 0 10px; background: #16181F;
border-bottom: 1px solid #23262F;
font-size: 13px; user-select: none;
}
.menubar .marke { font-weight: 800; color: #FFFFFF; margin-right: 14px; letter-spacing: -.01em; }
.menu { position: relative; }
.menu > button {
font: inherit; color: #C7CCD6; background: none; border: 0;
padding: 5px 10px; border-radius: 6px; cursor: pointer;
}
.menu > button:hover, .menu[data-auf] > button { background: #23262F; color: #FFFFFF; }
.menu .liste {
display: none; position: absolute; left: 0; top: 100%; min-width: 250px;
padding: 5px; background: #1B1E26; border: 1px solid #2E313B;
border-radius: 9px; flex-direction: column;
}
.menu[data-auf] .liste { display: flex; }
.menu .liste button {
font: inherit; text-align: left; color: #C7CCD6; background: none;
border: 0; padding: 7px 10px; border-radius: 6px; cursor: pointer;
}
.menu .liste button:hover { background: #262A34; color: #FFFFFF; }
.menu .liste hr { border: 0; border-top: 1px solid #2E313B; margin: 5px 2px; }
/* Derselbe gepunktete Grund wie im Generator, aber ohne dessen Canvas:
hier wird nur gescrollt. */
.stage { position: absolute; inset: 34px 0 0; overflow: hidden;
background-color: #101116;
background-image: radial-gradient(circle, #282B35 1.6px, transparent 1.6px);
background-size: 26px 26px;
}
/* Nur hoch und runter. Die Streifen sind auf die Breite eingepasst,
also gibt es waagerecht nichts zu holen. */
.scroller { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden;
overscroll-behavior: contain; }
.flaeche {
width: max-content; min-width: 100%; height: max-content; min-height: 100%;
padding: 54px; display: flex; flex-direction: column; gap: 46px;
align-items: flex-start;
}
.reihe { display: flex; flex-direction: column; gap: 10px; }
.reihe h2 {
margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .14em;
text-transform: uppercase; color: #6E7480;
}
.reihe h2 b { color: #C7CCD6; font-weight: 800; }
/* Der Rahmen bekommt die Größe des skalierten Streifens, das iframe darin
bleibt in Originalgröße und wird nur transformiert. Ohne den Rahmen
würde das ungeskalierte iframe die Fläche aufblähen. */
.rahmen { overflow: hidden; background: #FFFFFF; border: 1px solid #2E313B; }
/* pointer-events: none -- sonst zoomt das Mausrad im Streifen die
Post-Seite darin, die ja ihren eigenen Canvas mitbringt. Die Uebersicht
ist zum Ansehen da, bedient wird im Projekt selbst. */
.rahmen iframe {
display: block; border: 0; transform-origin: 0 0; background: #FFFFFF;
pointer-events: none;
}
.reihe.fehlt .rahmen { display: grid; place-items: center; width: 420px; height: 90px;
background: #16181F; color: #6E7480; font-size: 13px; }
.hud {
position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
display: flex; align-items: center; gap: 12px;
padding: 8px 12px; background: rgba(16,17,22,.92);
border-top: 1px solid #23262F;
}
.hud p { margin: 0; font-size: 12px; color: #6E7480; letter-spacing: .04em; }
</style>
</head>
<body>
<div class="menubar">
<span class="marke">PostGrid</span>
<div class="menu" data-menu="projekt">
<button type="button">Projekt</button>
<div class="liste">
<button type="button" data-gehe="./post1.html">Post 1 · Lösung</button>
<button type="button" data-gehe="./post2.html">Post 2 · Die Geschichte</button>
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
<button type="button" data-gehe="./post3.html">Post 3 · Verbinden statt ersetzen</button>
<button type="button" data-gehe="./post4.html">Post 4 · Workflow-Engine</button>
<hr>
<button type="button" data-klick="neu">Streifen neu laden</button>
</div>
</div>
</div>
<main class="stage">
<div class="scroller" id="scroller">
<div class="flaeche" id="flaeche"></div>
</div>
<div class="hud">
<p id="meta">Lade Streifen ...</p>
</div>
</main>
<script>
(() => {
"use strict";
const PROJEKTE = [
{ datei: "./post1.html", name: "Post 1 · Lösung" },
{ datei: "./post2.html", name: "Post 2 · Die Geschichte", theme: "notebook" },
{ datei: "./post2-alt.html", name: "Post 2 · Alternative", theme: "modern" },
{ datei: "./post3.html", name: "Post 3 · Verbinden statt ersetzen", theme: "modern" },
{ datei: "./post4.html", name: "Post 4 · Workflow-Engine", theme: "modern" },
];
const flaeche = document.getElementById("flaeche");
const scroller = document.getElementById("scroller");
const meta = document.getElementById("meta");
/* Kein Zoom. Ein Streifen ist bis zu 6480 px breit und wird immer auf die
Fensterbreite eingepasst; gescrollt wird nur hoch und runter. Ein
Zwischenstand hatte Rad-Zoom und Zoomknöpfe -- das war doppelt gemoppelt,
weil die Post-Seite im iframe ihren eigenen Canvas mitbringt, und die
Beschriftungen skalierten nicht mit, standen beim Herauszoomen also
riesig über winzigen Streifen. */
const reihen = [];
function bauen() {
flaeche.innerHTML = "";
reihen.length = 0;
PROJEKTE.forEach(pr => {
const reihe = document.createElement("div");
reihe.className = "reihe";
const h2 = document.createElement("h2");
h2.innerHTML = "<b>" + pr.name + "</b>" + (pr.theme ? " · Theme " + pr.theme : "");
const rahmen = document.createElement("div");
rahmen.className = "rahmen";
const ram = document.createElement("iframe");
ram.src = pr.datei + "?nur=streifen";
ram.loading = "eager";
rahmen.appendChild(ram);
reihe.appendChild(h2); reihe.appendChild(rahmen);
flaeche.appendChild(reihe);
reihen.push({ pr, reihe, rahmen, ram, w: 6480, h: 1350, da: false });
});
}
/* Jede Post-Seite meldet ihre Streifenbreite, sobald sie steht. Die Zahl
der Slides ist je Projekt anders und kann sich ändern, deshalb wird sie
nicht hier gepflegt. */
window.addEventListener("message", e => {
const d = e.data;
if (!d || d.hg !== "streifen") return;
const r = reihen.find(x => x.ram.contentWindow === e.source);
if (!r) return;
r.w = d.w; r.h = d.h; r.da = true;
legen();
});
// Eingepasst wird nur in die Breite, die Höhe ergibt sich und wird gescrollt.
const POLSTER = 54 * 2;
function passend() {
const breit = Math.max(...reihen.map(r => r.w), 1080);
return Math.min((scroller.clientWidth - POLSTER) / breit, 0.5);
}
function legen() {
const k = passend();
reihen.forEach(r => {
r.ram.style.width = r.w + "px";
r.ram.style.height = r.h + "px";
r.ram.style.transform = "scale(" + k + ")";
r.rahmen.style.width = Math.round(r.w * k) + "px";
r.rahmen.style.height = Math.round(r.h * k) + "px";
});
const offen = reihen.filter(r => r.da).length;
meta.textContent = offen < reihen.length
? "Lade Streifen ... " + offen + " von " + reihen.length
// Seit Post 3 gibt es Einzelbilder: "1 Slides" stand vorher da.
: reihen.length + " Projekte · " + reihen.map(r => {
const n = Math.round(r.w / 1080);
return n + (n === 1 ? " Slide" : " Slides");
}).join(" · ");
}
// Kein Ziehen, kein Rad-Zoom: die Seite scrollt wie eine gewoehnliche Seite.
window.addEventListener("resize", legen);
// ─── Menü ────────────────────────────────────────────────────────────────
document.querySelectorAll(".menu").forEach(m => {
m.querySelector("button").addEventListener("click", e => {
e.stopPropagation();
const auf = m.hasAttribute("data-auf");
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
if (!auf) m.setAttribute("data-auf", "");
});
});
document.addEventListener("click", e => {
const b = e.target.closest("button[data-gehe], button[data-klick]");
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
if (!b) return;
if (b.dataset.gehe) { location.href = b.dataset.gehe; return; }
if (b.dataset.klick === "neu") { bauen(); legen(); }
});
bauen();
legen();
})();
</script>
</body>
</html>