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
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:
@@ -227,6 +227,9 @@ export interface OcrWordBox {
|
||||
width: number // px
|
||||
height: number // px
|
||||
conf: number
|
||||
color?: string // hex color of detected text, e.g. '#dc2626'
|
||||
color_name?: string // 'black' | 'red' | 'blue' | 'green' | 'orange' | 'purple' | 'yellow'
|
||||
recovered?: boolean // true if this word was recovered via color detection
|
||||
}
|
||||
|
||||
export interface GridCell {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,6 +139,14 @@ export function GridTable({
|
||||
isSelected ? 'ring-2 ring-teal-500 ring-inset z-10 relative' : ''
|
||||
} ${isLowConf ? 'bg-amber-50/50 dark:bg-amber-900/10' : ''}`}
|
||||
>
|
||||
<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"
|
||||
@@ -136,11 +156,13 @@ export function GridTable({
|
||||
}}
|
||||
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 ${
|
||||
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>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user