125 lines
5.8 KiB
HTML
125 lines
5.8 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Planer-Vorschau: Creator (Teilscan)</title>
|
|
<style>
|
|
:root {
|
|
--bg: #f6f7f9; --karte: #ffffff; --text: #1a202c; --dezent: #64748b;
|
|
--rand: #e2e8f0; --akzent: #2563eb; --kern: #166534; --kern-bg: #dcfce7;
|
|
--rand-flag: #92400e; --rand-bg: #fef3c7; --code-bg: #f1f5f9;
|
|
}
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #0f1420; --karte: #1a2130; --text: #e2e8f0; --dezent: #94a3b8;
|
|
--rand: #2d3748; --akzent: #60a5fa; --kern: #86efac; --kern-bg: #14532d;
|
|
--rand-flag: #fcd34d; --rand-bg: #451a03; --code-bg: #111827;
|
|
}
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body { margin: 0; padding: 2rem 1rem 4rem; background: var(--bg); color: var(--text);
|
|
font: 15px/1.55 system-ui, sans-serif; }
|
|
main { max-width: 900px; margin: 0 auto; }
|
|
h1 { font-size: 1.4rem; margin: 0 0 .3rem; }
|
|
.untertitel { color: var(--dezent); margin: 0 0 2rem; font-size: .92rem; }
|
|
.bereich { background: var(--karte); border: 1px solid var(--rand); border-radius: 10px;
|
|
padding: 1rem 1.2rem; margin-bottom: 1rem; }
|
|
.bereich > h2 { font-size: 1.08rem; margin: 0; }
|
|
.bereich > p { color: var(--dezent); margin: .25rem 0 .8rem; font-size: .92rem; }
|
|
details { border-top: 1px solid var(--rand); }
|
|
summary { cursor: pointer; padding: .55rem .2rem; list-style: none; display: flex;
|
|
align-items: baseline; gap: .5rem; }
|
|
summary::before { content: "▸"; color: var(--dezent); font-size: .8em; transition: transform .12s; }
|
|
details[open] > summary::before { transform: rotate(90deg); }
|
|
summary:hover { color: var(--akzent); }
|
|
.flag { font-size: .7rem; font-weight: 600; padding: .1rem .45rem; border-radius: 99px;
|
|
text-transform: uppercase; letter-spacing: .04em; }
|
|
.flag.kern { color: var(--kern); background: var(--kern-bg); }
|
|
.flag.rand { color: var(--rand-flag); background: var(--rand-bg); }
|
|
.feature-besch { color: var(--dezent); font-size: .9rem; margin: 0 0 .5rem 1.15rem; }
|
|
.abschnitt { margin: .2rem 0 .7rem 1.15rem; }
|
|
.abschnitt > h4 { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em;
|
|
color: var(--dezent); margin: .6rem 0 .25rem; }
|
|
.entscheidungen { margin: 0; padding-left: 1.2rem; font-size: .92rem; }
|
|
.entscheidungen li { margin: .18rem 0; }
|
|
.teil { margin: 0 0 .35rem; border-top: 1px dashed var(--rand); }
|
|
.teil > summary { font-size: .93rem; padding: .35rem .2rem; }
|
|
.methode { margin: .3rem 0 .55rem 1.1rem; border: 1px solid var(--rand); border-radius: 8px;
|
|
background: var(--bg); overflow: hidden; }
|
|
.methode > summary { font-family: ui-monospace, monospace; font-size: .82rem;
|
|
padding: .45rem .6rem; flex-wrap: wrap; }
|
|
.io { font-size: .82rem; color: var(--dezent); padding: 0 .8rem .1rem;
|
|
display: grid; grid-template-columns: max-content 1fr; gap: .1rem .6rem; }
|
|
.io dt { font-weight: 600; }
|
|
.io dd { margin: 0; }
|
|
pre { margin: .4rem .8rem .7rem; padding: .55rem .7rem; background: var(--code-bg);
|
|
border-radius: 6px; font-size: .76rem; line-height: 1.45; overflow-x: auto; }
|
|
.hinweis { color: var(--dezent); font-size: .85rem; margin-top: 2rem;
|
|
border-top: 1px solid var(--rand); padding-top: 1rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>Creator — Projektansicht (Phase-0-Vorschau)</h1>
|
|
<p class="untertitel">Teilscan: kanban.py, pipeline.py, config.py · Belege mechanisch
|
|
verifiziert · Code wird live aus den Ankern gezogen, nie kopiert</p>
|
|
<div id="wurzel"></div>
|
|
<p class="hinweis">Wegwerf-Vorschau aus Phase 0 — kein Frontend-Code des Planers.
|
|
Bereich → Feature (Entscheidungen + Methoden) → Teilfeature → Methode (Input/Output, Code).</p>
|
|
</main>
|
|
<script>
|
|
const DATEN = /*DATEN*/;
|
|
const wurzel = document.getElementById("wurzel");
|
|
const esc = s => (s || "").replace(/[&<>"]/g, c => ({"&":"&","<":"<",">":">",'"':"""}[c]));
|
|
|
|
function methodenElement(id) {
|
|
const e = DATEN.einheiten[id];
|
|
const d = document.createElement("details");
|
|
d.className = "methode";
|
|
if (!e) { d.innerHTML = `<summary>${esc(id)} (nicht im Teilscan)</summary>`; return d; }
|
|
const code = DATEN.code[id];
|
|
d.innerHTML = `<summary>${esc(id)}</summary>
|
|
<dl class="io"><dt>Input</dt><dd>${esc(e.input) || "—"}</dd>
|
|
<dt>Output</dt><dd>${esc(e.output) || "—"}</dd></dl>
|
|
${code ? `<pre>${esc(code)}</pre>` : ""}`;
|
|
return d;
|
|
}
|
|
|
|
for (const b of DATEN.bereiche) {
|
|
const bx = document.createElement("section");
|
|
bx.className = "bereich";
|
|
bx.innerHTML = `<h2>${esc(b.name)}</h2><p>${esc(b.beschreibung)}</p>`;
|
|
for (const f of b.features) {
|
|
const ids = f.teilfeatures.flatMap(t => t.einheiten);
|
|
const entsch = [...new Set(ids.flatMap(id => (DATEN.einheiten[id]?.entscheidungen) || []))];
|
|
const fd = document.createElement("details");
|
|
fd.innerHTML = `<summary><strong>${esc(f.titel)}</strong>
|
|
<span class="flag ${f.flag}">${f.flag}</span></summary>
|
|
<p class="feature-besch">${esc(f.beschreibung)}</p>`;
|
|
if (entsch.length) {
|
|
const ab = document.createElement("div");
|
|
ab.className = "abschnitt";
|
|
ab.innerHTML = `<h4>Entscheidungen</h4>
|
|
<ul class="entscheidungen">${entsch.map(t => `<li>${esc(t)}</li>`).join("")}</ul>`;
|
|
fd.appendChild(ab);
|
|
}
|
|
const mb = document.createElement("div");
|
|
mb.className = "abschnitt";
|
|
mb.innerHTML = `<h4>Dateien & Methoden</h4>`;
|
|
for (const t of f.teilfeatures) {
|
|
const td = document.createElement("details");
|
|
td.className = "teil";
|
|
td.innerHTML = `<summary>${esc(t.titel)}</summary>`;
|
|
for (const id of t.einheiten) td.appendChild(methodenElement(id));
|
|
mb.appendChild(td);
|
|
}
|
|
fd.appendChild(mb);
|
|
bx.appendChild(fd);
|
|
}
|
|
wurzel.appendChild(bx);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|