nodes-ui-flow / src /nodes /ScriptFlowNode.jsx
markitzeroo's picture
Initial deploy: Dockerized FastAPI + React frontend
cfaaa6c verified
import { memo, useEffect } from 'react';
import { useUpdateNodeInternals } from '@xyflow/react';
import NodeShell from '../components/NodeShell.jsx';
import { useWorkflow } from '../context/WorkflowContext.jsx';
import { createBrowserId } from '../lib/ids.js';
import { getNodeAccent } from '../lib/nodeRegistry.js';
function ScriptFlowNode({ id, data, selected, type }) {
const { getNodeHandles, replaceNodeData, removeHandleConnections } = useWorkflow();
const updateNodeInternals = useUpdateNodeInternals();
const handles = getNodeHandles(type, data);
const runtime = data.runtime || {};
useEffect(() => {
updateNodeInternals(id);
}, [id, data.entries.length, data.hasScriptOutput, updateNodeInternals]);
const addEntry = (kind) => {
replaceNodeData(id, (current) => ({
...current,
entries: [...current.entries, { id: createBrowserId(`${kind}-slot`), kind }],
}));
};
const removeEntry = (entryId) => {
removeHandleConnections(id, entryId, 'target');
replaceNodeData(id, (current) => ({
...current,
entries: current.entries.filter((entry) => entry.id !== entryId),
}));
};
const toggleOutput = () => {
if (data.hasScriptOutput) {
removeHandleConnections(id, 'script-text', 'source');
}
replaceNodeData(id, (current) => ({
...current,
hasScriptOutput: !current.hasScriptOutput,
}));
};
return (
<NodeShell
nodeId={id}
title={data.title}
accent={getNodeAccent(type)}
selected={selected}
status={runtime.status}
inputs={handles.inputs}
outputs={handles.outputs}
className="node-shell--script"
bodyClassName="node-shell__body--script"
renderInputAddon={(input, index) =>
index === 0 ? null : (
<button type="button" className="nodrag node-shell__port-remove" onClick={() => removeEntry(input.id)}>
x
</button>
)
}
footer={
<div className="script-node__footer-buttons">
<button
type="button"
className="nodrag node-button node-button--ghost"
onClick={() => addEntry('user')}
disabled={data.hasScriptOutput}
>
+ User
</button>
<button
type="button"
className="nodrag node-button node-button--ghost"
onClick={() => addEntry('character')}
disabled={data.hasScriptOutput}
>
+ Character
</button>
<button
type="button"
className={`nodrag node-button ${data.hasScriptOutput ? 'is-active' : ''}`}
onClick={toggleOutput}
>
Output
</button>
</div>
}
>
<div className="script-node__spacer" />
{runtime.error ? <div className="node-error">{runtime.error}</div> : null}
</NodeShell>
);
}
export default memo(ScriptFlowNode);