Profil: Begrüßung in Zielsprache, Kategorie-Punkte-Übersicht, ruhigerer Header (kein rotierender Avatar/Online-Dot), Notch-Fix und kompaktere Aktivitäts-Heatmap. Außerdem Capacitor-iOS-Projekt und diverse Auth/Feed/Audio-Verbesserungen aus dem Premium-Redesign. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
43 lines
1.7 KiB
JavaScript
43 lines
1.7 KiB
JavaScript
// Bildregion-Overlay für Objekt-Chips.
|
|
// Standard: Rest des Bildes verdunkeln + Region umranden + Label (Klick-Highlight).
|
|
// outlineOnly: nur die Region umranden, kein Verdunkeln, kein Label (Karaoke-Vorlesen).
|
|
export default function SelectionOverlay({ chip, outlineOnly = false }) {
|
|
const sels = chip?.selections
|
|
if (!sels?.length) return null
|
|
const maskId = `selmask-${chip.id.slice(0, 8)}`
|
|
const label = chip.label || ''
|
|
const toPoints = pts => pts.map(p => `${p.x * 100},${p.y * 100}`).join(' ')
|
|
const firstPts = sels[0].points
|
|
const xs = firstPts.map(p => p.x * 100)
|
|
const ys = firstPts.map(p => p.y * 100)
|
|
const cx = (Math.min(...xs) + Math.max(...xs)) / 2
|
|
const labelY = Math.min(Math.max(...ys) + 6, 94)
|
|
return (
|
|
<svg className="pair-bbox-svg" viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
{!outlineOnly && (
|
|
<>
|
|
<defs>
|
|
<mask id={maskId}>
|
|
<rect width="100" height="100" fill="white" />
|
|
{sels.map((s, i) => <polygon key={i} points={toPoints(s.points)} fill="black" />)}
|
|
</mask>
|
|
</defs>
|
|
<rect width="100" height="100" fill="rgba(0,0,0,0.5)" mask={`url(#${maskId})`} />
|
|
</>
|
|
)}
|
|
{sels.map((s, i) => (
|
|
<polygon key={i} points={toPoints(s.points)}
|
|
fill="rgba(255,215,100,0.08)" stroke="rgba(255,215,100,0.92)"
|
|
strokeWidth="0.8" strokeLinejoin="round" />
|
|
))}
|
|
{!outlineOnly && label && (
|
|
<text x={cx} y={labelY} textAnchor="middle"
|
|
fill="white" fontSize="5.5" fontWeight="700" fontFamily="Nunito, sans-serif"
|
|
style={{ filter: 'drop-shadow(0 1px 4px rgba(0,0,0,1))' }}>
|
|
{label}
|
|
</text>
|
|
)}
|
|
</svg>
|
|
)
|
|
}
|