import useRuntimeStore from '../store/runtimeStore' import { runtime } from '../model/StudioRuntime' const GraphService = { // Graph mutations createEdge(edge: { id?: string; source: string; sourceHandle?: string; target: string; targetHandle?: string, style?: any, animated?: boolean }){ const srcHandle = edge.sourceHandle || '' const tgtHandle = edge.targetHandle || '' const strip = (h: string) => h.replace(/^output-/, '').replace(/^input-/, '') const srcPort = strip(srcHandle) || 'output' const tgtPort = strip(tgtHandle) || 'input' const srcModel = runtime.getNodeModel(edge.source) const tgtModel = runtime.getNodeModel(edge.target) if(!srcModel || !tgtModel){ console.warn('GraphService.createEdge: missing source/target model', edge); return null } const srcOutput = srcModel.outputs.find((o: any) => o.name === srcPort) const tgtInput = tgtModel.inputs.find((i: any) => i.name === tgtPort) const srcTypeInfo = srcOutput?.getTypeInfo() || { type: '', dim: 0 } const tgtTypeInfo = tgtInput?.getTypeInfo() || { type: '', dim: 0 } if(srcTypeInfo.type != tgtTypeInfo.type){ console.warn('GraphService.createEdge: type mismatch'); return null } const srcDim = srcTypeInfo.dim + srcModel.upDimension if(srcDim != tgtTypeInfo.dim && srcDim != tgtTypeInfo.dim + 1 && srcDim + 1 != tgtTypeInfo.dim){ console.warn('GraphService.createEdge: dimension mismatch'); return null } // remove existing edges for single-input ports const existing = runtime.getAllEdgesForView().filter(e => e.target === edge.target && (e.targetHandle || 'input') === (edge.targetHandle || 'input')) if(existing.length){ runtime.removeEdgesByTarget(edge.target, tgtPort) } const runtimeEdge = { ...(edge as any), source_port: srcPort, target_port: tgtPort, dimension_mode: 'none', style: edge.style || runtime.graph?.getEdgeStyle(srcTypeInfo.type, srcDim, tgtTypeInfo.dim), animated: !!edge.animated } const createdId = runtime.createEdge(runtimeEdge) return createdId || runtimeEdge.id || null }, removeEdge(edgeId: string){ return runtime.removeEdge(edgeId) }, updateEdgeStyle(edgeId: string, style: Record, animated?: boolean){ const all = runtime.getAllEdgesForView() const found = all.find((e:any) => e.id === edgeId) if(!found) return false const orig = found.data orig.style = style || {} orig.animated = !!animated return true }, deleteEdgesByHandle(edgeId: string){ const all = runtime.getAllEdgesForView() const target = all.find((e:any) => e.id === edgeId) if(!target) return false const toRemove = all.filter((e:any) => e.target === target.target && (e.targetHandle || e.target_port) === (target.targetHandle || target.target_port)) for(const r of toRemove){ runtime.removeEdge(r.id) } return true }, deleteNode(nodeId: string){ if(runtime.graph && typeof runtime.graph.removeNode === 'function') runtime.graph.removeNode(nodeId) const all = runtime.getAllEdgesForView() for(const e of all){ if(e.source === nodeId || e.target === nodeId){ runtime.removeEdge(e.id) } } return true }, duplicateNode(nodeId: string){ const node = runtime.getNodeModel(nodeId) if(!node) return null const newPos = { x: (node.position?.x || 0) + 50, y: (node.position?.y || 0) + 50 } const newId = runtime.createNode(node.schemaName, newPos, { ...(node.params || {}) }, node.meta) return newId }, createNode(schemaName: string, position: {x:number,y:number}, params: Record = {}, meta?: any){ const node = runtime.createNode(schemaName, position, params, meta) return node.id }, updateNodePosition(nodeId: string, position: {x:number,y:number}){ return runtime.updateNodePosition(nodeId, position) }, updateNodeParams(nodeId: string, params: Record){ try{ const node = runtime.getNodeModel(nodeId) if(!node) return false node.params = { ...(node.params || {}), ...(params || {}) } return true }catch(e){ console.error('GraphService.updateNodeParams failed', e); return false } }, disconnectNode(nodeId: string){ const all = runtime.getAllEdgesForView() for(const e of all){ if(e.source === nodeId || e.target === nodeId){ runtime.removeEdge(e.id) } } return true }, exposeParam(nodeId: string, paramName: string, paramType?: string){ return runtime.exposeParamAsInput(nodeId, paramName, paramType) }, unexposeParam(nodeId: string, paramName: string){ return runtime.removeExposedParam(nodeId, paramName) }, // View helpers getNodeModel(id: string) { return runtime.getNodeModel(id) }, getAllNodesForView() { return runtime.getAllNodesForView() }, getAllEdgesForView() { return runtime.getAllEdgesForView ? runtime.getAllEdgesForView() : [] }, subscribeGraphVersion(listener: (v: number) => void){ const unsub = useRuntimeStore.subscribe((s) => s.graphVersion, (v) => listener(v)) return unsub } } export default GraphService