diff --git a/frontend/src/tools/devtools.jsx b/frontend/src/tools/devtools.jsx index dbd299d..d99c32c 100644 --- a/frontend/src/tools/devtools.jsx +++ b/frontend/src/tools/devtools.jsx @@ -1424,8 +1424,9 @@ const RULE_TYPES = [ { id:'alnum_n', label:'Genau N Alphanumerisch',fields:['count'], hint:'8' }, { id:'minlength', label:'Mindestlänge', fields:['count'], hint:'5' }, { id:'maxlength', label:'Höchstlänge', fields:['count'], hint:'20' }, - { id:'numrange', label:'Zahlenbereich (enthält)', fields:['min','max'], hint:'' }, - { id:'numrange_exact',label:'Zahlenbereich (exakt)', fields:['min','max'], hint:'' }, + { id:'numrange', label:'Zahlenbereich (enthält)', fields:['min','max'], hint:'' }, + { id:'numrange_not', label:'Zahlenbereich (enthält nicht)',fields:['min','max'], hint:'' }, + { id:'numrange_exact',label:'Zahlenbereich (exakt)', fields:['min','max'], hint:'' }, { id:'onlynums', label:'Nur Zahlen', fields:[], hint:'' }, { id:'onlyletters', label:'Nur Buchstaben', fields:[], hint:'' }, { id:'onlyalnum', label:'Nur Alphanumerisch', fields:[], hint:'' }, @@ -1461,8 +1462,9 @@ function ruleToRegex(rule) { case 'alnum_n': return { pat: `[a-zA-Z0-9]{${f.count||1}}`, desc: `genau ${f.count||1} alphanumerische Zeichen` }; case 'minlength': return { pat: `.{${f.count||1},}`, desc: `mindestens ${f.count||1} Zeichen lang` }; case 'maxlength': return { pat: `.{0,${f.count||999}}`, desc: `höchstens ${f.count||999} Zeichen lang` }; - case 'numrange': return { pat: `(?:${buildNumRange(Number(f.min||0), Number(f.max||9))})`, desc: `enthält eine Zahl zwischen ${f.min||0} und ${f.max||9}` }; - case 'numrange_exact': return { pat: `^(?:${buildNumRange(Number(f.min||0), Number(f.max||9))})$`, desc: `ist exakt eine Zahl zwischen ${f.min||0} und ${f.max||9}` }; + case 'numrange': return { pat: `(?:${buildNumRange(Number(f.min||0), Number(f.max||9))})`, desc: `enthält eine Zahl zwischen ${f.min||0} und ${f.max||9}` }; + case 'numrange_not': return { pat: `^(?!.*(?:${buildNumRange(Number(f.min||0), Number(f.max||9))})).*$`, desc: `enthält KEINE Zahl zwischen ${f.min||0} und ${f.max||9}` }; + case 'numrange_exact': return { pat: `^(?:${buildNumRange(Number(f.min||0), Number(f.max||9))})$`, desc: `ist exakt eine Zahl zwischen ${f.min||0} und ${f.max||9}` }; case 'onlynums': return { pat: `^\\d+$`, desc: `nur Ziffern (0–9)` }; case 'onlyletters': return { pat: `^[a-zA-ZäöüÄÖÜß]+$`, desc: `nur Buchstaben` }; case 'onlyalnum': return { pat: `^[a-zA-Z0-9]+$`, desc: `nur Buchstaben und Ziffern` }; @@ -1697,6 +1699,7 @@ function RegexBuilder({ toast, mobile }) { // ── Build mode ── const [rules, setRules] = useState([]); const [testVal, setTestVal] = useState(''); + const [testExact, setTestExact] = useState(true); const [copied, setCopied] = useState(false); function addRule() { @@ -1728,7 +1731,8 @@ function RegexBuilder({ toast, mobile }) { let testResult = null; if (regex && testVal !== '') { try { - testResult = new RegExp(regex).test(testVal); + const pat = testExact ? `^(?:${regex})$` : regex; + testResult = new RegExp(pat).test(testVal); } catch(e) { testResult = null; } } @@ -1741,13 +1745,18 @@ function RegexBuilder({ toast, mobile }) { // ── Explain mode ── const [explainInput, setExplainInput] = useState(''); const [explainTestVal, setExplainTestVal] = useState(''); + const [explainExact, setExplainExact] = useState(false); const explained = explainInput ? explainRegex(explainInput) : []; let explainTestResult = null; if (explainInput && explainTestVal !== '') { - try { explainTestResult = new RegExp(explainInput).test(explainTestVal); } catch {} + try { + const pat = explainExact ? `^(?:${explainInput})$` : explainInput; + explainTestResult = new RegExp(pat).test(explainTestVal); + } catch {} } const inp = {...S.inp, fontSize:13, padding:'7px 9px', boxSizing:'border-box'}; + const lbl = {color:'rgba(255,255,255,0.55)', fontFamily:'monospace', fontSize:10, marginBottom:3}; return (