feat: Toolbar-Einstellungen merken (localStorage) + Tooltips überall
All checks were successful
Deploy bericht / deploy (push) Successful in 1s

- Farbe, Strichstärke, Schriftart, Schriftgröße, Bold/Italic und Zoom
  werden in localStorage unter bericht.editor.settings.v1 gespeichert
- Beim nächsten Öffnen werden alle Werte wiederhergestellt
- Alle Toolbar-Buttons und Inputs haben jetzt deutsche Tooltips
  (Farbe, Strichstärke, Schriftart, Größe, Fett, Kursiv, Zoom -/+/Reset,
   Rotation links/rechts)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
[deploy]
This commit is contained in:
Eduard Wisch 2026-04-08 16:35:08 +02:00
parent a6cb8ade44
commit a7a533f3b8
2 changed files with 58 additions and 14 deletions

View file

@ -272,8 +272,8 @@ if (!$bericht) {
print '<button type="button" class="tool-btn" data-tool="arrow" title="'.$langs->trans("BerichtToolArrow").'">↗</button>'; print '<button type="button" class="tool-btn" data-tool="arrow" title="'.$langs->trans("BerichtToolArrow").'">↗</button>';
print '<button type="button" class="tool-btn" data-tool="text" title="'.$langs->trans("BerichtToolText").'">T</button>'; print '<button type="button" class="tool-btn" data-tool="text" title="'.$langs->trans("BerichtToolText").'">T</button>';
print '<span class="sep"></span>'; print '<span class="sep"></span>';
print '<label>'.$langs->trans("BerichtColor").': <input type="color" id="tool-color" value="#ff0000"></label>'; print '<label title="Linien-/Textfarbe">'.$langs->trans("BerichtColor").': <input type="color" id="tool-color" value="#ff0000" title="Farbe wählen"></label>';
print '<label>'.$langs->trans("BerichtStrokeWidth").': <input type="range" id="tool-stroke" min="1" max="20" value="3"></label>'; print '<label title="Linien-/Strichstärke in Pixel">'.$langs->trans("BerichtStrokeWidth").': <input type="range" id="tool-stroke" min="1" max="20" value="3" title="Strichstärke"></label>';
print '<span class="sep"></span>'; print '<span class="sep"></span>';
print '<button type="button" id="btn-undo" title="'.$langs->trans("BerichtUndo").'">↶</button>'; print '<button type="button" id="btn-undo" title="'.$langs->trans("BerichtUndo").'">↶</button>';
print '<button type="button" id="btn-redo" title="'.$langs->trans("BerichtRedo").'">↷</button>'; print '<button type="button" id="btn-redo" title="'.$langs->trans("BerichtRedo").'">↷</button>';
@ -283,7 +283,7 @@ if (!$bericht) {
print '<div class="row-break"></div>'; print '<div class="row-break"></div>';
// Text-Optionen // Text-Optionen
print '<label class="text-tool-option">Schrift: <select id="tool-fontfamily">' print '<label class="text-tool-option" title="Schriftart für Text-Annotationen">Schrift: <select id="tool-fontfamily" title="Schriftart">'
.'<option value="Helvetica">Helvetica</option>' .'<option value="Helvetica">Helvetica</option>'
.'<option value="Arial">Arial</option>' .'<option value="Arial">Arial</option>'
.'<option value="Times New Roman">Times New Roman</option>' .'<option value="Times New Roman">Times New Roman</option>'
@ -291,18 +291,18 @@ if (!$bericht) {
.'<option value="Verdana">Verdana</option>' .'<option value="Verdana">Verdana</option>'
.'<option value="Georgia">Georgia</option>' .'<option value="Georgia">Georgia</option>'
.'</select></label>'; .'</select></label>';
print '<label class="text-tool-option">Größe: <input type="number" id="tool-fontsize" min="8" max="120" value="24" style="width:60px"></label>'; print '<label class="text-tool-option" title="Schriftgröße in Pixel">Größe: <input type="number" id="tool-fontsize" min="8" max="120" value="24" style="width:60px" title="Schriftgröße"></label>';
print '<label class="text-tool-option"><input type="checkbox" id="tool-bold"> <b>B</b></label>'; print '<label class="text-tool-option" title="Fett"><input type="checkbox" id="tool-bold" title="Fett"> <b>B</b></label>';
print '<label class="text-tool-option"><input type="checkbox" id="tool-italic"> <i>I</i></label>'; print '<label class="text-tool-option" title="Kursiv"><input type="checkbox" id="tool-italic" title="Kursiv"> <i>I</i></label>';
print '<span class="sep"></span>'; print '<span class="sep"></span>';
// Zoom // Zoom
print '<button type="button" id="btn-zoom-out" title="Zoom -">🔍−</button>'; print '<button type="button" id="btn-zoom-out" title="Verkleinern (Zoom -)">🔍−</button>';
print '<span id="zoom-label" style="min-width:42px;text-align:center;font-size:12px;">100%</span>'; print '<span id="zoom-label" title="Aktueller Zoom" style="min-width:42px;text-align:center;font-size:12px;">100%</span>';
print '<button type="button" id="btn-zoom-in" title="Zoom +">🔍+</button>'; print '<button type="button" id="btn-zoom-in" title="Vergrößern (Zoom +)">🔍+</button>';
print '<button type="button" id="btn-zoom-reset" title="Zoom 100%">⟳%</button>'; print '<button type="button" id="btn-zoom-reset" title="Zoom auf 100% zurücksetzen">⟳%</button>';
print '<span class="sep"></span>'; print '<span class="sep"></span>';
print '<button type="button" id="btn-rotate-left" title="'.$langs->trans("BerichtRotateLeft").'">⟲</button>'; print '<button type="button" id="btn-rotate-left" title="'.$langs->trans("BerichtRotateLeft").' (Seite 90° nach links drehen)">⟲</button>';
print '<button type="button" id="btn-rotate-right" title="'.$langs->trans("BerichtRotateRight").'">⟳</button>'; print '<button type="button" id="btn-rotate-right" title="'.$langs->trans("BerichtRotateRight").' (Seite 90° nach rechts drehen)">⟳</button>';
print '</div>'; print '</div>';
print '<div class="bericht-canvas-wrap">'; print '<div class="bericht-canvas-wrap">';
print '<canvas id="pdf-canvas"></canvas>'; print '<canvas id="pdf-canvas"></canvas>';

View file

@ -41,15 +41,58 @@
const pdfCanvas = document.getElementById('pdf-canvas'); const pdfCanvas = document.getElementById('pdf-canvas');
let currentTool = 'select'; let currentTool = 'select';
/* ---------- Settings-Persistenz (localStorage) ---------- */
const SETTINGS_KEY = 'bericht.editor.settings.v1';
function loadSettings() {
try {
const raw = localStorage.getItem(SETTINGS_KEY);
if (!raw) return {};
return JSON.parse(raw) || {};
} catch (e) { return {}; }
}
function saveSettings(patch) {
try {
const cur = loadSettings();
const merged = Object.assign({}, cur, patch);
localStorage.setItem(SETTINGS_KEY, JSON.stringify(merged));
} catch (e) { /* localStorage full/blocked */ }
}
/* ---------- Init ---------- */ /* ---------- Init ---------- */
function init() { function init() {
// Gespeicherte Einstellungen anwenden — VOR Fabric-Init
const s = loadSettings();
const colorEl = document.getElementById('tool-color');
const strokeEl = document.getElementById('tool-stroke');
const ffEl = document.getElementById('tool-fontfamily');
const fsEl = document.getElementById('tool-fontsize');
const boldEl = document.getElementById('tool-bold');
const italicEl = document.getElementById('tool-italic');
if (s.color) colorEl.value = s.color;
if (s.stroke) strokeEl.value = s.stroke;
if (s.fontFamily) ffEl.value = s.fontFamily;
if (s.fontSize) fsEl.value = s.fontSize;
if (typeof s.bold !== 'undefined') boldEl.checked = !!s.bold;
if (typeof s.italic !== 'undefined') italicEl.checked = !!s.italic;
if (s.zoom) currentZoom = parseFloat(s.zoom) || 1.0;
document.getElementById('zoom-label').textContent = Math.round(currentZoom * 100) + '%';
// Fabric initialisieren (wird beim ersten Seitenrendern dimensioniert) // Fabric initialisieren (wird beim ersten Seitenrendern dimensioniert)
fabricCanvas = new fabric.Canvas('fabric-canvas', { fabricCanvas = new fabric.Canvas('fabric-canvas', {
isDrawingMode: false, isDrawingMode: false,
selection: true, selection: true,
}); });
fabricCanvas.freeDrawingBrush.color = document.getElementById('tool-color').value; fabricCanvas.freeDrawingBrush.color = colorEl.value;
fabricCanvas.freeDrawingBrush.width = parseInt(document.getElementById('tool-stroke').value, 10); fabricCanvas.freeDrawingBrush.width = parseInt(strokeEl.value, 10);
// Listener: speichern bei jeder Änderung
colorEl.addEventListener('change', () => saveSettings({ color: colorEl.value }));
strokeEl.addEventListener('change', () => saveSettings({ stroke: parseInt(strokeEl.value, 10) }));
ffEl.addEventListener('change', () => saveSettings({ fontFamily: ffEl.value }));
fsEl.addEventListener('change', () => saveSettings({ fontSize: parseInt(fsEl.value, 10) }));
boldEl.addEventListener('change', () => saveSettings({ bold: boldEl.checked }));
italicEl.addEventListener('change', () => saveSettings({ italic: italicEl.checked }));
// Erste Seite laden (wenn vorhanden) // Erste Seite laden (wenn vorhanden)
const firstThumb = document.querySelector('#bericht-page-list .page-thumb'); const firstThumb = document.querySelector('#bericht-page-list .page-thumb');
@ -312,6 +355,7 @@
async function setZoom(z) { async function setZoom(z) {
currentZoom = Math.max(0.25, Math.min(3.0, Math.round(z * 100) / 100)); currentZoom = Math.max(0.25, Math.min(3.0, Math.round(z * 100) / 100));
document.getElementById('zoom-label').textContent = Math.round(currentZoom * 100) + '%'; document.getElementById('zoom-label').textContent = Math.round(currentZoom * 100) + '%';
saveSettings({ zoom: currentZoom });
await rerenderCurrent(); await rerenderCurrent();
} }