diff --git a/backend/dist/nsfwapp-linux-amd64 b/backend/dist/nsfwapp-linux-amd64 index 0a61d8b..f81b924 100644 Binary files a/backend/dist/nsfwapp-linux-amd64 and b/backend/dist/nsfwapp-linux-amd64 differ diff --git a/backend/dist/nsfwapp.exe b/backend/dist/nsfwapp.exe index b1a6b7d..c1e1148 100644 Binary files a/backend/dist/nsfwapp.exe and b/backend/dist/nsfwapp.exe differ diff --git a/backend/dist/nsfwapp_amd64.deb b/backend/dist/nsfwapp_amd64.deb index b287d13..73f9724 100644 Binary files a/backend/dist/nsfwapp_amd64.deb and b/backend/dist/nsfwapp_amd64.deb differ diff --git a/frontend/src/components/ui/TrainingTab.tsx b/frontend/src/components/ui/TrainingTab.tsx index 3d02081..cc42409 100644 --- a/frontend/src/components/ui/TrainingTab.tsx +++ b/frontend/src/components/ui/TrainingTab.tsx @@ -1,7 +1,7 @@ // frontend\src\components\ui\TrainingTab.tsx 'use client' -import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react' +import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from 'react' import Button from './Button' import LoadingSpinner from './LoadingSpinner' import { formatDuration } from './formatters' @@ -637,11 +637,15 @@ function poseKeypointLabel(name?: string | null) { return POSE_KEYPOINT_LABELS[key] || key.replace(/_/g, ' ') || 'Punkt' } -function poseKeypointMarkerClassName(present: boolean, pending: boolean) { +function poseKeypointMarkerClassName(present: boolean, pending: boolean, highlighted = false) { if (pending) { return 'fill-amber-500 stroke-white/90 dark:stroke-gray-950/90' } + if (highlighted) { + return 'fill-cyan-500 stroke-white/95 dark:stroke-gray-950/90' + } + if (present) { return 'fill-blue-500 stroke-white/90 dark:stroke-gray-950/90' } @@ -913,20 +917,6 @@ function poseCoordPx(value: number, size: number) { return clamp01(Number(value)) * Math.max(0, Number(size) || 0) } -function poseBoxPixelStyle(box: TrainingBox, layerWidth: number, layerHeight: number): CSSProperties { - const x = clamp01(Number(box.x)) - const y = clamp01(Number(box.y)) - const w = Math.max(0, Math.min(1 - x, clamp01(Number(box.w)))) - const h = Math.max(0, Math.min(1 - y, clamp01(Number(box.h)))) - - return { - left: poseCoordPx(x, layerWidth), - top: poseCoordPx(y, layerHeight), - width: w * Math.max(0, Number(layerWidth) || 0), - height: h * Math.max(0, Number(layerHeight) || 0), - } -} - function backendText(data: any, fallback: string) { return String( data?.message || @@ -4250,6 +4240,7 @@ export default function TrainingTab(props: { setShowPoseSkeleton(false) setActivePosePersonIndex(null) setPendingPoseKeypoint(null) + setHoveredPoseKeypoint(null) if (poseKeypointMapAnimationFrameRef.current !== null) { cancelAnimationFrame(poseKeypointMapAnimationFrameRef.current) poseKeypointMapAnimationFrameRef.current = null @@ -4409,6 +4400,7 @@ export default function TrainingTab(props: { const [boxInteraction, setBoxInteraction] = useState(null) const [poseInteraction, setPoseInteraction] = useState(null) const [pendingPoseKeypoint, setPendingPoseKeypoint] = useState(null) + const [hoveredPoseKeypoint, setHoveredPoseKeypoint] = useState(null) const [poseKeypointMapView, setPoseKeypointMapView] = useState('body') const [poseKeypointMapViewBoxValue, setPoseKeypointMapViewBoxValue] = useState(POSE_BODY_MAP_VIEW_BOX) @@ -4567,6 +4559,7 @@ export default function TrainingTab(props: { setShowPoseSkeleton(false) setActivePosePersonIndex(null) setPendingPoseKeypoint(null) + setHoveredPoseKeypoint(null) poseFaceImagePanGestureRef.current = null poseFaceImagePanCenterRef.current = null @@ -9782,7 +9775,39 @@ export default function TrainingTab(props: { return (
{ + if (uiLocked) return + setHoveredPoseKeypoint({ + personIndex, + keypointName: poseKeypointId(point.name), + }) + }} + onPointerLeave={() => { + const keypointName = poseKeypointId(point.name) + setHoveredPoseKeypoint((current) => + current?.personIndex === personIndex && + current.keypointName === keypointName + ? null + : current + ) + }} + onClick={(event) => { + event.stopPropagation() + if (uiLocked) return + setShowPoseSkeleton(true) + setActivePosePersonIndex(personIndex) + setPendingPoseKeypoint({ + personIndex, + keypointName: poseKeypointId(point.name), + }) + }} >
@@ -10244,6 +10269,9 @@ export default function TrainingTab(props: { const pending = pendingPoseKeypoint?.personIndex === targetIndex && pendingPoseKeypoint.keypointName === shape.keypointName + const highlighted = + hoveredPoseKeypoint?.personIndex === targetIndex && + hoveredPoseKeypoint.keypointName === shape.keypointName return ( { + if (disabled || targetIndex === null) return + setHoveredPoseKeypoint({ + personIndex: targetIndex, + keypointName, + }) + }} + onPointerLeave={() => { + setHoveredPoseKeypoint((current) => + current?.personIndex === targetIndex && + current.keypointName === keypointName + ? null + : current + ) + }} + onFocus={() => { + if (disabled || targetIndex === null) return + setHoveredPoseKeypoint({ + personIndex: targetIndex, + keypointName, + }) + }} + onBlur={() => { + setHoveredPoseKeypoint((current) => + current?.personIndex === targetIndex && + current.keypointName === keypointName + ? null + : current + ) + }} onClick={() => selectPoseKeypoint(keypointName)} onKeyDown={(event) => { if (event.key !== 'Enter' && event.key !== ' ') return @@ -10725,118 +10788,6 @@ export default function TrainingTab(props: { className="pointer-events-none absolute z-[305] overflow-visible" style={imageLayerStyle} > - {posePersons.map((person, personIndex) => { - if (!shouldRenderPoseOverlayPerson(personIndex)) { - return null - } - - if (poseFaceImageZoomActive) { - return null - } - - if (!hasVisiblePoseBox(person) && !person.keypoints?.some(isPoseKeypointVisible)) { - return null - } - - const reliable = isPosePersonReliable(person) - const activePose = activePosePersonIndex === personIndex - const color = reliable - ? POSE_PERSON_COLORS[personIndex % POSE_PERSON_COLORS.length] - : POSE_UNRELIABLE_COLOR - const score = Math.round(clamp01(Number(person.score)) * 100) - const quality = Math.round(posePersonQuality(person) * 100) - const visibleKeypoints = posePersonVisibleKeypoints(person) - - return ( -
{ - e.preventDefault() - e.stopPropagation() - setActivePosePersonIndex(personIndex) - }} - > -
-
- - -
-
-
- ) - })} - layerWidth * 0.72 - const label = poseKeypointLabel(point.name) - const confidence = Math.round(clamp01(Number(point.conf)) * 100) - - const dot = ( - - ) - - const labelToneClass = reliable - ? 'bg-white/95 text-gray-950 ring-black/10 hover:bg-gray-50 dark:bg-white/95 dark:text-gray-950 dark:ring-black/10 dark:hover:bg-gray-50' - : 'bg-white/90 text-slate-700 ring-slate-400/30 hover:bg-gray-50 dark:bg-white/90 dark:text-slate-700 dark:ring-black/10' - - const text = ( - - {label} - - ) return ( -
-
{ + if (uiLocked) return + setHoveredPoseKeypoint({ + personIndex, + keypointName: keypointId, + }) + }} + onPointerLeave={() => { + setHoveredPoseKeypoint((current) => + current?.personIndex === personIndex && + current.keypointName === keypointId + ? null + : current + ) + }} + onPointerDown={(e) => startPoseKeypointDrag(e, personIndex, pointIndex)} > - {labelToLeft ? ( - <> - - - - ) : ( - <> - - - - )} -
-
+ + + + {showLabel ? ( +
+ + {label} +
+ ) : null} + ) }) })} @@ -11683,13 +11588,6 @@ export default function TrainingTab(props: { })() : null}
- {poseFaceImageZoomApplied ? ( -