feat(mazenet): persist canvas layout per topology to localStorage

Dragging a LAN or decky, or resizing a NetBox, updates React state
but previously vanished on reload because the grid-layout adapter
rewrote everything from the graph. Add a per-topology localStorage
snapshot (key: mazenet.layout.<topologyId>) that captures net
x/y/w/h and decky x/y; useLayoutPersistor writes it debounced, and
getTopology merges it over adaptTopology's grid so entities without
a stored entry still fall back to a clean auto-layout. Deleting a
topology calls clearLayout to drop its snapshot.
This commit is contained in:
2026-04-20 23:52:00 -04:00
parent c4be1c721d
commit 167582b887
4 changed files with 121 additions and 1 deletions

View File

@@ -16,6 +16,7 @@ import type { Archetype, ServiceDef } from './data';
import type { Net, MazeNode, Edge, DeckyNode } from './types';
import { useMazeApi } from './useMazeApi';
import { useMazeInteraction, type PaletteDrag } from './useMazeInteraction';
import { useLayoutPersistor } from './useMazeLayoutStore';
import { ARCHETYPES as DEFAULT_ARCHETYPES } from './data';
/* Short unique suffix for default names — avoids the DB uniqueness
@@ -43,6 +44,8 @@ const MazeNET: React.FC = () => {
const [inspectorOpen, setInspectorOpen] = useState(true);
const [services, setServices] = useState<ServiceDef[]>(DEFAULT_SERVICES);
const [archetypes, setArchetypes] = useState<Archetype[]>(DEFAULT_ARCHETYPES);
useLayoutPersistor(topologyId || null, nets, nodes);
const [loadErr, setLoadErr] = useState<string | null>(null);
const [actionErr, setActionErr] = useState<string | null>(null);
const [deploying, setDeploying] = useState(false);