- werkzeuge/vorschau/vorschau.html: Anzeige per Drag & Drop oder Dateiauswahl, Zoom, Einpassen, Fehlermeldungen; funktioniert ohne Python direkt aus dem Dateisystem. - scripts/vorschau.py: erzeugt aus .bpmn oder Arbeitsschritt-.md eine in sich geschlossene Seite und öffnet sie; --stdin als Filter, etwa für einen Gitea-Renderer. Diagramm wird mit maskiertem '<' eingebettet, die Bibliothek unverändert und nur auf <script/</script geprüft. - bpmn-js 18.29.1 (navigated viewer + CSS + LICENSE) lokal eingebettet, npm-Integrität geprüft; lädt nichts aus dem Netz. Wasserzeichen bleibt laut Lizenzauflage sichtbar. - tests/test_vorschau.py: 11 Tests; Skill sn-bpmn, Anleitung, AGENTS.md, README, Leitfäden, Fallstricke und STATUS ergänzt. - Tests: python -m pytest -q -> 55 bestanden. Darstellung mit Edge headless geprüft (beide Beispiele, leere Seite, kaputte Datei). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
130 lines
5.2 KiB
HTML
130 lines
5.2 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>BPMN-Vorschau</title>
|
||
<!-- bpmn-js (bpmn.io), lokal eingebettet – siehe README.md. Nichts wird aus dem Netz geladen. -->
|
||
<link rel="stylesheet" href="bpmn-js/diagram-js.css">
|
||
<link rel="stylesheet" href="bpmn-js/bpmn-js.css">
|
||
<style>
|
||
:root {
|
||
--hintergrund: #ffffff;
|
||
--leiste: #f3f5f7;
|
||
--rand: #d0d7de;
|
||
--text: #1f2328;
|
||
--gedaempft: #59636e;
|
||
--akzent: #0b6bcb;
|
||
--fehler: #b42318;
|
||
--warnung: #9a6700;
|
||
}
|
||
html, body { height: 100%; margin: 0; }
|
||
body {
|
||
display: flex; flex-direction: column;
|
||
font-family: "Segoe UI", system-ui, sans-serif; font-size: 14px;
|
||
color: var(--text); background: var(--hintergrund);
|
||
}
|
||
header {
|
||
display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
|
||
padding: 8px 16px; background: var(--leiste); border-bottom: 1px solid var(--rand);
|
||
}
|
||
header h1 { font-size: 15px; font-weight: 600; margin: 0 12px 0 0; }
|
||
button, label.knopf {
|
||
font: inherit; padding: 4px 10px; border: 1px solid var(--rand); border-radius: 6px;
|
||
background: #fff; color: var(--text); cursor: pointer;
|
||
}
|
||
button:hover, label.knopf:hover { border-color: var(--akzent); }
|
||
button:disabled { opacity: .5; cursor: default; }
|
||
#datei { display: none; }
|
||
#dateiname { color: var(--gedaempft); overflow-wrap: anywhere; }
|
||
#meldung { flex-basis: 100%; margin: 0; }
|
||
#meldung:empty { display: none; }
|
||
#meldung.fehler { color: var(--fehler); }
|
||
#meldung.warnung { color: var(--warnung); }
|
||
/* Zeichenfläche: das bpmn.io-Wasserzeichen unten rechts muss laut Lizenz sichtbar und unverdeckt bleiben. */
|
||
main { position: relative; flex: 1; min-height: 0; }
|
||
#flaeche { position: absolute; inset: 0; }
|
||
#hinweis {
|
||
position: absolute; inset: 24px 24px 64px 24px; display: flex; align-items: center; justify-content: center;
|
||
text-align: center; color: var(--gedaempft); border: 2px dashed var(--rand); border-radius: 12px;
|
||
padding: 16px; pointer-events: none;
|
||
}
|
||
main.ziehen #hinweis { border-color: var(--akzent); color: var(--akzent); }
|
||
main.geladen #hinweis { display: none; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>BPMN-Vorschau</h1>
|
||
<label class="knopf" for="datei">Datei öffnen …</label>
|
||
<input type="file" id="datei" accept=".bpmn,.xml">
|
||
<button type="button" id="einpassen" disabled>Einpassen</button>
|
||
<button type="button" id="groesser" disabled title="Vergrößern">+</button>
|
||
<button type="button" id="kleiner" disabled title="Verkleinern">−</button>
|
||
<span id="dateiname"></span>
|
||
<p id="meldung" role="status"></p>
|
||
</header>
|
||
<main id="bereich">
|
||
<div id="flaeche"></div>
|
||
<div id="hinweis">.bpmn-Datei hierher ziehen oder über „Datei öffnen …“ wählen.<br>
|
||
Die Datei wird nur in diesem Browser angezeigt und nirgendwohin übertragen.</div>
|
||
</main>
|
||
<script src="bpmn-js/bpmn-navigated-viewer.production.min.js"></script>
|
||
<script>
|
||
// Wird von scripts/vorschau.py durch den Inhalt einer .bpmn und ihren Namen ersetzt.
|
||
const EINGEBETTET = null;
|
||
const DATEINAME = null;
|
||
|
||
const bereich = document.getElementById('bereich');
|
||
const meldung = document.getElementById('meldung');
|
||
const knoepfe = ['einpassen', 'groesser', 'kleiner'].map(id => document.getElementById(id));
|
||
const viewer = new BpmnJS({ container: '#flaeche' });
|
||
const canvas = () => viewer.get('canvas');
|
||
|
||
function melde(text, art) {
|
||
meldung.textContent = text || '';
|
||
meldung.className = art || '';
|
||
}
|
||
|
||
async function zeige(xml, name) {
|
||
melde('');
|
||
try {
|
||
const { warnings } = await viewer.importXML(xml);
|
||
canvas().zoom('fit-viewport', 'auto');
|
||
bereich.classList.add('geladen');
|
||
knoepfe.forEach(k => { k.disabled = false; });
|
||
document.getElementById('dateiname').textContent = name || '';
|
||
document.title = name ? `${name} – BPMN-Vorschau` : 'BPMN-Vorschau';
|
||
if (warnings.length) {
|
||
melde(`${warnings.length} Warnung(en) beim Einlesen: ` + warnings.map(w => w.message).join(' | '), 'warnung');
|
||
}
|
||
} catch (fehler) {
|
||
bereich.classList.remove('geladen');
|
||
knoepfe.forEach(k => { k.disabled = true; });
|
||
melde(`Die Datei ${name || ''} lässt sich nicht als BPMN lesen: ${fehler.message}`, 'fehler');
|
||
}
|
||
}
|
||
|
||
function lies(datei) {
|
||
if (!datei) return;
|
||
datei.text().then(xml => zeige(xml, datei.name));
|
||
}
|
||
|
||
document.getElementById('datei').addEventListener('change', e => lies(e.target.files[0]));
|
||
document.getElementById('einpassen').addEventListener('click', () => canvas().zoom('fit-viewport', 'auto'));
|
||
document.getElementById('groesser').addEventListener('click', () => canvas().zoom(canvas().zoom() * 1.25));
|
||
document.getElementById('kleiner').addEventListener('click', () => canvas().zoom(canvas().zoom() / 1.25));
|
||
|
||
['dragenter', 'dragover'].forEach(art => document.addEventListener(art, e => {
|
||
e.preventDefault(); bereich.classList.add('ziehen');
|
||
}));
|
||
['dragleave', 'drop'].forEach(art => document.addEventListener(art, e => {
|
||
e.preventDefault(); bereich.classList.remove('ziehen');
|
||
}));
|
||
document.addEventListener('drop', e => lies(e.dataTransfer.files[0]));
|
||
|
||
if (EINGEBETTET) zeige(EINGEBETTET, DATEINAME);
|
||
</script>
|
||
</body>
|
||
</html>
|