diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte
index ac381bb6b2..319a00e3b4 100644
--- a/frontend/src/lib/components/graph/FlowGraphV2.svelte
+++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte
@@ -53,7 +53,6 @@
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'
@@ -248,12 +247,33 @@
const noteEditorContext = getNoteEditorContext()
+ // Function to calculate extra gap needed for notes below the lowest flow nodes
+ function calculateNoteGap(notes: FlowNote[] | undefined): number {
+ if (!notes || notes.length === 0) {
+ return 0
+ }
+ let maxNoteBelowGap = 0
+
+ notes.forEach((note) => {
+ if (note.position?.y && note.position.y < 0) {
+ maxNoteBelowGap = Math.max(maxNoteBelowGap, -note.position.y)
+ }
+ })
+
+ return maxNoteBelowGap
+ }
+
+ // Calculate note gap based on current nodes and notes
+ const topPadding = editMode ? 100 : 24
+ const yOffset = calculateNoteGap(notes) + topPadding
+
setGraphContext({
selectionManager: selectionManager,
useDataflow,
showAssets,
noteManager,
- clearFlowSelection
+ clearFlowSelection,
+ yOffset
} as any)
if (triggerContext && allowSimplifiedPoll) {
@@ -438,8 +458,20 @@
let height = $state(0)
- // Counter to trigger initial viewport fit after first flow build
- let initialBuildTrigger = $state(0)
+ // Derived nodes with yOffset applied to all nodes uniformly
+ const nodesWithOffset = $derived.by(() => {
+ return nodes.map((node) => {
+ if (
+ node.type !== 'asset' &&
+ node.type !== 'assetsOverflowed' &&
+ node.type !== 'newAiTool' &&
+ node.type !== 'aiTool'
+ ) {
+ return { ...node, position: { ...node.position, y: node.position.y + yOffset } }
+ }
+ return node
+ })
+ })
// Note feature state
@@ -562,7 +594,13 @@
]
await tick()
- height = Math.max(...nodes.map((n) => n.position.y + NODE.height + 100), minHeight)
+ if (nodes.length === 0) {
+ height = minHeight
+ } else {
+ const minY = Math.min(...nodes.map((n) => n.position.y))
+ const maxBottom = Math.max(...nodes.map((n) => n.position.y + NODE.height + 100))
+ height = Math.max(maxBottom - minY, minHeight)
+ }
}
const nodeTypes = {
@@ -660,10 +698,6 @@
;[graph, allowSimplifiedPoll, $showAssets, showNotes, noteManager.renderCount]
untrack(async () => {
await updateStores()
- // Trigger initial viewport fit after first build
- if (initialBuildTrigger === 0 && nodes.length > 0) {
- initialBuildTrigger = 1
- }
})
})
@@ -809,7 +843,6 @@
bind:this={viewportSynchronizer}
/>
{/if}
-
{
@@ -822,13 +855,17 @@
onnodedragstop={(event) => {
const node = event.targetNode
if (node && node.type === 'note') {
- onNotePositionUpdate?.(node.id, node.position)
+ const positionWithOffset = {
+ x: node.position.x,
+ y: node.position.y - yOffset
+ }
+ onNotePositionUpdate?.(node.id, positionWithOffset)
}
}}
onmove={(event, viewport) => {
viewportSynchronizer?.handleLocalViewportChange(event, viewport)
}}
- {nodes}
+ nodes={nodesWithOffset}
{edges}
{edgeTypes}
{nodeTypes}
@@ -855,7 +892,7 @@
{#if noteMode}
-
+
{/if}
{#if multiSelectEnabled}
@@ -865,7 +902,10 @@
!id.startsWith('Settings') && !id.startsWith('Trigger') && !id.startsWith('Result')
)}
>
-
+
{/if}
diff --git a/frontend/src/lib/components/graph/InitialViewportFitter.svelte b/frontend/src/lib/components/graph/InitialViewportFitter.svelte
deleted file mode 100644
index ffec0bb00c..0000000000
--- a/frontend/src/lib/components/graph/InitialViewportFitter.svelte
+++ /dev/null
@@ -1,50 +0,0 @@
-
diff --git a/frontend/src/lib/components/graph/NoteTool.svelte b/frontend/src/lib/components/graph/NoteTool.svelte
index abdc460a77..ad41bd974a 100644
--- a/frontend/src/lib/components/graph/NoteTool.svelte
+++ b/frontend/src/lib/components/graph/NoteTool.svelte
@@ -7,9 +7,10 @@
interface Props {
exitNoteMode?: () => void
+ yOffset: number
}
- let { exitNoteMode }: Props = $props()
+ let { exitNoteMode, yOffset }: Props = $props()
// Get NoteEditor context for direct note creation
const noteEditorContext = getNoteEditorContext()
@@ -61,10 +62,14 @@
y: endPosition.y + rect.top
}
- const position = screenToFlowPosition({
+ const flowPosition = screenToFlowPosition({
x: Math.min(absoluteStartPosition.x, absoluteEndPosition.x),
y: Math.min(absoluteStartPosition.y, absoluteEndPosition.y)
})
+ const position = {
+ x: flowPosition.x,
+ y: flowPosition.y - (yOffset || 0)
+ }
const zoom = getViewport().zoom
const size = {
@@ -155,10 +160,14 @@
onpointerup={onPointerUp}
oncontextmenu={(e) => {
// Capture the position when context menu is triggered
- contextMenuPosition = screenToFlowPosition({
+ const flowPosition = screenToFlowPosition({
x: e.clientX,
y: e.clientY
})
+ contextMenuPosition = {
+ x: flowPosition.x,
+ y: flowPosition.y - yOffset
+ }
}}
role="button"
tabindex="0"
diff --git a/frontend/src/lib/components/graph/PaneContextMenu.svelte b/frontend/src/lib/components/graph/PaneContextMenu.svelte
index 7201cfe66b..b6b6b49ac9 100644
--- a/frontend/src/lib/components/graph/PaneContextMenu.svelte
+++ b/frontend/src/lib/components/graph/PaneContextMenu.svelte
@@ -9,6 +9,7 @@
CONTEXT_MENU_ITEM_BASE_CLASS,
CONTEXT_MENU_ITEM_HOVER_CLASS
} from '../common/contextmenu/contextMenuStyles'
+ import { getGraphContext } from './graphContext'
interface Props {
editMode?: boolean
@@ -19,6 +20,8 @@
const { screenToFlowPosition } = useSvelteFlow()
const noteEditorContext = getNoteEditorContext()
+ const graphContext = getGraphContext()
+
let contextMenuVisible = $state(false)
let contextMenuPosition = $state<{ x: number; y: number }>({ x: 0, y: 0 })
let pendingFlowPosition = $state<{ x: number; y: number } | null>(null)
@@ -51,7 +54,10 @@
if (noteEditorContext?.noteEditor && pendingFlowPosition) {
noteEditorContext.noteEditor.addNote({
text: '### Free note\nDouble click to edit me',
- position: pendingFlowPosition,
+ position: {
+ x: pendingFlowPosition.x,
+ y: pendingFlowPosition.y - (graphContext?.yOffset || 0)
+ },
size: { width: 300, height: 200 },
color: DEFAULT_NOTE_COLOR,
type: 'free',
diff --git a/frontend/src/lib/components/graph/graphContext.ts b/frontend/src/lib/components/graph/graphContext.ts
index d4b3a6b556..176fc5d3bf 100644
--- a/frontend/src/lib/components/graph/graphContext.ts
+++ b/frontend/src/lib/components/graph/graphContext.ts
@@ -9,6 +9,7 @@ export type GraphContext = {
showAssets: Writable
noteManager?: NoteManager
clearFlowSelection?: () => void
+ yOffset?: number
}
const graphContextKey = 'FlowGraphContext'