Phase 1 — Python (klausur-service): 5 monoliths → 36 files - dsfa_corpus_ingestion.py (1,828 LOC → 5 files) - cv_ocr_engines.py (2,102 LOC → 7 files) - cv_layout.py (3,653 LOC → 10 files) - vocab_worksheet_api.py (2,783 LOC → 8 files) - grid_build_core.py (1,958 LOC → 6 files) Phase 2 — Go (edu-search-service, school-service): 8 monoliths → 19 files - staff_crawler.go (1,402 → 4), policy/store.go (1,168 → 3) - policy_handlers.go (700 → 2), repository.go (684 → 2) - search.go (592 → 2), ai_extraction_handlers.go (554 → 2) - seed_data.go (591 → 2), grade_service.go (646 → 2) Phase 3 — TypeScript (admin-lehrer): 45 monoliths → 220+ files - sdk/types.ts (2,108 → 16 domain files) - ai/rag/page.tsx (2,686 → 14 files) - 22 page.tsx files split into _components/ + _hooks/ - 11 component files split into sub-components - 10 SDK data catalogs added to loc-exceptions - Deleted dead backup index_original.ts (4,899 LOC) All original public APIs preserved via re-export facades. Zero new errors: Python imports verified, Go builds clean, TypeScript tsc --noEmit shows only pre-existing errors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
154 lines
5.5 KiB
TypeScript
154 lines
5.5 KiB
TypeScript
'use client'
|
|
|
|
import type { OverlayReconstructionState, TextColor, PositioningMode } from './overlay-reconstruction-types'
|
|
|
|
interface OverlayToolbarProps {
|
|
state: OverlayReconstructionState
|
|
}
|
|
|
|
export function OverlayToolbar({ state }: OverlayToolbarProps) {
|
|
const {
|
|
status,
|
|
cells,
|
|
changedCount,
|
|
undoStack,
|
|
redoStack,
|
|
fontScale,
|
|
globalBold,
|
|
imageRotation,
|
|
textOpacity,
|
|
textColor,
|
|
positioningMode,
|
|
undo,
|
|
redo,
|
|
setFontScale,
|
|
setGlobalBold,
|
|
setImageRotation,
|
|
setTextOpacity,
|
|
setTextColor,
|
|
setPositioningMode,
|
|
saveReconstruction,
|
|
} = state
|
|
|
|
return (
|
|
<div className="flex items-center justify-between bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 px-3 py-2">
|
|
<div className="flex items-center gap-2">
|
|
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
Overlay-Rekonstruktion
|
|
</h3>
|
|
<span className="text-xs text-gray-400">
|
|
{cells.length} Zellen · {changedCount} geaendert
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{/* Undo/Redo */}
|
|
<button
|
|
onClick={undo}
|
|
disabled={undoStack.length === 0}
|
|
className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-30"
|
|
title="Rueckgaengig (Ctrl+Z)"
|
|
>
|
|
↩
|
|
</button>
|
|
<button
|
|
onClick={redo}
|
|
disabled={redoStack.length === 0}
|
|
className="px-2 py-1 text-xs border border-gray-300 dark:border-gray-600 rounded hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-30"
|
|
title="Wiederholen (Ctrl+Shift+Z)"
|
|
>
|
|
↪
|
|
</button>
|
|
|
|
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
|
|
|
{/* Font scale */}
|
|
<label className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400">
|
|
Schrift
|
|
<input
|
|
type="range" min={30} max={120} value={Math.round(fontScale * 100)}
|
|
onChange={e => setFontScale(Number(e.target.value) / 100)}
|
|
className="w-20 h-1 accent-teal-600"
|
|
/>
|
|
<span className="w-8 text-right font-mono">{Math.round(fontScale * 100)}%</span>
|
|
</label>
|
|
<button
|
|
onClick={() => setGlobalBold(b => !b)}
|
|
className={`px-2 py-1 text-xs rounded border transition-colors font-bold ${
|
|
globalBold
|
|
? 'bg-teal-600 text-white border-teal-600'
|
|
: 'bg-white dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-300 dark:border-gray-600'
|
|
}`}
|
|
>
|
|
B
|
|
</button>
|
|
<button
|
|
onClick={() => setImageRotation(r => r === 0 ? 180 : 0)}
|
|
className={`px-2 py-1 text-xs rounded border transition-colors ${
|
|
imageRotation === 180
|
|
? 'bg-teal-600 text-white border-teal-600'
|
|
: 'bg-white dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-300 dark:border-gray-600'
|
|
}`}
|
|
title="Bild 180° drehen"
|
|
>
|
|
180°
|
|
</button>
|
|
|
|
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
|
|
|
{/* Positioning mode toggle */}
|
|
<button
|
|
onClick={() => setPositioningMode((m: PositioningMode) => m === 'slide' ? 'cluster' : 'slide')}
|
|
className={`px-2 py-1 text-xs rounded border transition-colors ${
|
|
positioningMode === 'slide'
|
|
? 'bg-orange-500 text-white border-orange-500'
|
|
: 'bg-white dark:bg-gray-700 text-gray-600 dark:text-gray-400 border-gray-300 dark:border-gray-600'
|
|
}`}
|
|
title={positioningMode === 'slide'
|
|
? 'Slide-Modus: Woerter von links nach rechts schieben (klick fuer Cluster-Modus)'
|
|
: 'Cluster-Modus: Woerter an Pixel-Cluster zuordnen (klick fuer Slide-Modus)'}
|
|
>
|
|
{positioningMode === 'slide' ? 'Slide' : 'Cluster'}
|
|
</button>
|
|
|
|
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
|
|
|
{/* Text color */}
|
|
{(['red', 'blue', 'black'] as const).map((c: TextColor) => (
|
|
<button
|
|
key={c}
|
|
onClick={() => setTextColor(c)}
|
|
className={`w-5 h-5 rounded-full border-2 transition-colors ${
|
|
textColor === c ? 'border-teal-500 ring-1 ring-teal-300' : 'border-gray-300 dark:border-gray-600'
|
|
}`}
|
|
style={{ backgroundColor: c === 'black' ? '#1a1a1a' : c }}
|
|
title={`Textfarbe: ${c}`}
|
|
/>
|
|
))}
|
|
|
|
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
|
|
|
{/* Text opacity */}
|
|
<label className="flex items-center gap-1 text-xs text-gray-600 dark:text-gray-400">
|
|
Text
|
|
<input
|
|
type="range" min={0} max={100} value={textOpacity}
|
|
onChange={e => setTextOpacity(Number(e.target.value))}
|
|
className="w-16 h-1 accent-teal-600"
|
|
/>
|
|
<span className="w-8 text-right font-mono">{textOpacity}%</span>
|
|
</label>
|
|
|
|
<div className="w-px h-5 bg-gray-300 dark:bg-gray-600 mx-1" />
|
|
|
|
<button
|
|
onClick={saveReconstruction}
|
|
disabled={status === 'saving'}
|
|
className="px-4 py-1.5 text-xs bg-teal-600 text-white rounded-lg hover:bg-teal-700 disabled:opacity-50 transition-colors font-medium"
|
|
>
|
|
Speichern
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|