@pauloricca/spagbol 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"SpagbolNodeFrame.d.ts","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,QAAQ,EAIT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgF,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnJ,OAAO,EAOL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,WAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BAyB7G;AACD,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,qFAAqF;IACrF,iBAAiB,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAAC;IACpD,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AACD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BAwL5D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,QAAQ,UAAQ,EAChB,SAAS,UAAQ,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,oBAAoB,CAmCtB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,sCAW/D"}
1
+ {"version":3,"file":"SpagbolNodeFrame.d.ts","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,QAAQ,EAIT,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgF,KAAK,aAAa,EAAE,KAAK,GAAG,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnJ,OAAO,EAOL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,cAAc,CAAC;AAKtB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,WAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,oBAAoB,+BA0B7G;AACD,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;IAC9B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC;IAC5C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,qFAAqF;IACrF,iBAAiB,CAAC,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,CAAC;IACpD,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AACD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,+BAyL5D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,QAAQ,UAAQ,EAChB,SAAS,UAAQ,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,oBAAoB,CAkCtB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,EAAE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE,sCAW/D"}
@@ -12,17 +12,19 @@ import { nodeFlowId, sourceHandle, targetHandle } from "./geometry.js";
12
12
  /** Also usable in a completely custom node renderer. */
13
13
  export function SpagbolNodePort({ port, connectable = true, style, position, className }) {
14
14
  const { editor } = useSpagbol();
15
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
16
15
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
17
16
  const flowId = useNodeId();
18
- const selectedLink = mode === 'edit' && selection.linkIds.length === 1
19
- ? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;
20
17
  const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;
21
- const reconnecting = selectedLink && (port.direction === 'output'
22
- ? selectedLink.source.nodeId === nodeId && selectedLink.source.portId === port.id
23
- : selectedLink.target.type === 'port' && selectedLink.target.nodeId === nodeId && selectedLink.target.portId === port.id);
18
+ const reconnecting = useEditorSelector(editor, snapshot => {
19
+ if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1)
20
+ return false;
21
+ const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);
22
+ return !!link && (port.direction === 'output'
23
+ ? link.source.nodeId === nodeId && link.source.portId === port.id
24
+ : link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);
25
+ });
24
26
  const direction = port.direction;
25
- return (_jsx(Handle, { id: direction === "input" ? targetHandle(port.id) : sourceHandle(port.id), type: direction === "input" ? "target" : "source", position: position ?? (direction === "input" ? Position.Left : Position.Right), className: `spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`, isConnectable: editor.editing && port.connectable !== false && connectable, style: style, "aria-label": `${port.label} ${direction}` }));
27
+ return (_jsx(Handle, { id: direction === "input" ? targetHandle(port.id) : sourceHandle(port.id), type: direction === "input" ? "target" : "source", position: position ?? (direction === "input" ? Position.Left : Position.Right), className: `spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`, isConnectable: mode === "edit" && port.connectable !== false && connectable, style: style, "aria-label": `${port.label} ${direction}` }));
26
28
  }
27
29
  export function SpagbolNodeFrame(props) {
28
30
  const { node, selected, projected, children } = props;
@@ -30,7 +32,8 @@ export function SpagbolNodeFrame(props) {
30
32
  const [pointerOver, setPointerOver] = useState(false);
31
33
  const updateInternals = useUpdateNodeInternals();
32
34
  const flowId = useNodeId();
33
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
35
+ const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);
36
+ const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);
34
37
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
35
38
  const path = useEditorSelector(editor, snapshot => snapshot.path);
36
39
  const boundary = isGroupBoundary(node);
@@ -38,7 +41,7 @@ export function SpagbolNodeFrame(props) {
38
41
  const ports = portsFor(node, editor.nodes);
39
42
  const inputPorts = ports.filter((port) => port.direction === "input" && props.portVisible?.(port) !== false);
40
43
  const outputPorts = ports.filter((port) => port.direction === "output" && props.portVisible?.(port) !== false);
41
- const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && selection.nodeIds.length === 1) && !(connecting && pointerOver);
44
+ const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);
42
45
  const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;
43
46
  const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;
44
47
  const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);
@@ -86,7 +89,7 @@ export function SpagbolNodeFrame(props) {
86
89
  }
