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
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:
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ${
|
||||
|
||||
Reference in New Issue
Block a user