@@ -538,15 +538,15 @@ private async Task RenameSelectedNodeAsync(BlueprintNodeVM nodeVm)
538538 : nodeVm . VarName ;
539539 break ;
540540
541- case BlueprintNodeType . Get :
541+ case BlueprintNodeType . BuiltinFunction when nodeVm . BuiltinFunctionName == " Get" :
542542 dialogTitle = ViewModelBase . TranslateTextWithSuffix ( "Blueprint" , "RenameGetNode" ) ?? "Rename Get Node" ;
543543 dialogPrompt = ViewModelBase . TranslateTextWithSuffix ( "Blueprint" , "RenameVariablePrompt" ) ?? "Enter new variable name:" ;
544544 currentName = nodeVm . DisplayTitle . StartsWith ( "Get:" )
545545 ? nodeVm . DisplayTitle [ "Get:" . Length ..] . Trim ( )
546546 : nodeVm . DisplayTitle ;
547547 break ;
548548
549- case BlueprintNodeType . Set :
549+ case BlueprintNodeType . BuiltinFunction when nodeVm . BuiltinFunctionName == " Set" :
550550 dialogTitle = ViewModelBase . TranslateTextWithSuffix ( "Blueprint" , "RenameSetNode" ) ?? "Rename Set Node" ;
551551 dialogPrompt = ViewModelBase . TranslateTextWithSuffix ( "Blueprint" , "RenameVariablePrompt" ) ?? "Enter new variable name:" ;
552552 currentName = nodeVm . DisplayTitle . StartsWith ( "Set:" )
@@ -578,12 +578,12 @@ private async Task RenameSelectedNodeAsync(BlueprintNodeVM nodeVm)
578578 PropagateVariableRename ( oldVarName , newName ) ;
579579 break ;
580580
581- case BlueprintNodeType . Get :
581+ case BlueprintNodeType . BuiltinFunction when nodeVm . BuiltinFunctionName == " Get" :
582582 nodeVm . DisplayTitle = $ "Get: { newName } ";
583583 nodeVm . Metadata [ "VarName" ] = newName ;
584584 break ;
585585
586- case BlueprintNodeType . Set :
586+ case BlueprintNodeType . BuiltinFunction when nodeVm . BuiltinFunctionName == " Set" :
587587 nodeVm . DisplayTitle = $ "Set: { newName } ";
588588 nodeVm . Metadata [ "VarName" ] = newName ;
589589 break ;
@@ -601,10 +601,11 @@ private void PropagateVariableRename(string oldName, string newName)
601601 {
602602 foreach ( var n in Nodes . OfType < BlueprintNodeVM > ( ) )
603603 {
604- if ( n . NodeType is not ( BlueprintNodeType . Get or BlueprintNodeType . Set ) )
604+ if ( n . NodeType != BlueprintNodeType . BuiltinFunction
605+ || ( n . BuiltinFunctionName != "Get" && n . BuiltinFunctionName != "Set" ) )
605606 continue ;
606607
607- var prefix = n . NodeType == BlueprintNodeType . Get ? "Get: " : "Set: " ;
608+ var prefix = n . BuiltinFunctionName == " Get" ? "Get: " : "Set: " ;
608609 var currentRef = n . DisplayTitle . StartsWith ( prefix )
609610 ? n . DisplayTitle [ prefix . Length ..] . Trim ( )
610611 : "" ;
@@ -1009,8 +1010,27 @@ private BlueprintNodeVM ConvertBlueprintNodeToViewModel(BlueprintNode blueprintN
10091010
10101011 // Preserve BuiltinFunctionNode metadata for round-trip
10111012 if ( blueprintNode is BuiltinFunctionNode bfNode && ! string . IsNullOrEmpty ( bfNode . FunctionName ) )
1013+ {
1014+ nodeVm . BuiltinFunctionName = bfNode . FunctionName ;
10121015 nodeVm . Metadata [ "BuiltinFunctionName" ] = bfNode . FunctionName ;
10131016
1017+ // For Get/Set, build display title from VarName pin's default value
1018+ if ( bfNode . FunctionName is "Get" or "Set" )
1019+ {
1020+ var varPin = bfNode . InputPins . FirstOrDefault ( p => p . Name == "VarName" ) ;
1021+ var varName = varPin ? . DefaultValue ;
1022+ if ( ! string . IsNullOrEmpty ( varName ) )
1023+ {
1024+ nodeVm . DisplayTitle = $ "{ bfNode . FunctionName } : { varName } ";
1025+ nodeVm . Title = nodeVm . DisplayTitle ;
1026+ // Use function-specific colors
1027+ var ( pc , lc ) = BlueprintNodeVM . GetBuiltinFunctionColors ( bfNode . FunctionName ) ;
1028+ nodeVm . CategoryColor = pc ;
1029+ nodeVm . CategoryColorLight = lc ;
1030+ }
1031+ }
1032+ }
1033+
10141034 // Preserve PluginTriggerNode metadata for round-trip
10151035 if ( blueprintNode is PluginTriggerNode ptNode )
10161036 {
@@ -1210,8 +1230,19 @@ private void BuildBlockScopesFromScopeBlocks(Blueprint blueprint)
12101230 /// </summary>
12111231 private BlueprintNode ConvertViewModelToBlueprintNode ( BlueprintNodeVM nodeVm )
12121232 {
1213- // Use NodeType from VM directly (more reliable than name-based inference)
1214- var blueprintNode = _nodeRegistry . Create ( nodeVm . NodeType ) ;
1233+ // Use CreateBuiltinFunctionNode for builtin function nodes to get proper pins
1234+ BlueprintNode blueprintNode ;
1235+ if ( nodeVm . NodeType == BlueprintNodeType . BuiltinFunction
1236+ && nodeVm . Metadata . TryGetValue ( "BuiltinFunctionName" , out var funcName )
1237+ && ! string . IsNullOrEmpty ( funcName )
1238+ && funcName is "Get" or "Set" or "Print" or "Pause" or "Branch" or "Loop" or "Break" or "ToLoopCond" )
1239+ {
1240+ blueprintNode = _nodeRegistry . CreateBuiltinFunctionNode ( funcName ) ;
1241+ }
1242+ else
1243+ {
1244+ blueprintNode = _nodeRegistry . Create ( nodeVm . NodeType ) ;
1245+ }
12151246
12161247 // Preserve original node ID so connections can reference it
12171248 blueprintNode . Id = nodeVm . BlueprintNodeId ;
@@ -1230,14 +1261,6 @@ private BlueprintNode ConvertViewModelToBlueprintNode(BlueprintNodeVM nodeVm)
12301261 vNode . VarType = nodeVm . VarType ;
12311262 }
12321263
1233- // Special handling: BuiltinFunctionNode โ restore FunctionName from Metadata
1234- if ( blueprintNode is BuiltinFunctionNode bfNode
1235- && nodeVm . Metadata . TryGetValue ( "BuiltinFunctionName" , out var funcName )
1236- && ! string . IsNullOrEmpty ( funcName ) )
1237- {
1238- bfNode . FunctionName = funcName ;
1239- }
1240-
12411264 // Special handling: PluginTriggerNode โ restore PluginName/TriggerName from Metadata
12421265 if ( blueprintNode is PluginTriggerNode ptNode )
12431266 {
@@ -1331,21 +1354,19 @@ private void ApplyDisplayTitleToNode(BlueprintNode node, string title, Blueprint
13311354 case CallHelperNode helperNode when title . StartsWith ( "Helper:" ) :
13321355 helperNode . HelperFunctionName = title [ "Helper:" . Length ..] . Trim ( ) ;
13331356 break ;
1334- case GetNode getNode when title . StartsWith ( "Get:" ) :
1335- getNode . VarName = title [ "Get:" . Length ..] . Trim ( ) ;
1336- // Re-resolve pin type when VarName changes
1357+ case BuiltinFunctionNode bfn when title . StartsWith ( "Get:" ) :
1358+ bfn . Properties [ "VarName" ] = title [ "Get:" . Length ..] . Trim ( ) ;
13371359 if ( nodeVm != null )
13381360 {
1339- var pinType = ResolveVariablePinType ( getNode . VarName ) ;
1361+ var pinType = ResolveVariablePinType ( bfn . Properties [ " VarName" ] ) ;
13401362 UpdateNodeValuePinType ( nodeVm , pinType ) ;
13411363 }
13421364 break ;
1343- case SetNode setNode when title . StartsWith ( "Set:" ) :
1344- setNode . VarName = title [ "Set:" . Length ..] . Trim ( ) ;
1345- // Re-resolve pin type when VarName changes
1365+ case BuiltinFunctionNode bfn2 when title . StartsWith ( "Set:" ) :
1366+ bfn2 . Properties [ "VarName" ] = title [ "Set:" . Length ..] . Trim ( ) ;
13461367 if ( nodeVm != null )
13471368 {
1348- var pinType = ResolveVariablePinType ( setNode . VarName ) ;
1369+ var pinType = ResolveVariablePinType ( bfn2 . Properties [ " VarName" ] ) ;
13491370 UpdateNodeValuePinType ( nodeVm , pinType ) ;
13501371 }
13511372 break ;
@@ -1435,7 +1456,8 @@ private void PropagateTypeToGetSetNodes(string varName, PinType pinType)
14351456
14361457 foreach ( var node in Nodes . OfType < BlueprintNodeVM > ( ) )
14371458 {
1438- if ( node . NodeType is not ( BlueprintNodeType . Get or BlueprintNodeType . Set ) ) continue ;
1459+ if ( node . NodeType != BlueprintNodeType . BuiltinFunction
1460+ || ( node . BuiltinFunctionName != "Get" && node . BuiltinFunctionName != "Set" ) ) continue ;
14391461
14401462 // Extract VarName from display title ("Get: myVar" or "Set: myVar")
14411463 var nodeVarName = node . DisplayTitle . Contains ( ':' )
@@ -1456,7 +1478,7 @@ private void UpdateNodeValuePinType(BlueprintNodeVM node, PinType pinType)
14561478 {
14571479 // For GetNode: Value is an output connector
14581480 // For SetNode: Value is an input connector
1459- var connectors = node . NodeType == BlueprintNodeType . Get
1481+ var connectors = node . BuiltinFunctionName == " Get"
14601482 ? node . Output . OfType < BlueprintConnectorVM > ( )
14611483 : node . Input . OfType < BlueprintConnectorVM > ( ) ;
14621484
@@ -1506,7 +1528,8 @@ private void ResolveAllGetSetPinTypes()
15061528 {
15071529 foreach ( var node in Nodes . OfType < BlueprintNodeVM > ( ) )
15081530 {
1509- if ( node . NodeType is not ( BlueprintNodeType . Get or BlueprintNodeType . Set ) ) continue ;
1531+ if ( node . NodeType != BlueprintNodeType . BuiltinFunction
1532+ || ( node . BuiltinFunctionName != "Get" && node . BuiltinFunctionName != "Set" ) ) continue ;
15101533
15111534 var varName = node . DisplayTitle . Contains ( ':' )
15121535 ? node . DisplayTitle [ ( node . DisplayTitle . IndexOf ( ':' ) + 1 ) ..] . Trim ( )
@@ -1620,34 +1643,80 @@ private void RefreshCounts()
16201643 [ RelayCommand ]
16211644 public void AddEntryNode ( ) => AddNodeFromTemplate ( BlueprintNodeType . Entry ) ;
16221645
1646+ /// <summary>
1647+ /// Creates a builtin function node from the registry with proper descriptor and UI setup.
1648+ /// </summary>
1649+ private void AddBuiltinFunctionNode ( string functionName )
1650+ {
1651+ var builtinNode = _nodeRegistry . CreateBuiltinFunctionNode ( functionName ) ;
1652+ var descriptor = builtinNode . GetDescriptor ( ) ;
1653+ var title = $ "{ ( functionName == "Get" ? "Get: " : functionName == "Set" ? "Set: " : "" ) } { functionName } ";
1654+ var ( primaryColor , lightColor ) = BlueprintNodeVM . GetBuiltinFunctionColors ( functionName ) ;
1655+
1656+ var node = new BlueprintNodeVM
1657+ {
1658+ Location = new Avalonia . Point ( 100 , 100 ) ,
1659+ BlueprintNodeId = Guid . NewGuid ( ) . ToString ( ) ,
1660+ NodeType = BlueprintNodeType . BuiltinFunction ,
1661+ BuiltinFunctionName = functionName ,
1662+ DisplayTitle = title ,
1663+ CategoryColor = primaryColor ,
1664+ CategoryColorLight = lightColor ,
1665+ Title = title ,
1666+ Name = functionName ,
1667+ Input = new ObservableCollection < object > ( ) ,
1668+ Output = new ObservableCollection < object > ( )
1669+ } ;
1670+
1671+ foreach ( var pinDesc in descriptor . InputPins )
1672+ {
1673+ var pinId = Guid . NewGuid ( ) . ToString ( ) ;
1674+ node . Input . Add ( new BlueprintConnectorVM
1675+ {
1676+ Title = pinDesc . Name ,
1677+ Flow = ConnectorViewModelBase . ConnectorFlow . Input ,
1678+ PinType = pinDesc . Type ,
1679+ OriginalPinId = pinId
1680+ } ) ;
1681+ }
1682+
1683+ foreach ( var pinDesc in descriptor . OutputPins )
1684+ {
1685+ var pinId = Guid . NewGuid ( ) . ToString ( ) ;
1686+ node . Output . Add ( new BlueprintConnectorVM
1687+ {
1688+ Title = pinDesc . Name ,
1689+ Flow = ConnectorViewModelBase . ConnectorFlow . Output ,
1690+ PinType = pinDesc . Type ,
1691+ OriginalPinId = pinId
1692+ } ) ;
1693+ }
1694+
1695+ Nodes . Add ( node ) ;
1696+ RefreshCounts ( ) ;
1697+ Log . Information ( "Added BuiltinFunction node: {FunctionName}" , functionName ) ;
1698+ }
1699+
16231700 [ RelayCommand ]
16241701 public void AddBranchNode ( )
16251702 {
1626- AddNodeFromTemplate ( BlueprintNodeType . Branch ) ;
1703+ AddBuiltinFunctionNode ( " Branch" ) ;
16271704 var branchNode = Nodes . OfType < BlueprintNodeVM > ( ) . Last ( ) ;
1628-
1629- // Create True scope block
16301705 CreateScopeBlockForNode ( branchNode , "True" ) ;
1631-
1632- // Create False scope block
16331706 CreateScopeBlockForNode ( branchNode , "False" ) ;
16341707 }
16351708
16361709 [ RelayCommand ]
16371710 public void AddLoopNode ( )
16381711 {
1639- AddNodeFromTemplate ( BlueprintNodeType . Loop ) ;
1712+ AddBuiltinFunctionNode ( " Loop" ) ;
16401713 var loopNode = Nodes . OfType < BlueprintNodeVM > ( ) . Last ( ) ;
1641-
1642- // Create LoopBody scope block
16431714 CreateScopeBlockForNode ( loopNode , "LoopBody" ) ;
1644-
1645- // Create LoopEnd scope block
16461715 CreateScopeBlockForNode ( loopNode , "LoopEnd" ) ;
16471716 }
16481717
16491718 [ RelayCommand ]
1650- public void AddBreakNode ( ) => AddNodeFromTemplate ( BlueprintNodeType . Break ) ;
1719+ public void AddBreakNode ( ) => AddBuiltinFunctionNode ( " Break" ) ;
16511720
16521721 [ RelayCommand ]
16531722 public void AddConstNode ( )
@@ -1889,10 +1958,10 @@ private void AddHelperCallNode(HelperFunctionPaletteItem item)
18891958 }
18901959
18911960 [ RelayCommand ]
1892- public void AddPrintNode ( ) => AddNodeFromTemplate ( BlueprintNodeType . Print ) ;
1961+ public void AddPrintNode ( ) => AddBuiltinFunctionNode ( " Print" ) ;
18931962
18941963 [ RelayCommand ]
1895- public void AddPauseNode ( ) => AddNodeFromTemplate ( BlueprintNodeType . Pause ) ;
1964+ public void AddPauseNode ( ) => AddBuiltinFunctionNode ( " Pause" ) ;
18961965
18971966 // โโโ Blueprint Commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
18981967
@@ -2270,8 +2339,8 @@ public async Task LoadBlueprintAsync(string filePath)
22702339 }
22712340 catch ( Exception ex )
22722341 {
2273- StatusText = $ "Load error: { ex . Message } " ;
2274- Log . Error ( ex , "Blueprint load error" ) ;
2342+ Log . Error ( ex , "Failed to load blueprint from {FilePath}" , filePath ) ;
2343+ StatusText = "Failed to load file" ;
22752344 }
22762345 }
22772346
0 commit comments