Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3112dba
add dom element resize observer registry for vue node components
christian-byrne Sep 8, 2025
b6269c0
Update src/renderer/extensions/vueNodes/composables/useVueNodeResizeT…
christian-byrne Sep 9, 2025
4287526
refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeO…
benceruleanlu Sep 9, 2025
110ecf3
chore: make TransformState interface non-exported to satisfy knip pre…
benceruleanlu Sep 9, 2025
9786ecf
Revert "chore: make TransformState interface non-exported to satisfy …
benceruleanlu Sep 10, 2025
dbacbc5
Revert "refactor(vue-nodes): typed TransformState InjectionKey, safer…
benceruleanlu Sep 10, 2025
d4c2e83
[refactor] Improve resize tracking composable documentation and test …
christian-byrne Sep 9, 2025
db5a68b
remove typo comment
christian-byrne Sep 9, 2025
c39cdaf
convert to functional bounds collection
christian-byrne Sep 9, 2025
d39bf36
remove inline import
christian-byrne Sep 10, 2025
ea93135
add interfaces for bounds mutations
christian-byrne Sep 10, 2025
ea9c121
remove change log
christian-byrne Sep 10, 2025
ed94827
fix bounds collection when vue nodes turned off
christian-byrne Sep 10, 2025
ffede77
fix title offset on y
christian-byrne Sep 10, 2025
5022f14
move from resize observer to selection toolbox bounds
christian-byrne Sep 10, 2025
f63118b
refactor(vue-nodes): typed TransformState InjectionKey, safer ResizeO…
benceruleanlu Sep 9, 2025
cb2069c
Fix conversion
benceruleanlu Sep 10, 2025
a0ed9d9
Readd padding
benceruleanlu Sep 10, 2025
3a7cc3f
revert churn reducings from layoutStore.ts
benceruleanlu Sep 10, 2025
0ba660f
Rely on RO for resize, and batch
benceruleanlu Sep 10, 2025
ed7a4e9
Improve churn
benceruleanlu Sep 11, 2025
121221d
Cache canvas offset
benceruleanlu Sep 11, 2025
4de2c2f
Merge remote-tracking branch 'origin/main' into bl-update-slots
benceruleanlu Sep 11, 2025
57a1359
rename from measure
benceruleanlu Sep 12, 2025
cb211eb
Merge remote-tracking branch 'origin/main' into bl-update-slots
benceruleanlu Sep 12, 2025
1dfa72c
remove unused
benceruleanlu Sep 12, 2025
69f5391
address review comments
benceruleanlu Sep 15, 2025
9203ed5
Merge remote-tracking branch 'origin/main' into bl-update-slots
benceruleanlu Sep 15, 2025
7d657a2
Update legacy injection
benceruleanlu Sep 15, 2025
a86cd97
nit
benceruleanlu Sep 16, 2025
98a259d
Split into store
benceruleanlu Sep 16, 2025
fc3d609
nit
benceruleanlu Sep 16, 2025
04c4c54
perf improvement
benceruleanlu Sep 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/composables/canvas/useSelectionToolboxPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ export function useSelectionToolboxPosition(

worldPosition.value = {
x: unionBounds.x + unionBounds.width / 2,
// createBounds() applied a default padding of 10px
// so adjust Y to maintain visual consistency
y: unionBounds.y - 10
}

Expand Down
17 changes: 14 additions & 3 deletions src/composables/element/useCanvasPositionConversion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useElementBounding } from '@vueuse/core'

import type { LGraphCanvas, Vector2 } from '@/lib/litegraph/src/litegraph'
import type { LGraphCanvas, Point } from '@/lib/litegraph/src/litegraph'
import { useCanvasStore } from '@/renderer/core/canvas/canvasStore'

let sharedConverter: ReturnType<typeof useCanvasPositionConversion> | null =
null

