From f535d3c9675491e19e0301e5b0f9ce5d9ab8aca9 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Fri, 27 Feb 2026 08:45:49 +0100 Subject: [PATCH] fix(ocr-pipeline): manual editor layout + no re-detection on cached result - ManualColumnEditor now uses grid-cols-2 layout (image left, controls right) matching the normal view size so the image doesn't zoom in - StepColumnDetection only runs auto-detection when no cached result exists; revisiting step 3 loads cached columns without re-running detection Co-Authored-By: Claude Opus 4.6 --- .../ocr-pipeline/ManualColumnEditor.tsx | 279 ++++++++++-------- .../ocr-pipeline/StepColumnDetection.tsx | 46 ++- 2 files changed, 168 insertions(+), 157 deletions(-) diff --git a/admin-lehrer/components/ocr-pipeline/ManualColumnEditor.tsx b/admin-lehrer/components/ocr-pipeline/ManualColumnEditor.tsx index 84b0e91..deea2d6 100644 --- a/admin-lehrer/components/ocr-pipeline/ManualColumnEditor.tsx +++ b/admin-lehrer/components/ocr-pipeline/ManualColumnEditor.tsx @@ -184,141 +184,158 @@ export function ManualColumnEditor({ return (
-
-
- Klicken Sie auf das Bild, um vertikale Trennlinien zu setzen. - {dividers.length > 0 && ( - - {dividers.length} Linien = {dividers.length + 1} Spalten - + {/* Two-column layout: image (left) + controls (right) */} +
+ {/* Left: Interactive image */} +
+
+
+ Klicken um Trennlinien zu setzen +
+ +
+
+ {/* eslint-disable-next-line @next/next/no-img-element */} + Entzerrtes Bild setImageLoaded(true)} + /> + + {imageLoaded && ( + <> + {/* Column overlays */} + {columnRegions.map((region, i) => ( +
+ + {i + 1} + +
+ ))} + + {/* Divider lines */} + {sorted.map((xPct, i) => ( +
handleDividerMouseDown(e, i)} + > + {/* Visible line */} +
+ {/* Delete button */} + +
+ ))} + + )} +
+
+ + {/* Right: Column type assignment + actions */} +
+
+ Spaltentypen +
+ + {dividers.length === 0 ? ( +
+
👆
+

+ Klicken Sie auf das Bild links, um vertikale Trennlinien zwischen den Spalten zu setzen. +

+

+ Linien koennen per Drag verschoben und per Hover geloescht werden. +

+
+ ) : ( +
+
+ + {dividers.length} Linien = {dividers.length + 1} Spalten + +
+
+ {columnRegions.map((region, i) => ( +
+ + Spalte {i + 1} + + + + {Math.round(region.rightPct - region.leftPct)}% + +
+ ))} +
+
)} -
- -
- {/* Interactive image area */} -
- {/* eslint-disable-next-line @next/next/no-img-element */} - Entzerrtes Bild setImageLoaded(true)} - /> - - {imageLoaded && ( - <> - {/* Column overlays */} - {columnRegions.map((region, i) => ( -
- - {i + 1} - -
- ))} - - {/* Divider lines */} - {sorted.map((xPct, i) => ( -
handleDividerMouseDown(e, i)} - > - {/* Visible line */} -
- {/* Delete button */} - -
- ))} - - )} -
- - {/* Column type assignment */} - {dividers.length > 0 && ( -
-
- Spaltentypen zuweisen -
-
- {columnRegions.map((region, i) => ( -
- - Spalte {i + 1} - - - - {Math.round(region.rightPct - region.leftPct)}% - -
- ))} + {/* Action buttons */} +
+ +
- )} - - {/* Action buttons */} -
- -
) diff --git a/admin-lehrer/components/ocr-pipeline/StepColumnDetection.tsx b/admin-lehrer/components/ocr-pipeline/StepColumnDetection.tsx index 5f14fbb..fe31287 100644 --- a/admin-lehrer/components/ocr-pipeline/StepColumnDetection.tsx +++ b/admin-lehrer/components/ocr-pipeline/StepColumnDetection.tsx @@ -20,15 +20,12 @@ export function StepColumnDetection({ sessionId, onNext }: StepColumnDetectionPr const [applying, setApplying] = useState(false) const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number } | null>(null) - // Auto-trigger column detection on mount + // Fetch session info (image dimensions) + check for cached column result useEffect(() => { - if (!sessionId || columnResult) return + if (!sessionId || imageDimensions) return - const runDetection = async () => { - setDetecting(true) - setError(null) + const fetchSessionInfo = async () => { try { - // First check if columns already detected (reload case) const infoRes = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}`) if (infoRes.ok) { const info = await infoRes.json() @@ -37,32 +34,22 @@ export function StepColumnDetection({ sessionId, onNext }: StepColumnDetectionPr } if (info.column_result) { setColumnResult(info.column_result) - setDetecting(false) return } } - - // Run detection - const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/columns`, { - method: 'POST', - }) - if (!res.ok) { - const err = await res.json().catch(() => ({ detail: res.statusText })) - throw new Error(err.detail || 'Spaltenerkennung fehlgeschlagen') - } - const data: ColumnResult = await res.json() - setColumnResult(data) } catch (e) { - setError(e instanceof Error ? e.message : 'Unbekannter Fehler') - } finally { - setDetecting(false) + console.error('Failed to fetch session info:', e) } + + // No cached result - run auto-detection + runAutoDetection() } - runDetection() - }, [sessionId, columnResult]) + fetchSessionInfo() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sessionId]) - const handleRerun = useCallback(async () => { + const runAutoDetection = useCallback(async () => { if (!sessionId) return setDetecting(true) setError(null) @@ -70,16 +57,23 @@ export function StepColumnDetection({ sessionId, onNext }: StepColumnDetectionPr const res = await fetch(`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/columns`, { method: 'POST', }) - if (!res.ok) throw new Error('Spaltenerkennung fehlgeschlagen') + if (!res.ok) { + const err = await res.json().catch(() => ({ detail: res.statusText })) + throw new Error(err.detail || 'Spaltenerkennung fehlgeschlagen') + } const data: ColumnResult = await res.json() setColumnResult(data) } catch (e) { - setError(e instanceof Error ? e.message : 'Fehler') + setError(e instanceof Error ? e.message : 'Unbekannter Fehler') } finally { setDetecting(false) } }, [sessionId]) + const handleRerun = useCallback(() => { + runAutoDetection() + }, [runAutoDetection]) + const handleGroundTruth = useCallback(async (gt: ColumnGroundTruth) => { if (!sessionId) return try {