You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split filtering from conversion, add a reusable reference-stable index, and replace the full-replace + structure-version counter with a pure reconciler. This is one behavioral change — the graph stops rebuilding on every update — so the split, the memo, and the reconciler ship together.
Details
Filtering (presence) as its own concept:
A hook returning the surviving source Vertex[] (reads node filter state; answers only "is this vertex present?").
A hook returning the surviving source Edge[] (reads edge filter state + endpoint existence).
Conversion — pure total functions:Vertex → Canvas Vertex, Edge → Canvas Edge. No branches, no filtering, no volatile fields.
Reference-stable index — reusable util: maps each source item to an output, reusing the previous output when the source reference is unchanged; absent keys are evicted for free. Held in a ref so it persists across renders. Simultaneously the diff mirror, converter cache, and carry-forward index. Used by both the Graph View and Schema View.
Reconciler (internal seams, not exported from the module barrel):
Pure diffElements(prev, next) returning { added, removed, updated }, keyed on id presence + reference identity: same id + same ref → skip; same id + different ref → update (remove + add); new id → add; missing id → remove.
Thin applyDelta(cy, delta): one batch doing removes, node-adds before edge-adds, updates as remove-then-add. No patching, no data inspection.
Task
Split filtering from conversion, add a reusable reference-stable index, and replace the full-replace + structure-version counter with a pure reconciler. This is one behavioral change — the graph stops rebuilding on every update — so the split, the memo, and the reconciler ship together.
Details
Filtering (presence) as its own concept:
Vertex[](reads node filter state; answers only "is this vertex present?").Edge[](reads edge filter state + endpoint existence).Conversion — pure total functions:
Vertex → Canvas Vertex,Edge → Canvas Edge. No branches, no filtering, no volatile fields.Reference-stable index — reusable util: maps each source item to an output, reusing the previous output when the source reference is unchanged; absent keys are evicted for free. Held in a ref so it persists across renders. Simultaneously the diff mirror, converter cache, and carry-forward index. Used by both the Graph View and Schema View.
Reconciler (internal seams, not exported from the module barrel):
diffElements(prev, next)returning{ added, removed, updated }, keyed on id presence + reference identity: same id + same ref → skip; same id + different ref → update (remove + add); new id → add; missing id → remove.applyDelta(cy, delta): one batch doing removes, node-adds before edge-adds, updates as remove-then-add. No patching, no data inspection.{ addedNodeIds, structureRevision }.addedNodeIds= genuinely-new vertices only.Retire the structure-version counter:
addedNodeIds; delete its previous-nodes bookkeeping.structureRevisiontoken.Schema View: same three-part shape (presence = existing endpoint-existence check; conversion = vertex-type-config → element; shared reference-stable index).
Acceptance
diffElementsunit tests: add, remove, update (ref change → remove+add), unchanged (same ref → skip), node-before-edge ordering, empty↔populated, no-op → empty delta.applyDeltatests against a headless Cytoscape (or fake): batch add/remove/re-add, never patches.pnpm check:typesandpnpm testgreen.Related Issues
Important
Internal only — this issue is maintained by the core team and is not accepting external contributions.