/** * Node graph helpers — Blueprint-style typed pins, edges, validation. */ export type GraphPortSide = 'left' | 'right' | 'top' | 'bottom'; export type PinKind = 'exec' | 'data'; export type PinDataType = 'bool' | 'int' | 'float' | 'string' | 'object' | 'exec' | 'any'; export interface GraphPort { id: string; label?: string; side?: GraphPortSide; /** 0–1 along the side (0.5 = center). */ offset?: number; kind?: PinKind; dataType?: PinDataType; /** Inline literal when the pin is not wired (Blueprint-style). */ value?: unknown; } export interface GraphNodeData { label?: string; description?: string; color?: string; /** Comment box fill */ commentColor?: string; [key: string]: unknown; } export interface GraphNode { id: string; type: string; x: number; y: number; width?: number; height?: number; data?: GraphNodeData; inputs?: GraphPort[]; outputs?: GraphPort[]; } export interface GraphEdge { id: string; source: string; target: string; sourceHandle?: string; targetHandle?: string; label?: string; } export interface GraphDocument { version: 1; nodes: GraphNode[]; edges: GraphEdge[]; } /** * Graph kind / “class context” — like Unreal Blueprint graphs. * Nodes without `contexts` are available in every graph. */ export type GraphNodeContext = 'event_graph' | 'function_graph' | 'macro_graph' | 'construction_script'; export interface GraphNodeTypeDef { type: string; label: string; description?: string; category?: string; color?: string; inputs?: GraphPort[]; outputs?: GraphPort[]; defaultData?: GraphNodeData; width?: number; height?: number; /** If set, node only appears in these graph contexts (Unreal-style). */ contexts?: GraphNodeContext[]; /** Hide from palette (still creatable via API / variable menu). */ hidden?: boolean; } export declare const DEFAULT_NODE_WIDTH = 220; export declare const DEFAULT_NODE_HEIGHT = 80; export declare const PORT_SIZE = 12; /** Half of the pin glyph — wire starts/ends at the rim of the circle/play icon. */ export declare const EDGE_PORT_PAD: number; /** SVG marker tip length (viewBox units ≈ user units with markerWidth 8). */ export declare const EDGE_ARROW_LENGTH = 8; export declare const PIN_TYPE_COLORS: Record; export declare function pinColor(port: GraphPort): string; export declare const DEFAULT_NODE_TYPES: GraphNodeTypeDef[]; /** Unreal-inspired blueprint catalog */ export declare const DEFAULT_BLUEPRINT_NODE_TYPES: GraphNodeTypeDef[]; export declare function emptyGraphDocument(): GraphDocument; export declare function resolveNodeType(type: string, catalog?: GraphNodeTypeDef[]): GraphNodeTypeDef | undefined; export declare function defaultPortsForType(type: string, catalog?: GraphNodeTypeDef[]): { inputs: GraphPort[]; outputs: GraphPort[]; }; export declare function createGraphNode(partial: Partial & { type: string; x: number; y: number; }, catalog?: GraphNodeTypeDef[]): GraphNode; /** Duplicate nodes (+ internal edges), like UE Blueprint Ctrl+W / duplicate. */ export declare function duplicateGraphSelection(doc: GraphDocument, nodeIds: string[], opts?: { offsetX?: number; offsetY?: number; }): { doc: GraphDocument; newNodeIds: string[]; }; export declare function cloneGraphDocument(doc: GraphDocument): GraphDocument; export declare const GRAPH_CLIPBOARD_MIME = "application/x-r2-graph-fragment"; export type GraphClipboardPayload = { mime: typeof GRAPH_CLIPBOARD_MIME; version: 1; fragment: GraphDocument; }; export declare function extractGraphSelection(doc: GraphDocument, nodeIds: string[]): GraphDocument; export declare function encodeGraphClipboard(fragment: GraphDocument): string; export declare function decodeGraphClipboard(raw: string): GraphDocument | null; export declare function pasteGraphSelection(doc: GraphDocument, fragment: GraphDocument, opts?: { offsetX?: number; offsetY?: number; origin?: { x: number; y: number; }; }): { doc: GraphDocument; newNodeIds: string[]; }; export type AlignAxis = 'left' | 'right' | 'top' | 'bottom' | 'centerX' | 'centerY'; export type DistributeAxis = 'horizontal' | 'vertical'; export declare function alignGraphNodes(doc: GraphDocument, nodeIds: string[], align: AlignAxis): GraphDocument; export declare function distributeGraphNodes(doc: GraphDocument, nodeIds: string[], axis: DistributeAxis): GraphDocument; export declare function selectionBounds(doc: GraphDocument, nodeIds: string[], padding?: number): { x: number; y: number; width: number; height: number; } | null; export declare function createCommentFromSelection(doc: GraphDocument, nodeIds: string[], opts?: { label?: string; }): { doc: GraphDocument; commentId: string; } | null; export declare function createRerouteNode(pos: { x: number; y: number; }, port: Pick): GraphNode; export declare function insertRerouteOnEdge(doc: GraphDocument, edgeId: string, pos: { x: number; y: number; }): { doc: GraphDocument; rerouteId: string; } | null; export declare function breakLinksAtHandle(doc: GraphDocument, nodeId: string, handleId: string | null | undefined, handleType: 'source' | 'target' | null | undefined): GraphDocument; export declare function collapseSelectionToFunction(doc: GraphDocument, nodeIds: string[], opts?: { label?: string; }): { doc: GraphDocument; collapsedId: string; } | null; export declare function expandCollapsedGraph(doc: GraphDocument, nodeId: string): { doc: GraphDocument; newNodeIds: string[]; } | null; export declare function createGraphEdge(source: string, target: string, opts?: { label?: string; sourceHandle?: string; targetHandle?: string; id?: string; }): GraphEdge; export declare function serializeGraphDocument(doc: GraphDocument): string; export declare function parseGraphDocument(raw: string | GraphDocument): GraphDocument; export declare function removeNode(doc: GraphDocument, nodeId: string): GraphDocument; export declare function removeEdge(doc: GraphDocument, edgeId: string): GraphDocument; export declare function updateNodePosition(doc: GraphDocument, nodeId: string, x: number, y: number): GraphDocument; export declare function nodeSize(node: GraphNode, fallbackW?: number, fallbackH?: number): { w: number; h: number; }; export declare function nodeCenter(node: GraphNode, width?: number, height?: number): { x: number; y: number; }; export declare function portPosition(node: GraphNode, port: GraphPort, kind: 'input' | 'output', index?: number, total?: number, fallbackW?: number, fallbackH?: number): { x: number; y: number; }; export declare function findPort(node: GraphNode, handleId: string | undefined, kind: 'input' | 'output'): { port: GraphPort; index: number; total: number; } | null; export declare function edgeEndpoints(doc: GraphDocument, edge: GraphEdge, fallbackW?: number, fallbackH?: number): { from: { x: number; y: number; }; to: { x: number; y: number; }; } | null; /** Offset a port-center point along its side normal (away from the node). */ export declare function offsetPortPoint(point: { x: number; y: number; }, side: GraphPortSide, distance: number): { x: number; y: number; }; export declare function isExecPort(port: GraphPort): boolean; export declare function isDataPort(port: GraphPort): boolean; export declare function formatPortLiteral(port: GraphPort): string; export declare function parsePortLiteral(port: GraphPort, raw: string): unknown; export declare function defaultPortLiteral(port: GraphPort): unknown; /** Map nodeId → input handle ids that have an incoming edge. */ export declare function wiredInputIdsByNode(edges: GraphEdge[]): Map>; /** Bezier with endpoints on the pin rim along the side normal (not the chord). */ export declare function edgePathForPorts(from: { x: number; y: number; }, to: { x: number; y: number; }, opts?: { padStart?: number; padEnd?: number; curvature?: number; fromSide?: GraphPortSide; toSide?: GraphPortSide; }): string; /** True if the type exposes at least one input pin compatible with `source`. */ export declare function nodeTypeAcceptsPort(typeDef: GraphNodeTypeDef, source: GraphPort): boolean; export declare function firstCompatibleInput(node: GraphNode, source: GraphPort): GraphPort | null; export declare function edgePathFromPoints(from: { x: number; y: number; }, to: { x: number; y: number; }, curvature?: number): string; export declare function pinsCompatible(source: GraphPort, target: GraphPort): boolean; export type GraphValidationIssue = { id: string; level: 'error' | 'warning'; message: string; edgeId?: string; nodeId?: string; }; export declare function validateGraph(doc: GraphDocument): GraphValidationIssue[]; export declare function snap(value: number, grid: number): number; export declare function fitView(nodes: GraphNode[], viewport: { width: number; height: number; }, padding?: number, fallbackW?: number, fallbackH?: number): { pan: { x: number; y: number; }; scale: number; }; export declare function groupNodeTypesByCategory(types: GraphNodeTypeDef[]): { category: string; types: GraphNodeTypeDef[]; }[]; export declare function nodeTypeAllowedInContext(type: GraphNodeTypeDef, context?: GraphNodeContext | null): boolean; /** Palette / place-menu catalog: context + hide templates. */ export declare function filterNodeTypesForGraph(types: GraphNodeTypeDef[], opts?: { context?: GraphNodeContext | null; includeHidden?: boolean; }): GraphNodeTypeDef[]; /** Blueprint document wrapper */ export interface BlueprintVariable { id: string; name: string; dataType: PinDataType; defaultValue?: unknown; } export declare function variablePinColor(dataType: PinDataType): string; export declare function defaultValueForPinType(dataType: PinDataType): unknown; export declare function createVarGetNode(variable: BlueprintVariable, pos: { x: number; y: number; }): GraphNode; export declare function createVarSetNode(variable: BlueprintVariable, pos: { x: number; y: number; }): GraphNode; /** Place-menu entries for Get/Set of each blueprint variable. */ export declare function variablePlaceItems(variables: BlueprintVariable[]): { id: string; kind: 'get' | 'set'; variable: BlueprintVariable; label: string; color: string; }[]; export interface BlueprintDocument { version: 1; name?: string; graph: GraphDocument; variables?: BlueprintVariable[]; } export declare function emptyBlueprintDocument(name?: string): BlueprintDocument; /** Data payload carried by xyflow custom blueprint nodes. */ export type BlueprintFlowNodeData = { graphType: string; label?: string; description?: string; color?: string; commentColor?: string; inputs: GraphPort[]; outputs: GraphPort[]; width?: number; height?: number; /** Input handle ids that currently have a wire. */ _wiredInputs?: string[]; /** Set a literal on an unwired data input. */ _onLiteral?: (portId: string, value: unknown) => void; [key: string]: unknown; }; export type BlueprintFlowNode = { id: string; type: 'blueprint'; position: { x: number; y: number; }; data: BlueprintFlowNodeData; selected?: boolean; draggable?: boolean; width?: number; height?: number; zIndex?: number; class?: string; }; export type BlueprintFlowEdge = { id: string; source: string; target: string; sourceHandle?: string | null; targetHandle?: string | null; label?: string; selected?: boolean; style?: string; markerEnd?: { type: string; width?: number; height?: number; color?: string; }; }; export declare function toFlowNodes(doc: GraphDocument, catalog?: GraphNodeTypeDef[], selectedIds?: string[]): BlueprintFlowNode[]; export declare function toFlowEdges(doc: GraphDocument, selectedIds?: string[]): BlueprintFlowEdge[]; export declare function fromFlowNodes(nodes: BlueprintFlowNode[]): GraphNode[]; export declare function fromFlowEdges(edges: BlueprintFlowEdge[]): GraphEdge[]; export declare function flowDocumentFromFlow(nodes: BlueprintFlowNode[], edges: BlueprintFlowEdge[]): GraphDocument; /** Resolve a port on a flow node by handle id + direction. */ export declare function flowPortOnNode(node: BlueprintFlowNode | undefined, handleId: string | null | undefined, kind: 'input' | 'output'): GraphPort | null;