'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}
))}
); }