From 44ce9379ebd398dfc774671657387a616a72cdde Mon Sep 17 00:00:00 2001 From: pythongosssss <125205205+pythongosssss@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:48:20 +0000 Subject: [PATCH] Defer vue node layout calculations on hidden browser tabs (#8805) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary If you load the window in Nodes 2.0, then switch tabs while it is still loading, the position of the nodes is calculated incorrectly due to useElementBounding returning left=0, top=0 for the canvas element in a hidden tab, causing clientPosToCanvasPos to miscalculate node positions from the ResizeObserver measurements ## Changes - **What**: - Store observed elements while document is in hidden state - Re-observe when tab becomes visible ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8805-Defer-vue-node-layout-calculations-on-hidden-browser-tabs-3046d73d365081019ae6c403c0ac6d1a) by [Unito](https://www.unito.io) --- .../composables/useVueNodeResizeTracking.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts b/src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts index 4d4e274d3c..07c60a4b1e 100644 --- a/src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts +++ b/src/renderer/extensions/vueNodes/composables/useVueNodeResizeTracking.ts @@ -8,9 +8,11 @@ * Supports different element types (nodes, slots, widgets, etc.) with * customizable data attributes and update handlers. */ -import { getCurrentInstance, onMounted, onUnmounted, toValue } from 'vue' +import { getCurrentInstance, onMounted, onUnmounted, toValue, watch } from 'vue' import type { MaybeRefOrGetter } from 'vue' +import { useDocumentVisibility } from '@vueuse/core' + import { useSharedCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion' import { LiteGraph } from '@/lib/litegraph/src/litegraph' import { layoutStore } from '@/renderer/core/layout/store/layoutStore' @@ -59,9 +61,37 @@ const trackingConfigs: Map = new Map([ ] ]) +// Elements whose ResizeObserver fired while the tab was hidden +const deferredElements = new Set() +const visibility = useDocumentVisibility() + +watch(visibility, (state) => { + if (state !== 'visible' || deferredElements.size === 0) return + + // Re-observe deferred elements to trigger fresh measurements + for (const element of deferredElements) { + if (element.isConnected) { + resizeObserver.observe(element) + } + } + deferredElements.clear() +}) + // Single ResizeObserver instance for all Vue elements const resizeObserver = new ResizeObserver((entries) => { if (useCanvasStore().linearMode) return + + // Skip measurements when tab is hidden — bounding rects are unreliable + if (visibility.value === 'hidden') { + for (const entry of entries) { + if (entry.target instanceof HTMLElement) { + deferredElements.add(entry.target) + resizeObserver.unobserve(entry.target) + } + } + return + } + // Canvas is ready when this code runs; no defensive guards needed. const conv = useSharedCanvasPositionConversion() // Group updates by type, then flush via each config's handler