feat: Abhängigkeiten (:#a,#b) parsen — unbekannte ID warnt, Zyklen zulässig (SPEC §1, D37)

Extraktionsschritt 7: alle alleinstehend angesetzten `:#a,#b`-Token —
EIN zusammenhängendes Token ohne Leerraum, mehrere je Zeile werden
zusammengeführt; eingeklammerte Erwähnungen wie `(:#auth,#api)` bleiben
Label (Zitier-Konvention wie `(#auth)`, deshalb enger als die frühere
§11-Formulierung). `deps` sind ID-Strings am Knoten; der Parser prüft
nur Existenz (Warnung unknownDep, Vorwärts-Referenzen normal), Zyklen
werden bewusst nicht einmal erkannt. Sichtbar als `→ #a, #b` im Tooltip
und als a11yDeps; Legendenzeile + Warntext in allen 9 Sprachen. Im
mitgelieferten Plan: Zyklen-legal und unknown-id-Warnung auf [x],
Abhängigkeits-Knoten auf [~]. 12 neue Tests (tests/deps.test.js).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-22 16:25:09 +02:00
co-authored by Claude Fable 5
parent 6ecf3e4c69
commit 3ba37fc5a0
12 changed files with 239 additions and 39 deletions
+5 -2
View File
@@ -51,9 +51,11 @@ function nodeAria(n, opts){
if(n.size) parts.push(t('a11ySize', {size: n.size}));
else if(cheapPath) parts.push(t('a11ySizeImplicit'));
if(n.tags && n.tags.length) parts.push(t('a11yTags', {names: n.tags.join(', ')}));
/* Knoten-ID (SPEC §1, D36): keine eigene Darstellung im Diagramm — sichtbar
nur im Tooltip und hier. */
/* Knoten-ID und Abhängigkeiten (SPEC §1, D36/D37): keine eigene Darstellung
im Diagramm — sichtbar nur im Tooltip und hier. */
if(n.id) parts.push(t('a11yId', {id: n.id}));
if(n.deps && n.deps.length)
parts.push(t('a11yDeps', {ids: n.deps.map(d => '#' + d).join(', ')}));
if(n.optional) parts.push(t('a11yOptional'));
/* Die Fokusmarke ist rein als box-shadow sichtbar — ohne diese Ansage wüsste
ein Screenreader nichts davon. Zugleich der einzige Ort, an dem sie sich von
@@ -73,6 +75,7 @@ function nodeHtml(n, extra, opts){
Tooltip macht die sonst unsichtbare Alt-Klick-Geste auffindbar. */
const lineAttr = n.line ? ` data-line="${n.line}"` : '';
const tip = [n.id ? '#' + n.id : '',
n.deps && n.deps.length ? '→ ' + n.deps.map(d => '#' + d).join(', ') : '',
n.status ? t('st_' + n.status.key) : '',
n.optional ? t('a11yOptional') : '', t('jumpHint')]
.filter(Boolean).join(' · ');