Add IPA and syllable mode toggles, fix false IPA on German documents
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 24s
CI / test-go-edu-search (push) Successful in 26s
CI / test-python-klausur (push) Failing after 2m1s
CI / test-python-agent-core (push) Successful in 15s
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 24s
CI / test-go-edu-search (push) Successful in 26s
CI / test-python-klausur (push) Failing after 2m1s
CI / test-python-agent-core (push) Successful in 15s
CI / test-nodejs-website (push) Successful in 15s
Backend: Remove en_col_type fallback heuristic (longest avg text) that incorrectly identified German columns as English. IPA now only applied when OCR bracket patterns are actually found. Add ipa_mode (auto/all/none) and syllable_mode (auto/all/none) query params to build-grid API. Frontend: Add IPA and Silben dropdown selects to GridToolbar. Modes are passed as query params on rebuild. Auto = current smart detection, All = force for all words, Aus = skip entirely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,10 @@ export function GridEditor({ sessionId, onNext }: GridEditorProps) {
|
||||
addColumn,
|
||||
deleteRow,
|
||||
addRow,
|
||||
ipaMode,
|
||||
setIpaMode,
|
||||
syllableMode,
|
||||
setSyllableMode,
|
||||
} = useGridEditor(sessionId)
|
||||
|
||||
const [showOverlay, setShowOverlay] = useState(false)
|
||||
@@ -183,11 +187,15 @@ export function GridEditor({ sessionId, onNext }: GridEditorProps) {
|
||||
canUndo={canUndo}
|
||||
canRedo={canRedo}
|
||||
showOverlay={showOverlay}
|
||||
ipaMode={ipaMode}
|
||||
syllableMode={syllableMode}
|
||||
onSave={saveGrid}
|
||||
onUndo={undo}
|
||||
onRedo={redo}
|
||||
onRebuild={buildGrid}
|
||||
onToggleOverlay={() => setShowOverlay(!showOverlay)}
|
||||
onIpaModeChange={setIpaMode}
|
||||
onSyllableModeChange={setSyllableMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,16 +1,34 @@
|
||||
'use client'
|
||||
|
||||
import type { IpaMode, SyllableMode } from './useGridEditor'
|
||||
|
||||
interface GridToolbarProps {
|
||||
dirty: boolean
|
||||
saving: boolean
|
||||
canUndo: boolean
|
||||
canRedo: boolean
|
||||
showOverlay: boolean
|
||||
ipaMode: IpaMode
|
||||
syllableMode: SyllableMode
|
||||
onSave: () => void
|
||||
onUndo: () => void
|
||||
onRedo: () => void
|
||||
onRebuild: () => void
|
||||
onToggleOverlay: () => void
|
||||
onIpaModeChange: (mode: IpaMode) => void
|
||||
onSyllableModeChange: (mode: SyllableMode) => void
|
||||
}
|
||||
|
||||
const IPA_LABELS: Record<IpaMode, string> = {
|
||||
auto: 'IPA: Auto',
|
||||
all: 'IPA: Alle',
|
||||
none: 'IPA: Aus',
|
||||
}
|
||||
|
||||
const SYLLABLE_LABELS: Record<SyllableMode, string> = {
|
||||
auto: 'Silben: Original',
|
||||
all: 'Silben: Alle',
|
||||
none: 'Silben: Aus',
|
||||
}
|
||||
|
||||
export function GridToolbar({
|
||||
@@ -19,11 +37,15 @@ export function GridToolbar({
|
||||
canUndo,
|
||||
canRedo,
|
||||
showOverlay,
|
||||
ipaMode,
|
||||
syllableMode,
|
||||
onSave,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onRebuild,
|
||||
onToggleOverlay,
|
||||
onIpaModeChange,
|
||||
onSyllableModeChange,
|
||||
}: GridToolbarProps) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
@@ -67,6 +89,30 @@ export function GridToolbar({
|
||||
Bild-Overlay
|
||||
</button>
|
||||
|
||||
{/* IPA mode */}
|
||||
<select
|
||||
value={ipaMode}
|
||||
onChange={(e) => onIpaModeChange(e.target.value as IpaMode)}
|
||||
className="px-2 py-1.5 text-xs rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400"
|
||||
title="Lautschrift (IPA): Auto = nur bei erkannten englischen Woertern, Alle = fuer alle Vokabeln, Aus = keine"
|
||||
>
|
||||
{(Object.keys(IPA_LABELS) as IpaMode[]).map((m) => (
|
||||
<option key={m} value={m}>{IPA_LABELS[m]}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
{/* Syllable mode */}
|
||||
<select
|
||||
value={syllableMode}
|
||||
onChange={(e) => onSyllableModeChange(e.target.value as SyllableMode)}
|
||||
className="px-2 py-1.5 text-xs rounded-md border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-600 dark:text-gray-400"
|
||||
title="Silbentrennung: Original = nur wo im Scan vorhanden, Alle = fuer alle Woerter, Aus = keine"
|
||||
>
|
||||
{(Object.keys(SYLLABLE_LABELS) as SyllableMode[]).map((m) => (
|
||||
<option key={m} value={m}>{SYLLABLE_LABELS[m]}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
{/* Rebuild */}
|
||||
<button
|
||||
onClick={onRebuild}
|
||||
|
||||
@@ -14,6 +14,9 @@ export interface GridEditorState {
|
||||
selectedZone: number | null
|
||||
}
|
||||
|
||||
export type IpaMode = 'auto' | 'all' | 'none'
|
||||
export type SyllableMode = 'auto' | 'all' | 'none'
|
||||
|
||||
export function useGridEditor(sessionId: string | null) {
|
||||
const [grid, setGrid] = useState<StructuredGrid | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -22,6 +25,8 @@ export function useGridEditor(sessionId: string | null) {
|
||||
const [dirty, setDirty] = useState(false)
|
||||
const [selectedCell, setSelectedCell] = useState<string | null>(null)
|
||||
const [selectedZone, setSelectedZone] = useState<number | null>(null)
|
||||
const [ipaMode, setIpaMode] = useState<IpaMode>('auto')
|
||||
const [syllableMode, setSyllableMode] = useState<SyllableMode>('auto')
|
||||
|
||||
// Undo/redo stacks store serialized zone arrays
|
||||
const undoStack = useRef<string[]>([])
|
||||
@@ -44,8 +49,11 @@ export function useGridEditor(sessionId: string | null) {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const params = new URLSearchParams()
|
||||
params.set('ipa_mode', ipaMode)
|
||||
params.set('syllable_mode', syllableMode)
|
||||
const res = await fetch(
|
||||
`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/build-grid`,
|
||||
`${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/build-grid?${params}`,
|
||||
{ method: 'POST' },
|
||||
)
|
||||
if (!res.ok) {
|
||||
@@ -62,7 +70,7 @@ export function useGridEditor(sessionId: string | null) {
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [sessionId])
|
||||
}, [sessionId, ipaMode, syllableMode])
|
||||
|
||||
const loadGrid = useCallback(async () => {
|
||||
if (!sessionId) return
|
||||
@@ -915,5 +923,9 @@ export function useGridEditor(sessionId: string | null) {
|
||||
toggleSelectedBold,
|
||||
autoCorrectColumnPatterns,
|
||||
setCellColor,
|
||||
ipaMode,
|
||||
setIpaMode,
|
||||
syllableMode,
|
||||
setSyllableMode,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ export function StepGridReview({ sessionId, onNext, saveRef }: StepGridReviewPro
|
||||
toggleSelectedBold,
|
||||
autoCorrectColumnPatterns,
|
||||
setCellColor,
|
||||
ipaMode,
|
||||
setIpaMode,
|
||||
syllableMode,
|
||||
setSyllableMode,
|
||||
} = useGridEditor(sessionId)
|
||||
|
||||
const [showImage, setShowImage] = useState(true)
|
||||
@@ -283,11 +287,15 @@ export function StepGridReview({ sessionId, onNext, saveRef }: StepGridReviewPro
|
||||
canUndo={canUndo}
|
||||
canRedo={canRedo}
|
||||
showOverlay={false}
|
||||
ipaMode={ipaMode}
|
||||
syllableMode={syllableMode}
|
||||
onSave={saveGrid}
|
||||
onUndo={undo}
|
||||
onRedo={redo}
|
||||
onRebuild={buildGrid}
|
||||
onToggleOverlay={() => setShowImage(!showImage)}
|
||||
onIpaModeChange={setIpaMode}
|
||||
onSyllableModeChange={setSyllableMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user