import { useEffect, useState } from 'react'; import { Checkbox } from '../ui/checkbox'; import { PACKET_LEGEND_ITEMS } from '../../utils/visualizerUtils'; import { NODE_LEGEND_ITEMS } from './shared'; interface VisualizerControlsProps { showControls: boolean; setShowControls: (value: boolean) => void; fullScreen?: boolean; onFullScreenChange?: (fullScreen: boolean) => void; showAmbiguousPaths: boolean; setShowAmbiguousPaths: (value: boolean) => void; showAmbiguousNodes: boolean; setShowAmbiguousNodes: (value: boolean) => void; useAdvertPathHints: boolean; setUseAdvertPathHints: (value: boolean) => void; collapseLikelyKnownSiblingRepeaters: boolean; setCollapseLikelyKnownSiblingRepeaters: (value: boolean) => void; splitAmbiguousByTraffic: boolean; setSplitAmbiguousByTraffic: (value: boolean) => void; observationWindowSec: number; setObservationWindowSec: (value: number) => void; pruneStaleNodes: boolean; setPruneStaleNodes: (value: boolean) => void; pruneStaleMinutes: number; setPruneStaleMinutes: (value: number) => void; letEmDrift: boolean; setLetEmDrift: (value: boolean) => void; autoOrbit: boolean; setAutoOrbit: (value: boolean) => void; chargeStrength: number; setChargeStrength: (value: number) => void; particleSpeedMultiplier: number; setParticleSpeedMultiplier: (value: number) => void; nodeCount: number; linkCount: number; onExpandContract: () => void; onClearAndReset: () => void; } export function VisualizerControls({ showControls, setShowControls, fullScreen, onFullScreenChange, showAmbiguousPaths, setShowAmbiguousPaths, showAmbiguousNodes, setShowAmbiguousNodes, useAdvertPathHints, setUseAdvertPathHints, collapseLikelyKnownSiblingRepeaters, setCollapseLikelyKnownSiblingRepeaters, splitAmbiguousByTraffic, setSplitAmbiguousByTraffic, observationWindowSec, setObservationWindowSec, pruneStaleNodes, setPruneStaleNodes, pruneStaleMinutes, setPruneStaleMinutes, letEmDrift, setLetEmDrift, autoOrbit, setAutoOrbit, chargeStrength, setChargeStrength, particleSpeedMultiplier, setParticleSpeedMultiplier, nodeCount, linkCount, onExpandContract, onClearAndReset, }: VisualizerControlsProps) { const [observationWindowInput, setObservationWindowInput] = useState( String(observationWindowSec) ); const [pruneWindowInput, setPruneWindowInput] = useState(String(pruneStaleMinutes)); useEffect(() => { setObservationWindowInput(String(observationWindowSec)); }, [observationWindowSec]); useEffect(() => { setPruneWindowInput(String(pruneStaleMinutes)); }, [pruneStaleMinutes]); return ( <> {showControls && (
Packets
{PACKET_LEGEND_ITEMS.map((item) => (
{item.label}
{item.description}
))}
Nodes
{NODE_LEGEND_ITEMS.map((item) => (
{item.label}
))}
)}
{onFullScreenChange && ( )}
{showControls && ( <>
Advanced
{ const nextValue = e.target.value; setObservationWindowInput(nextValue); if (nextValue === '') return; const parsed = Number.parseInt(nextValue, 10); if (Number.isNaN(parsed)) return; setObservationWindowSec(Math.max(1, Math.min(60, parsed))); }} onBlur={() => { const parsed = Number.parseInt(observationWindowInput, 10); const nextValue = Number.isNaN(parsed) ? observationWindowSec : Math.max(1, Math.min(60, parsed)); setObservationWindowInput(String(nextValue)); if (nextValue !== observationWindowSec) { setObservationWindowSec(nextValue); } }} className="w-12 px-1 py-0.5 bg-background border border-border rounded text-xs text-center" /> sec
{pruneStaleNodes && (
{ const nextValue = e.target.value; setPruneWindowInput(nextValue); if (nextValue === '') return; const parsed = Number.parseInt(nextValue, 10); if (Number.isNaN(parsed)) return; if (parsed >= 1 && parsed <= 60) setPruneStaleMinutes(parsed); }} onBlur={() => { const parsed = Number.parseInt(pruneWindowInput, 10); const nextValue = Number.isNaN(parsed) || parsed < 1 || parsed > 60 ? pruneStaleMinutes : parsed; setPruneWindowInput(String(nextValue)); if (nextValue !== pruneStaleMinutes) { setPruneStaleMinutes(nextValue); } }} className="w-14 rounded border border-border bg-background px-2 py-0.5 text-sm" />
)}
setChargeStrength(-parseInt(e.target.value, 10))} className="w-full h-2 bg-border rounded-lg appearance-none cursor-pointer accent-primary" />
setParticleSpeedMultiplier(parseFloat(e.target.value))} className="w-full h-2 bg-border rounded-lg appearance-none cursor-pointer accent-primary" />
Nodes: {nodeCount}
Links: {linkCount}
)}
); }