mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 11:11:53 +00:00
[TS] Use strict nullability in LGraphCanvas (#665)
- Adds runtime null checking - Converts canvas context to non-nullable - Adds explicit throws for some edge cases - Improves TS types
This commit is contained in:
@@ -75,9 +75,9 @@ import { toClass } from "./utils/type"
|
|||||||
import { WIDGET_TYPE_MAP } from "./widgets/widgetMap"
|
import { WIDGET_TYPE_MAP } from "./widgets/widgetMap"
|
||||||
|
|
||||||
interface IShowSearchOptions {
|
interface IShowSearchOptions {
|
||||||
node_to?: LGraphNode
|
node_to?: LGraphNode | null
|
||||||
node_from?: LGraphNode
|
node_from?: LGraphNode | null
|
||||||
slot_from: number | INodeOutputSlot | INodeInputSlot
|
slot_from: number | INodeOutputSlot | INodeInputSlot | null | undefined
|
||||||
type_filter_in?: ISlotType
|
type_filter_in?: ISlotType
|
||||||
type_filter_out?: ISlotType | false
|
type_filter_out?: ISlotType | false
|
||||||
|
|
||||||
@@ -471,7 +471,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
graph: LGraph | null
|
graph: LGraph | null
|
||||||
canvas: HTMLCanvasElement
|
canvas: HTMLCanvasElement
|
||||||
bgcanvas: HTMLCanvasElement
|
bgcanvas: HTMLCanvasElement
|
||||||
ctx?: CanvasRenderingContext2D | null
|
ctx: CanvasRenderingContext2D
|
||||||
_events_binded?: boolean
|
_events_binded?: boolean
|
||||||
_mousedown_callback?(e: PointerEvent): void
|
_mousedown_callback?(e: PointerEvent): void
|
||||||
_mousewheel_callback?(e: WheelEvent): void
|
_mousewheel_callback?(e: WheelEvent): void
|
||||||
@@ -522,11 +522,11 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
/** The start position of the drag zoom. */
|
/** The start position of the drag zoom. */
|
||||||
#dragZoomStart: { pos: Point, scale: number } | null = null
|
#dragZoomStart: { pos: Point, scale: number } | null = null
|
||||||
|
|
||||||
getMenuOptions?(): IContextMenuValue[]
|
getMenuOptions?(): IContextMenuValue<string>[]
|
||||||
getExtraMenuOptions?(
|
getExtraMenuOptions?(
|
||||||
canvas: LGraphCanvas,
|
canvas: LGraphCanvas,
|
||||||
options: IContextMenuValue[],
|
options: IContextMenuValue<string>[],
|
||||||
): IContextMenuValue[]
|
): IContextMenuValue<string>[]
|
||||||
static active_node: LGraphNode
|
static active_node: LGraphNode
|
||||||
/** called before modifying the graph */
|
/** called before modifying the graph */
|
||||||
onBeforeChange?(graph: LGraph): void
|
onBeforeChange?(graph: LGraph): void
|
||||||
@@ -676,6 +676,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
// TypeScript strict workaround: cannot use method to initialize properties.
|
// TypeScript strict workaround: cannot use method to initialize properties.
|
||||||
this.canvas = undefined!
|
this.canvas = undefined!
|
||||||
this.bgcanvas = undefined!
|
this.bgcanvas = undefined!
|
||||||
|
this.ctx = undefined!
|
||||||
|
|
||||||
this.setCanvas(canvas, options.skip_events)
|
this.setCanvas(canvas, options.skip_events)
|
||||||
this.clear()
|
this.clear()
|
||||||
@@ -805,10 +806,10 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static onMenuAdd(
|
static onMenuAdd(
|
||||||
node: LGraphNode,
|
value: unknown,
|
||||||
options: IContextMenuOptions,
|
options: unknown,
|
||||||
e: MouseEvent,
|
e: MouseEvent,
|
||||||
prev_menu: ContextMenu<string>,
|
prev_menu?: ContextMenu<string>,
|
||||||
callback?: (node: LGraphNode | null) => void,
|
callback?: (node: LGraphNode | null) => void,
|
||||||
): boolean | undefined {
|
): boolean | undefined {
|
||||||
const canvas = LGraphCanvas.active_canvas
|
const canvas = LGraphCanvas.active_canvas
|
||||||
@@ -1663,14 +1664,14 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
this.bgcanvas.width = this.canvas.width
|
this.bgcanvas.width = this.canvas.width
|
||||||
this.bgcanvas.height = this.canvas.height
|
this.bgcanvas.height = this.canvas.height
|
||||||
|
|
||||||
if (element.getContext == null) {
|
const ctx = element.getContext?.("2d")
|
||||||
|
if (ctx == null) {
|
||||||
if (element.localName != "canvas") {
|
if (element.localName != "canvas") {
|
||||||
throw `Element supplied for LGraphCanvas must be a <canvas> element, you passed a ${element.localName}`
|
throw `Element supplied for LGraphCanvas must be a <canvas> element, you passed a ${element.localName}`
|
||||||
}
|
}
|
||||||
throw "This browser doesn't support Canvas"
|
throw "This browser doesn't support Canvas"
|
||||||
}
|
}
|
||||||
|
this.ctx = ctx
|
||||||
this.ctx = element.getContext("2d")
|
|
||||||
|
|
||||||
if (!skip_events) this.bindEvents()
|
if (!skip_events) this.bindEvents()
|
||||||
}
|
}
|
||||||
@@ -2050,26 +2051,28 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
// clone node ALT dragging
|
// clone node ALT dragging
|
||||||
if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && !e.ctrlKey && node && this.allow_interaction) {
|
if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && !e.ctrlKey && node && this.allow_interaction) {
|
||||||
const node_data = node.clone()?.serialize()
|
const node_data = node.clone()?.serialize()
|
||||||
const cloned = LiteGraph.createNode(node_data.type)
|
if (node_data?.type != null) {
|
||||||
if (cloned) {
|
const cloned = LiteGraph.createNode(node_data.type)
|
||||||
cloned.configure(node_data)
|
if (cloned) {
|
||||||
cloned.pos[0] += 5
|
cloned.configure(node_data)
|
||||||
cloned.pos[1] += 5
|
cloned.pos[0] += 5
|
||||||
|
cloned.pos[1] += 5
|
||||||
|
|
||||||
if (this.allow_dragnodes) {
|
if (this.allow_dragnodes) {
|
||||||
pointer.onDragStart = (pointer) => {
|
pointer.onDragStart = (pointer) => {
|
||||||
graph.add(cloned, false)
|
graph.add(cloned, false)
|
||||||
this.#startDraggingItems(cloned, pointer)
|
this.#startDraggingItems(cloned, pointer)
|
||||||
}
|
}
|
||||||
pointer.onDragEnd = e => this.#processDraggedItems(e)
|
pointer.onDragEnd = e => this.#processDraggedItems(e)
|
||||||
} else {
|
} else {
|
||||||
// TODO: Check if before/after change are necessary here.
|
// TODO: Check if before/after change are necessary here.
|
||||||
graph.beforeChange()
|
graph.beforeChange()
|
||||||
graph.add(cloned, false)
|
graph.add(cloned, false)
|
||||||
graph.afterChange()
|
graph.afterChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2083,9 +2086,15 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
if (reroute) {
|
if (reroute) {
|
||||||
if (e.shiftKey) {
|
if (e.shiftKey) {
|
||||||
// Connect new link from reroute
|
// Connect new link from reroute
|
||||||
const link = graph._links.get(reroute.linkIds.values().next().value)
|
const linkId = reroute.linkIds.values().next().value
|
||||||
|
if (linkId == null) return
|
||||||
|
|
||||||
|
const link = graph._links.get(linkId)
|
||||||
|
if (!link) return
|
||||||
|
|
||||||
const outputNode = graph.getNodeById(link.origin_id)
|
const outputNode = graph.getNodeById(link.origin_id)
|
||||||
|
if (!outputNode) return
|
||||||
|
|
||||||
const slot = link.origin_slot
|
const slot = link.origin_slot
|
||||||
const connecting: ConnectingLink = {
|
const connecting: ConnectingLink = {
|
||||||
node: outputNode,
|
node: outputNode,
|
||||||
@@ -2130,6 +2139,9 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
|
|
||||||
if (e.shiftKey && !e.altKey) {
|
if (e.shiftKey && !e.altKey) {
|
||||||
const slot = linkSegment.origin_slot
|
const slot = linkSegment.origin_slot
|
||||||
|
if (slot == null) return console.warn("Connecting link from corrupt link segment: `slot` null", linkSegment)
|
||||||
|
if (linkSegment.origin_id == null) return console.warn("Connecting link from corrupt link segment: `origin_id` null", linkSegment)
|
||||||
|
|
||||||
const originNode = graph._nodes_by_id[linkSegment.origin_id]
|
const originNode = graph._nodes_by_id[linkSegment.origin_id]
|
||||||
|
|
||||||
const connecting: ConnectingLink = {
|
const connecting: ConnectingLink = {
|
||||||
@@ -2168,7 +2180,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
|
|
||||||
// Groups
|
// Groups
|
||||||
const group = graph.getGroupOnPos(x, y)
|
const group = graph.getGroupOnPos(x, y)
|
||||||
this.selected_group = group
|
this.selected_group = group ?? null
|
||||||
if (group) {
|
if (group) {
|
||||||
if (group.isInResize(x, y)) {
|
if (group.isInResize(x, y)) {
|
||||||
// Resize group
|
// Resize group
|
||||||
@@ -2186,7 +2198,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
eMove.canvasY - group.pos[1] - offsetY,
|
eMove.canvasY - group.pos[1] - offsetY,
|
||||||
]
|
]
|
||||||
// Unless snapping.
|
// Unless snapping.
|
||||||
snapPoint(pos, this.#snapToGrid)
|
if (this.#snapToGrid) snapPoint(pos, this.#snapToGrid)
|
||||||
|
|
||||||
const resized = group.resize(pos[0], pos[1])
|
const resized = group.resize(pos[0], pos[1])
|
||||||
if (resized) this.dirty_bgcanvas = true
|
if (resized) this.dirty_bgcanvas = true
|
||||||
@@ -2301,7 +2313,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
eMove.canvasY - node.pos[1] - offsetY,
|
eMove.canvasY - node.pos[1] - offsetY,
|
||||||
]
|
]
|
||||||
// Unless snapping.
|
// Unless snapping.
|
||||||
snapPoint(pos, this.#snapToGrid)
|
if (this.#snapToGrid) snapPoint(pos, this.#snapToGrid)
|
||||||
|
|
||||||
const min = node.computeSize()
|
const min = node.computeSize()
|
||||||
pos[0] = Math.max(min[0], pos[0])
|
pos[0] = Math.max(min[0], pos[0])
|
||||||
@@ -2328,10 +2340,12 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
const link_pos = node.getConnectionPos(false, i)
|
const link_pos = node.getConnectionPos(false, i)
|
||||||
if (isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)) {
|
if (isInRectangle(x, y, link_pos[0] - 15, link_pos[1] - 10, 30, 20)) {
|
||||||
// Drag multiple output links
|
// Drag multiple output links
|
||||||
if (e.shiftKey && output.links?.length > 0) {
|
if (e.shiftKey && output.links?.length) {
|
||||||
this.connecting_links = []
|
this.connecting_links = []
|
||||||
for (const linkId of output.links) {
|
for (const linkId of output.links) {
|
||||||
const link = graph._links.get(linkId)
|
const link = graph._links.get(linkId)
|
||||||
|
if (!link) continue
|
||||||
|
|
||||||
const slot = link.target_slot
|
const slot = link.target_slot
|
||||||
const linked_node = graph._nodes_by_id[link.target_id]
|
const linked_node = graph._nodes_by_id[link.target_id]
|
||||||
const input = linked_node.inputs[slot]
|
const input = linked_node.inputs[slot]
|
||||||
@@ -4079,10 +4093,8 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
drawFrontCanvas(): void {
|
drawFrontCanvas(): void {
|
||||||
this.dirty_canvas = false
|
this.dirty_canvas = false
|
||||||
|
|
||||||
const ctx = this.ctx
|
const { ctx, canvas } = this
|
||||||
if (!ctx) return
|
|
||||||
|
|
||||||
const canvas = this.canvas
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
if (ctx.start2D && !this.viewport) {
|
if (ctx.start2D && !this.viewport) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
@@ -6542,7 +6554,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
node: LGraphNode,
|
node: LGraphNode,
|
||||||
property: string,
|
property: string,
|
||||||
options: IDialogOptions,
|
options: IDialogOptions,
|
||||||
): IDialog {
|
): IDialog | undefined {
|
||||||
if (!node || node.properties[property] === undefined) return
|
if (!node || node.properties[property] === undefined) return
|
||||||
|
|
||||||
options = options || {}
|
options = options || {}
|
||||||
@@ -7102,8 +7114,8 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCanvasMenuOptions(): IContextMenuValue[] {
|
getCanvasMenuOptions(): IContextMenuValue<string>[] {
|
||||||
let options: IContextMenuValue[] = null
|
let options: IContextMenuValue<string>[]
|
||||||
if (this.getMenuOptions) {
|
if (this.getMenuOptions) {
|
||||||
options = this.getMenuOptions()
|
options = this.getMenuOptions()
|
||||||
} else {
|
} else {
|
||||||
@@ -7260,7 +7272,8 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
getGroupMenuOptions(group: LGraphGroup): IContextMenuValue[] {
|
/** @deprecated */
|
||||||
|
getGroupMenuOptions(group: LGraphGroup) {
|
||||||
console.warn("LGraphCanvas.getGroupMenuOptions is deprecated, use LGraphGroup.getMenuOptions instead")
|
console.warn("LGraphCanvas.getGroupMenuOptions is deprecated, use LGraphGroup.getMenuOptions instead")
|
||||||
return group.getMenuOptions()
|
return group.getMenuOptions()
|
||||||
}
|
}
|
||||||
@@ -7369,13 +7382,15 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
)
|
)
|
||||||
const setDirty = () => this.setDirty(true)
|
const setDirty = () => this.setDirty(true)
|
||||||
|
|
||||||
function inner_option_clicked(v, options) {
|
function inner_option_clicked(v: IContextMenuValue<unknown>, options: IDialogOptions) {
|
||||||
if (!v) return
|
if (!v) return
|
||||||
|
|
||||||
if (v.content == "Remove Slot") {
|
if (v.content == "Remove Slot") {
|
||||||
if (!node.graph) throw new NullGraphError()
|
if (!node?.graph) throw new NullGraphError()
|
||||||
|
|
||||||
const info = v.slot
|
const info = v.slot
|
||||||
|
if (!info) throw new TypeError("Found-slot info was null when processing context menu.")
|
||||||
|
|
||||||
node.graph.beforeChange()
|
node.graph.beforeChange()
|
||||||
if (info.input) {
|
if (info.input) {
|
||||||
node.removeInput(info.slot)
|
node.removeInput(info.slot)
|
||||||
@@ -7385,9 +7400,11 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
node.graph.afterChange()
|
node.graph.afterChange()
|
||||||
return
|
return
|
||||||
} else if (v.content == "Disconnect Links") {
|
} else if (v.content == "Disconnect Links") {
|
||||||
if (!node.graph) throw new NullGraphError()
|
if (!node?.graph) throw new NullGraphError()
|
||||||
|
|
||||||
const info = v.slot
|
const info = v.slot
|
||||||
|
if (!info) throw new TypeError("Found-slot info was null when processing context menu.")
|
||||||
|
|
||||||
node.graph.beforeChange()
|
node.graph.beforeChange()
|
||||||
if (info.output) {
|
if (info.output) {
|
||||||
node.disconnectOutput(info.slot)
|
node.disconnectOutput(info.slot)
|
||||||
@@ -7397,7 +7414,11 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
node.graph.afterChange()
|
node.graph.afterChange()
|
||||||
return
|
return
|
||||||
} else if (v.content == "Rename Slot") {
|
} else if (v.content == "Rename Slot") {
|
||||||
|
if (!node) throw new TypeError("`node` was null when processing the context menu.")
|
||||||
|
|
||||||
const info = v.slot
|
const info = v.slot
|
||||||
|
if (!info) throw new TypeError("Found-slot info was null when processing context menu.")
|
||||||
|
|
||||||
const slot_info = info.input
|
const slot_info = info.input
|
||||||
? node.getInputInfo(info.slot)
|
? node.getInputInfo(info.slot)
|
||||||
: node.getOutputInfo(info.slot)
|
: node.getOutputInfo(info.slot)
|
||||||
@@ -7411,7 +7432,7 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
if (!node.graph) throw new NullGraphError()
|
if (!node.graph) throw new NullGraphError()
|
||||||
|
|
||||||
node.graph.beforeChange()
|
node.graph.beforeChange()
|
||||||
if (input.value) {
|
if (input?.value) {
|
||||||
if (slot_info) {
|
if (slot_info) {
|
||||||
slot_info.label = input.value
|
slot_info.label = input.value
|
||||||
}
|
}
|
||||||
@@ -7420,7 +7441,9 @@ export class LGraphCanvas implements ConnectionColorContext {
|
|||||||
dialog.close()
|
dialog.close()
|
||||||
node.graph.afterChange()
|
node.graph.afterChange()
|
||||||
}
|
}
|
||||||
dialog.querySelector("button").addEventListener("click", inner)
|
dialog.querySelector("button")?.addEventListener("click", inner)
|
||||||
|
if (!input) throw new TypeError("Input element was null when processing context menu.")
|
||||||
|
|
||||||
input.addEventListener("keydown", function (e) {
|
input.addEventListener("keydown", function (e) {
|
||||||
dialog.is_modified = true
|
dialog.is_modified = true
|
||||||
if (e.key == "Escape") {
|
if (e.key == "Escape") {
|
||||||
|
|||||||
Reference in New Issue
Block a user