Add 6 new tools: Hasher, Base64, JWT Decoder, Password Gen, Timestamp, Text Diff

- backend/tools/hasher.py: POST /api/hash/sha256 and /api/hash/bcrypt (bcrypt added to requirements)
- backend/tools/base64tool.py: POST /api/base64/encode and /api/base64/decode
- backend/tools/jwtdecoder.py: POST /api/jwt/decode (signature verification disabled)
- backend/tools/passwordgen.py: POST /api/password/generate with charset and length options
- backend/tools/timestamp.py: POST /api/timestamp/convert (unix<->date, ISO 8601 + German format)
- backend/tools/textdiff.py: POST /api/text/diff returning structured added/removed/unchanged lines
- All blueprints registered in app.py and tools/__init__.py
- React components with copy button, dark/light mode support via CSS variables
- ToolOverview rebuilt as card grid; App.jsx routes added for all tools

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Nirodan
2026-04-24 14:28:18 +02:00
parent 80ec5eca7b
commit 7f9c5c874a
17 changed files with 788 additions and 6 deletions
+84
View File
@@ -0,0 +1,84 @@
import { useState } from 'react';
import axios from '../services/api';
const diffColors = {
added: { bg: 'rgba(34, 197, 94, 0.12)', text: '#22c55e', prefix: '+ ' },
removed: { bg: 'rgba(239, 68, 68, 0.12)', text: '#ef4444', prefix: '- ' },
unchanged: { bg: 'transparent', text: 'var(--muted)', prefix: ' ' },
};
function TextDiffTool() {
const [text1, setText1] = useState('');
const [text2, setText2] = useState('');
const [diff, setDiff] = useState(null);
const compare = async () => {
try {
const res = await axios.post('/api/text/diff', { text1, text2 });
setDiff(res.data.diff);
} catch {
alert('Fehler beim Vergleich');
}
};
return (
<div className="main-content">
<h2>Text Diff</h2>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
<div>
<p className="muted" style={{ marginBottom: '6px', fontWeight: 600 }}>Text A</p>
<textarea
rows={10}
value={text1}
onChange={(e) => { setText1(e.target.value); setDiff(null); }}
placeholder="Originaltext eingeben..."
style={{ resize: 'vertical', fontFamily: 'monospace', fontSize: '0.875rem' }}
/>
</div>
<div>
<p className="muted" style={{ marginBottom: '6px', fontWeight: 600 }}>Text B</p>
<textarea
rows={10}
value={text2}
onChange={(e) => { setText2(e.target.value); setDiff(null); }}
placeholder="Geänderter Text eingeben..."
style={{ resize: 'vertical', fontFamily: 'monospace', fontSize: '0.875rem' }}
/>
</div>
</div>
<button onClick={compare}>Vergleichen</button>
{diff !== null && (
<div style={{ marginTop: '16px', border: '1px solid var(--border)', borderRadius: '12px', overflow: 'hidden' }}>
{diff.length === 0 ? (
<p style={{ padding: '12px 14px', color: 'var(--muted)', margin: 0 }}>Keine Unterschiede gefunden.</p>
) : (
diff.map((line, i) => {
const c = diffColors[line.type] || diffColors.unchanged;
return (
<div
key={i}
style={{
padding: '2px 14px',
background: c.bg,
color: c.text,
fontFamily: 'monospace',
fontSize: '0.875rem',
whiteSpace: 'pre-wrap',
wordBreak: 'break-all',
borderLeft: `3px solid ${line.type !== 'unchanged' ? c.text : 'transparent'}`,
}}
>
{c.prefix}{line.text}
</div>
);
})
)}
</div>
)}
</div>
);
}
export default TextDiffTool;