diff --git a/src/lib/layout/LeafPane.tsx b/src/lib/layout/LeafPane.tsx index 73d8f49..e8e1ffe 100644 --- a/src/lib/layout/LeafPane.tsx +++ b/src/lib/layout/LeafPane.tsx @@ -41,7 +41,7 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) { if (!editingLabel) return; orch.setLabel(leaf.id, labelDraft); setEditingLabel(false); - }, [editingLabel, orch, leaf.id, labelDraft]); + }, [editingLabel, orch.setLabel, leaf.id, labelDraft]); const cancelLabel = useCallback(() => setEditingLabel(false), []); const onLabelKey = useCallback( (e: KeyboardEvent) => { @@ -67,7 +67,7 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) { setDistroOpen(false); if (d !== leaf.distro) orch.setDistro(leaf.id, d); }, - [orch, leaf.id, leaf.distro], + [orch.setDistro, leaf.id, leaf.distro], ); // Dismiss popover on outside click useEffect(() => { @@ -95,14 +95,17 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) { } }, 1000); return () => clearInterval(id); - }, [leaf.label, leaf.distro, orch]); + // Depend on the stable notify function, not the whole orch object. + // orch is recreated every time activeLeafId/distros change; depending + // on it would tear down and rebuild this interval on every click. + }, [leaf.label, leaf.distro, orch.notify]); // ---- broadcast --------------------------------------------------------- const onTerminalInput = useCallback( (b64: string) => { if (isBroadcasting) orch.broadcastFrom(leaf.id, b64); }, - [isBroadcasting, orch, leaf.id], + [isBroadcasting, orch.broadcastFrom, leaf.id], ); // ---- focus / active highlighting --------------------------------------- @@ -114,20 +117,26 @@ export default function LeafPane({ leaf }: { leaf: LeafNode }) { const onPaneClick = useCallback(() => { orch.setActive(leaf.id); - }, [orch, leaf.id]); + }, [orch.setActive, leaf.id]); const onPaneSpawned = useCallback( (paneId: number) => { orch.registerPaneId(leaf.id, paneId); }, - [orch, leaf.id], + [orch.registerPaneId, leaf.id], ); - // Unregister on unmount + // Unregister on TRUE unmount only — depending on `orch` here would + // delete the paneId from App's lookup on every activeLeafId change, + // which broke broadcast routing (peers found, but their paneIds + // had been silently removed from the map). useEffect(() => { return () => orch.registerPaneId(leaf.id, null); - }, [orch, leaf.id]); + }, [orch.registerPaneId, leaf.id]); - const onXtermFocus = useCallback(() => orch.setActive(leaf.id), [orch, leaf.id]); + const onXtermFocus = useCallback( + () => orch.setActive(leaf.id), + [orch.setActive, leaf.id], + ); const onStatus = useCallback((msg: string, ok: boolean) => { setStatus(msg);