@@ -74,7 +74,7 @@ const VisualScripting = () => {
7474 e . preventDefault ( ) ;
7575 const rect = canvasRef . current . getBoundingClientRect ( ) ;
7676 const { x, y } = camera . screenToWorld ( e . clientX - rect . left , e . clientY - rect . top ) ;
77-
77+
7878 const clickedNode = findClickedNode ( x , y ) ;
7979 if ( clickedNode ) {
8080 setNodeContextMenu ( { visible : true , x, y } ) ;
@@ -241,39 +241,40 @@ const VisualScripting = () => {
241241
242242 // #region Node Operations
243243 const findClickedNode = ( x , y ) => {
244- return nodes . find ( node =>
245- node . isPointInside ( x , y , renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) )
246- ) ;
244+ return nodes . find ( node => {
245+ const nodeInstance = Node . createInstance ( node , nodeTypes ) ;
246+ return nodeInstance . isPointInside ( x , y , renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ) ;
247+ } ) ;
247248 } ;
248249
249250 const findClickedPort = ( x , y ) => {
250251 for ( const node of nodes ) {
251252 const nodeType = nodeTypes [ node . type ] ;
252253 const dimensions = renderer . getNodeDimensions ( node , canvasRef . current . getContext ( '2d' ) ) ;
253-
254+
254255 // Port icon dimensions (from Renderer.drawPortIcon)
255256 const portIconWidth = 6 * 1.5 ; // Base width of triangle * scale
256257 const portIconHeight = 10 * 1.5 ; // Base height of triangle * scale
257258 const portOffset = 5 ; // Distance from node border
258-
259+
259260 // Calculate port Y position using the same logic as in drawEdges
260261 const getPortY = ( index ) => {
261262 const titleHeight = 25 ;
262263 const portSpacing = 14 ;
263264 const portVerticalGap = 5 ;
264265 return node . y + titleHeight + portVerticalGap + ( index * portSpacing ) + 4 ;
265266 } ;
266-
267+
267268 // Check input ports
268269 for ( let i = 0 ; i < nodeType . inputs . length ; i ++ ) {
269270 const portY = getPortY ( i ) ;
270271 const portX = node . x - portOffset ;
271-
272+
272273 // Create a square hitbox around the port
273- if ( x >= portX - portIconWidth &&
274- x <= portX + portIconWidth &&
275- y >= portY - portIconHeight / 2 &&
276- y <= portY + portIconHeight / 2 ) {
274+ if ( x >= portX - portIconWidth &&
275+ x <= portX + portIconWidth &&
276+ y >= portY - portIconHeight / 2 &&
277+ y <= portY + portIconHeight / 2 ) {
277278 return node . getPortPosition ( i , true , dimensions ) ;
278279 }
279280 }
@@ -282,12 +283,12 @@ const VisualScripting = () => {
282283 for ( let i = 0 ; i < nodeType . outputs . length ; i ++ ) {
283284 const portY = getPortY ( i ) ;
284285 const portX = node . x + dimensions . width + portOffset ;
285-
286+
286287 // Create a square hitbox around the port
287- if ( x >= portX - portIconWidth &&
288- x <= portX + portIconWidth &&
289- y >= portY - portIconHeight / 2 &&
290- y <= portY + portIconHeight / 2 ) {
288+ if ( x >= portX - portIconWidth &&
289+ x <= portX + portIconWidth &&
290+ y >= portY - portIconHeight / 2 &&
291+ y <= portY + portIconHeight / 2 ) {
291292 return node . getPortPosition ( i , false , dimensions ) ;
292293 }
293294 }
@@ -313,7 +314,7 @@ const VisualScripting = () => {
313314 }
314315 return node ;
315316 } ) ;
316-
317+
317318 setUndoStack ( [ ...undoStack , { nodes, edges } ] ) ;
318319 setRedoStack ( [ ] ) ;
319320 setNodes ( updatedNodes ) ;
@@ -325,7 +326,7 @@ const VisualScripting = () => {
325326 }
326327 return node ;
327328 } ) ) ;
328-
329+
329330 setNeedsRedraw ( true ) ;
330331 } ;
331332
@@ -342,6 +343,35 @@ const VisualScripting = () => {
342343 setNeedsRedraw ( true ) ;
343344 }
344345 } ;
346+
347+ const setNodeLabel = ( ) => {
348+ if ( selectedNodes . length === 0 ) return ;
349+
350+ const currentNode = selectedNodes [ 0 ] ;
351+ const newLabel = prompt ( 'Enter node label:' , currentNode . label || '' ) ;
352+
353+ if ( newLabel !== null ) { // Check if user didn't cancel
354+ setNodes ( nodes . map ( node => {
355+ if ( node . id === currentNode . id ) {
356+ const updatedNode = Node . createInstance ( node , nodeTypes ) ;
357+ updatedNode . label = newLabel ;
358+ return updatedNode ;
359+ }
360+ return node ;
361+ } ) ) ;
362+
363+ setSelectedNodes ( prevSelected => prevSelected . map ( node => {
364+ if ( node . id === currentNode . id ) {
365+ const updatedNode = Node . createInstance ( node , nodeTypes ) ;
366+ updatedNode . label = newLabel ;
367+ return updatedNode ;
368+ }
369+ return node ;
370+ } ) ) ;
371+
372+ setNeedsRedraw ( true ) ;
373+ }
374+ } ;
345375 // #endregion
346376
347377 // #region Menu Operations
@@ -381,19 +411,19 @@ const VisualScripting = () => {
381411 break ;
382412 case 'loadExample' :
383413 if ( examples [ param ] ) {
384- const exampleNodes = examples [ param ] . nodes . map ( node =>
414+ const exampleNodes = examples [ param ] . nodes . map ( node =>
385415 Node . createFromExample ( node , nodeTypes )
386416 ) ;
387-
417+
388418 // Reconstruct edges with proper port positions
389419 const reconstructedEdges = examples [ param ] . edges . map ( edge => {
390420 const startNode = exampleNodes . find ( n => n . id === edge . start . nodeId ) ;
391421 const endNode = exampleNodes . find ( n => n . id === edge . end . nodeId ) ;
392-
422+
393423 if ( startNode && endNode ) {
394424 const startDimensions = renderer . getNodeDimensions ( startNode , canvasRef . current . getContext ( '2d' ) ) ;
395425 const endDimensions = renderer . getNodeDimensions ( endNode , canvasRef . current . getContext ( '2d' ) ) ;
396-
426+
397427 return {
398428 start : {
399429 ...edge . start ,
@@ -811,26 +841,28 @@ const VisualScripting = () => {
811841 const handleNodeContextMenuAction = ( action ) => {
812842 switch ( action ) {
813843 case 'copy' :
814- setCopiedNodes ( [ ... selectedNodes ] ) ;
844+ setCopiedNodes ( selectedNodes . map ( node => Node . createInstance ( node , nodeTypes ) ) ) ;
815845 break ;
816846 case 'delete' :
817847 deleteSelectedNodes ( ) ;
818848 break ;
819849 case 'cut' :
820- setCopiedNodes ( [ ... selectedNodes ] ) ;
850+ setCopiedNodes ( selectedNodes . map ( node => Node . createInstance ( node , nodeTypes ) ) ) ;
821851 deleteSelectedNodes ( ) ;
822852 break ;
823853 case 'duplicate' :
824854 const newNodes = selectedNodes . map ( node => {
825- // Create a proper Node instance using the static create method
826855 const duplicatedNode = Node . create ( node . type , node . x + 20 , node . y + 20 , nodeTypes ) ;
827- // Copy over the properties
828856 duplicatedNode . properties = { ...node . properties } ;
857+ duplicatedNode . label = node . label ;
829858 return duplicatedNode ;
830859 } ) ;
831860 setNodes ( [ ...nodes , ...newNodes ] ) ;
832861 setSelectedNodes ( newNodes ) ;
833862 break ;
863+ case 'setLabel' :
864+ setNodeLabel ( ) ;
865+ break ;
834866 default :
835867 console . log ( `Unhandled node context menu action: ${ action } ` ) ;
836868 }
0 commit comments