import { useHandTrackingSnapshot } from "@/hooks/handTracking/useHandTrackingSnapshot";
import {
useHandTrackingGloveStatus,
type HandTrackingGloveHandedness,
} from "@/hooks/handTracking/useHandTrackingGloveStatus";
// Hand silhouettes used as a last-resort fallback when the rigged glove
// model has failed to load. Both icons share a 100x120 viewBox so finger
// lengths and the thumb angle stay anatomically readable.
const OpenHandShape = (): React.JSX.Element => (
);
const FistShape = (): React.JSX.Element => (
<>
>
);
function getHandedness(raw: string): HandTrackingGloveHandedness | null {
const lower = raw.toLowerCase();
if (lower === "left" || lower === "right") return lower;
return null;
}
export function HandTrackingFallback(): React.JSX.Element | null {
const { hands } = useHandTrackingSnapshot();
const gloveStatus = useHandTrackingGloveStatus((state) => state.gloves);
const visibleHands = hands.flatMap((hand, index) => {
const handedness = getHandedness(hand.handedness);
if (!handedness) return [];
if (gloveStatus[handedness] !== "error") return [];
const wrist = hand.landmarks[0];
if (!wrist) return [];
return [{ hand, handedness, wrist, index }];
});
if (visibleHands.length === 0) return null;
return (
{visibleHands.map(({ hand, handedness, wrist, index }) => {
// MediaPipe coords are mirrored (selfie cam), keep the same
// mapping the SVG visualizer uses.
const leftPercent = (1 - wrist.x) * 100;
const topPercent = wrist.y * 100;
const flipX = handedness === "right" ? -1 : 1;
return (
);
})}
);
}