refactor: move version tracking into useContentBounds

Move updateContentBounds logic (version guard, workflow switch
detection, node iteration) from TransformPane into useContentBounds
as an update() method. TransformPane now calls
contentBounds.update(nodes, version) in the RAF loop.
This commit is contained in:
Rizumu Ayaka
2026-04-15 17:39:21 +08:00
parent bf606a209e
commit 7e5143d2f1
2 changed files with 31 additions and 32 deletions

View File

@@ -46,36 +46,6 @@ const offsetWrapperRef = useTemplateRef('offsetWrapperRef')
const contentBounds = useContentBounds()
const allNodes = layoutStore.getAllNodes()
const storeVersion = layoutStore.getVersion()
let lastTrackedVersion = -1
let sampleNodeId: string | null = null
/**
* When the layout store version changes, expand the tracked content
* bounds to include any nodes that moved beyond the current area.
*
* Detects workflow switches by checking whether a previously tracked
* node still exists. When the entire node set is replaced (e.g. on
* workflow load), resets bounds so they don't accumulate across
* unrelated workflows.
*/
function updateContentBounds() {
const currentVersion = storeVersion.value
if (currentVersion === lastTrackedVersion) return
lastTrackedVersion = currentVersion
const nodes = allNodes.value
// Detect workflow switch: if the sampled node is gone, the node set
// was replaced wholesale — reset bounds to avoid unbounded growth.
if (sampleNodeId !== null && nodes.size > 0 && !nodes.has(sampleNodeId)) {
contentBounds.reset()
}
sampleNodeId = nodes.size > 0 ? (nodes.keys().next().value ?? null) : null
for (const [, layout] of nodes) {
contentBounds.expandToInclude(layout.bounds)
}
}
// --- DOM mutation (avoids Vue vdom diffing on every frame) ---
@@ -120,7 +90,7 @@ useRafFn(
() => {
if (!canvas) return
syncWithCanvas(canvas)
updateContentBounds()
contentBounds.update(allNodes.value, storeVersion.value)
contentBounds.flush()
applyStyles()
},

View File

@@ -1,6 +1,6 @@
import { reactive, readonly } from 'vue'
import type { Bounds, Point } from '@/renderer/core/layout/types'
import type { Bounds, NodeLayout, Point } from '@/renderer/core/layout/types'
/**
* Margin added around tracked content bounds to avoid frequent resizing
@@ -29,6 +29,7 @@ interface ContentBoundsState {
*/
export function useContentBounds(): ContentBoundsState & {
expandToInclude(bounds: Bounds): void
update(nodes: ReadonlyMap<string, NodeLayout>, version: number): void
flush(): boolean
reset(): void
} {
@@ -65,6 +66,33 @@ export function useContentBounds(): ContentBoundsState & {
}
}
let lastTrackedVersion = -1
let sampleNodeId: string | null = null
/**
* Update bounds from the current set of node layouts, skipping work
* when the version hasn't changed. Detects workflow switches by
* checking whether a previously sampled node still exists — when
* the entire node set is replaced, resets bounds to prevent
* unbounded growth across unrelated workflows.
*/
function update(
nodes: ReadonlyMap<string, NodeLayout>,
version: number
) {
if (version === lastTrackedVersion) return
lastTrackedVersion = version
if (sampleNodeId !== null && nodes.size > 0 && !nodes.has(sampleNodeId)) {
reset()
}
sampleNodeId = nodes.size > 0 ? (nodes.keys().next().value ?? null) : null
for (const [, layout] of nodes) {
expandToInclude(layout.bounds)
}
}
/**
* Applies pending bound changes to the reactive offset and size.
* Returns true if the values actually changed.
@@ -95,6 +123,7 @@ export function useContentBounds(): ContentBoundsState & {
offset: readonly(offset),
size: readonly(size),
expandToInclude,
update,
flush,
reset
}