feat: graph runtime cache + node-position persistence by andrewaylett · Pull Request #12369 · logseq/logseq

@andrewaylett

Two problems with the global graph page, solved together:

1. Navigating away from /graph and back forced a full worker round-trip
   before anything rendered. A module-level atom (*cached-graph-data in
   page.cljs) now caches the last worker result keyed by [theme settings].
   On remount graph-aux seeds React state from it, so the graph renders
   immediately. The worker still fires in the background; when its result
   arrives, graph-2d's existing should-update predicate suppresses the
   pixi re-render if the data hasn't changed — the refresh is free.

2. The d3 force simulation started from random positions every time,
   causing the layout to re-settle on every visit or hard refresh. Node
   x/y coordinates are now captured into an in-memory map keyed by page
   title (the only stable cross-session identifier — db/ids are ephemeral).
   The map is persisted to localStorage under the key
   logseq-global-graph-node-positions. layout! seeds nodes with cached
   positions before forceSimulation is constructed; d3 respects
   pre-existing .x/.y and starts from those positions.

Position capture runs on two paths:
- Unmount (user navigates away): graph-2d's :will-unmount
- Re-render (new data arrives while graph is mounted): top of
  destroy-instance!
Both are idempotent — whichever fires second is a no-op.

The cache logic lives in its own namespace
(frontend.extensions.graph.position-cache) with no browser-only
dependencies, so it can be unit-tested under Node.js. pixi-graph-fork
crashes at module load in Node (self is not defined), which is why the
extraction was necessary rather than testing pixi.cljs directly.

Testing:
- 5 unit tests (9 assertions) covering extraction, merge-without-clobber,
  no-op on nil, skipping of invalid nodes, and empty-array handling. Picked
  up by CI automatically (no :fix-me metadata, runs under
  `node static/tests.js -e fix-me`).
- Manually validated: positions survive navigation, hard refresh, and
  settings changes. localStorage key verified in DevTools.