Skip to content

fix: never mint a node id that is already on the canvas - #65

Open
yeabsira-gashaw wants to merge 1 commit into
xevrion:mainfrom
yeabsira-gashaw:fix/unique-node-ids
Open

fix: never mint a node id that is already on the canvas#65
yeabsira-gashaw wants to merge 1 commit into
xevrion:mainfrom
yeabsira-gashaw:fix/unique-node-ids

Conversation

@yeabsira-gashaw

Copy link
Copy Markdown

Adding a component from the palette could reuse an id that a node on the canvas already had. makeNode numbers new nodes from a module counter that restarts at zero on every page load, while the ids it minted in earlier sessions come back through localStorage, share links and design files. After a reload of a design holding service-1 to service-10, the next add was service-1 again.

Two nodes then shared one id, and everything keyed by id conflated them: selecting either selected both (the inspector read "2 components"), an edge drawn to one landed on both, and a config change applied to both. It only happens after a reload of a restored design, which is why it looks intermittent, and it gets more likely as the canvas grows.

Fix

makeNode takes the set of ids already on the canvas and skips past any it would collide with, and handleAddNode in App.tsx passes that set. This is the same dedupe the paste and duplicate paths already do through freshId in clipboard.ts; the add path was the only one without it.

Before / after

Headless, with service-1..service-10 on the canvas and the counter at zero (a fresh page load):

before: new id service-1   collides with existing node: true
after:  new id service-11  collides with existing node: false

Before: the newly added service shares an id with an existing one, so the inspector reads "2 components" and edits apply to both.

image

After: the new node gets its own id and is the only thing selected.

image

Tests

src/sim/makeNode.ids.test.ts covers a single add against taken ids and repeated adds staying distinct. bun run test and bun run build pass locally.

makeNode's counter restarts at zero on every page load, but the ids it
minted earlier come back through localStorage, share links and design
files. Adding a component after a reload could therefore reuse an id
already on the canvas, and two nodes then shared one id: selecting one
selected both, the inspector read "2 components", and edges and config
changes applied to both. The add path now skips ids the topology already
holds, the way the paste path always has.
@vercel

vercel Bot commented Sep 7, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the whoarrryou's projects Team on Vercel.

A member of the Team first needs to authorize it.

@xevrion

xevrion commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Putting the dedupe inside makeNode is the better spot, it covers every caller instead of just the palette path.

One change before I merge: new Set(topology.nodes.map(...)) should read topoLiveRef.current instead. The React topology binding is stale inside that callback, so two quick adds both see the pre-add snapshot and the second drops the first node. That ref exists at App.tsx:1086 for exactly this. Dropping topology from the dep array too.

Credit where due, #47 found that stale closure first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants