Added New object creation node for creating a new node from object from class

This commit is contained in:
Ankitkumar Satapara
2026-04-19 18:39:22 +05:30
parent 813c78eb40
commit dcedbfdc18
5 changed files with 233 additions and 1 deletions

View File

@@ -31,6 +31,9 @@ namespace Nodify.Calculator
// Dynamic Split node: populate outputs when a model connects to the Square input // Dynamic Split node: populate outputs when a model connects to the Square input
if (!IsLoading) HandleSplitNodeConnected(c); if (!IsLoading) HandleSplitNodeConnected(c);
// Dynamic New Object node: populate inputs when a model connects
if (!IsLoading) HandleNewObjectNodeConnected(c);
// Dynamic Copy node: adapt connectors when something connects // Dynamic Copy node: adapt connectors when something connects
if (!IsLoading) HandleCopyNodeConnected(c); if (!IsLoading) HandleCopyNodeConnected(c);
@@ -57,6 +60,9 @@ namespace Nodify.Calculator
// Dynamic Split node: clear outputs when model disconnects // Dynamic Split node: clear outputs when model disconnects
HandleSplitNodeDisconnected(c); HandleSplitNodeDisconnected(c);
// Dynamic New Object node: clear inputs when model disconnects
HandleNewObjectNodeDisconnected(c);
// Dynamic Copy node: reset on disconnect // Dynamic Copy node: reset on disconnect
HandleCopyNodeDisconnected(c); HandleCopyNodeDisconnected(c);
@@ -336,6 +342,118 @@ namespace Nodify.Calculator
sysVm.Title = "Split"; sysVm.Title = "Split";
} }
private void HandleNewObjectNodeConnected(ConnectionViewModel c)
{
// Find the New Object node's Square input connector
var squareInput = c.Input.Shape == ConnectorShape.Square ? c.Input : null;
if (squareInput == null) squareInput = c.Output.Shape == ConnectorShape.Square ? c.Output : null;
if (squareInput == null) return;
var newObjOp = squareInput.Operation;
if (newObjOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.NEW_OBJECT)
return;
// The other end is the source — find which model class it comes from
var sourceConnector = (c.Input == squareInput) ? c.Output : c.Input;
var sourceOp = sourceConnector.Operation;
string className = null;
if (sourceOp is SystemOperationViewModel srcSys)
{
var title = srcSys.Title ?? "";
if (title.StartsWith("GET ")) className = title.Substring(4).Trim();
else if (title.StartsWith("SET ")) className = title.Split(' ').ElementAtOrDefault(1);
}
else if (sourceOp is APIOperationViewModel apiVm)
{
className = apiVm.ResponseModelClassName;
if (!string.IsNullOrEmpty(className) && className.StartsWith("List<") && className.EndsWith(">"))
className = className.Substring(5, className.Length - 6);
}
if (string.IsNullOrEmpty(className) && !string.IsNullOrEmpty(sourceConnector.DataType))
{
var dt = sourceConnector.DataType;
if (dt.StartsWith("List<") && dt.EndsWith(">"))
dt = dt.Substring(5, dt.Length - 6);
if (!new[] { "string", "int", "double", "bool", "object" }.Contains(dt.ToLower()))
className = dt;
}
if (string.IsNullOrEmpty(className)) return;
var customModelDir = Path.Combine(ProjectManager.ProjectDirectory, "CustomModels");
var filePath = Path.Combine(customModelDir, $"{className}.cs");
if (!File.Exists(filePath)) return;
var fileContent = File.ReadAllText(filePath);
var properties = OperationFactory.GetPropertiesFromClassPublic(fileContent);
if (properties == null || properties.Count == 0) return;
// Remove existing dynamic inputs (non-triangle, non-square)
var toRemove = newObjOp.Input.Where(i => i.Shape != ConnectorShape.Triangle && i.Shape != ConnectorShape.Square).ToList();
toRemove.ForEach(i =>
{
DisconnectConnector(i);
newObjOp.Input.Remove(i);
});
// Add property inputs
foreach (var prop in properties)
{
var propColor = ConnectorViewModel.GetColorForType(prop.Type);
newObjOp.Input.Add(new ConnectorViewModel
{
Title = $"{prop.Name} ({prop.Type})",
Shape = ConnectorShape.Circle,
ConnectorColor = propColor,
DataType = prop.Type.ToLower()
});
}
// Update the output connector to reflect the model type
var modelOutput = newObjOp.Output.FirstOrDefault(o => o.Shape == ConnectorShape.Square);
if (modelOutput != null)
{
modelOutput.Title = className;
modelOutput.DataType = className;
}
sysVm.Title = $"New {className}";
}
private void HandleNewObjectNodeDisconnected(ConnectionViewModel c)
{
var squareInput = c.Input.Shape == ConnectorShape.Square ? c.Input : null;
if (squareInput == null) squareInput = c.Output.Shape == ConnectorShape.Square ? c.Output : null;
if (squareInput == null) return;
var newObjOp = squareInput.Operation;
if (newObjOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.NEW_OBJECT)
return;
var stillConnected = Connections.Any(con => con.Input == squareInput || con.Output == squareInput);
if (stillConnected) return;
// Remove dynamic property inputs (keep triangle and square connectors)
var toRemove = newObjOp.Input.Where(i => i.Shape != ConnectorShape.Triangle && i.Shape != ConnectorShape.Square).ToList();
toRemove.ForEach(i =>
{
DisconnectConnector(i);
newObjOp.Input.Remove(i);
});
// Reset output
var modelOutput = newObjOp.Output.FirstOrDefault(o => o.Shape == ConnectorShape.Square);
if (modelOutput != null)
{
modelOutput.Title = "Object";
modelOutput.DataType = "object";
}
sysVm.Title = "New Object";
}
private void HandleCopyNodeConnected(ConnectionViewModel c) private void HandleCopyNodeConnected(ConnectionViewModel c)
{ {
// Find the COPY node's input connector // Find the COPY node's input connector

View File

@@ -456,6 +456,52 @@ namespace Nodify.Calculator
return; return;
} }
// Handle New Object nodes — construct JSON from property inputs
if (op is SystemOperationViewModel newObjSysOp && newObjSysOp.SystemOperationType == SystemOperations.NEW_OBJECT)
{
OnLogMe?.Invoke($"Constructing new object: {newObjSysOp.Title}");
var jObj = new JObject();
// Iterate property inputs (skip triangle flow + square model connectors)
foreach (var inp in op.Input)
{
if (inp.Shape == ConnectorShape.Triangle || inp.Shape == ConnectorShape.Square) continue;
// Extract property name from title like "Name (string)"
var propName = inp.Title ?? "";
var parenIdx = propName.IndexOf(" (");
if (parenIdx > 0) propName = propName.Substring(0, parenIdx);
// Find connection feeding this input
var inputCon = connections.FirstOrDefault(cn => cn.Input == inp);
string val = null;
if (inputCon != null)
{
var srcNodeId = inputCon.Output.Operation.NodeId;
if (outputs.TryGetValue(srcNodeId, out var srcVal))
val = srcVal;
else if (inputCon.Output.Value != null)
val = inputCon.Output.Value.ToString();
}
if (val != null)
{
// Try to parse as JSON token, fallback to string
try { jObj[propName] = JToken.Parse(val); }
catch { jObj[propName] = val; }
}
else
{
jObj[propName] = null;
}
}
var json = jObj.ToString(Formatting.None);
outputs[op.NodeId] = json;
variables[op.NodeId] = json;
OnLogMe?.Invoke($"Object constructed: {json}");
return;
}
// Handle Function nodes — execute the inner flow // Handle Function nodes — execute the inner flow
if (op is FunctionOperationViewModel funcOp) if (op is FunctionOperationViewModel funcOp)
{ {

View File

@@ -247,6 +247,10 @@ namespace Nodify.Calculator
case SystemOperations.SPLIT: case SystemOperations.SPLIT:
info.Input.Add(""); info.Input.Add("");
break; break;
case SystemOperations.NEW_OBJECT:
info.Input.Add("");
info.IsFlowNode = true;
break;
case SystemOperations.IF: case SystemOperations.IF:
info.Output.Add(""); info.Output.Add("");
info.IsFlowNode = true; info.IsFlowNode = true;
@@ -340,6 +344,30 @@ namespace Nodify.Calculator
// Update title from saved // Update title from saved
sysVm.Title = nd.Title; sysVm.Title = nd.Title;
} }
else if (sysVm.SystemOperationType == SystemOperations.NEW_OBJECT)
{
// Restore dynamic property inputs (keep triangle + square, replace the rest)
var dynamicInputs = op.Input.Where(i => i.Shape != ConnectorShape.Triangle && i.Shape != ConnectorShape.Square).ToList();
foreach (var d in dynamicInputs) op.Input.Remove(d);
foreach (var ic in nd.InputConnectors)
{
if (ic.Shape == "Triangle" || ic.Shape == "Square") continue;
op.Input.Add(DeserializeConnector(ic, true));
}
// Restore output connector metadata (Square model output)
var savedModelOutput = nd.OutputConnectors.FirstOrDefault(o => o.Shape == "Square");
var modelOutput = op.Output.FirstOrDefault(o => o.Shape == ConnectorShape.Square);
if (savedModelOutput != null && modelOutput != null)
{
modelOutput.Title = savedModelOutput.Title;
modelOutput.DataType = savedModelOutput.DataType;
modelOutput.ConnectorColor = Color.FromArgb(savedModelOutput.ColorArgb);
}
sysVm.Title = nd.Title;
}
else if (sysVm.SystemOperationType == SystemOperations.COPY) else if (sysVm.SystemOperationType == SystemOperations.COPY)
{ {
// Restore adapted connectors // Restore adapted connectors

View File

@@ -108,9 +108,19 @@ namespace Nodify.Calculator
}; };
debugNode.Input.Add("Value"); debugNode.Input.Add("Value");
var newObjectNode = new OperationInfoViewModel()
{
Title = "New Object",
Type = OperationType.System,
sysOp = SystemOperations.NEW_OBJECT,
IsFlowNode = true
};
newObjectNode.Input.Add("");
systemNodes.Add(authNode); systemNodes.Add(authNode);
systemNodes.Add(copynode); systemNodes.Add(copynode);
systemNodes.Add(debugNode); systemNodes.Add(debugNode);
systemNodes.Add(newObjectNode);
systemNodes.Add(begin); systemNodes.Add(begin);
systemNodes.Add(ending); systemNodes.Add(ending);
systemNodes.Add(debugAndCreateModels); systemNodes.Add(debugAndCreateModels);
@@ -331,6 +341,35 @@ namespace Nodify.Calculator
return funcOp; return funcOp;
} }
if (info.sysOp == SystemOperations.NEW_OBJECT)
{
var newObjOp = new SystemOperationViewModel
{
Title = info.Title,
SystemOperationType = SystemOperations.NEW_OBJECT
};
// Flow connectors
newObjOp.Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle });
newObjOp.Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Triangle, IsInput = false });
// Model class input (Square — user connects a model source)
foreach (var inp in input)
{
inp.Shape = ConnectorShape.Square;
inp.ConnectorColor = Color.MediumPurple;
newObjOp.Input.Add(inp);
}
// Model output (Square — returns the constructed object)
newObjOp.Output.Add(new ConnectorViewModel
{
Title = "Object",
IsInput = false,
Shape = ConnectorShape.Square,
ConnectorColor = Color.MediumPurple,
DataType = "object"
});
return newObjOp;
}
if (info.sysOp == SystemOperations.DEBUG) if (info.sysOp == SystemOperations.DEBUG)
{ {
var debugOp = new SystemOperationViewModel var debugOp = new SystemOperationViewModel

View File

@@ -19,7 +19,8 @@ namespace Nodify.Calculator
SPLIT, SPLIT,
AUTH, AUTH,
FUNCTION, FUNCTION,
DEBUG DEBUG,
NEW_OBJECT
} }
public class SystemOperationViewModel : OperationViewModel public class SystemOperationViewModel : OperationViewModel