/** * Use as second argument to React.memo() for node components. * Skips re-render when only position (or other unrelated props) changed, * so dragging one node doesn't force other nodes to re-render. */ import { getIdPrefix, getDefaultDataForType as getDefaultDataFromRegistry, getResetDataForType as getResetDataFromRegistry, getDefaultStyle, } from './nodeRegistry' export function nodePropsAreEqual

( prev: P, next: P ): boolean { return ( prev.id === next.id && prev.data === next.data && prev.width === next.width && prev.height === next.height && prev.selected === next.selected ) } /** @deprecated Use getIdPrefix from nodeRegistry for new code. Kept for compatibility. */ export const PREFIX_BY_TYPE: Record = { config: 'cfg_', render: 'rnd_', variable: 'var_', function: 'fn_', } /** Next node id for type: prefix + 3-digit increasing number (001, 002, …). Uses nodeRegistry for prefix when available. */ export function getNextNodeId(type: string, existingIds: string[]): string { const prefix = getIdPrefix(type) const re = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\d+)$`) let max = 0 for (const id of existingIds) { const m = id.match(re) if (m) max = Math.max(max, parseInt(m[1], 10)) } return `${prefix}${String(max + 1).padStart(3, '0')}` } /** Recursively replace oldId with newId in string values (for rename propagation) */ function replaceInData(value: unknown, oldId: string, newId: string): unknown { if (typeof value === 'string') return value.split(oldId).join(newId) if (value === null || typeof value !== 'object') return value if (Array.isArray(value)) return value.map((v) => replaceInData(v, oldId, newId)) const out: Record = {} for (const [k, v] of Object.entries(value)) out[k] = replaceInData(v, oldId, newId) return out } /** Update graph after renaming a node: change node id and all references in data and edges */ export function replaceNodeIdInGraph( nodes: Array<{ id: string; data?: any; [k: string]: any }>, edges: Array<{ id: string; source: string; target: string; [k: string]: any }>, oldId: string, newId: string ): { nodes: typeof nodes; edges: typeof edges } { const newNodes = nodes.map((n) => n.id === oldId ? { ...n, id: newId } : { ...n, data: replaceInData(n.data, oldId, newId) as any } ) const newEdges = edges.map((e) => ({ ...e, id: e.id.includes(oldId) ? e.id.split(oldId).join(newId) : e.id, source: e.source === oldId ? newId : e.source, target: e.target === oldId ? newId : e.target, })) return { nodes: newNodes, edges: newEdges } } /** @deprecated Use getDefaultStyle from nodeRegistry. Kept for compatibility. */ export const DEFAULT_NODE_STYLE: Record = { config: { width: 320, height: 320 }, render: { width: 384, height: 320 }, variable: { width: 224, height: 180 }, function: { width: 288, height: 260 }, } /** Default data for a new node. Uses nodeRegistry when type is registered. */ export function getDefaultDataForType(type: string, newId?: string): any { return getDefaultDataFromRegistry(type, newId) } /** Data for Reset action. Uses nodeRegistry when type is registered. */ export function getResetDataForType(type: string, nodeId?: string): any { return getResetDataFromRegistry(type, nodeId) } /** Default style for a type. Uses nodeRegistry when type is registered. */ export function getDefaultStyleForType(type: string): { width: number; height: number } { return getDefaultStyle(type) }