diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 74430cffde..184c4e2170 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -734,11 +734,9 @@ return false } - // Clear SvelteFlow's internal selection by creating new nodes array function clearFlowSelection() { - // xyflow owns `selected` on the objects it was handed, and drops it only when it sees a - // node it does not recognise. Serving the cached mapping back would hand it the very - // object it marked selected, so the clear has to go through fresh objects. + // Resetting the cache and reassigning `nodes` hands xyflow objects it has not seen, the + // only lever on its selection available from our own array. offsetNodeCache = new WeakMap() nodes = nodes.map((node) => { if (node.selected) { @@ -1411,7 +1409,7 @@ /> - + {#if leftHeader}
diff --git a/frontend/src/lib/components/graph/SelectionTool.svelte b/frontend/src/lib/components/graph/SelectionTool.svelte index dd5033bf55..5fd6147c71 100644 --- a/frontend/src/lib/components/graph/SelectionTool.svelte +++ b/frontend/src/lib/components/graph/SelectionTool.svelte @@ -4,16 +4,18 @@ import type { SelectionManager } from './selectionUtils.svelte' interface Props { selectionManager: SelectionManager - clearGraphSelection: () => void } - let { selectionManager, clearGraphSelection }: Props = $props() + let { selectionManager }: Props = $props() - untrack(() => selectionManager).setClearGraphSelection(untrack(() => clearGraphSelection)) - - // Get store to access selectionRect const store = useStore() + // Clear through xyflow's store, never by handing it fresh node objects: replacing them + // re-creates every node's DOM, and a click whose node is rebuilt between pointerdown and + // release retargets to the pane, which clears the selection that same gesture just made. + // While xyflow holds a selection, useOnSelectionChange below re-broadcasts it over ours. + untrack(() => selectionManager).setClearGraphSelection(() => store.unselectNodesAndEdges()) + // Handle selection changes from SvelteFlow useOnSelectionChange(({ nodes: selectedNodes, edges: _selectedEdges }) => { // Notes are already non-selectable, so no filtering needed diff --git a/frontend/src/lib/components/graph/graphContext.ts b/frontend/src/lib/components/graph/graphContext.ts index a6245769cf..567bfb6348 100644 --- a/frontend/src/lib/components/graph/graphContext.ts +++ b/frontend/src/lib/components/graph/graphContext.ts @@ -12,6 +12,8 @@ export type GraphContext = { showAssets: Writable noteManager?: NoteManager moveManager?: MoveManager + /** Clears xyflow's selection by replacing every node object, so only for callers that rebuild + * the graph anyway. A selection change must use `selectionManager` instead. */ clearFlowSelection?: () => void yOffset?: number diffManager: FlowDiffManager