refactor: extract litegraph types to dedicated types directory

- Create src/lib/litegraph/types/ with pure type definitions
- Extract geometry, slot, id, and utility types
- Create type-only barrel for importing without runtime deps
- Update interfaces.ts to import from types/ and re-export
- Move utility.ts from src/types to types/ (consolidate)
- Enables type-only imports to reduce circular dependencies

Amp-Thread-ID: https://ampcode.com/threads/T-019bfe73-6a29-7638-8160-8de515af8707
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Alexander Brown
2026-01-27 00:15:33 -08:00
parent f01da3c462
commit a7bf3f38b7
9 changed files with 361 additions and 184 deletions

View File

@@ -99,7 +99,7 @@ import type {
ISerialisedNode,
SubgraphIO
} from './types/serialisation'
import type { NeverNever, PickNevers } from './types/utility'
import type { NeverNever, PickNevers } from '../types/utility'
import type { IBaseWidget, TWidgetValue } from './types/widgets'
import { alignNodes, distributeNodes, getBoundaryNodes } from './utils/arrange'
import { findFirstNode, getAllNestedItems } from './utils/collections'

View File

@@ -1,4 +1,4 @@
import type { NeverNever, PickNevers } from '@/lib/litegraph/src/types/utility'
import type { NeverNever, PickNevers } from '@/lib/litegraph/types/utility'
type EventListeners<T> = {
readonly [K in keyof T]:

View File

@@ -3,65 +3,72 @@ import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import type { TWidgetValue } from '@/lib/litegraph/src/types/widgets'
import type { ContextMenu } from './ContextMenu'
import type { LGraphNode, NodeId } from './LGraphNode'
import type { LLink, LinkId } from './LLink'
import type { Reroute, RerouteId } from './Reroute'
import type { LGraphNode } from './LGraphNode'
import type { LLink } from './LLink'
import type { Reroute } from './Reroute'
import type { SubgraphInput } from './subgraph/SubgraphInput'
import type { SubgraphInputNode } from './subgraph/SubgraphInputNode'
import type { SubgraphOutputNode } from './subgraph/SubgraphOutputNode'
import type { LinkDirection, RenderShape } from './types/globalEnums'
import type { LinkDirection } from './types/globalEnums'
import type { IBaseWidget } from './types/widgets'
export type Dictionary<T> = { [key: string]: T }
// Re-export pure types from the types directory for backwards compatibility
export type {
CanvasColour,
CompassCorners,
Direction,
Point,
ReadOnlyRect,
ReadOnlyTypedArray,
Rect,
Size
} from '../types/geometry'
/** Allows all properties to be null. The same as `Partial<T>`, but adds null instead of undefined. */
export type NullableProperties<T> = {
[P in keyof T]: T[P] | null
export type { LinkId, NodeId, RerouteId } from '../types/ids'
export type {
HasBoundingRect,
INodeFlags,
INodeSlotBase,
ISlotType,
IWidgetLocator
} from '../types/slots'
export type {
Dictionary,
MethodNames,
NullableProperties,
OptionalProps,
RequiredProps,
SharedIntersection,
WhenNullish
} from '../types/utility'
// Import types we need locally
import type { CanvasColour, Point, Size } from '../types/geometry'
import type { LinkId, NodeId, RerouteId } from '../types/ids'
import type {
HasBoundingRect,
INodeInputSlotBase,
INodeOutputSlotBase,
INodeSlotBase,
ISlotType,
IWidgetLocator
} from '../types/slots'
export interface NewNodePosition {
node: LGraphNode
newPos: {
x: number
y: number
}
}
/**
* If {@link T} is `null` or `undefined`, evaluates to {@link Result}. Otherwise, evaluates to {@link T}.
* Useful for functions that return e.g. `undefined` when a param is nullish.
*/
export type WhenNullish<T, Result> =
| (T & {})
| (T extends null ? Result : T extends undefined ? Result : T & {})
/** A type with each of the {@link Properties} made optional. */
export type OptionalProps<T, Properties extends keyof T> = Omit<
T,
Properties
> & { [K in Properties]?: T[K] }
/** A type with each of the {@link Properties} marked as required. */
export type RequiredProps<T, Properties extends keyof T> = Omit<
T,
Properties
> & { [K in Properties]-?: T[K] }
/** Bitwise AND intersection of two types; returns a new, non-union type that includes only properties that exist on both types. */
export type SharedIntersection<T1, T2> = {
[P in keyof T1 as P extends keyof T2 ? P : never]: T1[P]
} & {
[P in keyof T2 as P extends keyof T1 ? P : never]: T2[P]
}
export type CanvasColour = string | CanvasGradient | CanvasPattern
/**
* Any object that has a {@link boundingRect}.
*/
export interface HasBoundingRect {
/**
* A rectangle that represents the outer edges of the item.
*
* Used for various calculations, such as overlap, selective rendering, and click checks.
* For most items, this is cached position & size as `x, y, width, height`.
* Some items (such as nodes and slots) may extend above and/or to the left of their {@link pos}.
* @readonly
* @see {@link move}
*/
readonly boundingRect: ReadOnlyRect
export interface IBoundaryNodes {
top: LGraphNode
right: LGraphNode
bottom: LGraphNode
left: LGraphNode
}
/** An object containing a set of child objects */
@@ -226,130 +233,22 @@ export interface IFoundSlot extends IInputOrOutput {
link_pos: Point
}
/** A point represented as `[x, y]` co-ordinates */
export type Point = [x: number, y: number]
/** A size represented as `[width, height]` */
export type Size = [width: number, height: number]
/** A rectangle starting at top-left coordinates `[x, y, width, height]` */
export type Rect =
| [x: number, y: number, width: number, height: number]
| Float64Array
/** A rectangle starting at top-left coordinates `[x, y, width, height]` that will not be modified */
export type ReadOnlyRect =
| readonly [x: number, y: number, width: number, height: number]
| ReadOnlyTypedArray<Float64Array>
export type ReadOnlyTypedArray<T extends Float64Array> = Omit<
Readonly<T>,
'fill' | 'copyWithin' | 'reverse' | 'set' | 'sort' | 'subarray'
>
/** Union of property names that are of type Match */
type KeysOfType<T, Match> = Exclude<
{ [P in keyof T]: T[P] extends Match ? P : never }[keyof T],
undefined
>
/** The names of all (optional) methods and functions in T */
export type MethodNames<T> = KeysOfType<
T,
((...args: unknown[]) => unknown) | undefined
>
export interface NewNodePosition {
node: LGraphNode
newPos: {
x: number
y: number
}
}
export interface IBoundaryNodes {
top: LGraphNode
right: LGraphNode
bottom: LGraphNode
left: LGraphNode
}
export type Direction = 'top' | 'bottom' | 'left' | 'right'
/** Resize handle positions (compass points) */
export type CompassCorners = 'NE' | 'SE' | 'SW' | 'NW'
/**
* A string that represents a specific data / slot type, e.g. `STRING`.
*
* Can be comma-delimited to specify multiple allowed types, e.g. `STRING,INT`.
* Full slot interface with runtime-dependent properties.
* Extends the base slot from types/slots.ts with LLink reference.
*/
export type ISlotType = number | string
export interface INodeSlot extends HasBoundingRect {
/**
* The name of the slot in English.
* Will be included in the serialized data.
*/
name: string
/**
* The localized name of the slot to display in the UI.
* Takes higher priority than {@link name} if set.
* Will be included in the serialized data.
*/
localized_name?: string
/**
* The name of the slot to display in the UI, modified by the user.
* Takes higher priority than {@link display_name} if set.
* Will be included in the serialized data.
*/
label?: string
type: ISlotType
dir?: LinkDirection
removable?: boolean
shape?: RenderShape
color_off?: CanvasColour
color_on?: CanvasColour
locked?: boolean
nameLocked?: boolean
pos?: Point
/** @remarks Automatically calculated; not included in serialisation. */
boundingRect: ReadOnlyRect
export interface INodeSlot extends INodeSlotBase {
/**
* A list of floating link IDs that are connected to this slot.
* This is calculated at runtime; it is **not** serialized.
*/
_floatingLinks?: Set<LLink>
/**
* Whether the slot has errors. It is **not** serialized.
*/
hasErrors?: boolean
}
export interface INodeFlags {
skip_repeated_outputs?: boolean
allow_interaction?: boolean
pinned?: boolean
collapsed?: boolean
/** Configuration setting for {@link LGraphNode.connectInputToOutput} */
keepAllLinksOnBypass?: boolean
}
/**
* A widget that is linked to a slot.
*
* This is set by the ComfyUI_frontend logic. See
* https://github.com/Comfy-Org/ComfyUI_frontend/blob/b80e0e1a3c74040f328c4e344326c969c97f67e0/src/extensions/core/widgetInputs.ts#L659
* Full input slot interface with runtime-dependent properties.
*/
export interface IWidgetLocator {
name: string
type?: string
}
export interface INodeInputSlot extends INodeSlot {
link: LinkId | null
widget?: IWidgetLocator
alwaysVisible?: boolean
export interface INodeInputSlot extends INodeInputSlotBase, INodeSlot {
/**
* Internal use only; API is not finalised and may change at any time.
*/
@@ -360,11 +259,10 @@ export interface IWidgetInputSlot extends INodeInputSlot {
widget: IWidgetLocator
}
export interface INodeOutputSlot extends INodeSlot {
links: LinkId[] | null
_data?: unknown
slot_index?: number
}
/**
* Full output slot interface with runtime-dependent properties.
*/
export interface INodeOutputSlot extends INodeOutputSlotBase, INodeSlot {}
/** Links */
export interface ConnectingLink extends IInputOrOutput {

View File

@@ -1,13 +0,0 @@
/**
* General-purpose, TypeScript utility types.
*/
/** {@link Pick} only properties that evaluate to `never`. */
export type PickNevers<T> = {
[K in keyof T as T[K] extends never ? K : never]: T[K]
}
/** {@link Omit} all properties that evaluate to `never`. */
export type NeverNever<T> = {
[K in keyof T as T[K] extends never ? never : K]: T[K]
}

View File

@@ -0,0 +1,40 @@
/**
* Pure geometry types for litegraph.
* These have no dependencies on runtime code.
*/
/** A point represented as `[x, y]` co-ordinates */
export type Point = [x: number, y: number]
/** A size represented as `[width, height]` */
export type Size = [width: number, height: number]
/** A rectangle starting at top-left coordinates `[x, y, width, height]` */
export type Rect =
| [x: number, y: number, width: number, height: number]
| Float64Array
/** A rectangle starting at top-left coordinates `[x, y, width, height]` that will not be modified */
export type ReadOnlyRect =
| readonly [x: number, y: number, width: number, height: number]
| ReadOnlyTypedArray<Float64Array>
export type ReadOnlyTypedArray<T extends Float64Array> = Omit<
Readonly<T>,
'fill' | 'copyWithin' | 'reverse' | 'set' | 'sort' | 'subarray'
>
/** A 2D vector */
export type Vector2 = [x: number, y: number]
/** A 4D vector */
export type Vector4 = [x: number, y: number, z: number, w: number]
/** Direction as cardinal points */
export type Direction = 'top' | 'bottom' | 'left' | 'right'
/** Resize handle positions (compass points) */
export type CompassCorners = 'NE' | 'SE' | 'SW' | 'NW'
/** A color value for canvas rendering */
export type CanvasColour = string | CanvasGradient | CanvasPattern

View File

@@ -0,0 +1,25 @@
/**
* ID types for litegraph entities.
* These are branded types that can be used without importing runtime modules.
*/
/**
* Node ID type.
* @remarks Re-exported from LGraphNode for backwards compatibility,
* but defined here to avoid circular imports.
*/
export type NodeId = number | string
/**
* Link ID type.
* @remarks Re-exported from LLink for backwards compatibility,
* but defined here to avoid circular imports.
*/
export type LinkId = number
/**
* Reroute ID type.
* @remarks Re-exported from Reroute for backwards compatibility,
* but defined here to avoid circular imports.
*/
export type RerouteId = number

View File

@@ -0,0 +1,54 @@
/**
* Type-only barrel export for litegraph types.
*
* This module exports pure type definitions that can be imported
* without pulling in any runtime code, helping to avoid circular
* dependency issues.
*
* @example
* ```typescript
* import type { Point, NodeId, INodeSlot } from '@/lib/litegraph/types'
* ```
*/
// Geometry types
export type {
CanvasColour,
CompassCorners,
Direction,
Point,
ReadOnlyRect,
ReadOnlyTypedArray,
Rect,
Size,
Vector2,
Vector4
} from './geometry'
// ID types
export type { LinkId, NodeId, RerouteId } from './ids'
// Slot types
export type {
HasBoundingRect,
INodeFlags,
INodeInputSlotBase,
INodeOutputSlotBase,
INodeSlotBase,
ISlotType,
IWidgetInputSlotBase,
IWidgetLocator
} from './slots'
// Utility types
export type {
Dictionary,
MethodNames,
NeverNever,
NullableProperties,
OptionalProps,
PickNevers,
RequiredProps,
SharedIntersection,
WhenNullish
} from './utility'

View File

@@ -0,0 +1,113 @@
/**
* Slot-related types for litegraph.
* These have minimal dependencies and can be imported without pulling in runtime code.
*/
import type { LinkDirection, RenderShape } from '../src/types/globalEnums'
import type { CanvasColour, Point, ReadOnlyRect } from './geometry'
import type { LinkId } from './ids'
/**
* A string that represents a specific data / slot type, e.g. `STRING`.
*
* Can be comma-delimited to specify multiple allowed types, e.g. `STRING,INT`.
*/
export type ISlotType = number | string
/**
* Any object that has a {@link boundingRect}.
*/
export interface HasBoundingRect {
/**
* A rectangle that represents the outer edges of the item.
*
* Used for various calculations, such as overlap, selective rendering, and click checks.
* For most items, this is cached position & size as `x, y, width, height`.
* Some items (such as nodes and slots) may extend above and/or to the left of their {@link pos}.
* @readonly
* @see {@link move}
*/
readonly boundingRect: ReadOnlyRect
}
/**
* Base slot interface without runtime-dependent properties.
* The full INodeSlot in interfaces.ts extends this with runtime properties.
*/
export interface INodeSlotBase extends HasBoundingRect {
/**
* The name of the slot in English.
* Will be included in the serialized data.
*/
name: string
/**
* The localized name of the slot to display in the UI.
* Takes higher priority than {@link name} if set.
* Will be included in the serialized data.
*/
localized_name?: string
/**
* The name of the slot to display in the UI, modified by the user.
* Takes higher priority than {@link display_name} if set.
* Will be included in the serialized data.
*/
label?: string
type: ISlotType
dir?: LinkDirection
removable?: boolean
shape?: RenderShape
color_off?: CanvasColour
color_on?: CanvasColour
locked?: boolean
nameLocked?: boolean
pos?: Point
/** @remarks Automatically calculated; not included in serialisation. */
boundingRect: ReadOnlyRect
/**
* Whether the slot has errors. It is **not** serialized.
*/
hasErrors?: boolean
}
/**
* A widget that is linked to a slot.
*
* This is set by the ComfyUI_frontend logic. See
* https://github.com/Comfy-Org/ComfyUI_frontend/blob/b80e0e1a3c74040f328c4e344326c969c97f67e0/src/extensions/core/widgetInputs.ts#L659
*/
export interface IWidgetLocator {
name: string
type?: string
}
/**
* Base input slot interface without runtime-dependent properties.
*/
export interface INodeInputSlotBase extends INodeSlotBase {
link: LinkId | null
widget?: IWidgetLocator
alwaysVisible?: boolean
}
export interface IWidgetInputSlotBase extends INodeInputSlotBase {
widget: IWidgetLocator
}
/**
* Base output slot interface without runtime-dependent properties.
*/
export interface INodeOutputSlotBase extends INodeSlotBase {
links: LinkId[] | null
_data?: unknown
slot_index?: number
}
export interface INodeFlags {
skip_repeated_outputs?: boolean
allow_interaction?: boolean
pinned?: boolean
collapsed?: boolean
/** Configuration setting for {@link LGraphNode.connectInputToOutput} */
keepAllLinksOnBypass?: boolean
}

View File

@@ -0,0 +1,60 @@
/**
* General-purpose TypeScript utility types for litegraph.
* These have no dependencies on runtime code.
*/
export type Dictionary<T> = { [key: string]: T }
/** Allows all properties to be null. The same as `Partial<T>`, but adds null instead of undefined. */
export type NullableProperties<T> = {
[P in keyof T]: T[P] | null
}
/**
* If {@link T} is `null` or `undefined`, evaluates to {@link Result}. Otherwise, evaluates to {@link T}.
* Useful for functions that return e.g. `undefined` when a param is nullish.
*/
export type WhenNullish<T, Result> =
| (T & {})
| (T extends null ? Result : T extends undefined ? Result : T & {})
/** A type with each of the {@link Properties} made optional. */
export type OptionalProps<T, Properties extends keyof T> = Omit<
T,
Properties
> & { [K in Properties]?: T[K] }
/** A type with each of the {@link Properties} marked as required. */
export type RequiredProps<T, Properties extends keyof T> = Omit<
T,
Properties
> & { [K in Properties]-?: T[K] }
/** Bitwise AND intersection of two types; returns a new, non-union type that includes only properties that exist on both types. */
export type SharedIntersection<T1, T2> = {
[P in keyof T1 as P extends keyof T2 ? P : never]: T1[P]
} & {
[P in keyof T2 as P extends keyof T1 ? P : never]: T2[P]
}
/** Union of property names that are of type Match */
type KeysOfType<T, Match> = Exclude<
{ [P in keyof T]: T[P] extends Match ? P : never }[keyof T],
undefined
>
/** The names of all (optional) methods and functions in T */
export type MethodNames<T> = KeysOfType<
T,
((...args: unknown[]) => unknown) | undefined
>
/** {@link Pick} only properties that evaluate to `never`. */
export type PickNevers<T> = {
[K in keyof T as T[K] extends never ? K : never]: T[K]
}
/** {@link Omit} all properties that evaluate to `never`. */
export type NeverNever<T> = {
[K in keyof T as T[K] extends never ? never : K]: T[K]
}