feat: color bar respects edits + column pattern auto-correction
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 23s
CI / test-go-edu-search (push) Successful in 25s
CI / test-python-klausur (push) Failing after 1m56s
CI / test-python-agent-core (push) Successful in 14s
CI / test-nodejs-website (push) Successful in 15s

- Color bar (red/colored indicator) now only shows when word_boxes
  text still matches the cell text — editing the cell hides stale colors
- New "Auto-Korrektur" button: detects dominant prefix+number patterns
  per column (e.g. p.70, p.71) and completes partial entries (.65 → p.65)
  — requires 3+ matching entries before correcting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-24 08:38:11 +01:00
parent d6f4944bcc
commit d54814fa70
3 changed files with 90 additions and 1 deletions

View File

@@ -443,7 +443,6 @@ export function GridTable({
const isBold = col.bold || cell?.is_bold
const isLowConf = cell && cell.confidence > 0 && cell.confidence < 60
const isMultiSelected = selectedCells?.has(cellId)
const cellColor = getCellColor(cell)
// Show per-word colored display only when word_boxes
// match the cell text. Post-processing steps (e.g. 5h
// slash-IPA → bracket conversion) modify cell.text but
@@ -451,6 +450,8 @@ export function GridTable({
// plain input when they diverge.
const wbText = cell?.word_boxes?.map((wb) => wb.text).join(' ') ?? ''
const textMatches = !cell?.text || wbText === cell.text
// Color bar only when word_boxes still match edited text
const cellColor = textMatches ? getCellColor(cell) : null
const hasColoredWords =
textMatches &&
(cell?.word_boxes?.some(

View File

@@ -655,6 +655,81 @@ export function useGridEditor(sessionId: string | null) {
[grid, pushUndo],
)
// ------------------------------------------------------------------
// Column pattern auto-correction
// ------------------------------------------------------------------
/**
* Detect dominant prefix+number patterns per column and complete
* partial matches. E.g. if 3+ cells read "p.70", "p.71", etc.,
* a cell reading ".65" is corrected to "p.65".
* Returns the number of corrections made.
*/
const autoCorrectColumnPatterns = useCallback(() => {
if (!grid) return 0
pushUndo(grid.zones)
let totalFixed = 0
const numberPattern = /^(.+?)(\d+)\s*$/
setGrid((prev) => {
if (!prev) return prev
return {
...prev,
zones: prev.zones.map((zone) => {
// Group cells by column
const cellsByCol = new Map<number, { cell: (typeof zone.cells)[0]; idx: number }[]>()
zone.cells.forEach((cell, idx) => {
const arr = cellsByCol.get(cell.col_index) || []
arr.push({ cell, idx })
cellsByCol.set(cell.col_index, arr)
})
const newCells = [...zone.cells]
for (const [, colEntries] of cellsByCol) {
// Count prefix occurrences
const prefixCounts = new Map<string, number>()
for (const { cell } of colEntries) {
const m = cell.text.trim().match(numberPattern)
if (m) {
prefixCounts.set(m[1], (prefixCounts.get(m[1]) || 0) + 1)
}
}
// Find dominant prefix (>= 3 occurrences)
let dominantPrefix = ''
let maxCount = 0
for (const [prefix, count] of prefixCounts) {
if (count >= 3 && count > maxCount) {
dominantPrefix = prefix
maxCount = count
}
}
if (!dominantPrefix) continue
// Fix partial matches — entries that are just [.?\s*]NUMBER
for (const { cell, idx } of colEntries) {
const text = cell.text.trim()
if (!text || text.startsWith(dominantPrefix)) continue
const numMatch = text.match(/^[.\s]*(\d+)\s*$/)
if (numMatch) {
newCells[idx] = { ...newCells[idx], text: `${dominantPrefix}${numMatch[1]}` }
totalFixed++
}
}
}
return { ...zone, cells: newCells }
}),
}
})
if (totalFixed > 0) setDirty(true)
return totalFixed
}, [grid, pushUndo])
// ------------------------------------------------------------------
// Multi-select & bulk formatting
// ------------------------------------------------------------------
@@ -805,5 +880,6 @@ export function useGridEditor(sessionId: string | null) {
toggleCellSelection,
clearCellSelection,
toggleSelectedBold,
autoCorrectColumnPatterns,
}
}

View File

@@ -55,6 +55,7 @@ export function StepGridReview({ sessionId, onNext, saveRef }: StepGridReviewPro
toggleCellSelection,
clearCellSelection,
toggleSelectedBold,
autoCorrectColumnPatterns,
} = useGridEditor(sessionId)
const [showImage, setShowImage] = useState(true)
@@ -246,6 +247,17 @@ export function StepGridReview({ sessionId, onNext, saveRef }: StepGridReviewPro
</button>
)}
<div className="ml-auto flex items-center gap-2">
<button
onClick={() => {
const n = autoCorrectColumnPatterns()
if (n === 0) alert('Keine Muster-Korrekturen gefunden.')
else alert(`${n} Zelle(n) korrigiert (Muster-Vervollstaendigung).`)
}}
className="px-2.5 py-1 rounded text-xs border border-purple-200 dark:border-purple-700 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 hover:bg-purple-100 dark:hover:bg-purple-900/40 transition-colors"
title="Erkennt Muster wie p.70, p.71 und vervollstaendigt partielle Eintraege wie .65 zu p.65"
>
Auto-Korrektur
</button>
<button
onClick={() => setShowImage(!showImage)}
className={`px-2.5 py-1 rounded text-xs border transition-colors ${