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 37s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m17s
CI / test-python-agent-core (push) Successful in 17s
CI / test-nodejs-website (push) Successful in 24s
- orientation_crop_api.py: `array or array` durch `is not None` ersetzt (ValueError bei numpy Arrays) - ocr_pipeline_api.py: gleicher Fix fuer Deskew-Fallback-Kette - ImageCompareView.tsx: Fallback-Text nutzt rightLabel statt "Begradigung" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
144 lines
4.2 KiB
TypeScript
144 lines
4.2 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
|
|
const A4_WIDTH_MM = 210
|
|
const A4_HEIGHT_MM = 297
|
|
|
|
interface ImageCompareViewProps {
|
|
originalUrl: string | null
|
|
deskewedUrl: string | null
|
|
showGrid: boolean
|
|
showGridLeft?: boolean
|
|
showBinarized: boolean
|
|
binarizedUrl: string | null
|
|
leftLabel?: string
|
|
rightLabel?: string
|
|
}
|
|
|
|
function MmGridOverlay() {
|
|
const lines: React.ReactNode[] = []
|
|
|
|
// Vertical lines every 10mm
|
|
for (let mm = 0; mm <= A4_WIDTH_MM; mm += 10) {
|
|
const x = (mm / A4_WIDTH_MM) * 100
|
|
const is50 = mm % 50 === 0
|
|
lines.push(
|
|
<line
|
|
key={`v-${mm}`}
|
|
x1={x} y1={0} x2={x} y2={100}
|
|
stroke={is50 ? 'rgba(59, 130, 246, 0.4)' : 'rgba(59, 130, 246, 0.15)'}
|
|
strokeWidth={is50 ? 0.12 : 0.05}
|
|
/>
|
|
)
|
|
// Label every 50mm
|
|
if (is50 && mm > 0) {
|
|
lines.push(
|
|
<text key={`vl-${mm}`} x={x} y={1.2} fill="rgba(59,130,246,0.6)" fontSize="1.2" textAnchor="middle">
|
|
{mm}
|
|
</text>
|
|
)
|
|
}
|
|
}
|
|
|
|
// Horizontal lines every 10mm
|
|
for (let mm = 0; mm <= A4_HEIGHT_MM; mm += 10) {
|
|
const y = (mm / A4_HEIGHT_MM) * 100
|
|
const is50 = mm % 50 === 0
|
|
lines.push(
|
|
<line
|
|
key={`h-${mm}`}
|
|
x1={0} y1={y} x2={100} y2={y}
|
|
stroke={is50 ? 'rgba(59, 130, 246, 0.4)' : 'rgba(59, 130, 246, 0.15)'}
|
|
strokeWidth={is50 ? 0.12 : 0.05}
|
|
/>
|
|
)
|
|
if (is50 && mm > 0) {
|
|
lines.push(
|
|
<text key={`hl-${mm}`} x={0.5} y={y + 0.6} fill="rgba(59,130,246,0.6)" fontSize="1.2">
|
|
{mm}
|
|
</text>
|
|
)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<svg
|
|
viewBox="0 0 100 100"
|
|
preserveAspectRatio="none"
|
|
className="absolute inset-0 w-full h-full pointer-events-none"
|
|
style={{ zIndex: 10 }}
|
|
>
|
|
<g style={{ pointerEvents: 'none' }}>{lines}</g>
|
|
</svg>
|
|
)
|
|
}
|
|
|
|
export function ImageCompareView({
|
|
originalUrl,
|
|
deskewedUrl,
|
|
showGrid,
|
|
showGridLeft,
|
|
showBinarized,
|
|
binarizedUrl,
|
|
leftLabel,
|
|
rightLabel,
|
|
}: ImageCompareViewProps) {
|
|
const [leftError, setLeftError] = useState(false)
|
|
const [rightError, setRightError] = useState(false)
|
|
|
|
const rightUrl = showBinarized && binarizedUrl ? binarizedUrl : deskewedUrl
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
{/* Left: Original */}
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium text-gray-500 dark:text-gray-400">{leftLabel || 'Original (unbearbeitet)'}</h3>
|
|
<div className="relative bg-gray-100 dark:bg-gray-900 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700"
|
|
style={{ aspectRatio: '210/297' }}>
|
|
{originalUrl && !leftError ? (
|
|
<>
|
|
<img
|
|
src={originalUrl}
|
|
alt="Original Scan"
|
|
className="w-full h-full object-contain"
|
|
onError={() => setLeftError(true)}
|
|
/>
|
|
{showGridLeft && <MmGridOverlay />}
|
|
</>
|
|
) : (
|
|
<div className="flex items-center justify-center h-full text-gray-400">
|
|
{leftError ? 'Fehler beim Laden' : 'Noch kein Bild'}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Right: Deskewed with Grid */}
|
|
<div className="space-y-2">
|
|
<h3 className="text-sm font-medium text-gray-500 dark:text-gray-400">
|
|
{rightLabel || `${showBinarized ? 'Binarisiert' : 'Begradigt'}${showGrid ? ' + Raster (mm)' : ''}`}
|
|
</h3>
|
|
<div className="relative bg-gray-100 dark:bg-gray-900 rounded-lg overflow-hidden border border-gray-200 dark:border-gray-700"
|
|
style={{ aspectRatio: '210/297' }}>
|
|
{rightUrl && !rightError ? (
|
|
<>
|
|
<img
|
|
src={rightUrl}
|
|
alt={rightLabel || 'Bearbeitetes Bild'}
|
|
className="w-full h-full object-contain"
|
|
onError={() => setRightError(true)}
|
|
/>
|
|
{showGrid && <MmGridOverlay />}
|
|
</>
|
|
) : (
|
|
<div className="flex items-center justify-center h-full text-gray-400">
|
|
{rightError ? 'Fehler beim Laden' : `${rightLabel || 'Verarbeitung'} laeuft...`}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|