import { useEffect, useRef, useState, type PointerEvent as ReactPointerEvent, type ReactNode, } from 'react'; const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value)); const panelMaxWidth = (minWidth: number) => Math.max(minWidth, Math.min(576, window.innerWidth * 0.4)); function storedWidth(key: string, fallback: number, minWidth: number) { try { const value = Number(localStorage.getItem(key)); return clamp( Number.isFinite(value) && value > 0 ? value : fallback, minWidth, panelMaxWidth(minWidth), ); } catch { return clamp(fallback, minWidth, panelMaxWidth(minWidth)); } } export function ResizablePanel({ side, storageKey, visible = true, defaultWidth = 288, minWidth = 224, children, className = '', }: { side: 'left' | 'right'; storageKey: string; visible?: boolean; defaultWidth?: number; minWidth?: number; children: ReactNode; className?: string; }) { const [width, setWidth] = useState(() => storedWidth(storageKey, defaultWidth, minWidth)), cleanupRef = useRef<() => void>(() => {}); const update = (next: number) => { const value = clamp(next, minWidth, panelMaxWidth(minWidth)); setWidth(value); try { localStorage.setItem(storageKey, String(value)); } catch { /* 无持久化权限时仍可调整 */ } }; useEffect(() => { const persist = (next: number) => { const value = clamp(next, minWidth, panelMaxWidth(minWidth)); setWidth(value); try { localStorage.setItem(storageKey, String(value)); } catch { /* 忽略 */ } }, resize = () => setWidth((value) => clamp(value, minWidth, panelMaxWidth(minWidth))), layout = (event: Event) => { const widths = (event as CustomEvent<{ left: number; right: number }>).detail; persist(widths[side]); }; window.addEventListener('resize', resize); window.addEventListener('mujoco-layout-widths', layout); return () => { window.removeEventListener('resize', resize); window.removeEventListener('mujoco-layout-widths', layout); cleanupRef.current(); }; }, [minWidth, side, storageKey]); const start = (event: ReactPointerEvent) => { event.preventDefault(); cleanupRef.current(); const origin = event.clientX, startWidth = width, pointerId = event.pointerId; const move = (moveEvent: PointerEvent) => { if (moveEvent.pointerId === pointerId) update(startWidth + (moveEvent.clientX - origin) * (side === 'left' ? 1 : -1)); }; const stop = (stopEvent: PointerEvent) => { if (stopEvent.pointerId !== pointerId) return; cleanup(); }; const cleanup = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', stop); window.removeEventListener('pointercancel', stop); cleanupRef.current = () => {}; }; cleanupRef.current = cleanup; window.addEventListener('pointermove', move); window.addEventListener('pointerup', stop); window.addEventListener('pointercancel', stop); }; return ( ); }