@selenite/graph-editor
Version:
A graph editor for visual programming, based on rete and svelte.
100 lines (99 loc) • 3.48 kB
JavaScript
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);
}
}