UNPKG

@selenite/graph-editor

Version:

A graph editor for visual programming, based on rete and svelte.

100 lines (99 loc) 3.48 kB
import { NodeEditor, Scope } from 'rete'; import { Dataflow, createCancellblePromise } from 'rete-engine'; import { Cache } from './utils/Cache'; /** * DataflowEngine is a plugin that integrates Dataflow with NodeEditor making it easy to use. * Additionally, it provides a cache for the data of each node in order to avoid recurring calculations. * @priority 10 * @listens nodecreated * @listens noderemoved * */ export class PythonDataflowEngine extends Scope { configure; editor; dataflow; cache = new Cache((data) => data?.cancel && data.cancel()); constructor(configure) { super('dataflow-engine'); this.configure = configure; this.addPipe((context) => { if (context.type === 'nodecreated') { this.add(context.data); } if (context.type === 'noderemoved') { this.remove(context.data); } return context; }); } setParent(scope) { super.setParent(scope); this.editor = this.parentScope(NodeEditor); this.dataflow = new Dataflow(this.editor); } getDataflow() { if (!this.dataflow) throw new Error(`DataflowEngine isn't attached to NodeEditor`); return this.dataflow; } add(node) { const options = this.configure ? this.configure(node) : { inputs: () => Object.keys(node.inputs), outputs: () => Object.keys(node.outputs) }; this.getDataflow().add(node, { inputs: options.inputs, outputs: options.outputs, data: async (fetchInputs) => { const cache = this.cache.get(node.id); if (cache) return cache; const cancellable = createCancellblePromise(() => fetchInputs(), (inputs) => node.pythonComponent.data(inputs)); this.cache.add(node.id, cancellable); return cancellable; } }); } remove(node) { this.getDataflow().remove(node.id); } /** * Resets the cache of the node and all its predecessors. * @param nodeId Node id to reset. If not specified, all nodes will be reset. */ reset(nodeId, alreadyResetNodes = new Set()) { if (nodeId) { if (alreadyResetNodes.has(nodeId)) return; const setup = this.getDataflow().setups.get(nodeId); if (!setup) throw 'setup'; const outputKeys = setup.outputs(); const node = this.editor.getNode(nodeId); this.cache.delete(nodeId); alreadyResetNodes.add(nodeId); Object.values(node.outConnections) .flat() .forEach((c) => this.reset(c.target, alreadyResetNodes)); } else { this.cache.clear(); } } /** * Fetches input data for the node by fetching data for all its predecessors recursively. * @param nodeId Node id to fetch input data for * @throws `Cancelled when `reset` is called while fetching data */ async fetchInputs(nodeId) { return this.getDataflow().fetchInputs(nodeId); } /** * Fetches output data of the node * @param nodeId Node id to fetch data from * @throws `Cancelled` when `reset` is called while fetching data */ async fetch(nodeId) { return this.getDataflow().fetch(nodeId); } }