function HighlightedExcerpt({ excerpt, highlightStart, highlightEnd, label }) { if (!excerpt) return null const start = Number(highlightStart) const end = Number(highlightEnd) if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && end <= excerpt.length && end > start) { return (

{excerpt.slice(0, start)} {excerpt.slice(start, end)} {excerpt.slice(end)}

) } const needle = label || '' const at = needle ? excerpt.toLowerCase().indexOf(needle.toLowerCase()) : -1 if (at < 0) return

{excerpt}

return (

{excerpt.slice(0, at)} {excerpt.slice(at, at + needle.length)} {excerpt.slice(at + needle.length)}

) } function reviewCopy(kind) { if (kind === 'confirm_remote') { return { title: 'Klartext-Detect bestätigen', status: 'GLiNER fehlt. Ein öffentliches Detect darf den Klartext nur senden, wenn du das ausdrücklich bestätigst. „Identität“ heißt senden, „Nicht schützenswert“ bricht ab.' } } if (kind === 'manual') { return { title: 'Maskierung prüfen', status: 'Manuelles Lernen: bitte jede vorgeschlagene Nennung bestätigen oder als nicht schützenswert markieren.' } } return { title: 'Maskierung prüfen', status: 'Abweichung oder neue Identität. Nur die gelb markierte Nennung. Bestätigen legt die Bezeichnung lokal ab. „Nicht schützenswert“ verhindert die Maskierung.' } } export default function MaskReviewPanel({ review, busy, onSubmit }) { if (!review?.candidates?.length) return null const copy = reviewCopy(review.kind) return (

{copy.title}

{copy.status}

{ event.preventDefault() const data = new FormData(event.currentTarget) const decisions = review.candidates.map((item) => ({ id: item.id, decision: data.get(`decision-${item.id}`) || 'identity' })) onSubmit(decisions) }} >
    {review.candidates.map((item) => (
  • {item.text} · {item.entity_type}{item.ambiguous ? ' · mehrdeutig' : ''}
  • ))}
) }