Spaces:
Sleeping
Sleeping
| 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); | |