Improve initial viewport position

This commit is contained in:
Guilhem
2025-11-12 14:44:41 +01:00
parent a8418bfe02
commit 7eacccc8b8
3 changed files with 65 additions and 4 deletions
@@ -53,6 +53,7 @@
import SubflowBound from './renderers/nodes/SubflowBound.svelte'
import ViewportResizer from './ViewportResizer.svelte'
import ViewportSynchronizer from './ViewportSynchronizer.svelte'
import InitialViewportFitter from './InitialViewportFitter.svelte'
import AssetNode, { computeAssetNodes } from './renderers/nodes/AssetNode.svelte'
import AssetsOverflowedNode from './renderers/nodes/AssetsOverflowedNode.svelte'
import type { FlowGraphAssetContext } from '../flows/types'
@@ -356,7 +357,6 @@
boxSize = layout(dag as any)
}
const yOffset = insertable ? 100 : 0
const newNodes = dag.descendants().map((des) => ({
id: des.data.id,
position: {
@@ -370,7 +370,7 @@
NODE.width / 2 -
(width - fullWidth) / 2
: 0,
y: (des.y || 0) + yOffset + (nodeSpacingMap[des.data.id] ?? 0) / 2
y: (des.y || 0) + (nodeSpacingMap[des.data.id] ?? 0) / 2
}
}))
@@ -456,6 +456,9 @@
let height = $state(0)
// Counter to trigger initial viewport fit after first flow build
let initialBuildTrigger = $state(0)
// Note feature state
function isSimplifiable(modules: FlowModule[] | undefined): boolean {
@@ -634,7 +637,13 @@
$effect(() => {
;[graph, allowSimplifiedPoll, $showAssets, noteManager.renderCount]
untrack(() => updateStores())
untrack(async () => {
await updateStores()
// Trigger initial viewport fit after first build
if (initialBuildTrigger === 0 && nodes.length > 0) {
initialBuildTrigger = 1
}
})
})
// Add global keyboard event listener for selection controls
@@ -743,6 +752,7 @@
bind:this={viewportSynchronizer}
/>
{/if}
<InitialViewportFitter {nodes} triggerCount={initialBuildTrigger} />
<SvelteFlow
onpaneclick={() => {
document.dispatchEvent(new Event('focus'))
@@ -0,0 +1,50 @@
<script lang="ts">
import { useSvelteFlow } from '@xyflow/svelte'
import { tick, untrack } from 'svelte'
interface Props {
nodes: any[]
triggerCount: number
}
let { nodes, triggerCount }: Props = $props()
const { fitView } = useSvelteFlow()
let hasRunInitialFit = false
/**
* Smart fitView that fits all nodes and notes with minimal zoom adjustment
* Allows zoom to change by ±0.2 to fit content if needed
* Only runs once after the first flow build
*/
async function smartFitView() {
if (hasRunInitialFit || nodes.length === 0) {
return
}
await tick()
try {
fitView({
minZoom: Math.max(1),
maxZoom: Math.min(1),
padding: 0.2,
duration: 0
})
hasRunInitialFit = true
} catch (e) {
console.debug('smartFitView error:', e)
}
}
// Run when nodes are ready and trigger count changes (after first build)
$effect(() => {
triggerCount
untrack(() => {
if (!hasRunInitialFit && nodes.length > 0) {
smartFitView()
}
})
})
</script>
+2 -1
View File
@@ -4,7 +4,7 @@
import { decodeState } from '$lib/utils'
let content = localStorage.getItem('svelvet')
const { modules, failureModule, preprocessorModule } = content
const { modules, failureModule, preprocessorModule, notes } = content
? decodeState(content)
: { modules: [], failureModule: undefined, preprocessorModule: undefined }
</script>
@@ -15,6 +15,7 @@
{modules}
{failureModule}
{preprocessorModule}
{notes}
/>
<a
download="flow.json"