feat: display detected text colors in grid editor UI
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 32s
CI / test-go-edu-search (push) Successful in 26s
CI / test-python-klausur (push) Failing after 2m8s
CI / test-python-agent-core (push) Successful in 16s
CI / test-nodejs-website (push) Successful in 18s

- Add color/color_name/recovered fields to OcrWordBox type
- GridTable: show colored text + left-edge color indicator strip
- GridEditor: show color stats and recovered count in summary bar

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-15 01:03:09 +01:00
parent bcd55e12d7
commit 4a8d43fd71
4 changed files with 58 additions and 14 deletions

View File

@@ -143,6 +143,23 @@ export function GridEditor({ sessionId, onNext }: GridEditorProps) {
{grid.boxes_detected} Box(en) erkannt
</span>
)}
{grid.summary.color_stats && Object.entries(grid.summary.color_stats)
.filter(([name]) => name !== 'black')
.map(([name, count]) => (
<span key={name} className="inline-flex items-center gap-1">
<span className="w-2 h-2 rounded-full" style={{ backgroundColor: {
red: '#dc2626', blue: '#2563eb', green: '#16a34a',
orange: '#ea580c', purple: '#9333ea', yellow: '#ca8a04',
}[name] || '#6b7280' }} />
<span>{count} {name}</span>
</span>
))
}
{(grid.summary.recovered_colored ?? 0) > 0 && (
<span className="text-purple-600 dark:text-purple-400">
+{grid.summary.recovered_colored} recovered
</span>
)}
<span className="text-gray-400">
{grid.duration_seconds.toFixed(1)}s
</span>

View File

@@ -51,6 +51,17 @@ export function GridTable({
cellMap.set(`${cell.row_index}_${cell.col_index}`, cell)
}
/** Dominant non-black color from a cell's word_boxes, or null. */
const getCellColor = (cell: (typeof zone.cells)[0] | undefined): string | null => {
if (!cell?.word_boxes?.length) return null
for (const wb of cell.word_boxes) {
if (wb.color_name && wb.color_name !== 'black' && wb.color) {
return wb.color
}
}
return null
}
const isBoxZone = zone.zone_type === 'box'
return (
@@ -119,6 +130,7 @@ export function GridTable({
const isSelected = selectedCell === cellId
const isBold = col.bold || cell?.is_bold
const isLowConf = cell && cell.confidence > 0 && cell.confidence < 60
const cellColor = getCellColor(cell)
return (
<td
@@ -127,20 +139,30 @@ export function GridTable({
isSelected ? 'ring-2 ring-teal-500 ring-inset z-10 relative' : ''
} ${isLowConf ? 'bg-amber-50/50 dark:bg-amber-900/10' : ''}`}
>
<input
id={`cell-${cellId}`}
type="text"
value={cell?.text ?? ''}
onChange={(e) => {
if (cell) onCellTextChange(cellId, e.target.value)
}}
onFocus={() => onSelectCell(cellId)}
onKeyDown={(e) => handleKeyDown(e, cellId)}
className={`w-full px-2 py-1.5 bg-transparent border-0 outline-none text-gray-800 dark:text-gray-200 ${
isBold ? 'font-bold' : 'font-normal'
} ${row.is_header ? 'text-base' : 'text-sm'}`}
spellCheck={false}
/>
<div className="flex items-center">
{cellColor && (
<span
className="flex-shrink-0 w-1.5 h-full min-h-[28px] rounded-l-sm"
style={{ backgroundColor: cellColor }}
title={`Farbe: ${cell?.word_boxes?.find(wb => wb.color_name !== 'black')?.color_name}`}
/>
)}
<input
id={`cell-${cellId}`}
type="text"
value={cell?.text ?? ''}
onChange={(e) => {
if (cell) onCellTextChange(cellId, e.target.value)
}}
onFocus={() => onSelectCell(cellId)}
onKeyDown={(e) => handleKeyDown(e, cellId)}
className={`w-full px-2 py-1.5 bg-transparent border-0 outline-none ${
isBold ? 'font-bold' : 'font-normal'
} ${row.is_header ? 'text-base' : 'text-sm'}`}
style={cellColor ? { color: cellColor } : undefined}
spellCheck={false}
/>
</div>
</td>
)
})}

View File

@@ -22,6 +22,8 @@ export interface GridSummary {
total_rows: number
total_cells: number
total_words: number
recovered_colored?: number
color_stats?: Record<string, number>
}
export interface GridFormatting {