mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-05 07:30:11 +00:00
[Refactor] Split NodeSlot out to separate files (#960)
Split files only; no code changes. New files moved to `/node`.
This commit is contained in:
@@ -26,6 +26,7 @@ import type {
|
||||
Size,
|
||||
} from "./interfaces"
|
||||
import type { LGraph } from "./LGraph"
|
||||
import type { ConnectionColorContext } from "./node/NodeSlot"
|
||||
import type {
|
||||
CanvasEventDetail,
|
||||
CanvasMouseEvent,
|
||||
@@ -57,7 +58,6 @@ import {
|
||||
overlapBounding,
|
||||
snapPoint,
|
||||
} from "./measure"
|
||||
import { type ConnectionColorContext } from "./NodeSlot"
|
||||
import { Reroute, type RerouteId } from "./Reroute"
|
||||
import { stringOrEmpty } from "./strings"
|
||||
import {
|
||||
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
Size,
|
||||
} from "./interfaces"
|
||||
import type { LGraph } from "./LGraph"
|
||||
import type { ConnectionColorContext } from "./node/NodeSlot"
|
||||
import type { Reroute, RerouteId } from "./Reroute"
|
||||
import type { CanvasMouseEvent } from "./types/events"
|
||||
import type { ISerialisedNode } from "./types/serialisation"
|
||||
@@ -32,7 +33,9 @@ import { LGraphCanvas } from "./LGraphCanvas"
|
||||
import { type LGraphNodeConstructor, LiteGraph } from "./litegraph"
|
||||
import { LLink } from "./LLink"
|
||||
import { createBounds, isInRect, isInRectangle, isPointInRect, snapPoint } from "./measure"
|
||||
import { ConnectionColorContext, inputAsSerialisable, isINodeInputSlot, isWidgetInputSlot, NodeInputSlot, NodeOutputSlot, outputAsSerialisable, toNodeSlotClass } from "./NodeSlot"
|
||||
import { NodeInputSlot } from "./node/NodeInputSlot"
|
||||
import { NodeOutputSlot } from "./node/NodeOutputSlot"
|
||||
import { inputAsSerialisable, isINodeInputSlot, isWidgetInputSlot, outputAsSerialisable, toNodeSlotClass } from "./node/slotUtils"
|
||||
import {
|
||||
LGraphEventMode,
|
||||
NodeSlotType,
|
||||
|
||||
40
src/node/NodeInputSlot.ts
Normal file
40
src/node/NodeInputSlot.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
||||
import type { LinkId } from "@/LLink"
|
||||
|
||||
import { LabelPosition } from "@/draw"
|
||||
import { LiteGraph } from "@/litegraph"
|
||||
import { type IDrawOptions, NodeSlot } from "@/node/NodeSlot"
|
||||
|
||||
export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
||||
link: LinkId | null
|
||||
|
||||
get isWidgetInputSlot(): boolean {
|
||||
return !!this.widget
|
||||
}
|
||||
|
||||
constructor(slot: OptionalProps<INodeInputSlot, "boundingRect">) {
|
||||
super(slot)
|
||||
this.link = slot.link
|
||||
}
|
||||
|
||||
override isConnected(): boolean {
|
||||
return this.link != null
|
||||
}
|
||||
|
||||
override isValidTarget(fromSlot: INodeInputSlot | INodeOutputSlot): boolean {
|
||||
return "links" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type)
|
||||
}
|
||||
|
||||
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
|
||||
const originalTextAlign = ctx.textAlign
|
||||
ctx.textAlign = "left"
|
||||
|
||||
super.draw(ctx, {
|
||||
...options,
|
||||
labelPosition: LabelPosition.Right,
|
||||
doStroke: false,
|
||||
})
|
||||
|
||||
ctx.textAlign = originalTextAlign
|
||||
}
|
||||
}
|
||||
47
src/node/NodeOutputSlot.ts
Normal file
47
src/node/NodeOutputSlot.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { INodeInputSlot, INodeOutputSlot, OptionalProps } from "@/interfaces"
|
||||
import type { LinkId } from "@/LLink"
|
||||
|
||||
import { LabelPosition } from "@/draw"
|
||||
import { LiteGraph } from "@/litegraph"
|
||||
import { type IDrawOptions, NodeSlot } from "@/node/NodeSlot"
|
||||
|
||||
export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
links: LinkId[] | null
|
||||
_data?: unknown
|
||||
slot_index?: number
|
||||
|
||||
get isWidgetInputSlot(): false {
|
||||
return false
|
||||
}
|
||||
|
||||
constructor(slot: OptionalProps<INodeOutputSlot, "boundingRect">) {
|
||||
super(slot)
|
||||
this.links = slot.links
|
||||
this._data = slot._data
|
||||
this.slot_index = slot.slot_index
|
||||
}
|
||||
|
||||
override isValidTarget(fromSlot: INodeInputSlot | INodeOutputSlot): boolean {
|
||||
return "link" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type)
|
||||
}
|
||||
|
||||
override isConnected(): boolean {
|
||||
return this.links != null && this.links.length > 0
|
||||
}
|
||||
|
||||
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
|
||||
const originalTextAlign = ctx.textAlign
|
||||
const originalStrokeStyle = ctx.strokeStyle
|
||||
ctx.textAlign = "right"
|
||||
ctx.strokeStyle = "black"
|
||||
|
||||
super.draw(ctx, {
|
||||
...options,
|
||||
labelPosition: LabelPosition.Left,
|
||||
doStroke: true,
|
||||
})
|
||||
|
||||
ctx.textAlign = originalTextAlign
|
||||
ctx.strokeStyle = originalStrokeStyle
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetInputSlot, IWidgetLocator, OptionalProps, Point, Rect, SharedIntersection } from "./interfaces"
|
||||
import type { LinkId } from "./LLink"
|
||||
import type { IWidget } from "./types/widgets"
|
||||
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetLocator, OptionalProps, Point, Rect } from "@/interfaces"
|
||||
|
||||
import { LabelPosition, SlotShape, SlotType } from "./draw"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
import { getCentre } from "./measure"
|
||||
import { LinkDirection, RenderShape } from "./types/globalEnums"
|
||||
import { ISerialisableNodeInput, ISerialisableNodeOutput } from "./types/serialisation"
|
||||
import { LabelPosition, SlotShape, SlotType } from "@/draw"
|
||||
import { LiteGraph } from "@/litegraph"
|
||||
import { getCentre } from "@/measure"
|
||||
import { LinkDirection, RenderShape } from "@/types/globalEnums"
|
||||
|
||||
import { NodeInputSlot } from "./NodeInputSlot"
|
||||
|
||||
export interface ConnectionColorContext {
|
||||
default_connection_color: {
|
||||
@@ -19,7 +18,7 @@ export interface ConnectionColorContext {
|
||||
default_connection_color_byTypeOff: Dictionary<CanvasColour>
|
||||
}
|
||||
|
||||
interface IDrawOptions {
|
||||
export interface IDrawOptions {
|
||||
colorContext: ConnectionColorContext
|
||||
labelPosition?: LabelPosition
|
||||
lowQuality?: boolean
|
||||
@@ -27,58 +26,6 @@ interface IDrawOptions {
|
||||
highlight?: boolean
|
||||
}
|
||||
|
||||
type CommonIoSlotProps = SharedIntersection<ISerialisableNodeInput, ISerialisableNodeOutput>
|
||||
|
||||
export function shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {
|
||||
const { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type } = slot
|
||||
return { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type }
|
||||
}
|
||||
|
||||
export function inputAsSerialisable(slot: INodeInputSlot): ISerialisableNodeInput {
|
||||
const { link } = slot
|
||||
const widgetOrPos = slot.widget
|
||||
? { widget: { name: slot.widget.name } }
|
||||
: { pos: slot.pos }
|
||||
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
...widgetOrPos,
|
||||
link,
|
||||
}
|
||||
}
|
||||
|
||||
export function outputAsSerialisable(slot: INodeOutputSlot & { widget?: IWidget }): ISerialisableNodeOutput {
|
||||
const { pos, slot_index, links, widget } = slot
|
||||
// Output widgets do not exist in Litegraph; this is a temporary downstream workaround.
|
||||
const outputWidget = widget
|
||||
? { widget: { name: widget.name } }
|
||||
: null
|
||||
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
...outputWidget,
|
||||
pos,
|
||||
slot_index,
|
||||
links,
|
||||
}
|
||||
}
|
||||
|
||||
export function toNodeSlotClass(slot: INodeInputSlot | INodeOutputSlot): NodeInputSlot | NodeOutputSlot {
|
||||
if (slot instanceof NodeInputSlot || slot instanceof NodeOutputSlot) return slot
|
||||
|
||||
return "link" in slot
|
||||
? new NodeInputSlot(slot)
|
||||
: new NodeOutputSlot(slot)
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard: Whether this input slot is attached to a widget.
|
||||
* @param slot The slot to check.
|
||||
*/
|
||||
export function isWidgetInputSlot(slot: INodeInputSlot): slot is IWidgetInputSlot {
|
||||
return !!slot.widget
|
||||
}
|
||||
|
||||
export abstract class NodeSlot implements INodeSlot {
|
||||
name: string
|
||||
localized_name?: string
|
||||
@@ -291,86 +238,3 @@ export abstract class NodeSlot implements INodeSlot {
|
||||
ctx.fillStyle = originalFillStyle
|
||||
}
|
||||
}
|
||||
|
||||
export function isINodeInputSlot(slot: INodeSlot): slot is INodeInputSlot {
|
||||
return "link" in slot
|
||||
}
|
||||
|
||||
export class NodeInputSlot extends NodeSlot implements INodeInputSlot {
|
||||
link: LinkId | null
|
||||
|
||||
get isWidgetInputSlot(): boolean {
|
||||
return !!this.widget
|
||||
}
|
||||
|
||||
constructor(slot: OptionalProps<INodeInputSlot, "boundingRect">) {
|
||||
super(slot)
|
||||
this.link = slot.link
|
||||
}
|
||||
|
||||
override isConnected(): boolean {
|
||||
return this.link != null
|
||||
}
|
||||
|
||||
override isValidTarget(fromSlot: INodeInputSlot | INodeOutputSlot): boolean {
|
||||
return "links" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type)
|
||||
}
|
||||
|
||||
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
|
||||
const originalTextAlign = ctx.textAlign
|
||||
ctx.textAlign = "left"
|
||||
|
||||
super.draw(ctx, {
|
||||
...options,
|
||||
labelPosition: LabelPosition.Right,
|
||||
doStroke: false,
|
||||
})
|
||||
|
||||
ctx.textAlign = originalTextAlign
|
||||
}
|
||||
}
|
||||
|
||||
export function isINodeOutputSlot(slot: INodeSlot): slot is INodeOutputSlot {
|
||||
return "links" in slot
|
||||
}
|
||||
|
||||
export class NodeOutputSlot extends NodeSlot implements INodeOutputSlot {
|
||||
links: LinkId[] | null
|
||||
_data?: unknown
|
||||
slot_index?: number
|
||||
|
||||
get isWidgetInputSlot(): false {
|
||||
return false
|
||||
}
|
||||
|
||||
constructor(slot: OptionalProps<INodeOutputSlot, "boundingRect">) {
|
||||
super(slot)
|
||||
this.links = slot.links
|
||||
this._data = slot._data
|
||||
this.slot_index = slot.slot_index
|
||||
}
|
||||
|
||||
override isValidTarget(fromSlot: INodeInputSlot | INodeOutputSlot): boolean {
|
||||
return "link" in fromSlot && LiteGraph.isValidConnection(this.type, fromSlot.type)
|
||||
}
|
||||
|
||||
override isConnected(): boolean {
|
||||
return this.links != null && this.links.length > 0
|
||||
}
|
||||
|
||||
override draw(ctx: CanvasRenderingContext2D, options: Omit<IDrawOptions, "doStroke" | "labelPosition">) {
|
||||
const originalTextAlign = ctx.textAlign
|
||||
const originalStrokeStyle = ctx.strokeStyle
|
||||
ctx.textAlign = "right"
|
||||
ctx.strokeStyle = "black"
|
||||
|
||||
super.draw(ctx, {
|
||||
...options,
|
||||
labelPosition: LabelPosition.Left,
|
||||
doStroke: true,
|
||||
})
|
||||
|
||||
ctx.textAlign = originalTextAlign
|
||||
ctx.strokeStyle = originalStrokeStyle
|
||||
}
|
||||
}
|
||||
67
src/node/slotUtils.ts
Normal file
67
src/node/slotUtils.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { IWidgetInputSlot, SharedIntersection } from "@/interfaces"
|
||||
import type { INodeInputSlot, INodeOutputSlot, INodeSlot, IWidget } from "@/litegraph"
|
||||
import type { ISerialisableNodeInput, ISerialisableNodeOutput } from "@/types/serialisation"
|
||||
|
||||
import { NodeInputSlot } from "./NodeInputSlot"
|
||||
import { NodeOutputSlot } from "./NodeOutputSlot"
|
||||
|
||||
type CommonIoSlotProps = SharedIntersection<ISerialisableNodeInput, ISerialisableNodeOutput>
|
||||
|
||||
export function shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {
|
||||
const { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type } = slot
|
||||
return { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type }
|
||||
}
|
||||
|
||||
export function inputAsSerialisable(slot: INodeInputSlot): ISerialisableNodeInput {
|
||||
const { link } = slot
|
||||
const widgetOrPos = slot.widget
|
||||
? { widget: { name: slot.widget.name } }
|
||||
: { pos: slot.pos }
|
||||
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
...widgetOrPos,
|
||||
link,
|
||||
}
|
||||
}
|
||||
|
||||
export function outputAsSerialisable(slot: INodeOutputSlot & { widget?: IWidget }): ISerialisableNodeOutput {
|
||||
const { pos, slot_index, links, widget } = slot
|
||||
// Output widgets do not exist in Litegraph; this is a temporary downstream workaround.
|
||||
const outputWidget = widget
|
||||
? { widget: { name: widget.name } }
|
||||
: null
|
||||
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
...outputWidget,
|
||||
pos,
|
||||
slot_index,
|
||||
links,
|
||||
}
|
||||
}
|
||||
|
||||
export function isINodeInputSlot(slot: INodeSlot): slot is INodeInputSlot {
|
||||
return "link" in slot
|
||||
}
|
||||
|
||||
export function isINodeOutputSlot(slot: INodeSlot): slot is INodeOutputSlot {
|
||||
return "links" in slot
|
||||
}
|
||||
|
||||
/**
|
||||
* Type guard: Whether this input slot is attached to a widget.
|
||||
* @param slot The slot to check.
|
||||
*/
|
||||
|
||||
export function isWidgetInputSlot(slot: INodeInputSlot): slot is IWidgetInputSlot {
|
||||
return !!slot.widget
|
||||
}
|
||||
|
||||
export function toNodeSlotClass(slot: INodeInputSlot | INodeOutputSlot): NodeInputSlot | NodeOutputSlot {
|
||||
if (slot instanceof NodeInputSlot || slot instanceof NodeOutputSlot) return slot
|
||||
|
||||
return "link" in slot
|
||||
? new NodeInputSlot(slot)
|
||||
: new NodeOutputSlot(slot)
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import { describe, expect } from "vitest"
|
||||
|
||||
import { LGraphNode, LiteGraph } from "@/litegraph"
|
||||
import { LGraph } from "@/litegraph"
|
||||
import { NodeInputSlot, NodeOutputSlot } from "@/NodeSlot"
|
||||
import { NodeInputSlot } from "@/node/NodeInputSlot"
|
||||
import { NodeOutputSlot } from "@/node/NodeOutputSlot"
|
||||
|
||||
import { test } from "./testExtensions"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { INodeInputSlot, INodeOutputSlot } from "@/interfaces"
|
||||
import { inputAsSerialisable, outputAsSerialisable } from "@/NodeSlot"
|
||||
import { inputAsSerialisable, outputAsSerialisable } from "@/node/slotUtils"
|
||||
|
||||
describe("NodeSlot", () => {
|
||||
describe("inputAsSerialisable", () => {
|
||||
|
||||
Reference in New Issue
Block a user