Files
werkbaum/frontend/src/parser.js
T
mhoennigandClaude Haiku 4.5 68322b13d9 feat: Add High Risk status [!] for uncertain effort estimation
Neuer Status-Code [!] für Items mit unbekannter Größe/Aufwand:

Parser:
- STATUS_BY_CODE erweitert: '!' -> {key:'highrisk', name:'High Risk – Aufwand unklar'}

Style:
- .node.st-highrisk: Pastellorange (#FFE5CC) mit orange Rahmen (#F97316)
- .chip.st-highrisk: Gleiche Farben für Agenda-Beispiele

i18n (alle 9 Sprachen):
- DE: "High Risk"
- EN: "high risk"
- ES: "alto riesgo"
- FR: "risque élevé"
- PL: "wysokie ryzyko"
- RU: "высокий риск"
- HI: "उच्च जोखिम"
- ZH: "高风险"
- JA: "高リスク"

Tests:
- parser.test.js aktualisiert: 8 Status-Codes (waren 7)

Damit können Items mit ungeklärtem Aufwand im Lean-Pathfinding-Prozess
gekennzeichnet werden – wichtig für Risk-Flagging in Phase 1.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-23 07:52:02 +02:00

67 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Werkbaum-Parser — headless, ohne DOM/UI nutzbar.
Setzt docs/SPEC.md §1–§8 um: Zeilenformat, Hierarchie (Einrückung),
Zerlegungsart (Gate), Status, Größe, Links, Personen-Tags, Kommentare.
Verhalten ist normativ gegen SPEC — Änderungen zuerst dort dokumentieren. */
/* T-Shirt-Größen (SPEC §5), aufsteigend geordnet. */
export const SIZE_RANK = { XS: 0, S: 1, M: 2, L: 3, XL: 4, XXL: 5 };
/* Status-Vokabular (SPEC §4): Checkbox-Code -> {key, name}.
`name` ist der deutsche Anzeigename (Quellsprache). */
export const STATUS_BY_CODE = {
'?': {key:'idee', name:'Idee'},
' ': {key:'geplant', name:'geplant'},
'~': {key:'arbeit', name:'in Arbeit'},
'/': {key:'durchstich', name:'Durchstich funktionsbereit, Feinarbeiten offen'},
'x': {key:'fertig', name:'fertig'},
'^': {key:'prod', name:'in Produktion'},
'-': {key:'verworfen', name:'verworfen'},
'!': {key:'highrisk', name:'High Risk Aufwand unklar'}
};
/* Parst den Notationstext zu { roots, warnings }.
Jeder Knoten: {label, type:'and'|'or', status, url, size, tags, children, line}.
Extraktionsreihenfolge (SPEC §1): Kommentar -> Zeichen/Status -> URL -> Größe
-> Tags -> Label. Hierarchie über Einrückungsbreite (Tab = 2 Leerzeichen);
Elternknoten ist die nächste vorangehende Zeile mit kleinerer Breite. */
export function parse(text){
const virtualRoot = {label:'', type:'and', children:[]};
const stack = [{node:virtualRoot, width:-1}];
const warnings = [];
text.split('\n').forEach((raw, i) => {
raw = raw.replace(/%%.*$/, ''); /* %%-Kommentare entfernen (Mermaid-Konvention) */
if(!raw.trim()) return;
/* Statusbox tolerant erfassen: irgendein einzelnes Zeichen in [ ] an der
Statusposition. Gültige Codes -> Status; unbekannte -> Warnung + neutral
(fehlertolerant: die Zeile geht nicht verloren). */
const m = raw.match(/^([ \t]*)([-|])?\s*(?:\[([^\]])\]\s*)?(.*)$/);
const width = m[1].replace(/\t/g,' ').length;
const type = m[2] === '|' ? 'or' : 'and';
const boxChar = m[3]; // undefined, wenn keine Statusbox
let rest = m[4], url = null, size = null;
const tags = [];
rest = rest.replace(/https?:\/\/\S+/i, s => { url = s; return ''; });
rest = rest.replace(/\((XXL|XS|XL|S|M|L)\)/i, (s, g) => { size = g.toUpperCase(); return ''; });
rest = rest.replace(/@([\p{L}\p{N}._-]+)/gu, (s, g) => { tags.push(g); return ''; });
const label = rest.replace(/\s+/g, ' ').trim();
if(!label) return;
let status = null;
if(boxChar != null){
status = STATUS_BY_CODE[boxChar.toLowerCase()] || null;
if(!status) warnings.push({type:'unknownStatus', line:i+1, code:boxChar});
}
while(stack.length > 1 && stack[stack.length-1].width >= width) stack.pop();
const parent = stack[stack.length-1].node;
const node = {label, type, status, url, size, tags, children:[], line:i+1};
parent.children.push(node);
stack.push({node, width});
});
return {roots: virtualRoot.children, warnings};
}