/**
* Convert between canvas and client positions
Expand All @@ -14,15 +18,15 @@ export const useCanvasPositionConversion = (
) => {
const { left, top, update } = useElementBounding(canvasElement)

const clientPosToCanvasPos = (pos: Vector2): Vector2 => {
const clientPosToCanvasPos = (pos: Point): Point => {
const { offset, scale } = lgCanvas.ds
return [
(pos[0] - left.value) / scale - offset[0],
(pos[1] - top.value) / scale - offset[1]
]
}

const canvasPosToClientPos = (pos: Vector2): Vector2 => {
const canvasPosToClientPos = (pos: Point): Point => {
const { offset, scale } = lgCanvas.ds
return [
(pos[0] + offset[0]) * scale + left.value,
Expand All @@ -36,3 +40,10 @@ export const useCanvasPositionConversion = (
update
}
}

export function useSharedCanvasPositionConversion() {
if (sharedConverter) return sharedConverter
const lgCanvas = useCanvasStore().getCanvas()
sharedConverter = useCanvasPositionConversion(lgCanvas.canvas, lgCanvas)
return sharedConverter
}
14 changes: 7 additions & 7 deletions src/composables/useCanvasDrop.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Ref } from 'vue'

import { useSharedCanvasPositionConversion } from '@/composables/element/useCanvasPositionConversion'
import { usePragmaticDroppable } from '@/composables/usePragmaticDragAndDrop'
import { LGraphNode } from '@/lib/litegraph/src/litegraph'
import { LiteGraph } from '@/lib/litegraph/src/litegraph'
Expand Down Expand Up @@ -27,16 +28,19 @@ export const useCanvasDrop = (canvasRef: Ref<HTMLCanvasElement>) => {

if (dndData.type === 'tree-explorer-node') {
const node = dndData.data as RenderedTreeExplorerNode
const conv = useSharedCanvasPositionConversion()
const basePos = conv.clientPosToCanvasPos([loc.clientX, loc.clientY])

if (node.data instanceof ComfyNodeDefImpl) {
const nodeDef = node.data
const pos = comfyApp.clientPosToCanvasPos([loc.clientX, loc.clientY])
const pos = [...basePos]
// Add an offset on y to make sure after adding the node, the cursor
// is on the node (top left corner)
pos[1] += LiteGraph.NODE_TITLE_HEIGHT
litegraphService.addNodeOnGraph(nodeDef, { pos })
} else if (node.data instanceof ComfyModelDef) {
const model = node.data
const pos = comfyApp.clientPosToCanvasPos([loc.clientX, loc.clientY])
const pos = basePos
const nodeAtPos = comfyApp.graph.getNodeOnPos(pos[0], pos[1])
let targetProvider: ModelNodeProvider | null = null
let targetGraphNode: LGraphNode | null = null
Expand Down Expand Up @@ -73,11 +77,7 @@ export const useCanvasDrop = (canvasRef: Ref<HTMLCanvasElement>) => {
}
} else if (node.data instanceof ComfyWorkflow) {
const workflow = node.data
const position = comfyApp.clientPosToCanvasPos([
loc.clientX,
loc.clientY
])
await workflowService.insertWorkflow(workflow, { position })
await workflowService.insertWorkflow(workflow, { position: basePos })
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/extensions/core/widgetInputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
INodeOutputSlot,
ISlotType,
LLink,
Vector2
Point
} from '@/lib/litegraph/src/litegraph'
import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import type { IBaseWidget } from '@/lib/litegraph/src/types/widgets'
Expand Down Expand Up @@ -557,7 +557,7 @@ app.registerExtension({
}
)

function isNodeAtPos(pos: Vector2) {
function isNodeAtPos(pos: Point) {
for (const n of app.graph.nodes) {
if (n.pos[0] === pos[0] && n.pos[1] === pos[1]) {
return true
Expand Down
4 changes: 2 additions & 2 deletions src/platform/workflow/core/services/workflowService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { toRaw } from 'vue'

import { t } from '@/i18n'
import { LGraph, LGraphCanvas } from '@/lib/litegraph/src/litegraph'
import type { SerialisableGraph, Vector2 } from '@/lib/litegraph/src/litegraph'
import type { Point, SerialisableGraph } from '@/lib/litegraph/src/litegraph'
import { useSettingStore } from '@/platform/settings/settingStore'
import { useToastStore } from '@/platform/updates/common/toastStore'
import {
Expand Down Expand Up @@ -346,7 +346,7 @@ export const useWorkflowService = () => {
*/
const insertWorkflow = async (
workflow: ComfyWorkflow,
options: { position?: Vector2 } = {}
options: { position?: Point } = {}
) => {
const loadedWorkflow = await workflow.load()
const workflowJSON = toRaw(loadedWorkflow.initialState)
Expand Down
49 changes: 49 additions & 0 deletions src/renderer/core/layout/injectionKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { InjectionKey } from 'vue'

import type { Point } from '@/renderer/core/layout/types'

/**
* Lightweight, injectable transform state used by layout-aware components.
*
* Consumers use this interface to convert coordinates between LiteGraph's
* canvas space and the DOM's screen space, access the current pan/zoom
* (camera), and perform basic viewport culling checks.
*
* Coordinate mapping:
* - screen = (canvas + offset) * scale
* - canvas = screen / scale - offset
*
* The full implementation and additional helpers live in
* `useTransformState()`. This interface deliberately exposes only the
* minimal surface needed outside that composable.
*
* @example
* const state = inject(TransformStateKey)!
* const screen = state.canvasToScreen({ x: 100, y: 50 })
*/
interface TransformState {
/** Convert a screen-space point (CSS pixels) to canvas space. */
screenToCanvas: (p: Point) => Point
/** Convert a canvas-space point to screen space (CSS pixels). */
canvasToScreen: (p: Point) => Point
/** Current pan/zoom; `x`/`y` are offsets, `z` is scale. */
camera?: { x: number; y: number; z: number }
/**
* Test whether a node's rectangle intersects the (expanded) viewport.
* Handy for viewport culling and lazy work.
*
* @param nodePos Top-left in canvas space `[x, y]`
* @param nodeSize Size in canvas units `[width, height]`
* @param viewport Screen-space viewport `{ width, height }`
* @param margin Optional fractional margin (e.g. `0.2` = 20%)
*/
isNodeInViewport?: (
nodePos: ArrayLike<number>,
nodeSize: ArrayLike<number>,
viewport: { width: number; height: number },
margin?: number
) => boolean
}

export const TransformStateKey: InjectionKey<TransformState> =
Symbol('transformState')
Loading