diff --git a/Examples/Nodify.Calculator/ApplicationViewModel.cs b/Examples/Nodify.Calculator/ApplicationViewModel.cs
index fac7041..8879220 100644
--- a/Examples/Nodify.Calculator/ApplicationViewModel.cs
+++ b/Examples/Nodify.Calculator/ApplicationViewModel.cs
@@ -103,6 +103,15 @@ namespace Nodify.Calculator
GraphSerializer.Load(firstEditor.Calculator, firstEditor.Calculator.OperationsMenu);
}
catch { }
+
+ // When the log panel is closed, clear any lingering execution animation state
+ LogPanel.PanelClosed += () =>
+ {
+ foreach (var editor in Editors)
+ {
+ editor.Calculator?.ClearExecutionState();
+ }
+ };
}
private void OnOpenInnerCalculator(EditorViewModel parentEditor, CalculatorViewModel calculator)
diff --git a/Examples/Nodify.Calculator/CalculatorViewModel.cs b/Examples/Nodify.Calculator/CalculatorViewModel.cs
index 7bb917e..de9418d 100644
--- a/Examples/Nodify.Calculator/CalculatorViewModel.cs
+++ b/Examples/Nodify.Calculator/CalculatorViewModel.cs
@@ -149,6 +149,14 @@ namespace Nodify.Calculator
Operations.Add(knot);
}
+ public void ClearExecutionState()
+ {
+ foreach (var op in Operations)
+ op.ExecutionState = ExecutionState.None;
+ foreach (var con in Connections)
+ con.IsActiveInExecution = false;
+ }
+
public INodifyCommand StartConnectionCommand { get; }
public INodifyCommand CreateConnectionCommand { get; }
public INodifyCommand DisconnectConnectorCommand { get; }
diff --git a/Examples/Nodify.Calculator/ConnectionViewModel.cs b/Examples/Nodify.Calculator/ConnectionViewModel.cs
index 445d7c1..eeaa4ff 100644
--- a/Examples/Nodify.Calculator/ConnectionViewModel.cs
+++ b/Examples/Nodify.Calculator/ConnectionViewModel.cs
@@ -32,5 +32,12 @@
set { _outputNodeId = value; }
}
+ private bool _isActiveInExecution;
+ public bool IsActiveInExecution
+ {
+ get => _isActiveInExecution;
+ set => SetProperty(ref _isActiveInExecution, value);
+ }
+
}
}
diff --git a/Examples/Nodify.Calculator/EditorView.xaml b/Examples/Nodify.Calculator/EditorView.xaml
index 079e741..1945c2e 100644
--- a/Examples/Nodify.Calculator/EditorView.xaml
+++ b/Examples/Nodify.Calculator/EditorView.xaml
@@ -99,8 +99,27 @@
Target="{Binding Input.Anchor}"
Foreground="{Binding Input.Color}"
Stroke="{Binding Input.Color}"
- StrokeThickness="2"
- />
+ StrokeThickness="2">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Nodify.Calculator/ExecutionState.cs b/Examples/Nodify.Calculator/ExecutionState.cs
new file mode 100644
index 0000000..b9c04f7
--- /dev/null
+++ b/Examples/Nodify.Calculator/ExecutionState.cs
@@ -0,0 +1,10 @@
+namespace Nodify.Calculator
+{
+ public enum ExecutionState
+ {
+ None,
+ Running,
+ Completed,
+ Error
+ }
+}
diff --git a/Examples/Nodify.Calculator/Executor.cs b/Examples/Nodify.Calculator/Executor.cs
index 21567c4..3ed74d1 100644
--- a/Examples/Nodify.Calculator/Executor.cs
+++ b/Examples/Nodify.Calculator/Executor.cs
@@ -34,6 +34,9 @@ namespace Nodify.Calculator
static Dictionary outputs = new Dictionary();
static Dictionary variables = new Dictionary();
+ // Animation delay between node steps (milliseconds) while executing
+ private const int AnimationDelayMs = 600;
+
// Auth configuration populated from the Auth node
private string _authBaseUrl = string.Empty;
private string _authType = string.Empty;
@@ -76,6 +79,9 @@ namespace Nodify.Calculator
var allnodes = calcModel.Operations;
var allConnections = calcModel.Connections;
+ // Reset any previous execution animation state
+ ResetExecutionState(calcModel);
+
// Resolve Auth node configuration before execution
ResolveAuthNode(allnodes);
@@ -97,6 +103,36 @@ namespace Nodify.Calculator
return errorString;
}
+ private static void RunOnUI(Action action)
+ {
+ var dispatcher = System.Windows.Application.Current?.Dispatcher;
+ if (dispatcher == null) { action(); return; }
+ if (dispatcher.CheckAccess()) action();
+ else dispatcher.Invoke(action);
+ }
+
+ private void ResetExecutionState(CalculatorViewModel calc)
+ {
+ RunOnUI(() =>
+ {
+ foreach (var op in calc.Operations)
+ op.ExecutionState = ExecutionState.None;
+ foreach (var con in calc.Connections)
+ con.IsActiveInExecution = false;
+ });
+ }
+
+ private void SetNodeState(OperationViewModel node, ExecutionState state)
+ {
+ RunOnUI(() => node.ExecutionState = state);
+ }
+
+ private void SetConnectionActive(ConnectionViewModel conn, bool active)
+ {
+ if (conn == null) return;
+ RunOnUI(() => conn.IsActiveInExecution = active);
+ }
+
public static string GenerateClassFromJson(string json, string className)
{
// Parse the JSON to understand its structure
@@ -947,12 +983,30 @@ namespace Nodify.Calculator
if (isExecute)
{
+ // Mark current node as running and animate
+ SetNodeState(currentNode, ExecutionState.Running);
+ System.Threading.Thread.Sleep(AnimationDelayMs / 2);
+
StartExecution(currentNode, connections);
+
+ // Mark current node as completed
+ SetNodeState(currentNode, ExecutionState.Completed);
+
+ // Animate the flow connection leading to the next node
+ SetConnectionActive(connection, true);
+ System.Threading.Thread.Sleep(AnimationDelayMs);
+ SetConnectionActive(connection, false);
}
// Recursively check the next node; stop on the first failure
if (TraverseChain(nextNode, endNodeTitle, connections, visitedNodes, isExecute))
{
+ if (isExecute)
+ {
+ // Mark the terminal "end" node as completed too
+ if (nextNode.Title?.Equals(endNodeTitle, StringComparison.OrdinalIgnoreCase) == true)
+ SetNodeState(nextNode, ExecutionState.Completed);
+ }
return true; // If a valid path to the "end" is found, exit
}
}
diff --git a/Examples/Nodify.Calculator/LogPanelViewModel.cs b/Examples/Nodify.Calculator/LogPanelViewModel.cs
index 55d30c1..fd702f3 100644
--- a/Examples/Nodify.Calculator/LogPanelViewModel.cs
+++ b/Examples/Nodify.Calculator/LogPanelViewModel.cs
@@ -18,9 +18,18 @@ namespace Nodify.Calculator
public bool IsOpen
{
get => _isOpen;
- set => SetProperty(ref _isOpen, value);
+ set
+ {
+ var wasOpen = _isOpen;
+ if (SetProperty(ref _isOpen, value) && wasOpen && !value)
+ {
+ PanelClosed?.Invoke();
+ }
+ }
}
+ public event Action PanelClosed;
+
private bool _isRunning;
public bool IsRunning
{
diff --git a/Examples/Nodify.Calculator/OperationViewModel.cs b/Examples/Nodify.Calculator/OperationViewModel.cs
index dcd638b..0a9f0ac 100644
--- a/Examples/Nodify.Calculator/OperationViewModel.cs
+++ b/Examples/Nodify.Calculator/OperationViewModel.cs
@@ -72,6 +72,15 @@ namespace Nodify.Calculator
public bool IsReadOnly { get; set; }
+ private ExecutionState _executionState;
+ [BsonIgnore]
+ [Newtonsoft.Json.JsonIgnore]
+ public ExecutionState ExecutionState
+ {
+ get => _executionState;
+ set => SetProperty(ref _executionState, value);
+ }
+
[BsonIgnore]
private IOperation? _operation;
[BsonIgnore]