diff --git a/admin-lehrer/components/ocr-kombi/SpreadsheetView.tsx b/admin-lehrer/components/ocr-kombi/SpreadsheetView.tsx index 5954d43..8da3189 100644 --- a/admin-lehrer/components/ocr-kombi/SpreadsheetView.tsx +++ b/admin-lehrer/components/ocr-kombi/SpreadsheetView.tsx @@ -24,48 +24,7 @@ interface SpreadsheetViewProps { height?: number } -/** Expand multi-line cells (\n) into separate rows for better display. */ -function expandMultiLineCells(zone: GridZone): { cells: any[]; numRows: number; rowMeta: any[] } { - const expandedCells: any[] = [] - const rowMeta: any[] = [] // metadata per expanded row - let expandedRow = 0 - - for (const row of (zone.rows || [])) { - const rowCells = (zone.cells || []).filter((c) => c.row_index === row.index) - const isHeader = row.is_header ?? false - - // Check if any cell in this row has \n - const maxLines = Math.max(1, ...rowCells.map((c) => (c.text ?? '').split('\n').length)) - - if (maxLines > 1) { - // Expand: each line becomes its own row - for (let lineIdx = 0; lineIdx < maxLines; lineIdx++) { - const isFirstLine = lineIdx === 0 - rowMeta.push({ isHeader, isIndented: !isFirstLine, originalRow: row.index }) - for (const cell of rowCells) { - const lines = (cell.text ?? '').split('\n') - const lineText = lineIdx < lines.length ? lines[lineIdx] : '' - expandedCells.push({ - ...cell, - row_index: expandedRow, - text: lineText, - _isIndented: !isFirstLine, - _isFirstBulletLine: isFirstLine, - }) - } - expandedRow++ - } - } else { - // Single line — keep as is - rowMeta.push({ isHeader, isIndented: false, originalRow: row.index }) - for (const cell of rowCells) { - expandedCells.push({ ...cell, row_index: expandedRow }) - } - expandedRow++ - } - } - return { cells: expandedCells, numRows: expandedRow, rowMeta } -} +/** No expansion — keep multi-line cells as single cells with \n and text-wrap. */ /** Convert a single zone to a Fortune Sheet sheet object. */ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any { @@ -83,9 +42,8 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any } const numCols = zone.columns?.length || 1 - - // Expand multi-line cells into separate rows - const { cells: expandedCells, numRows, rowMeta } = expandMultiLineCells(zone) + const numRows = zone.rows?.length || 0 + const expandedCells = zone.cells || [] // Compute zone-wide median word height for font-size detection const allWordHeights = zone.cells @@ -103,7 +61,10 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any const r = cell.row_index const c = cell.col_index const text = cell.text ?? '' - const meta = rowMeta[r] || {} + + // Row metadata + const row = zone.rows?.find((rr) => rr.index === r) + const isHeader = row?.is_header ?? false // Font size detection from word_boxes const avgWbH = cell.word_boxes?.length @@ -113,8 +74,8 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any const v: any = { v: text, m: text } - // Bold: headers, is_bold, larger font, first bullet line with • - if (cell.is_bold || meta.isHeader || isLargerFont) { + // Bold: headers, is_bold, larger font + if (cell.is_bold || isHeader || isLargerFont) { v.bl = 1 } @@ -123,28 +84,19 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any v.fs = 12 } - // Indentation for bullet continuation lines - if (cell._isIndented) { - v.ht = 0 // left-align - // Add visual indent via leading spaces (Fortune Sheet has no padding-left) - if (text && !text.startsWith(' ')) { - v.v = ' ' + text - v.m = ' ' + text - } - } - - // Bullet line: keep • visible - if (cell._isFirstBulletLine && text.startsWith('•')) { - v.bl = 1 // bold bullet marker line + // Multi-line text (bullets with \n): enable text wrap + vertical top align + if (text.includes('\n')) { + v.tb = '2' // text wrap + v.vt = 0 // vertical align: top } // Header row background - if (meta.isHeader) { + if (isHeader) { v.bg = isBox ? `${boxColor || '#2563eb'}18` : '#f0f4ff' } // Box cells: light tinted background - if (isBox && !meta.isHeader && boxColor) { + if (isBox && !isHeader && boxColor) { v.bg = `${boxColor}08` } @@ -155,9 +107,9 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any celldata.push({ r, c, v }) - // Colspan → merge (only on non-expanded rows) + // Colspan → merge const colspan = cell.colspan || 0 - if ((colspan > 1 || cell.col_type === 'spanning_header') && !cell._isIndented) { + if (colspan > 1 || cell.col_type === 'spanning_header') { const cs = colspan || numCols merges[`${r}_${c}`] = { r, c, rs: 1, cs } } @@ -180,10 +132,13 @@ function zoneToSheet(zone: GridZone, sheetIndex: number, isFirst: boolean): any columnlen[String(col.index)] = Math.round(Math.max(autoWidth, scaledPxW)) } - // Row heights + // Row heights — taller for multi-line cells const rowlen: Record = {} - for (let ri = 0; ri < numRows; ri++) { - rowlen[String(ri)] = 24 + for (const row of (zone.rows || [])) { + const rowCells = expandedCells.filter((c: any) => c.row_index === row.index) + const maxLines = Math.max(1, ...rowCells.map((c: any) => (c.text ?? '').split('\n').length)) + const baseH = 24 + rowlen[String(row.index)] = Math.max(baseH, baseH * maxLines) } // Border info