"use client"; import type { PointerEvent } from "react"; import { useEffect, useMemo, useRef, useState } from "react"; import { cn } from "@/lib/utils"; type DebugOverlayProps = { title?: string; entries: Record; className?: string; }; function formatValue(value: unknown): string { if (value === null) return "null"; if (value === undefined) return "undefined"; if (typeof value === "string") return value; if (typeof value === "number" || typeof value === "boolean") return String(value); try { return JSON.stringify(value); } catch { return String(value); } } export function DebugOverlay({ title = "Debug", entries, className }: DebugOverlayProps) { const [position, setPosition] = useState({ x: 0, y: 0 }); const dragOffsetRef = useRef<{ x: number; y: number } | null>(null); const positionRef = useRef(position); useEffect(() => { positionRef.current = position; }, [position]); const containerRef = useRef(null); const [hasCentered, setHasCentered] = useState(false); const setContainerRef = (node: HTMLDivElement | null) => { containerRef.current = node; if (node && !hasCentered) { const rect = node.getBoundingClientRect(); setPosition({ x: Math.max(12, Math.round(window.innerWidth / 2 - rect.width / 2)), y: 24, }); setHasCentered(true); } }; const clampPosition = (x: number, y: number) => { if (!containerRef.current) { return { x, y }; } const rect = containerRef.current.getBoundingClientRect(); const maxX = Math.max(12, window.innerWidth - rect.width - 12); const maxY = Math.max(12, window.innerHeight - rect.height - 12); return { x: Math.min(Math.max(12, x), maxX), y: Math.min(Math.max(12, y), maxY), }; }; const displayEntries = useMemo( () => Object.entries(entries).map(([key, value]) => ({ key, value: formatValue(value), })), [entries], ); const handlePointerDown = (event: PointerEvent) => { dragOffsetRef.current = { x: event.clientX - positionRef.current.x, y: event.clientY - positionRef.current.y, }; event.currentTarget.setPointerCapture(event.pointerId); }; const handlePointerMove = (event: PointerEvent) => { if (!dragOffsetRef.current) return; const nextX = event.clientX - dragOffsetRef.current.x; const nextY = event.clientY - dragOffsetRef.current.y; setPosition(clampPosition(nextX, nextY)); }; const handlePointerUp = (event: PointerEvent) => { dragOffsetRef.current = null; event.currentTarget.releasePointerCapture(event.pointerId); }; return (
{title} drag
{displayEntries.map((entry) => (
{entry.key} {entry.value}
))}
); }