Skip to content

Commit 8b7c3fc

Browse files
committed
Fix fit-to-view bounds: pass calculated dimensions to SvelteFlow node objects
1 parent ba85421 commit 8b7c3fc

3 files changed

Lines changed: 67 additions & 32 deletions

File tree

src/lib/components/canvas/flowConverters.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,35 @@ import type { Node, Edge } from '@xyflow/svelte';
66
import type { NodeInstance, Connection, Annotation } from '$lib/nodes/types';
77
import type { EventInstance } from '$lib/events/types';
88
import { HANDLE_ID } from '$lib/constants/handles';
9+
import { calculateNodeDimensions } from '$lib/constants/dimensions';
10+
import { nodeRegistry } from '$lib/nodes';
911

1012
/**
1113
* Convert a NodeInstance to a SvelteFlow Node
1214
*/
1315
export function toFlowNode(node: NodeInstance, options: { deletable?: boolean } = {}): Node<NodeInstance> {
16+
// Calculate valid pinned params count
17+
const typeDef = nodeRegistry.get(node.type);
18+
const pinnedParamCount = typeDef && node.pinnedParams
19+
? node.pinnedParams.filter(name => typeDef.params.some(p => p.name === name)).length
20+
: 0;
21+
22+
const rotation = (node.params?.['_rotation'] as number) || 0;
23+
const { width, height } = calculateNodeDimensions(
24+
node.name,
25+
node.inputs.length,
26+
node.outputs.length,
27+
pinnedParamCount,
28+
rotation
29+
);
30+
1431
return {
1532
id: node.id,
1633
type: 'pathview',
1734
position: { ...node.position },
1835
data: node,
36+
width,
37+
height,
1938
selectable: true,
2039
draggable: true,
2140
deletable: options.deletable ?? true

src/lib/components/nodes/BaseNode.svelte

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import { showTooltip, hideTooltip } from '$lib/components/Tooltip.svelte';
1212
import { paramInput } from '$lib/actions/paramInput';
1313
import { plotDataStore } from '$lib/plotting/processing/plotDataStore';
14-
import { NODE, snapTo2G, getPortPositionCalc } from '$lib/constants/dimensions';
14+
import { NODE, getPortPositionCalc, calculateNodeDimensions } from '$lib/constants/dimensions';
1515
import PlotPreview from './PlotPreview.svelte';
1616
1717
interface Props {
@@ -136,37 +136,16 @@
136136
const maxPortsOnSide = $derived(Math.max(data.inputs.length, data.outputs.length));
137137
const pinnedCount = $derived(validPinnedParams().length);
138138
139-
// Node dimensions - calculated from data, always grid-snapped
140-
// Port dimension: space needed for ports on one side
141-
const minPortDimension = $derived(Math.max(1, maxPortsOnSide) * NODE.portSpacing);
142-
143-
// Pinned params height: border(1) + padding(10) + rows(20 each) + gaps(4 between)
144-
// = 11 + 20*N + 4*(N-1) = 7 + 24*N for N > 0
145-
const pinnedParamsHeight = $derived(pinnedCount > 0 ? 7 + 24 * pinnedCount : 0);
146-
147-
// Width calculation:
148-
// - Base width: 100px
149-
// - Name width: ~6px per char + 24px padding, estimate from name length
150-
// - Pinned params: need ~120px minimum for label + input
151-
// - Vertical orientation: also consider port count
152-
const nameWidth = $derived(data.name.length * 6 + 24);
153-
const pinnedParamsWidth = $derived(pinnedCount > 0 ? 160 : 0);
154-
const nodeWidth = $derived(
155-
snapTo2G(Math.max(
156-
NODE.baseWidth,
157-
nameWidth,
158-
pinnedParamsWidth,
159-
isVertical ? minPortDimension : 0
160-
))
161-
);
162-
163-
// Height: total content height vs port requirements (they share vertical space)
164-
const contentHeight = $derived(NODE.baseHeight + pinnedParamsHeight);
165-
const nodeHeight = $derived(
166-
isVertical
167-
? snapTo2G(contentHeight)
168-
: snapTo2G(Math.max(contentHeight, minPortDimension))
169-
);
139+
// Node dimensions - calculated from shared utility (same as SvelteFlow bounds)
140+
const nodeDimensions = $derived(calculateNodeDimensions(
141+
data.name,
142+
data.inputs.length,
143+
data.outputs.length,
144+
pinnedCount,
145+
rotation
146+
));
147+
const nodeWidth = $derived(nodeDimensions.width);
148+
const nodeHeight = $derived(nodeDimensions.height);
170149
171150
// Check if this is a Subsystem or Interface node (using shapes utility)
172151
const isSubsystemNode = $derived(isSubsystem(data));

src/lib/constants/dimensions.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,40 @@ export function getPortPositionCalc(index: number, total: number): string {
7171
}
7272
return `calc(50% + ${offsetFromCenter}px)`;
7373
}
74+
75+
/**
76+
* Calculate node dimensions from node data.
77+
* Used by both SvelteFlow (for bounds) and BaseNode (for CSS).
78+
*/
79+
export function calculateNodeDimensions(
80+
name: string,
81+
inputCount: number,
82+
outputCount: number,
83+
pinnedParamCount: number,
84+
rotation: number
85+
): { width: number; height: number } {
86+
const isVertical = rotation === 1 || rotation === 3;
87+
const maxPortsOnSide = Math.max(inputCount, outputCount);
88+
const minPortDimension = Math.max(1, maxPortsOnSide) * NODE.portSpacing;
89+
90+
// Pinned params height: border(1) + padding(10) + rows(20 each) + gaps(4 between)
91+
const pinnedParamsHeight = pinnedParamCount > 0 ? 7 + 24 * pinnedParamCount : 0;
92+
93+
// Width: base, name estimate, pinned params minimum, port dimension (if vertical)
94+
const nameWidth = name.length * 6 + 24;
95+
const pinnedParamsWidth = pinnedParamCount > 0 ? 160 : 0;
96+
const width = snapTo2G(Math.max(
97+
NODE.baseWidth,
98+
nameWidth,
99+
pinnedParamsWidth,
100+
isVertical ? minPortDimension : 0
101+
));
102+
103+
// Height: content height vs port dimension (they share vertical space)
104+
const contentHeight = NODE.baseHeight + pinnedParamsHeight;
105+
const height = isVertical
106+
? snapTo2G(contentHeight)
107+
: snapTo2G(Math.max(contentHeight, minPortDimension));
108+
109+
return { width, height };
110+
}

0 commit comments

Comments
 (0)