"use client"; import { useState, useEffect, useRef, useCallback, useSyncExternalStore } from "react"; type DevValues = { headerTx: number; cursorTop: number; cursorBlink: boolean; subtitleLh: number; downloadAbove: number; downloadBelow: number; featuresLh: number; featuresPt: number; featuresPb: number; communityGap: number; faqPt: number; docsPt: number; }; const defaults: DevValues = { headerTx: -4, cursorTop: 2.5, cursorBlink: true, subtitleLh: 1.5, downloadAbove: 21, downloadBelow: 16, featuresLh: 1.275, featuresPt: 12, featuresPb: 15, communityGap: 6, faqPt: 0, docsPt: 8, }; // Tiny external store (avoids setState-during-render) let snapshot = { ...defaults }; const listeners = new Set<() => void>(); function getSnapshot() { return snapshot; } function getServerSnapshot() { return defaults; } function setStore(patch: Partial) { snapshot = { ...snapshot, ...patch }; listeners.forEach((l) => l()); } function subscribe(cb: () => void) { listeners.add(cb); return () => { listeners.delete(cb); }; } export function useDevValues() { return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); } function el(name: string) { return document.querySelector(`[data-dev="${name}"]`) as HTMLElement | null; } function applyToDOM(v: DevValues) { const header = el("header"); if (header) header.style.transform = `translateX(${v.headerTx}px)`; const subtitle = el("subtitle"); if (subtitle) subtitle.style.lineHeight = `${v.subtitleLh}`; const download = el("download"); if (download) { download.style.marginTop = `${v.downloadAbove}px`; download.style.marginBottom = `${v.downloadBelow}px`; } const featuresUl = el("features-ul"); if (featuresUl) featuresUl.style.lineHeight = `${v.featuresLh}`; const features = el("features"); if (features) { features.style.paddingTop = `${v.featuresPt}px`; features.style.paddingBottom = `${v.featuresPb}px`; } const communityUl = el("community-ul"); if (communityUl) { communityUl.style.display = "flex"; communityUl.style.flexDirection = "column"; communityUl.style.gap = `${v.communityGap}px`; } const faqTopSpacer = el("faq-top-spacer"); if (faqTopSpacer) faqTopSpacer.style.height = `${v.faqPt}px`; const docsContent = el("docs-content"); if (docsContent) docsContent.style.paddingTop = `${v.docsPt}px`; } export function DevPanel() { const [visible, setVisible] = useState(false); const [pos, setPos] = useState({ x: 0, y: 0 }); const [dragging, setDragging] = useState(false); const [copied, setCopied] = useState(false); const vals = useDevValues(); const dragOffset = useRef({ x: 0, y: 0 }); useEffect(() => { setPos({ x: window.innerWidth - 340, y: window.innerHeight - 320 }); }, []); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.metaKey && e.key === ".") { e.preventDefault(); setVisible((v) => !v); } }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, []); const update = useCallback((patch: Partial) => { setStore(patch); applyToDOM({ ...snapshot, ...patch }); }, []); const onPointerDown = useCallback((e: React.PointerEvent) => { if ((e.target as HTMLElement).closest("input, button, label")) return; setDragging(true); dragOffset.current = { x: e.clientX - pos.x, y: e.clientY - pos.y }; (e.target as HTMLElement).setPointerCapture(e.pointerId); }, [pos]); const onPointerMove = useCallback((e: React.PointerEvent) => { if (!dragging) return; setPos({ x: e.clientX - dragOffset.current.x, y: e.clientY - dragOffset.current.y }); }, [dragging]); const onPointerUp = useCallback(() => setDragging(false), []); if (process.env.NODE_ENV !== "development" || !visible) return null; return (
Dev Controls ⌘.
update({ headerTx: v })} min={-50} max={50} step={1} unit="px" />
update({ cursorTop: v })} min={-5} max={5} step={0.5} unit="px" />
update({ subtitleLh: v })} min={1} max={2.5} step={0.025} unit="" w={16} />
update({ downloadAbove: v })} /> update({ downloadBelow: v })} />
update({ featuresLh: v })} min={1} max={2.5} step={0.025} unit="" w={16} /> update({ featuresPt: v })} /> update({ featuresPb: v })} />
update({ communityGap: v })} />
update({ faqPt: v })} />
update({ docsPt: v })} />
); } function Section({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); } function Row({ label, value, onChange, min = 0, max = 128, step = 1, unit = "px", w = 10 }: { label: string; value: number; onChange: (v: number) => void; min?: number; max?: number; step?: number; unit?: string; w?: number; }) { return (
{label} onChange(parseFloat(e.target.value))} className="w-28 accent-blue-500 cursor-pointer" /> {Number.isInteger(step) ? value : value.toFixed(step < 0.1 ? 2 : 1)}{unit}
); }