From 7085c8761837b9a97fd897127134cf5899f46043 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 14 Apr 2026 17:43:02 +0200 Subject: [PATCH] StepAnsicht: dominant row height for content + proportional box rows Content sections: use dominant (median) row height from all content rows instead of per-section average. This ensures uniform row height above and below boxes (the standard case on textbook pages). Box sections: distribute height proportionally by text line count per row. A header (1 line) gets 1/7 of box height, a bullet with 3 lines gets 3/7. Fixes Box 2 where row 3 was cut off because even distribution didn't account for multi-line cells. Removed overflow:hidden from box container to prevent clipping. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/ocr-kombi/StepAnsicht.tsx | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/admin-lehrer/components/ocr-kombi/StepAnsicht.tsx b/admin-lehrer/components/ocr-kombi/StepAnsicht.tsx index 841a4e1..5fc1520 100644 --- a/admin-lehrer/components/ocr-kombi/StepAnsicht.tsx +++ b/admin-lehrer/components/ocr-kombi/StepAnsicht.tsx @@ -173,6 +173,22 @@ export function StepAnsicht({ sessionId, onNext }: StepAnsichtProps) { const panelHeight = imgH * scale const contentZone = grid.zones.find((z) => z.zone_type === 'content') + // Dominant row height: median of row-to-row spacings (excluding box-gap jumps) + const dominantRowH = useMemo(() => { + const cz = grid.zones.find((z) => z.zone_type === 'content') + if (!cz || cz.rows.length < 2) return 47 + const spacings: number[] = [] + for (let i = 0; i < cz.rows.length - 1; i++) { + const y1 = cz.rows[i].y_min_px ?? (cz.rows[i] as any).y_min ?? 0 + const y2 = cz.rows[i + 1].y_min_px ?? (cz.rows[i + 1] as any).y_min ?? 0 + const d = y2 - y1 + if (d > 0 && d < 100) spacings.push(d) + } + if (spacings.length === 0) return 47 + spacings.sort((a, b) => a - b) + return spacings[Math.floor(spacings.length / 2)] + }, [grid]) + return (
{/* Header */} @@ -237,7 +253,7 @@ export function StepAnsicht({ sessionId, onNext }: StepAnsichtProps) { rows={sec.rows} yStart={sec.yStart} scale={scale} - avgRowH={sec.avgRowH} + avgRowH={dominantRowH} /> ) } @@ -375,13 +391,19 @@ function BoxSectionRenderer({ zone, scale, avgRowH }: { const colScale = totalColW > 0 ? width / totalColW : 1 const numCols = zone.columns.length - // Evenly distribute rows within the box - const numRows = zone.rows.length - const evenRowH = numRows > 0 ? height / numRows : rowH + // Distribute box height proportionally by text line count per row + const rowLineCounts = zone.rows.map((row) => { + const maxLines = Math.max(1, ...zone.cells + .filter((c) => c.row_index === row.index) + .map((c) => (c.text ?? '').split('\n').length)) + return maxLines + }) + const totalLines = rowLineCounts.reduce((s, n) => s + n, 0) + const lineUnitH = totalLines > 0 ? height / totalLines : height return (
`${(w * colScale).toFixed(1)}px`).join(' ') }}> - {zone.rows.map((row) => { + {zone.rows.map((row, rowIdx) => { const isSpanning = zone.cells.some((c) => c.row_index === row.index && c.col_type === 'spanning_header') - // Multi-line height - const maxLines = Math.max(1, ...zone.cells - .filter((c) => c.row_index === row.index) - .map((c) => (c.text ?? '').split('\n').length)) - const cellRowH = evenRowH * (maxLines > 1 ? maxLines * 0.7 : 1) + // Height proportional to text line count + const rowLines = rowLineCounts[rowIdx] || 1 + const cellRowH = lineUnitH * rowLines return (