87
90
  function row(port, index) {
88
91
  const input = port.direction === "input";
89
- return (_jsxs("div", { className: `spagbol-node-port-row spagbol-node-port-row-${port.direction}`, "data-group-port": boundary ? port.id : undefined, style: { gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }, children: [!projected && !props.hidePorts && _jsx(SpagbolNodePort, { port: port }), props.renderPortContent ? props.renderPortContent(port) : _jsxs(_Fragment, { children: [boundary && !projected ? _jsx(SpagbolNodeGroupPort, { name: port.label, editable: editor.editing && !props.readOnly, draggable: editor.editing && !props.readOnly, selected: selection.port?.nodeId === node.id && selection.port.portId === port.id, onChange: label => { try {
92
+ return (_jsxs("div", { className: `spagbol-node-port-row spagbol-node-port-row-${port.direction}`, "data-group-port": boundary ? port.id : undefined, style: { gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }, children: [!projected && !props.hidePorts && _jsx(SpagbolNodePort, { port: port }), props.renderPortContent ? props.renderPortContent(port) : _jsxs(_Fragment, { children: [boundary && !projected ? _jsx(SpagbolNodeGroupPort, { name: port.label, editable: editor.editing && !props.readOnly, draggable: editor.editing && !props.readOnly, selected: selectedPortId === port.id, onChange: label => { try {
90
93
  editor.renameBoundaryPort(node.id, port.id, label);
91
94
  }
92
95
  catch { /* Keep the valid label. */ } }, onPointerDown: event => {
@@ -122,7 +125,6 @@ export function useNodeViewProps(node, selected = false, projected = false, grou
122
125
  const { editor, projection } = useSpagbol();
123
126
  const owner = projection?.groupId ?? groupId;
124
127
  const controlPath = projection?.path ?? [node.id];
125
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
126
128
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
127
129
  const path = useEditorSelector(editor, snapshot => snapshot.path);
128
130
  return useMemo(() => ({
@@ -1 +1 @@
1
- {"version":3,"file":"SpagbolNodeFrame.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"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","sourcesContent":["import { graphPortStates, emptyPortState } from \"./portState.js\";\nimport { SpagbolNodeGroupPortAdd } from \"./SpagbolNodeGroupPortAdd.js\";\nimport { SpagbolNodeGroupPort } from \"./SpagbolNodeGroupPort.js\";\nimport {\n Handle,\n Position,\n NodeResizer,\n useUpdateNodeInternals,\n useNodeId,\n} from \"@xyflow/react\";\nimport { useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent, type CSSProperties, type Ref, type ReactNode } from \"react\";\nimport {\n portsFor,\n graphAt,\n isGroupBoundary,\n spagbolGroupKinds,\n spagbolNewPortId,\n groupPanelLayout,\n type GraphNode,\n type PortDefinition,\n} from \"../core/index.js\";\nimport {\n GraphContext,\n useEditorSelector,\n useSpagbol,\n type SpagbolNodeViewProps,\n} from \"./context.js\";\nimport { SpagbolControlNumber } from \"./SpagbolControlNumber.js\";\nimport { SpagbolNodeFrameTypePicker } from './SpagbolNodeFrameTypePicker.js';\nimport { nodeFlowId, sourceHandle, targetHandle } from \"./geometry.js\";\n\nexport interface SpagbolNodePortProps {\n port: PortDefinition;\n connectable?: boolean;\n style?: CSSProperties;\n position?: Position;\n className?: string;\n}\n/** Also usable in a completely custom node renderer. */\nexport function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {\n const { editor } = useSpagbol();\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const flowId = useNodeId();\n const selectedLink = mode === 'edit' && selection.linkIds.length === 1\n ? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;\n const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;\n const reconnecting = selectedLink && (port.direction === 'output'\n ? selectedLink.source.nodeId === nodeId && selectedLink.source.portId === port.id\n : selectedLink.target.type === 'port' && selectedLink.target.nodeId === nodeId && selectedLink.target.portId === port.id);\n const direction = port.direction;\n return (\n <Handle\n id={direction === \"input\" ? targetHandle(port.id) : sourceHandle(port.id)}\n type={direction === \"input\" ? \"target\" : \"source\"}\n position={position ?? (direction === \"input\" ? Position.Left : Position.Right)}\n className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? \"\"}`}\n isConnectable={\n editor.editing && port.connectable !== false && connectable\n }\n style={style}\n aria-label={`${port.label} ${direction}`}\n />\n );\n}\nexport interface SpagbolNodeFrameProps extends SpagbolNodeViewProps {\n children?: ReactNode;\n overlay?: ReactNode;\n rootRef?: Ref<HTMLDivElement>;\n title?: ReactNode;\n className?: string;\n hidePorts?: boolean;\n headerOutput?: boolean;\n portVisible?(port: PortDefinition): boolean;\n style?: CSSProperties;\n /** Domain widgets inside a shared port row. The canvas owns handles and gestures. */\n renderPortContent?(port: PortDefinition): ReactNode;\n portExtras?: ReactNode;\n portsHeader?: ReactNode;\n forceCompact?: boolean;\n}\nexport function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {\n const { node, selected, projected, children } = props;\n const { editor, views, features, connecting, diagnostics } = useSpagbol();\n const [pointerOver, setPointerOver] = useState(false);\n const updateInternals = useUpdateNodeInternals();\n const flowId = useNodeId();\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n const boundary = isGroupBoundary(node);\n const reorder = useRef<{ pointer: number; portId: string; element: HTMLElement } | null>(null);\n const ports = portsFor(node, editor.nodes);\n const inputPorts = ports.filter((port) => port.direction === \"input\" && props.portVisible?.(port) !== false);\n const outputPorts = ports.filter((port) => port.direction === \"output\" && props.portVisible?.(port) !== false);\n const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && selection.nodeIds.length === 1) && !(connecting && pointerOver);\n const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;\n const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;\n const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);\n const connectedInputs = portState.inputs;\n const connectedOutputs = portState.outputs;\n const visibleInputs = inputPorts.filter(port => port !== headerInput && (!compact || connectedInputs.has(port.id)));\n const visibleOutputs = outputPorts.filter(port => port !== headerOutput && (!compact || connectedOutputs.has(port.id)));\n const definition = editor.nodes.get(node.kind);\n const view = views[node.kind];\n const newPortDirection = boundary ? (node.kind === spagbolGroupKinds.inputs ? \"output\" : \"input\") : definition?.editablePorts;\n const newPortLabel = definition?.newPortLabel ?? (node.kind === spagbolGroupKinds.inputs ? \"new input\" : \"new output\");\n const errors = diagnostics.filter((item) => item.nodeId === node.id);\n const portKey = ports\n .map((port) => `${port.direction}:${port.id}:${port.label}`)\n .join(\"|\");\n useEffect(() => {\n if (projected || !flowId) return;\n const frame = requestAnimationFrame(() => updateInternals(flowId));\n return () => cancelAnimationFrame(frame);\n }, [flowId, portKey, compact, projected, updateInternals]);\n const setInput = portState.replaced;\n function movePort(event: ReactPointerEvent<HTMLDivElement>) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.preventDefault(); event.stopPropagation();\n const rows = Array.from(event.currentTarget.querySelectorAll<HTMLElement>('[data-group-port]'));\n const target = rows.findIndex(row => event.clientY < row.getBoundingClientRect().bottom);\n editor.moveBoundaryPort(node.id, drag.portId, target < 0 ? rows.length - 1 : target);\n }\n function endPort(event: ReactPointerEvent<HTMLDivElement>, cancel = false) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.stopPropagation(); reorder.current = null;\n if (drag.element.hasPointerCapture(event.pointerId)) drag.element.releasePointerCapture(event.pointerId);\n if (cancel) editor.cancelGesture(); else editor.commitGesture();\n }\n function row(port: PortDefinition, index: number) {\n const input = port.direction === \"input\";\n return (\n <div\n className={`spagbol-node-port-row spagbol-node-port-row-${port.direction}`}\n key={`${port.direction}:${port.id}`}\n data-group-port={boundary ? port.id : undefined}\n style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}\n >\n {!projected && !props.hidePorts && <SpagbolNodePort port={port} />}\n {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selection.port?.nodeId === node.id && selection.port.portId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {\n if (!editor.editing || props.readOnly || event.button !== 0) return;\n event.preventDefault(); event.stopPropagation();\n editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });\n editor.beginGesture('Reorder group ports');\n reorder.current = { pointer: event.pointerId, portId: port.id, element: event.currentTarget };\n event.currentTarget.setPointerCapture(event.pointerId);\n }} /> : <span className=\"spagbol-node-port-name\" title={port.label}>{port.label}</span>}\n {((input && node.kind !== spagbolGroupKinds.outputs) || node.kind === spagbolGroupKinds.inputs) && port.valueEditor !== false && (\n <SpagbolControlNumber\n value={node.values[port.id] ?? port.defaultValue ?? 0}\n onChange={(value) => props.setValue(port.id, value)}\n label={`${node.label ?? definition?.label ?? node.kind}: ${port.label}`}\n min={port.min}\n max={port.max}\n step={port.step}\n integer={port.integer}\n disabled={props.readOnly}\n replaced={setInput.has(port.id)}\n onGestureStart={() => props.beginGesture(`Change ${port.label}`)}\n onGestureEnd={(cancel) => props.endGesture(cancel)}\n />\n )}</>}\n </div>\n );\n }\n return (\n <div\n onPointerEnter={() => setPointerOver(true)} onPointerLeave={() => setPointerOver(false)}\n ref={props.rootRef}\n style={{ ...props.style, ...(node.scale ? { zoom: node.scale, width: view?.autoWidth ? props.style?.width ?? \"max-content\" : `${100 / node.scale}%`, height: `${100 / node.scale}%` } : view?.autoWidth ? { width: props.style?.width ?? \"max-content\" } : {}) }}\n onPointerMove={movePort} onPointerUp={event => endPort(event)} onPointerCancel={event => endPort(event, true)}\n className={`spagbol-node-frame${selected ? \" spagbol-node-frame-selected\" : \"\"}${node.enabled === false ? \" spagbol-node-frame-disabled\" : \"\"}${compact ? \" spagbol-node-frame-compact\" : \"\"}${props.forceCompact ? \" spagbol-node-frame-auto-compact\" : \"\"}${projected ? \" spagbol-node-frame-projected nodrag nopan\" : \"\"} ${props.className ?? \"\"}`}\n >\n {!projected && view?.resizable && editor.editing && (\n <NodeResizer\n isVisible={selected && !compact}\n minWidth={view.minWidth ?? 180}\n minHeight={view.minHeight ?? 80}\n onResizeStart={() => props.beginGesture(\"Resize node\")}\n onResize={(_, size) =>\n editor.updateNode(\n node.id,\n {\n size: { width: size.width / (node.scale ?? 1), height: size.height / (node.scale ?? 1) },\n position: { x: size.x, y: size.y },\n },\n \"layout\",\n )\n }\n onResizeEnd={() => props.endGesture()}\n />\n )}\n <div\n style={{ position: \"relative\" }}\n className=\"spagbol-node-frame-header spagbol-canvas-drag-handle\"\n onDoubleClickCapture={event => { if (projected && props.resetOverride) { event.preventDefault(); event.stopPropagation(); props.resetOverride(); } }}\n >\n {!projected && !props.hidePorts && headerInput && <SpagbolNodePort port={headerInput} style={{ top: \"50%\" }} />}\n {!projected && !props.hidePorts && headerOutput && <SpagbolNodePort port={headerOutput} style={{ top: \"50%\" }} />}\n <span className=\"spagbol-node-frame-title\">\n {props.title ?? <SpagbolNodeFrameTypePicker node={node} compact={!!compact} selected={selected} disabled={projected || props.readOnly || !editor.editing} />}\n </span>\n {!projected && <button className=\"spagbol-node-frame-enabled\" type=\"button\" aria-label={node.enabled === false ? 'Enable node' : 'Disable node'} title={node.enabled === false ? 'Enable node' : 'Disable node'} aria-pressed={node.enabled !== false} disabled={!editor.editing || props.readOnly} onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()} onClick={event => { event.preventDefault(); event.stopPropagation(); editor.updateNode(node.id, { enabled: node.enabled === false }, 'structure'); }}><span className=\"spagbol-node-frame-enabled-icon\" aria-hidden=\"true\" /></button>}\n {!projected && features.compact && !props.forceCompact && view?.compact !== false && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label={node.compact ? \"Expand ports\" : \"Compact ports\"}\n title={node.compact ? \"Expand ports\" : \"Compact ports\"}\n disabled={!editor.editing}\n onClick={(event) => {\n event.stopPropagation();\n editor.updateNode(node.id, { compact: !node.compact }, \"layout\");\n }}\n >\n {node.compact ? \"+\" : \"−\"}\n </button>\n )}\n {!projected && node.graph && features.nestedGraphs && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label=\"Enter nested graph\"\n title=\"Enter nested graph\"\n onClick={(event) => {\n event.stopPropagation();\n editor.enter(node.id);\n }}\n >\n ↳\n </button>\n )}\n </div>\n <>\n {children && (\n <div\n className=\"spagbol-node-frame-body nodrag nopan\"\n onPointerDown={(event) => event.stopPropagation()}\n >\n {children}\n </div>\n )}\n {!props.hidePorts && (visibleInputs.length > 0 || visibleOutputs.length > 0 || newPortDirection) && (\n <div style={inputPorts.length === 0 || outputPorts.length === 0 ? { gridTemplateColumns: \"minmax(0, 1fr)\" } : undefined} className={`spagbol-node-frame-ports${boundary ? \" spagbol-node-frame-ports-boundary\" : \"\"}`}>\n {props.portsHeader}\n {visibleInputs.map(row)}\n {visibleOutputs.map(row)}\n {newPortDirection && !projected && editor.editing && <div style={{ gridColumn: newPortDirection === \"input\" || inputPorts.length === 0 ? 1 : 2, gridRow: ports.length + 1 + (props.portsHeader ? 1 : 0), position: \"relative\" }}><SpagbolNodePort port={{ id: spagbolNewPortId, label: newPortLabel, direction: newPortDirection }} style={{ top: \"50%\" }} /><SpagbolNodeGroupPortAdd label={newPortLabel} onAdd={label => { try { editor.addBoundaryPort(node.id, label); } catch { /* Keep the valid boundary. */ } }} /></div>}\n </div>\n )}\n {!compact && props.portExtras}\n {errors.length > 0 && (\n <div\n className=\"spagbol-node-frame-diagnostics\"\n role=\"status\"\n title={errors.map((error) => error.message).join(\"\\n\")}\n >\n {errors[0]?.message}\n </div>\n )}\n </>\n {props.overlay}\n </div>\n );\n}\n\nexport function useNodeViewProps(\n node: GraphNode,\n selected = false,\n projected = false,\n groupId?: string,\n): SpagbolNodeViewProps {\n const { editor, projection } = useSpagbol();\n const owner = projection?.groupId ?? groupId;\n const controlPath = projection?.path ?? [node.id];\n const selection = useEditorSelector(editor, snapshot => snapshot.selection);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n return useMemo(\n () => ({\n node,\n selected,\n projected,\n path: projection ? [...path, projection.groupId, ...projection.path.slice(0, -1)] : path,\n resetOverride: owner ? () => editor.resetOverride(owner, controlPath) : undefined,\n readOnly: mode === \"readonly\",\n setValue: (portId: string, value: number) =>\n owner\n ? editor.setOverride(owner, controlPath, portId, value)\n : editor.setValue(node.id, portId, value),\n patchData: (data: Partial<GraphNode[\"data\"]>, structural = false) => {\n if (projected) { if (!structural && owner) editor.setDataOverride(owner, controlPath, data); return; }\n editor.updateNode(\n node.id,\n { data: { ...node.data, ...data } as GraphNode[\"data\"] },\n structural ? \"structure\" : \"values\",\n );\n },\n beginGesture: (label = \"Edit control\") => {\n if (!editor.getSnapshot().gesture) editor.beginGesture(label);\n },\n endGesture: (cancel = false) =>\n cancel ? editor.cancelGesture() : editor.commitGesture(),\n }),\n [editor, owner, projection, node, projected, selected, mode, path],\n );\n}\n\nexport function SpagbolNodePanel({ group }: { group: GraphNode }) {\n const context = useSpagbol();\n const layout = groupPanelLayout(group);\n if (!layout) return null;\n return <div className=\"spagbol-node-panel nodrag nopan\" style={{ position: 'relative', width: layout.size.width, height: layout.size.height }} aria-label=\"Instance controls\" onPointerDown={event => event.stopPropagation()}>\n {layout.nodes.map(node => <GraphContext.Provider key={node.id} value={{ ...context, projection: { groupId: context.projection?.groupId ?? group.id, path: [...(context.projection?.path ?? []), node.id] } }}>\n <div className=\"spagbol-node-panel-item\" style={{ position: 'absolute', left: node.position.x - layout.position.x, top: node.position.y - layout.position.y, width: context.views[node.kind]?.autoWidth ? \"max-content\" : (node.size?.width ?? 220) * (node.scale ?? 1) }}>\n <SpagbolNodePanelItem node={node} />\n </div>\n </GraphContext.Provider>)}\n </div>;\n}\nfunction SpagbolNodePanelItem({ node }: { node: GraphNode }) {\n const { views } = useSpagbol();\n const props = useNodeViewProps(node, false, true);\n const Renderer = views[node.kind]?.projected ?? views[node.kind]?.render;\n if (Renderer) return <Renderer {...props} />;\n const Body = views[node.kind]?.body;\n return <SpagbolNodeFrame {...props} hidePorts={!!Body}>{Body ? <Body {...props} /> : null}</SpagbolNodeFrame>;\n}\n"]}
1
+ {"version":3,"file":"SpagbolNodeFrame.js","sourceRoot":"","sources":["../../src/react/SpagbolNodeFrame.tsx"],"names":[],"mappings":"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","sourcesContent":["import { graphPortStates, emptyPortState } from \"./portState.js\";\nimport { SpagbolNodeGroupPortAdd } from \"./SpagbolNodeGroupPortAdd.js\";\nimport { SpagbolNodeGroupPort } from \"./SpagbolNodeGroupPort.js\";\nimport {\n Handle,\n Position,\n NodeResizer,\n useUpdateNodeInternals,\n useNodeId,\n} from \"@xyflow/react\";\nimport { useEffect, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent, type CSSProperties, type Ref, type ReactNode } from \"react\";\nimport {\n portsFor,\n graphAt,\n isGroupBoundary,\n spagbolGroupKinds,\n spagbolNewPortId,\n groupPanelLayout,\n type GraphNode,\n type PortDefinition,\n} from \"../core/index.js\";\nimport {\n GraphContext,\n useEditorSelector,\n useSpagbol,\n type SpagbolNodeViewProps,\n} from \"./context.js\";\nimport { SpagbolControlNumber } from \"./SpagbolControlNumber.js\";\nimport { SpagbolNodeFrameTypePicker } from './SpagbolNodeFrameTypePicker.js';\nimport { nodeFlowId, sourceHandle, targetHandle } from \"./geometry.js\";\n\nexport interface SpagbolNodePortProps {\n port: PortDefinition;\n connectable?: boolean;\n style?: CSSProperties;\n position?: Position;\n className?: string;\n}\n/** Also usable in a completely custom node renderer. */\nexport function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {\n const { editor } = useSpagbol();\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const flowId = useNodeId();\n const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;\n const reconnecting = useEditorSelector(editor, snapshot => {\n if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1) return false;\n const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);\n return !!link && (port.direction === 'output'\n ? link.source.nodeId === nodeId && link.source.portId === port.id\n : link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);\n });\n const direction = port.direction;\n return (\n <Handle\n id={direction === \"input\" ? targetHandle(port.id) : sourceHandle(port.id)}\n type={direction === \"input\" ? \"target\" : \"source\"}\n position={position ?? (direction === \"input\" ? Position.Left : Position.Right)}\n className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? \"\"}`}\n isConnectable={\n mode === \"edit\" && port.connectable !== false && connectable\n }\n style={style}\n aria-label={`${port.label} ${direction}`}\n />\n );\n}\nexport interface SpagbolNodeFrameProps extends SpagbolNodeViewProps {\n children?: ReactNode;\n overlay?: ReactNode;\n rootRef?: Ref<HTMLDivElement>;\n title?: ReactNode;\n className?: string;\n hidePorts?: boolean;\n headerOutput?: boolean;\n portVisible?(port: PortDefinition): boolean;\n style?: CSSProperties;\n /** Domain widgets inside a shared port row. The canvas owns handles and gestures. */\n renderPortContent?(port: PortDefinition): ReactNode;\n portExtras?: ReactNode;\n portsHeader?: ReactNode;\n forceCompact?: boolean;\n}\nexport function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {\n const { node, selected, projected, children } = props;\n const { editor, views, features, connecting, diagnostics } = useSpagbol();\n const [pointerOver, setPointerOver] = useState(false);\n const updateInternals = useUpdateNodeInternals();\n const flowId = useNodeId();\n const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);\n const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n const boundary = isGroupBoundary(node);\n const reorder = useRef<{ pointer: number; portId: string; element: HTMLElement } | null>(null);\n const ports = portsFor(node, editor.nodes);\n const inputPorts = ports.filter((port) => port.direction === \"input\" && props.portVisible?.(port) !== false);\n const outputPorts = ports.filter((port) => port.direction === \"output\" && props.portVisible?.(port) !== false);\n const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);\n const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;\n const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;\n const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);\n const connectedInputs = portState.inputs;\n const connectedOutputs = portState.outputs;\n const visibleInputs = inputPorts.filter(port => port !== headerInput && (!compact || connectedInputs.has(port.id)));\n const visibleOutputs = outputPorts.filter(port => port !== headerOutput && (!compact || connectedOutputs.has(port.id)));\n const definition = editor.nodes.get(node.kind);\n const view = views[node.kind];\n const newPortDirection = boundary ? (node.kind === spagbolGroupKinds.inputs ? \"output\" : \"input\") : definition?.editablePorts;\n const newPortLabel = definition?.newPortLabel ?? (node.kind === spagbolGroupKinds.inputs ? \"new input\" : \"new output\");\n const errors = diagnostics.filter((item) => item.nodeId === node.id);\n const portKey = ports\n .map((port) => `${port.direction}:${port.id}:${port.label}`)\n .join(\"|\");\n useEffect(() => {\n if (projected || !flowId) return;\n const frame = requestAnimationFrame(() => updateInternals(flowId));\n return () => cancelAnimationFrame(frame);\n }, [flowId, portKey, compact, projected, updateInternals]);\n const setInput = portState.replaced;\n function movePort(event: ReactPointerEvent<HTMLDivElement>) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.preventDefault(); event.stopPropagation();\n const rows = Array.from(event.currentTarget.querySelectorAll<HTMLElement>('[data-group-port]'));\n const target = rows.findIndex(row => event.clientY < row.getBoundingClientRect().bottom);\n editor.moveBoundaryPort(node.id, drag.portId, target < 0 ? rows.length - 1 : target);\n }\n function endPort(event: ReactPointerEvent<HTMLDivElement>, cancel = false) {\n const drag = reorder.current;\n if (!drag || drag.pointer !== event.pointerId) return;\n event.stopPropagation(); reorder.current = null;\n if (drag.element.hasPointerCapture(event.pointerId)) drag.element.releasePointerCapture(event.pointerId);\n if (cancel) editor.cancelGesture(); else editor.commitGesture();\n }\n function row(port: PortDefinition, index: number) {\n const input = port.direction === \"input\";\n return (\n <div\n className={`spagbol-node-port-row spagbol-node-port-row-${port.direction}`}\n key={`${port.direction}:${port.id}`}\n data-group-port={boundary ? port.id : undefined}\n style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}\n >\n {!projected && !props.hidePorts && <SpagbolNodePort port={port} />}\n {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selectedPortId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {\n if (!editor.editing || props.readOnly || event.button !== 0) return;\n event.preventDefault(); event.stopPropagation();\n editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });\n editor.beginGesture('Reorder group ports');\n reorder.current = { pointer: event.pointerId, portId: port.id, element: event.currentTarget };\n event.currentTarget.setPointerCapture(event.pointerId);\n }} /> : <span className=\"spagbol-node-port-name\" title={port.label}>{port.label}</span>}\n {((input && node.kind !== spagbolGroupKinds.outputs) || node.kind === spagbolGroupKinds.inputs) && port.valueEditor !== false && (\n <SpagbolControlNumber\n value={node.values[port.id] ?? port.defaultValue ?? 0}\n onChange={(value) => props.setValue(port.id, value)}\n label={`${node.label ?? definition?.label ?? node.kind}: ${port.label}`}\n min={port.min}\n max={port.max}\n step={port.step}\n integer={port.integer}\n disabled={props.readOnly}\n replaced={setInput.has(port.id)}\n onGestureStart={() => props.beginGesture(`Change ${port.label}`)}\n onGestureEnd={(cancel) => props.endGesture(cancel)}\n />\n )}</>}\n </div>\n );\n }\n return (\n <div\n onPointerEnter={() => setPointerOver(true)} onPointerLeave={() => setPointerOver(false)}\n ref={props.rootRef}\n style={{ ...props.style, ...(node.scale ? { zoom: node.scale, width: view?.autoWidth ? props.style?.width ?? \"max-content\" : `${100 / node.scale}%`, height: `${100 / node.scale}%` } : view?.autoWidth ? { width: props.style?.width ?? \"max-content\" } : {}) }}\n onPointerMove={movePort} onPointerUp={event => endPort(event)} onPointerCancel={event => endPort(event, true)}\n className={`spagbol-node-frame${selected ? \" spagbol-node-frame-selected\" : \"\"}${node.enabled === false ? \" spagbol-node-frame-disabled\" : \"\"}${compact ? \" spagbol-node-frame-compact\" : \"\"}${props.forceCompact ? \" spagbol-node-frame-auto-compact\" : \"\"}${projected ? \" spagbol-node-frame-projected nodrag nopan\" : \"\"} ${props.className ?? \"\"}`}\n >\n {!projected && view?.resizable && editor.editing && (\n <NodeResizer\n isVisible={selected && !compact}\n minWidth={view.minWidth ?? 180}\n minHeight={view.minHeight ?? 80}\n onResizeStart={() => props.beginGesture(\"Resize node\")}\n onResize={(_, size) =>\n editor.updateNode(\n node.id,\n {\n size: { width: size.width / (node.scale ?? 1), height: size.height / (node.scale ?? 1) },\n position: { x: size.x, y: size.y },\n },\n \"layout\",\n )\n }\n onResizeEnd={() => props.endGesture()}\n />\n )}\n <div\n style={{ position: \"relative\" }}\n className=\"spagbol-node-frame-header spagbol-canvas-drag-handle\"\n onDoubleClickCapture={event => { if (projected && props.resetOverride) { event.preventDefault(); event.stopPropagation(); props.resetOverride(); } }}\n >\n {!projected && !props.hidePorts && headerInput && <SpagbolNodePort port={headerInput} style={{ top: \"50%\" }} />}\n {!projected && !props.hidePorts && headerOutput && <SpagbolNodePort port={headerOutput} style={{ top: \"50%\" }} />}\n <span className=\"spagbol-node-frame-title\">\n {props.title ?? <SpagbolNodeFrameTypePicker node={node} compact={!!compact} selected={selected} disabled={projected || props.readOnly || !editor.editing} />}\n </span>\n {!projected && <button className=\"spagbol-node-frame-enabled\" type=\"button\" aria-label={node.enabled === false ? 'Enable node' : 'Disable node'} title={node.enabled === false ? 'Enable node' : 'Disable node'} aria-pressed={node.enabled !== false} disabled={!editor.editing || props.readOnly} onPointerDown={event => event.stopPropagation()} onDoubleClick={event => event.stopPropagation()} onClick={event => { event.preventDefault(); event.stopPropagation(); editor.updateNode(node.id, { enabled: node.enabled === false }, 'structure'); }}><span className=\"spagbol-node-frame-enabled-icon\" aria-hidden=\"true\" /></button>}\n {!projected && features.compact && !props.forceCompact && view?.compact !== false && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label={node.compact ? \"Expand ports\" : \"Compact ports\"}\n title={node.compact ? \"Expand ports\" : \"Compact ports\"}\n disabled={!editor.editing}\n onClick={(event) => {\n event.stopPropagation();\n editor.updateNode(node.id, { compact: !node.compact }, \"layout\");\n }}\n >\n {node.compact ? \"+\" : \"−\"}\n </button>\n )}\n {!projected && node.graph && features.nestedGraphs && (\n <button\n className=\"spagbol-node-frame-header-button nodrag nopan\"\n aria-label=\"Enter nested graph\"\n title=\"Enter nested graph\"\n onClick={(event) => {\n event.stopPropagation();\n editor.enter(node.id);\n }}\n >\n ↳\n </button>\n )}\n </div>\n <>\n {children && (\n <div\n className=\"spagbol-node-frame-body nodrag nopan\"\n onPointerDown={(event) => event.stopPropagation()}\n >\n {children}\n </div>\n )}\n {!props.hidePorts && (visibleInputs.length > 0 || visibleOutputs.length > 0 || newPortDirection) && (\n <div style={inputPorts.length === 0 || outputPorts.length === 0 ? { gridTemplateColumns: \"minmax(0, 1fr)\" } : undefined} className={`spagbol-node-frame-ports${boundary ? \" spagbol-node-frame-ports-boundary\" : \"\"}`}>\n {props.portsHeader}\n {visibleInputs.map(row)}\n {visibleOutputs.map(row)}\n {newPortDirection && !projected && editor.editing && <div style={{ gridColumn: newPortDirection === \"input\" || inputPorts.length === 0 ? 1 : 2, gridRow: ports.length + 1 + (props.portsHeader ? 1 : 0), position: \"relative\" }}><SpagbolNodePort port={{ id: spagbolNewPortId, label: newPortLabel, direction: newPortDirection }} style={{ top: \"50%\" }} /><SpagbolNodeGroupPortAdd label={newPortLabel} onAdd={label => { try { editor.addBoundaryPort(node.id, label); } catch { /* Keep the valid boundary. */ } }} /></div>}\n </div>\n )}\n {!compact && props.portExtras}\n {errors.length > 0 && (\n <div\n className=\"spagbol-node-frame-diagnostics\"\n role=\"status\"\n title={errors.map((error) => error.message).join(\"\\n\")}\n >\n {errors[0]?.message}\n </div>\n )}\n </>\n {props.overlay}\n </div>\n );\n}\n\nexport function useNodeViewProps(\n node: GraphNode,\n selected = false,\n projected = false,\n groupId?: string,\n): SpagbolNodeViewProps {\n const { editor, projection } = useSpagbol();\n const owner = projection?.groupId ?? groupId;\n const controlPath = projection?.path ?? [node.id];\n const mode = useEditorSelector(editor, snapshot => snapshot.mode);\n const path = useEditorSelector(editor, snapshot => snapshot.path);\n return useMemo(\n () => ({\n node,\n selected,\n projected,\n path: projection ? [...path, projection.groupId, ...projection.path.slice(0, -1)] : path,\n resetOverride: owner ? () => editor.resetOverride(owner, controlPath) : undefined,\n readOnly: mode === \"readonly\",\n setValue: (portId: string, value: number) =>\n owner\n ? editor.setOverride(owner, controlPath, portId, value)\n : editor.setValue(node.id, portId, value),\n patchData: (data: Partial<GraphNode[\"data\"]>, structural = false) => {\n if (projected) { if (!structural && owner) editor.setDataOverride(owner, controlPath, data); return; }\n editor.updateNode(\n node.id,\n { data: { ...node.data, ...data } as GraphNode[\"data\"] },\n structural ? \"structure\" : \"values\",\n );\n },\n beginGesture: (label = \"Edit control\") => {\n if (!editor.getSnapshot().gesture) editor.beginGesture(label);\n },\n endGesture: (cancel = false) =>\n cancel ? editor.cancelGesture() : editor.commitGesture(),\n }),\n [editor, owner, projection, node, projected, selected, mode, path],\n );\n}\n\nexport function SpagbolNodePanel({ group }: { group: GraphNode }) {\n const context = useSpagbol();\n const layout = groupPanelLayout(group);\n if (!layout) return null;\n return <div className=\"spagbol-node-panel nodrag nopan\" style={{ position: 'relative', width: layout.size.width, height: layout.size.height }} aria-label=\"Instance controls\" onPointerDown={event => event.stopPropagation()}>\n {layout.nodes.map(node => <GraphContext.Provider key={node.id} value={{ ...context, projection: { groupId: context.projection?.groupId ?? group.id, path: [...(context.projection?.path ?? []), node.id] } }}>\n <div className=\"spagbol-node-panel-item\" style={{ position: 'absolute', left: node.position.x - layout.position.x, top: node.position.y - layout.position.y, width: context.views[node.kind]?.autoWidth ? \"max-content\" : (node.size?.width ?? 220) * (node.scale ?? 1) }}>\n <SpagbolNodePanelItem node={node} />\n </div>\n </GraphContext.Provider>)}\n </div>;\n}\nfunction SpagbolNodePanelItem({ node }: { node: GraphNode }) {\n const { views } = useSpagbol();\n const props = useNodeViewProps(node, false, true);\n const Renderer = views[node.kind]?.projected ?? views[node.kind]?.render;\n if (Renderer) return <Renderer {...props} />;\n const Body = views[node.kind]?.body;\n return <SpagbolNodeFrame {...props} hidePorts={!!Body}>{Body ? <Body {...props} /> : null}</SpagbolNodeFrame>;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { Node, NodeChange } from "@xyflow/react";
2
+ import type { GraphDocument, Point } from "../core/index.js";
3
+ /** Accumulate only changed native node positions; keep model records immutable. */
4
+ export declare function previewNodePositions<N extends Node>(previous: ReadonlyMap<string, Point>, changes: readonly NodeChange<N>[]): ReadonlyMap<string, Point>;
5
+ /** Apply a completed multi-node drag as one atomic controller edit. */
6
+ export declare function applyNodePositions(graph: GraphDocument, positions: ReadonlyMap<string, Point>): void;
7
+ //# sourceMappingURL=nodeDrag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeDrag.d.ts","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE7D,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,IAAI,EACjD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAChC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAc5B;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GACpC,IAAI,CAKN"}
@@ -0,0 +1,27 @@
1
+ /** Accumulate only changed native node positions; keep model records immutable. */
2
+ export function previewNodePositions(previous, changes) {
3
+ let next;
4
+ for (const change of changes) {
5
+ if (change.type !== "position" || !change.position || !change.id.startsWith("node:"))
6
+ continue;
7
+ const { x, y } = change.position;
8
+ if (!Number.isFinite(x) || !Number.isFinite(y))
9
+ continue;
10
+ const id = change.id.slice(5);
11
+ const before = (next ?? previous).get(id);
12
+ if (before?.x === x && before.y === y)
13
+ continue;
14
+ next ??= new Map(previous);
15
+ next.set(id, { x, y });
16
+ }
17
+ return next ?? previous;
18
+ }
19
+ /** Apply a completed multi-node drag as one atomic controller edit. */
20
+ export function applyNodePositions(graph, positions) {
21
+ for (const node of graph.nodes) {
22
+ const position = positions.get(node.id);
23
+ if (position)
24
+ node.position = { ...position };
25
+ }
26
+ }
27
+ //# sourceMappingURL=nodeDrag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeDrag.js","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAGA,mFAAmF;AACnF,MAAM,UAAU,oBAAoB,CAClC,QAAoC,EACpC,OAAiC;IAEjC,IAAI,IAAoC,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAClF,SAAS;QACX,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QACzD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QAChD,IAAI,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,IAAI,QAAQ,CAAC;AAC1B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAChC,KAAoB,EACpB,SAAqC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAChD,CAAC;AACH,CAAC","sourcesContent":["import type { Node, NodeChange } from \"@xyflow/react\";\nimport type { GraphDocument, Point } from \"../core/index.js\";\n\n/** Accumulate only changed native node positions; keep model records immutable. */\nexport function previewNodePositions<N extends Node>(\n previous: ReadonlyMap<string, Point>,\n changes: readonly NodeChange<N>[],\n): ReadonlyMap<string, Point> {\n let next: Map<string, Point> | undefined;\n for (const change of changes) {\n if (change.type !== \"position\" || !change.position || !change.id.startsWith(\"node:\"))\n continue;\n const { x, y } = change.position;\n if (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n const id = change.id.slice(5);\n const before = (next ?? previous).get(id);\n if (before?.x === x && before.y === y) continue;\n next ??= new Map(previous);\n next.set(id, { x, y });\n }\n return next ?? previous;\n}\n\n/** Apply a completed multi-node drag as one atomic controller edit. */\nexport function applyNodePositions(\n graph: GraphDocument,\n positions: ReadonlyMap<string, Point>,\n): void {\n for (const node of graph.nodes) {\n const position = positions.get(node.id);\n if (position) node.position = { ...position };\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pauloricca/spagbol",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "node scripts/dev.mjs",
@@ -1,3 +1,4 @@
1
+ import { previewNodePositions, applyNodePositions } from "./nodeDrag.js";
1
2
  import { createPortal } from "react-dom";
2
3
  import { SpagbolCanvasLinkControls } from "./SpagbolCanvasLinkControls.js";
3
4
  import { SpagbolCanvasFlow } from "./SpagbolCanvasFlow.js";
@@ -146,6 +147,7 @@ export interface SpagbolCanvasProps extends SpagbolCanvasSlots {
146
147
  controls?: SpagbolCanvasControlsOptions | false;
147
148
  ariaLabel?: string;
148
149
  }
150
+ const emptyFlowNodes: FlowNode[] = [];
149
151
  const emptyViews: Readonly<Record<string, SpagbolNodeView>> = Object.freeze({});
150
152
  const emptyPresentations: Readonly<Record<string, SpagbolLinkPresentation>> =
151
153
  Object.freeze({});
@@ -404,8 +406,8 @@ const SpagbolCanvasLink = memo(function SpagbolCanvasLink(props: EdgeProps<FlowL
404
406
  const flow = useReactFlow();
405
407
  const viewport = useViewport();
406
408
  const [controlsVisible, setControlsVisible] = useState(false);
407
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
408
- const showControls = props.selected && selection.linkIds.length === 1 && editor.editing && !connecting && !props.data?.virtual;
409
+ const selectedLinkId = useEditorSelector(editor, snapshot => snapshot.selection.linkIds.length === 1 ? snapshot.selection.linkIds[0] : undefined);
410
+ const showControls = props.selected && selectedLinkId === props.id && editor.editing && !connecting && !props.data?.virtual;
409
411
  useEffect(() => {
410
412
  if (!showControls) { setControlsVisible(false); return; }
411
413
  const timer = window.setTimeout(() => setControlsVisible(true), 260);
@@ -507,7 +509,9 @@ function SpagbolCanvasInner({
507
509
  const flow = useReactFlow<FlowNode, FlowLink>();
508
510
  const flowStore = useStoreApi();
509
511
  const nodesInitialized = useNodesInitialized();
510
- const flowNodes = useStore((state) => state.nodes);
512
+ // Only cable-property anchors need live native geometry. Subscribing to every
513
+ // position change otherwise causes redundant full-canvas renders during drags.
514
+ const flowNodes = useStore((state) => features.linkProperties ? state.nodes : emptyFlowNodes);
511
515
  // Measurements are canvas state, not saved graph data. Retaining them keeps
512
516
  // React Flow from hiding/reinitializing every node on each drag preview frame.
513
517
  const measuredSizes = useRef(
@@ -559,10 +563,25 @@ function SpagbolCanvasInner({
559
563
  const duplicate = useRef<DuplicateDrag | null>(null);
560
564
  const [duplicatePreview, setDuplicatePreview] =
561
565
  useState<DuplicateDrag | null>(null);
562
- const cancelledDuplicate = useRef(false);
566
+ const cancelledNodeDrag = useRef(false);
567
+ // Position previews belong to the canvas, not the persistent document. A drag
568
+ // commits once, avoiding whole-document edits/validation on every pointer move.
569
+ const nodeDrag = useRef<ReadonlyMap<string, Point> | null>(null);
570
+ const [positionRevision, setPositionRevision] = useState(0);
571
+ const clearNodeDrag = () => {
572
+ nodeDrag.current = null;
573
+ // Force a controlled refresh on cancellation, even though the saved position
574
+ // never changed: React Flow currently holds the transient pointer position.
575
+ setPositionRevision(value => value + 1);
576
+ };
577
+
563
578
  useEffect(() => {
579
+ if (!snapshot.gesture && nodeDrag.current) {
580
+ cancelledNodeDrag.current = true;
581
+ clearNodeDrag();
582
+ }
564
583
  if (!snapshot.gesture && duplicate.current) {
565
- cancelledDuplicate.current = true;
584
+ cancelledNodeDrag.current = true;
566
585
  duplicate.current = null;
567
586
  setDuplicatePreview(null);
568
587
  }
@@ -612,7 +631,7 @@ function SpagbolCanvasInner({
612
631
  if (phase === 'start') {
613
632
  const area = editor.graph.areas.find(area => area.id === id);
614
633
  if (!area) return;
615
- cancelledDuplicate.current = false;
634
+ cancelledNodeDrag.current = false;
616
635
  const fragment = copyFragment(editor.graph, [], [id]);
617
636
  duplicate.current = { namespace: createId('preview'), fragment, nodeId: '', origin: area.position, positions: new Map(fragment.nodes.map(node => [node.id, node.position])), areaPositions: new Map(fragment.areas.map(area => [area.id, area.position])), external };
618
637
  setDuplicatePreview(duplicate.current);
@@ -723,7 +742,7 @@ function SpagbolCanvasInner({
723
742
  (node): FlowNode => ({
724
743
  id: nodeFlowId(node.id),
725
744
  type: "spagbolNode",
726
- position: node.position,
745
+ position: nodeDrag.current?.get(node.id) ?? node.position,
727
746
  selected: snapshot.selection.nodeIds.includes(node.id),
728
747
  hidden: collapsed.nodes.has(node.id),
729
748
  draggable:
@@ -742,6 +761,7 @@ function SpagbolCanvasInner({
742
761
  deletable: false,
743
762
  dragHandle: ".spagbol-canvas-drag-handle",
744
763
  data: {
764
+ positionRevision,
745
765
  node: duplicatePresentation?.originalNodes.get(node.id) ?? node,
746
766
  projected: collapsed.projected.has(node.id),
747
767
  stationaryOriginal: duplicatePresentation?.originalNodes.has(
@@ -770,6 +790,7 @@ function SpagbolCanvasInner({
770
790
  ],
771
791
  [
772
792
  renderGraph,
793
+ positionRevision,
773
794
  duplicatePresentation,
774
795
  snapshot.selection,
775
796
  snapshot.mode,
@@ -836,6 +857,7 @@ function SpagbolCanvasInner({
836
857
  collapsed,
837
858
  flow,
838
859
  measurementKey,
860
+ measurementRevision,
839
861
  settledViewport.zoom,
840
862
  editorSize,
841
863
  props.panBounds,
@@ -1181,7 +1203,12 @@ function SpagbolCanvasInner({
1181
1203
  setDuplicatePreview(next);
1182
1204
  return;
1183
1205
  }
1184
- if (cancelledDuplicate.current) return;
1206
+ if (cancelledNodeDrag.current) return;
1207
+ if (positions.length && nodeDrag.current) {
1208
+ const next = previewNodePositions(nodeDrag.current, positions);
1209
+ if (next !== nodeDrag.current) nodeDrag.current = next;
1210
+ return;
1211
+ }
1185
1212
  if (positions.length)
1186
1213
  editor.edit("Move nodes", "layout", (graph) => {
1187
1214
  for (const change of positions)
@@ -1362,7 +1389,8 @@ function SpagbolCanvasInner({
1362
1389
  const command = event.metaKey || event.ctrlKey,
1363
1390
  key = event.key.toLowerCase();
1364
1391
  if (key === "escape") {
1365
- if (duplicate.current) cancelledDuplicate.current = true;
1392
+ if (duplicate.current || nodeDrag.current) cancelledNodeDrag.current = true;
1393
+ if (nodeDrag.current) clearNodeDrag();
1366
1394
  duplicate.current = null;
1367
1395
  setDuplicatePreview(null);
1368
1396
  editor.cancelGesture();
@@ -1539,7 +1567,8 @@ function SpagbolCanvasInner({
1539
1567
  event.currentTarget.releasePointerCapture(event.pointerId);
1540
1568
  }}
1541
1569
  onPointerCancel={() => {
1542
- if (duplicate.current) cancelledDuplicate.current = true;
1570
+ if (duplicate.current || nodeDrag.current) cancelledNodeDrag.current = true;
1571
+ if (nodeDrag.current) clearNodeDrag();
1543
1572
  duplicate.current = null;
1544
1573
  setDuplicatePreview(null);
1545
1574
  setDrawing(null);
@@ -1569,10 +1598,11 @@ function SpagbolCanvasInner({
1569
1598
  }}
1570
1599
  onNodeDragStart={(event, node) => {
1571
1600
  if (!node.data.node) return;
1572
- cancelledDuplicate.current = false;
1601
+ cancelledNodeDrag.current = false;
1573
1602
  editor.beginGesture(
1574
1603
  event.altKey ? "Duplicate nodes" : "Move nodes",
1575
1604
  );
1605
+ if (!event.altKey) nodeDrag.current = new Map();
1576
1606
  if (event.altKey) {
1577
1607
  const selection = editor.getSnapshot().selection;
1578
1608
  const ids = selection.nodeIds.includes(node.data.node.id)
@@ -1594,11 +1624,13 @@ function SpagbolCanvasInner({
1594
1624
  }
1595
1625
  }}
1596
1626
  onNodeDragStop={(_event, node) => {
1627
+ const positions = nodeDrag.current;
1628
+ if (positions) clearNodeDrag();
1597
1629
  const current = duplicate.current;
1598
1630
  duplicate.current = null;
1599
1631
  setDuplicatePreview(null);
1600
- if (cancelledDuplicate.current) {
1601
- cancelledDuplicate.current = false;
1632
+ if (cancelledNodeDrag.current) {
1633
+ cancelledNodeDrag.current = false;
1602
1634
  editor.cancelGesture();
1603
1635
  editor.select(editor.getSnapshot().selection);
1604
1636
  return;
@@ -1620,6 +1652,8 @@ function SpagbolCanvasInner({
1620
1652
  return;
1621
1653
  }
1622
1654
  }
1655
+ if (positions?.size)
1656
+ editor.edit("Move nodes", "layout", graph => applyNodePositions(graph, positions));
1623
1657
  editor.commitGesture();
1624
1658
  }}
1625
1659
  onConnectStart={(_event, params) => {
@@ -39,15 +39,16 @@ export interface SpagbolNodePortProps {
39
39
  /** Also usable in a completely custom node renderer. */
40
40
  export function SpagbolNodePort({ port, connectable = true, style, position, className }: SpagbolNodePortProps) {
41
41
  const { editor } = useSpagbol();
42
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
43
42
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
44
43
  const flowId = useNodeId();
45
- const selectedLink = mode === 'edit' && selection.linkIds.length === 1
46
- ? editor.graph.links.find(link => link.id === selection.linkIds[0]) : undefined;
47
44
  const nodeId = flowId?.startsWith('node:') ? flowId.slice(5) : undefined;
48
- const reconnecting = selectedLink && (port.direction === 'output'
49
- ? selectedLink.source.nodeId === nodeId && selectedLink.source.portId === port.id
50
- : selectedLink.target.type === 'port' && selectedLink.target.nodeId === nodeId && selectedLink.target.portId === port.id);
45
+ const reconnecting = useEditorSelector(editor, snapshot => {
46
+ if (snapshot.mode !== 'edit' || snapshot.selection.linkIds.length !== 1) return false;
47
+ const link = graphAt(snapshot.document, snapshot.path).links.find(link => link.id === snapshot.selection.linkIds[0]);
48
+ return !!link && (port.direction === 'output'
49
+ ? link.source.nodeId === nodeId && link.source.portId === port.id
50
+ : link.target.type === 'port' && link.target.nodeId === nodeId && link.target.portId === port.id);
51
+ });
51
52
  const direction = port.direction;
52
53
  return (
53
54
  <Handle
@@ -56,7 +57,7 @@ export function SpagbolNodePort({ port, connectable = true, style, position, cla
56
57
  position={position ?? (direction === "input" ? Position.Left : Position.Right)}
57
58
  className={`spagbol-node-port${reconnecting ? ' spagbol-node-port-selected-link' : ''} ${className ?? ""}`}
58
59
  isConnectable={
59
- editor.editing && port.connectable !== false && connectable
60
+ mode === "edit" && port.connectable !== false && connectable
60
61
  }
61
62
  style={style}
62
63
  aria-label={`${port.label} ${direction}`}
@@ -85,7 +86,8 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
85
86
  const [pointerOver, setPointerOver] = useState(false);
86
87
  const updateInternals = useUpdateNodeInternals();
87
88
  const flowId = useNodeId();
88
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
89
+ const singleSelected = useEditorSelector(editor, snapshot => snapshot.selection.nodeIds.length === 1 && snapshot.selection.nodeIds[0] === node.id);
90
+ const selectedPortId = useEditorSelector(editor, snapshot => snapshot.selection.port?.nodeId === node.id ? snapshot.selection.port.portId : undefined);
89
91
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
90
92
  const path = useEditorSelector(editor, snapshot => snapshot.path);
91
93
  const boundary = isGroupBoundary(node);
@@ -93,7 +95,7 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
93
95
  const ports = portsFor(node, editor.nodes);
94
96
  const inputPorts = ports.filter((port) => port.direction === "input" && props.portVisible?.(port) !== false);
95
97
  const outputPorts = ports.filter((port) => port.direction === "output" && props.portVisible?.(port) !== false);
96
- const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && selection.nodeIds.length === 1) && !(connecting && pointerOver);
98
+ const compact = !projected && views[node.kind]?.compact !== false && features.compact && (props.forceCompact || node.compact) && !(selected && singleSelected) && !(connecting && pointerOver);
97
99
  const headerInput = !boundary ? inputPorts.find(port => port.label === 'signal') : undefined;
98
100
  const headerOutput = !boundary && props.headerOutput !== false ? outputPorts.find(port => port.label === 'signal') ?? (outputPorts.length === 1 ? outputPorts[0] : undefined) : undefined;
99
101
  const portState = useEditorSelector(editor, snapshot => graphPortStates(graphAt(snapshot.document, snapshot.path)).get(node.id) ?? emptyPortState);
@@ -140,7 +142,7 @@ export function SpagbolNodeFrame(props: SpagbolNodeFrameProps) {
140
142
  style={{ gridColumn: boundary || input || inputPorts.length === 0 ? 1 : 2, gridRow: index + 1 + (props.portsHeader ? 1 : 0) }}
141
143
  >
142
144
  {!projected && !props.hidePorts && <SpagbolNodePort port={port} />}
143
- {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selection.port?.nodeId === node.id && selection.port.portId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {
145
+ {props.renderPortContent ? props.renderPortContent(port) : <>{boundary && !projected ? <SpagbolNodeGroupPort name={port.label} editable={editor.editing && !props.readOnly} draggable={editor.editing && !props.readOnly} selected={selectedPortId === port.id} onChange={label => { try { editor.renameBoundaryPort(node.id, port.id, label); } catch { /* Keep the valid label. */ } }} onPointerDown={event => {
144
146
  if (!editor.editing || props.readOnly || event.button !== 0) return;
145
147
  event.preventDefault(); event.stopPropagation();
146
148
  editor.select({ nodeIds: [node.id], port: { nodeId: node.id, portId: port.id, direction: port.direction } });
@@ -274,7 +276,6 @@ export function useNodeViewProps(
274
276
  const { editor, projection } = useSpagbol();
275
277
  const owner = projection?.groupId ?? groupId;
276
278
  const controlPath = projection?.path ?? [node.id];
277
- const selection = useEditorSelector(editor, snapshot => snapshot.selection);
278
279
  const mode = useEditorSelector(editor, snapshot => snapshot.mode);
279
280
  const path = useEditorSelector(editor, snapshot => snapshot.path);
280
281
  return useMemo(
@@ -0,0 +1,33 @@
1
+ import type { Node, NodeChange } from "@xyflow/react";
2
+ import type { GraphDocument, Point } from "../core/index.js";
3
+
4
+ /** Accumulate only changed native node positions; keep model records immutable. */
5
+ export function previewNodePositions<N extends Node>(
6
+ previous: ReadonlyMap<string, Point>,
7
+ changes: readonly NodeChange<N>[],
8
+ ): ReadonlyMap<string, Point> {
9
+ let next: Map<string, Point> | undefined;
10
+ for (const change of changes) {
11
+ if (change.type !== "position" || !change.position || !change.id.startsWith("node:"))
12
+ continue;
13
+ const { x, y } = change.position;
14
+ if (!Number.isFinite(x) || !Number.isFinite(y)) continue;
15
+ const id = change.id.slice(5);
16
+ const before = (next ?? previous).get(id);
17
+ if (before?.x === x && before.y === y) continue;
18
+ next ??= new Map(previous);
19
+ next.set(id, { x, y });
20
+ }
21
+ return next ?? previous;
22
+ }
23
+
24
+ /** Apply a completed multi-node drag as one atomic controller edit. */
25
+ export function applyNodePositions(
26
+ graph: GraphDocument,
27
+ positions: ReadonlyMap<string, Point>,
28
+ ): void {
29
+ for (const node of graph.nodes) {
30
+ const position = positions.get(node.id);
31
+ if (position) node.position = { ...position };
32
+ }
33
+ }