diff --git a/docs/SPEC.md b/docs/SPEC.md
index 4d132d6..ff221f1 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -134,6 +134,17 @@ ist grau (`#6B7A8C`) — kein Petrol mehr im Diagramm. Der Modus ändert nur die
+ halbe Knotenhöhe). Abzweige zielen auf den **Knoten**, nie auf die Mitte
des Teilbaums.
+### Textexport des Diagramms
+Das Diagramm ist per Icon-Schaltfläche als einfacher, überall einsetzbarer
+ASCII-Baum in die Zwischenablage kopierbar. Format:
+
+- Eingerückter Baum mit `├─`/`└─` (all-of-Abzweige) bzw. `├◇`/`└◇`
+ (any-of-Alternativen); Fortsetzungsspalten mit `│`.
+- Je Knoten: `[Status] Label (Größe) @tags` — jeder Teil optional, Reihenfolge
+ wie hier. Der Status-Code entspricht §4 (`[ ]`, `[x]`, `[~]`, …).
+- Es wird genau die **sichtbare** Struktur exportiert (der „verworfene
+ einblenden“-Filter wirkt auch hier). Mehrere Wurzeln durch Leerzeile getrennt.
+
## 10. Beispiel (kanonisch)
```
diff --git a/frontend/index.html b/frontend/index.html
index 49c76a8..ee79dec 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -530,6 +530,10 @@
+
+
+
+
—
▢
@@ -577,6 +581,9 @@ const STATUS_BY_CODE = {
'-': {key:'verworfen', name:'verworfen'}
};
+/* Rück-Abbildung Status-Key → Code (für den Text-Export des Diagramms) */
+const CODE_BY_KEY = {idee:'?', geplant:' ', arbeit:'~', durchstich:'/', fertig:'x', prod:'^', verworfen:'-'};
+
const src = document.getElementById('src');
const out = document.getElementById('out');
const warnBox = document.getElementById('warn');
@@ -696,6 +703,43 @@ function render(){
warnBox.innerHTML = warnings.map(w => `
⚠ ${w}
`).join('');
}
+/* ---------- Diagramm als einfacher Text-Baum ---------- */
+/* Einfachstes, aber überall einsetzbares Format: eingerückter ASCII-Baum.
+ all-of-Abzweige mit ├─/└─, any-of-Alternativen mit ├◇/└◇. Je Knoten:
+ [Status] Label (Größe) @tags. Respektiert den „verworfene“-Filter, gibt
+ also genau die sichtbare Struktur wieder. */
+function nodeLabelText(n){
+ let s = n.status ? '[' + (CODE_BY_KEY[n.status.key] ?? ' ') + '] ' : '';
+ s += n.label;
+ if(n.size) s += ' (' + n.size + ')';
+ if(n.tags && n.tags.length) s += ' ' + n.tags.map(x => '@' + x).join(' ');
+ return s;
+}
+function walkText(node, prefix, lines){
+ const kids = visibleChildren(node);
+ const gate = gateOf(kids);
+ kids.forEach((k, i) => {
+ const last = i === kids.length - 1;
+ const conn = gate === 'or'
+ ? (last ? '└◇ ' : '├◇ ')
+ : (last ? '└─ ' : '├─ ');
+ lines.push(prefix + conn + nodeLabelText(k));
+ walkText(k, prefix + (last ? ' ' : '│ '), lines);
+ });
+}
+function treeToText(){
+ let {roots} = parse(src.value);
+ if(!showc.checked){
+ roots = roots.filter(r => !r.status || r.status.key !== 'verworfen');
+ }
+ const blocks = roots.map(root => {
+ const lines = [nodeLabelText(root)];
+ walkText(root, '', lines);
+ return lines.join('\n');
+ });
+ return blocks.join('\n\n'); /* mehrere Wurzeln durch Leerzeile trennen */
+}
+
/* Tab-Taste rückt ein statt den Fokus zu wechseln */
src.addEventListener('keydown', e => {
if(e.key === 'Tab'){
@@ -834,25 +878,32 @@ diagramEl.addEventListener('wheel', e => {
const showc = document.getElementById('showc');
showc.addEventListener('change', render);
-/* ---------- Text in die Zwischenablage kopieren ---------- */
-const copyBtn = document.getElementById('copy');
-copyBtn.addEventListener('click', async () => {
- try{
- await navigator.clipboard.writeText(src.value);
- }catch(_){
- src.select();
- document.execCommand('copy');
- src.setSelectionRange(0, 0);
- src.blur();
- }
- copyBtn.classList.add('done');
- copyBtn.title = t('copyDone');
- copyBtn.setAttribute('aria-label', t('copyDone'));
+/* ---------- In die Zwischenablage kopieren ---------- */
+async function writeClipboard(text){
+ try{ await navigator.clipboard.writeText(text); return; }catch(_){}
+ const ta = document.createElement('textarea'); /* Fallback ohne Clipboard-API */
+ ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
+ document.body.appendChild(ta); ta.select();
+ try{ document.execCommand('copy'); }catch(_){}
+ document.body.removeChild(ta);
+}
+function flashCopied(btn, restoreTitleKey){
+ btn.classList.add('done');
+ btn.title = t('copyDone');
+ btn.setAttribute('aria-label', t('copyDone'));
setTimeout(() => {
- copyBtn.classList.remove('done');
- copyBtn.title = t('copyTitle');
- copyBtn.setAttribute('aria-label', t('copyTitle'));
+ btn.classList.remove('done');
+ btn.title = t(restoreTitleKey);
+ btn.setAttribute('aria-label', t(restoreTitleKey));
}, 1500);
+}
+document.getElementById('copy').addEventListener('click', async () => {
+ await writeClipboard(src.value);
+ flashCopied(document.getElementById('copy'), 'copyTitle');
+});
+document.getElementById('copyDiagram').addEventListener('click', async () => {
+ await writeClipboard(treeToText());
+ flashCopied(document.getElementById('copyDiagram'), 'copyDiagramTitle');
});
/* ---------- Internationalisierung (DE/EN/ES/FR) ---------- */
@@ -861,6 +912,7 @@ const I18N = {
subtitle:"Werkbaum – Ein Text-basierter WBS/Projektstrukturplan/Feature-Tree Diagramm-Editor.",
editorTitle:"Struktur (Text)", diagramTitle:"Diagramm",
copy:"kopieren", copyDone:"kopiert ✓", copyTitle:"Text in die Zwischenablage kopieren",
+ copyDiagramTitle:"Diagramm als Textbaum kopieren",
minimize:"minimieren", normal:"normal", maximize:"maximieren",
agenda:"Agenda", discarded:"verworfene",
modeHorizontal:"Horizontal – Organigramm, Diagramm über dem Editor",
@@ -885,6 +937,7 @@ const I18N = {
subtitle:"Werkbaum – A text-based WBS / work breakdown structure / feature-tree diagram editor.",
editorTitle:"Structure (text)", diagramTitle:"Diagram",
copy:"copy", copyDone:"copied ✓", copyTitle:"Copy text to clipboard",
+ copyDiagramTitle:"Copy diagram as a text tree",
minimize:"minimize", normal:"normal", maximize:"maximize",
agenda:"Legend", discarded:"discarded",
modeHorizontal:"Horizontal – org chart, diagram above the editor",
@@ -909,6 +962,7 @@ const I18N = {
subtitle:"Werkbaum – Un editor de diagramas basado en texto para EDT / estructura de desglose del trabajo / árbol de características.",
editorTitle:"Estructura (texto)", diagramTitle:"Diagrama",
copy:"copiar", copyDone:"copiado ✓", copyTitle:"Copiar el texto al portapapeles",
+ copyDiagramTitle:"Copiar el diagrama como árbol de texto",
minimize:"minimizar", normal:"normal", maximize:"maximizar",
agenda:"Leyenda", discarded:"descartados",
modeHorizontal:"Horizontal – organigrama, diagrama sobre el editor",
@@ -933,6 +987,7 @@ const I18N = {
subtitle:"Werkbaum – Un éditeur de diagrammes basé sur le texte pour WBS / organigramme des tâches / arbre de fonctionnalités.",
editorTitle:"Structure (texte)", diagramTitle:"Diagramme",
copy:"copier", copyDone:"copié ✓", copyTitle:"Copier le texte dans le presse-papiers",
+ copyDiagramTitle:"Copier le diagramme en arbre de texte",
minimize:"réduire", normal:"normal", maximize:"agrandir",
agenda:"Légende", discarded:"abandonnés",
modeHorizontal:"Horizontal – organigramme, diagramme au-dessus de l'éditeur",