YAML: Auto-Fix bei Einrückungsfehlern; Skizze: Baustellen-Badge
This commit is contained in:
@@ -120,27 +120,86 @@ function collectPaths(data, path='root', paths=[]) {
|
|||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── YAML Auto-Fix: normalisiert Einrückung bevor js-yaml parst ───────────────
|
||||||
|
function fixYamlIndentation(text) {
|
||||||
|
// Normalisiert Tabs → Spaces, erkennt Einrückungs-Inkonsistenz
|
||||||
|
// Strategie: Einrückung auf Basis des ersten vorkommenden Levels normalisieren
|
||||||
|
const lines = text.split('\n');
|
||||||
|
const fixed = [];
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
// Tabs zu 2 Spaces
|
||||||
|
const noTabs = line.replace(/\t/g, ' ');
|
||||||
|
fixed.push(noTabs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalisiere die Einrückungslevel auf gerade 2-Vielfache
|
||||||
|
const result = [];
|
||||||
|
const indentStack = [0]; // Stack der gültigen Einrück-Level
|
||||||
|
|
||||||
|
for (const line of fixed) {
|
||||||
|
if (!line.trim()) { result.push(''); continue; }
|
||||||
|
if (line.trim().startsWith('#')) { result.push(line); continue; }
|
||||||
|
|
||||||
|
const current = line.search(/\S/);
|
||||||
|
const content = line.trimStart();
|
||||||
|
|
||||||
|
// Finde den nächsten passenden Level im Stack
|
||||||
|
let level = indentStack.length - 1;
|
||||||
|
while (level > 0 && indentStack[level] > current) level--;
|
||||||
|
|
||||||
|
if (current > indentStack[level]) {
|
||||||
|
// Neue Einrückungstiefe
|
||||||
|
indentStack.splice(level + 1); // alles nach level entfernen
|
||||||
|
indentStack.push(current);
|
||||||
|
level = indentStack.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize: depth * 2 spaces
|
||||||
|
const normalizedIndent = ' '.repeat(level);
|
||||||
|
result.push(normalizedIndent + content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
// ── YAML Tool ─────────────────────────────────────────────────────────────────
|
// ── YAML Tool ─────────────────────────────────────────────────────────────────
|
||||||
function YamlTool({ mobile }) {
|
function YamlTool({ mobile }) {
|
||||||
const [input, setInput] = useState('');
|
const [input, setInput] = useState('');
|
||||||
const [parsed, setParsed] = useState(null);
|
const [parsed, setParsed] = useState(null);
|
||||||
const [formatted,setFormatted]= useState('');
|
const [formatted,setFormatted]= useState('');
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [warning, setWarning] = useState('');
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
const [view, setView] = useState('tree'); // 'tree' | 'raw'
|
const [view, setView] = useState('tree');
|
||||||
const [collapsed,setCollapsed]= useState(new Set());
|
const [collapsed,setCollapsed]= useState(new Set());
|
||||||
|
|
||||||
const process = useCallback((text) => {
|
const process = useCallback((text) => {
|
||||||
if (!text.trim()) { setParsed(null); setFormatted(''); setError(''); return; }
|
if (!text.trim()) { setParsed(null); setFormatted(''); setError(''); setWarning(''); return; }
|
||||||
|
|
||||||
|
// Erste Versuch: direkt parsen
|
||||||
try {
|
try {
|
||||||
const obj = jsyaml.load(text);
|
const obj = jsyaml.load(text);
|
||||||
setParsed(obj);
|
setParsed(obj);
|
||||||
setFormatted(jsyaml.dump(obj, { indent:2, lineWidth:-1, noRefs:true }));
|
setFormatted(jsyaml.dump(obj, { indent:2, lineWidth:-1, noRefs:true }));
|
||||||
setError('');
|
setError(''); setWarning('');
|
||||||
setCollapsed(new Set()); // reset collapsed
|
setCollapsed(new Set());
|
||||||
|
return;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
setError(e.message);
|
// Zweiter Versuch: Auto-Fix und dann parsen
|
||||||
setParsed(null); setFormatted('');
|
try {
|
||||||
|
const fixed = fixYamlIndentation(text);
|
||||||
|
const obj = jsyaml.load(fixed);
|
||||||
|
setParsed(obj);
|
||||||
|
setFormatted(jsyaml.dump(obj, { indent:2, lineWidth:-1, noRefs:true }));
|
||||||
|
setWarning(`⚠ Einrückung automatisch korrigiert: ${e.message.split('\n')[0]}`);
|
||||||
|
setError('');
|
||||||
|
setCollapsed(new Set());
|
||||||
|
} catch(e2) {
|
||||||
|
setError(e.message);
|
||||||
|
setWarning('');
|
||||||
|
setParsed(null); setFormatted('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},[]);
|
},[]);
|
||||||
|
|
||||||
@@ -214,7 +273,15 @@ function YamlTool({ mobile }) {
|
|||||||
borderRadius:8,padding:14,color:'#ff6b9d',fontFamily:'monospace',fontSize:11,lineHeight:1.7}}>
|
borderRadius:8,padding:14,color:'#ff6b9d',fontFamily:'monospace',fontSize:11,lineHeight:1.7}}>
|
||||||
⚠ {error}
|
⚠ {error}
|
||||||
</div>
|
</div>
|
||||||
) : view==='raw' ? (
|
) : (<>
|
||||||
|
{warning && (
|
||||||
|
<div style={{background:'rgba(255,230,109,0.07)',border:'1px solid rgba(255,230,109,0.2)',
|
||||||
|
borderRadius:7,padding:'7px 12px',color:'#ffe66d',fontFamily:'monospace',
|
||||||
|
fontSize:10,lineHeight:1.6,marginBottom:8}}>
|
||||||
|
{warning}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{view==='raw' ? (
|
||||||
<pre style={{margin:0,padding:14,minHeight:200,background:'rgba(0,0,0,0.35)',
|
<pre style={{margin:0,padding:14,minHeight:200,background:'rgba(0,0,0,0.35)',
|
||||||
border:'1px solid rgba(255,255,255,0.07)',borderRadius:8,
|
border:'1px solid rgba(255,255,255,0.07)',borderRadius:8,
|
||||||
color:formatted?'#d4e6b5':'rgba(255,255,255,0.15)',
|
color:formatted?'#d4e6b5':'rgba(255,255,255,0.15)',
|
||||||
@@ -236,6 +303,7 @@ function YamlTool({ mobile }) {
|
|||||||
YAML eingeben → wird live formatiert
|
YAML eingeben → wird live formatiert
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{color:'rgba(255,255,255,0.15)',fontFamily:'monospace',fontSize:9}}>
|
<div style={{color:'rgba(255,255,255,0.15)',fontFamily:'monospace',fontSize:9}}>
|
||||||
|
|||||||
@@ -352,9 +352,19 @@ export default function Skizze({ toast, mobile }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{padding:mobile?'14px 14px 90px':'36px 44px',maxWidth:1100}}>
|
<div style={{padding:mobile?'14px 14px 90px':'36px 44px',maxWidth:1100}}>
|
||||||
{/* Header */}
|
|
||||||
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:12,flexWrap:'wrap',gap:8}}>
|
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:12,flexWrap:'wrap',gap:8}}>
|
||||||
<h1 style={{color:'#fff',fontFamily:"'Space Mono',monospace",fontSize:mobile?16:22,margin:0}}>Skizzen-Rechner</h1>
|
<div style={{display:'flex',alignItems:'center',gap:12,flexWrap:'wrap'}}>
|
||||||
|
<h1 style={{color:'#fff',fontFamily:"'Space Mono',monospace",fontSize:mobile?16:22,margin:0}}>Skizzen-Rechner</h1>
|
||||||
|
<div style={{
|
||||||
|
display:'flex',alignItems:'center',gap:6,
|
||||||
|
background:'repeating-linear-gradient(45deg,rgba(255,230,109,0.12),rgba(255,230,109,0.12) 6px,rgba(0,0,0,0) 6px,rgba(0,0,0,0) 12px)',
|
||||||
|
border:'1px solid rgba(255,230,109,0.3)',borderRadius:8,
|
||||||
|
padding:'4px 10px',flexShrink:0,
|
||||||
|
}}>
|
||||||
|
<span style={{fontSize:14}}>🚧</span>
|
||||||
|
<span style={{color:'#ffe66d',fontFamily:'monospace',fontSize:10,fontWeight:700,letterSpacing:0.5}}>IM AUFBAU</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div style={{display:'flex',gap:8,alignItems:'center'}}>
|
<div style={{display:'flex',gap:8,alignItems:'center'}}>
|
||||||
<label style={{display:'flex',alignItems:'center',gap:5,cursor:'pointer'}}>
|
<label style={{display:'flex',alignItems:'center',gap:5,cursor:'pointer'}}>
|
||||||
<input type="checkbox" checked={snapGrid} onChange={e=>setSnapGrid(e.target.checked)} style={{accentColor:'#4ecdc4'}}/>
|
<input type="checkbox" checked={snapGrid} onChange={e=>setSnapGrid(e.target.checked)} style={{accentColor:'#4ecdc4'}}/>
|
||||||
|
|||||||
Reference in New Issue
Block a user