diff --git a/src/App.jsx b/src/App.jsx index 2b074522..965ef10a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -272,7 +272,10 @@ const DnDFlow = () => { } // Create node data with label and initialize all expected fields as empty strings - let nodeData = { label: `${type} ${newNodeId}` }; + let nodeData = { + label: `${type} ${newNodeId}`, + nodeColor: '#DDE6ED' // Default node color + }; // if node in nodeDynamicHandles, ensure add outputCount and inputCount to data if (nodeDynamicHandles.includes(type)) { @@ -386,7 +389,7 @@ const DnDFlow = () => { return; } - // Load the graph data + // Load the graph data and ensure nodeColor exists on all nodes const { nodes: loadedNodes, edges: loadedEdges, @@ -396,7 +399,17 @@ const DnDFlow = () => { events: loadedEvents, pythonCode: loadedPythonCode } = graphData; - setNodes(loadedNodes || []); + + // Ensure all loaded nodes have a nodeColor property + const nodesWithColors = (loadedNodes || []).map(node => ({ + ...node, + data: { + ...node.data, + nodeColor: node.data.nodeColor || '#DDE6ED' + } + })); + + setNodes(nodesWithColors); setEdges(loadedEdges || []); setSelectedNode(null); setNodeCounter(loadedNodeCounter ?? loadedNodes.length); @@ -457,7 +470,17 @@ const DnDFlow = () => { events: loadedEvents, pythonCode: loadedPythonCode } = graphData; - setNodes(loadedNodes || []); + + // Ensure all loaded nodes have a nodeColor property + const nodesWithColors = (loadedNodes || []).map(node => ({ + ...node, + data: { + ...node.data, + nodeColor: node.data.nodeColor || '#DDE6ED' + } + })); + + setNodes(nodesWithColors); setEdges(loadedEdges || []); setSelectedNode(null); setNodeCounter(loadedNodeCounter ?? loadedNodes.length); diff --git a/src/components/NodeSidebar.jsx b/src/components/NodeSidebar.jsx index d00e2637..ba51037f 100644 --- a/src/components/NodeSidebar.jsx +++ b/src/components/NodeSidebar.jsx @@ -155,10 +155,10 @@ const NodeSidebar = ({ const nodeDefaults = defaultValues[selectedNode.type] || {}; // Create a list of all possible parameters (both current data and defaults) - // Exclude 'label' since it's now editable directly in the title + // Exclude 'label' and 'nodeColor' since they're handled separately const allParams = new Set([ - ...Object.keys(selectedNode.data).filter(key => key !== 'label'), - ...Object.keys(nodeDefaults).filter(key => key !== 'label') + ...Object.keys(selectedNode.data).filter(key => key !== 'label' && key !== 'nodeColor'), + ...Object.keys(nodeDefaults).filter(key => key !== 'label' && key !== 'nodeColor') ]); return Array.from(allParams) @@ -212,6 +212,133 @@ const NodeSidebar = ({ }); })()} + {/* Color Picker Section */} +
+

Node Color

+ +
+ { + const newColor = e.target.value; + const updatedNode = { + ...selectedNode, + data: { ...selectedNode.data, nodeColor: newColor }, + }; + + setNodes((nds) => + nds.map((node) => + node.id === selectedNode.id ? updatedNode : node + ) + ); + setSelectedNode(updatedNode); + }} + style={{ + width: '40px', + height: '40px', + borderRadius: '6px', + border: '2px solid #555', + backgroundColor: 'transparent', + cursor: 'pointer', + padding: '0' + }} + /> + + { + const newColor = e.target.value; + const updatedNode = { + ...selectedNode, + data: { ...selectedNode.data, nodeColor: newColor }, + }; + + setNodes((nds) => + nds.map((node) => + node.id === selectedNode.id ? updatedNode : node + ) + ); + setSelectedNode(updatedNode); + }} + placeholder="#DDE6ED" + style={{ + flex: 1, + padding: '8px 12px', + borderRadius: '4px', + border: '1px solid #555', + backgroundColor: '#2a2a3e', + color: '#ffffff', + fontSize: '14px', + fontFamily: 'monospace' + }} + /> +
+ + {/* Color preset buttons */} +
+ {['#DDE6ED', '#FFE5E5', '#E5F3FF', '#E5FFE5', '#FFF5E5', '#F0E5FF', '#FFE5F5', '#E5FFFF'].map(color => ( +
+
+