Add 5 new tools: QR-Code, Markdown, Color Converter, JSON Formatter, Regex Tester
- backend/tools/qrcode_gen.py: POST /api/qrcode/generate, returns base64 PNG (qrcode[pil]) - backend/tools/markdown_tool.py: POST /api/markdown/render, extensions: tables/fenced_code/nl2br - backend/tools/colorconverter.py: POST /api/color/convert, HEX/RGB/HSL via colorsys (no deps) - backend/tools/jsonformatter.py: POST /api/json/format, returns formatted JSON with line/col errors - backend/tools/regextester.py: POST /api/regex/test, flags i/m/s, returns matches with positions - QrCodeTool.jsx: generate + download PNG button - MarkdownTool.jsx: split editor/preview, debounce 500ms, white preview bg - ColorConverterTool.jsx: color swatch preview, per-format copy buttons - JsonFormatterTool.jsx: indent toggle 2/4, pre result box with copy - RegexTesterTool.jsx: debounce 400ms, yellow match highlighting, flag checkboxes - All blueprints registered in app.py; qrcode[pil] + markdown added to requirements.txt Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { useState } from 'react';
|
||||
import axios from '../services/api';
|
||||
|
||||
function JsonFormatterTool() {
|
||||
const [input, setInput] = useState('');
|
||||
const [indent, setIndent] = useState(2);
|
||||
const [result, setResult] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const format = async () => {
|
||||
setError('');
|
||||
setResult('');
|
||||
try {
|
||||
const res = await axios.post('/api/json/format', { text: input, indent });
|
||||
setResult(res.data.result);
|
||||
} catch (err) {
|
||||
setError(err.response?.data?.message || 'Ungültiges JSON');
|
||||
}
|
||||
};
|
||||
|
||||
const copy = () => {
|
||||
navigator.clipboard.writeText(result);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="main-content">
|
||||
<h2>JSON Formatter</h2>
|
||||
<textarea
|
||||
rows={10}
|
||||
value={input}
|
||||
onChange={(e) => { setInput(e.target.value); setResult(''); setError(''); }}
|
||||
placeholder='{"key": "value"}'
|
||||
style={{ resize: 'vertical', fontFamily: 'monospace', fontSize: '0.875rem' }}
|
||||
/>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '4px' }}>
|
||||
{[2, 4].map((n) => (
|
||||
<button key={n} className={indent === n ? '' : 'ghost'} onClick={() => setIndent(n)} style={{ margin: 0 }}>
|
||||
{n} Spaces
|
||||
</button>
|
||||
))}
|
||||
<button onClick={format} style={{ marginLeft: 'auto', margin: 0 }}>Formatieren</button>
|
||||
</div>
|
||||
{error && <p className="error">{error}</p>}
|
||||
{result && (
|
||||
<div style={{ marginTop: '12px', position: 'relative' }}>
|
||||
<button
|
||||
className="ghost"
|
||||
onClick={copy}
|
||||
style={{ position: 'absolute', top: '8px', right: '8px', margin: 0, zIndex: 1 }}
|
||||
>
|
||||
{copied ? 'Kopiert!' : 'Kopieren'}
|
||||
</button>
|
||||
<pre style={{
|
||||
margin: 0,
|
||||
padding: '14px',
|
||||
paddingTop: '40px',
|
||||
background: 'var(--surface-2)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '12px',
|
||||
color: 'var(--text)',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: '0.875rem',
|
||||
overflowX: 'auto',
|
||||
whiteSpace: 'pre-wrap',
|
||||
wordBreak: 'break-all',
|
||||
}}>
|
||||
{result}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default JsonFormatterTool;
|
||||
Reference in New Issue
Block a user