Add clear button

This commit is contained in:
Jack Kingsman
2026-01-20 16:54:11 -08:00
parent e04664d037
commit 2c668c1852
9 changed files with 625 additions and 549 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -13,8 +13,8 @@
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<script type="module" crossorigin src="/assets/index-aNIEzPAX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bg6UtK9Z.css">
<script type="module" crossorigin src="/assets/index-CEs7-vRv.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-6X8xpvpN.css">
</head>
<body>
<div id="root"></div>
+6 -1
View File
@@ -721,7 +721,12 @@ export function App() {
</div>
</>
) : activeConversation.type === 'visualizer' ? (
<VisualizerView packets={rawPackets} contacts={contacts} config={config} />
<VisualizerView
packets={rawPackets}
contacts={contacts}
config={config}
onClearPackets={() => setRawPackets([])}
/>
) : activeConversation.type === 'raw' ? (
<>
<div className="flex justify-between items-center px-4 py-3 border-b border-border font-medium text-lg">
@@ -276,6 +276,7 @@ interface VisualizerData {
stats: { processed: number; animated: number; nodes: number; links: number };
randomizePositions: () => void;
expandContract: () => void;
clearAndReset: () => void;
}
function useVisualizerData({
@@ -859,6 +860,56 @@ function useVisualizerData({
requestAnimationFrame(animate);
}, [chargeStrength]);
// Clear all state and reset to initial (keeps self node only)
const clearAndReset = useCallback(() => {
// Clear all pending timers
for (const timer of timersRef.current.values()) {
clearTimeout(timer);
}
timersRef.current.clear();
// Clear pending packets
pendingRef.current.clear();
// Clear processed packet IDs so they can be re-processed if needed
processedRef.current.clear();
// Clear particles
particlesRef.current.length = 0;
// Clear links
linksRef.current.clear();
// Clear nodes except self, then reset self position
const selfNode = nodesRef.current.get('self');
nodesRef.current.clear();
if (selfNode) {
selfNode.x = dimensions.width / 2;
selfNode.y = dimensions.height / 2;
selfNode.vx = 0;
selfNode.vy = 0;
selfNode.lastActivity = Date.now();
nodesRef.current.set('self', selfNode);
}
// Reset simulation with just self node
const sim = simulationRef.current;
if (sim) {
sim.nodes(Array.from(nodesRef.current.values()));
sim.force(
'link',
forceLink<GraphNode, GraphLink>([])
.id((d) => d.id)
.distance(80)
.strength(0.3)
);
sim.alpha(0.3).restart();
}
// Reset stats
setStats({ processed: 0, animated: 0, nodes: 1, links: 0 });
}, [dimensions]);
return {
nodes: nodesRef.current,
links: linksRef.current,
@@ -867,6 +918,7 @@ function useVisualizerData({
stats,
randomizePositions,
expandContract,
clearAndReset,
};
}
@@ -1013,6 +1065,7 @@ interface PacketVisualizerProps {
config: RadioConfig | null;
fullScreen?: boolean;
onFullScreenChange?: (fullScreen: boolean) => void;
onClearPackets?: () => void;
}
export function PacketVisualizer({
@@ -1021,6 +1074,7 @@ export function PacketVisualizer({
config,
fullScreen,
onFullScreenChange,
onClearPackets,
}: PacketVisualizerProps) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
@@ -1383,6 +1437,16 @@ export function PacketVisualizer({
>
Oooh Big Stretch!
</button>
<button
onClick={() => {
data.clearAndReset();
onClearPackets?.();
}}
className="mt-1 px-3 py-1.5 bg-yellow-500/20 hover:bg-yellow-500/30 text-yellow-500 rounded text-xs transition-colors"
title="Clear all nodes, links, and packets - reset to initial state"
>
Clear &amp; Reset
</button>
</div>
</>
)}
+9 -2
View File
@@ -9,9 +9,10 @@ interface VisualizerViewProps {
packets: RawPacket[];
contacts: Contact[];
config: RadioConfig | null;
onClearPackets?: () => void;
}
export function VisualizerView({ packets, contacts, config }: VisualizerViewProps) {
export function VisualizerView({ packets, contacts, config, onClearPackets }: VisualizerViewProps) {
const [fullScreen, setFullScreen] = useState(false);
return (
@@ -29,7 +30,12 @@ export function VisualizerView({ packets, contacts, config }: VisualizerViewProp
<TabsTrigger value="packets">Packet Feed</TabsTrigger>
</TabsList>
<TabsContent value="visualizer" className="flex-1 m-0 overflow-hidden">
<PacketVisualizer packets={packets} contacts={contacts} config={config} />
<PacketVisualizer
packets={packets}
contacts={contacts}
config={config}
onClearPackets={onClearPackets}
/>
</TabsContent>
<TabsContent value="packets" className="flex-1 m-0 overflow-hidden">
<RawPacketList packets={packets} />
@@ -52,6 +58,7 @@ export function VisualizerView({ packets, contacts, config }: VisualizerViewProp
config={config}
fullScreen={fullScreen}
onFullScreenChange={setFullScreen}
onClearPackets={onClearPackets}
/>
</div>