diff --git a/Examples/Nodify.Calculator/EditorView.xaml b/Examples/Nodify.Calculator/EditorView.xaml
index 6ec3c7a..f6cf966 100644
--- a/Examples/Nodify.Calculator/EditorView.xaml
+++ b/Examples/Nodify.Calculator/EditorView.xaml
@@ -91,6 +91,7 @@
+
@@ -590,8 +591,8 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/Nodify.Calculator/EditorView.xaml.cs b/Examples/Nodify.Calculator/EditorView.xaml.cs
index 82e1907..90224a5 100644
--- a/Examples/Nodify.Calculator/EditorView.xaml.cs
+++ b/Examples/Nodify.Calculator/EditorView.xaml.cs
@@ -83,6 +83,15 @@ namespace Nodify.Calculator
}
}
+ private void OnSwaggerItemDrag(object sender, MouseEventArgs e)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed && sender is FrameworkElement fe && fe.Tag is OperationInfoViewModel operation)
+ {
+ var data = new DataObject(typeof(OperationInfoViewModel), operation);
+ DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
+ }
+ }
+
private void OpenContextMenu_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Source is NodifyEditor editor && editor.DataContext is CalculatorViewModel calculator)
diff --git a/Examples/Nodify.Calculator/Models/SwaggerGroupViewModel.cs b/Examples/Nodify.Calculator/Models/SwaggerGroupViewModel.cs
new file mode 100644
index 0000000..16a937b
--- /dev/null
+++ b/Examples/Nodify.Calculator/Models/SwaggerGroupViewModel.cs
@@ -0,0 +1,8 @@
+namespace Nodify.Calculator
+{
+ public class SwaggerGroupViewModel
+ {
+ public string GroupName { get; set; } = string.Empty;
+ public NodifyObservableCollection Operations { get; set; } = new NodifyObservableCollection();
+ }
+}
diff --git a/Examples/Nodify.Calculator/Models/SwaggerNodeModel.cs b/Examples/Nodify.Calculator/Models/SwaggerNodeModel.cs
index dfb969b..2c8efc0 100644
--- a/Examples/Nodify.Calculator/Models/SwaggerNodeModel.cs
+++ b/Examples/Nodify.Calculator/Models/SwaggerNodeModel.cs
@@ -10,5 +10,6 @@ namespace Nodify.Calculator.Models
public List InputNames { get; set; } = new List();
public string SwaggerFileName { get; set; } = string.Empty;
public string ResponseModelClassName { get; set; } = string.Empty;
+ public string SwaggerGroup { get; set; } = string.Empty;
}
}
diff --git a/Examples/Nodify.Calculator/OperationInfoViewModel.cs b/Examples/Nodify.Calculator/OperationInfoViewModel.cs
index 50b0a25..d784c0e 100644
--- a/Examples/Nodify.Calculator/OperationInfoViewModel.cs
+++ b/Examples/Nodify.Calculator/OperationInfoViewModel.cs
@@ -36,5 +36,6 @@ namespace Nodify.Calculator
public List FunctionInputs { get; set; } = new List();
public List FunctionOutputs { get; set; } = new List();
public string ResponseModelClassName { get; set; } = string.Empty;
+ public string SwaggerGroup { get; set; } = string.Empty;
}
}
diff --git a/Examples/Nodify.Calculator/OperationsMenuViewModel.cs b/Examples/Nodify.Calculator/OperationsMenuViewModel.cs
index 7053583..95f072a 100644
--- a/Examples/Nodify.Calculator/OperationsMenuViewModel.cs
+++ b/Examples/Nodify.Calculator/OperationsMenuViewModel.cs
@@ -114,6 +114,12 @@ namespace Nodify.Calculator
public NodifyObservableCollection AvailableModels { get; }
public NodifyObservableCollection AvailableVariables { get; }
public NodifyObservableCollection AvailableFunctions { get; }
+
+ /// Swagger operations grouped by SwaggerGroup (tag), for the right-side panel.
+ public NodifyObservableCollection GroupedSwaggerOperations { get; } = new NodifyObservableCollection();
+
+ /// Non-API system nodes for the right-side panel.
+ public NodifyObservableCollection SystemNodes { get; } = new NodifyObservableCollection();
public INodifyCommand CreateOperationCommand { get; }
[Newtonsoft.Json.JsonIgnore]
@@ -150,8 +156,13 @@ namespace Nodify.Calculator
operations.AddRange(OperationFactory.GetSystemNodes());
//operations.AddRange(OperationFactory.GetOperationsInfo(typeof(OperationsContainer)));
+ // Populate system-only nodes for the right panel
+ foreach (var sysNode in operations)
+ SystemNodes.Add(sysNode);
+
SwaggerOperations = new NodifyObservableCollection();
LoadSwaggerNodesFromDb();
+ RebuildGroupedSwaggerOperations();
operations.AddRange(SwaggerOperations);
@@ -344,6 +355,8 @@ namespace Nodify.Calculator
MenuAvailableOperations.Add(node);
}
+ RebuildGroupedSwaggerOperations();
+
MessageBox.Show($"Successfully imported {nodes.Count} API endpoints from Swagger.", "Import Swagger", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
@@ -362,11 +375,18 @@ namespace Nodify.Calculator
{
foreach (var method in path.Value)
{
+ // Determine swagger group from operation tags or path segments
+ var tag = method.Value.Tags?.FirstOrDefault();
+ var group = !string.IsNullOrEmpty(tag)
+ ? tag
+ : ExtractGroupFromPath(path.Key);
+
var ovmodel = new OperationInfoViewModel
{
Title = path.Key,
OPType = method.Key,
- Type = OperationType.API
+ Type = OperationType.API,
+ SwaggerGroup = group
};
var addedParams = new HashSet();
@@ -522,6 +542,38 @@ namespace Nodify.Calculator
return new string(name.Where(c => char.IsLetterOrDigit(c) || c == '_').ToArray());
}
+ private static string ExtractGroupFromPath(string path)
+ {
+ // Extract group from path like "/api/Pizza/{id}" -> "Pizza"
+ var segments = path.Split('/', System.StringSplitOptions.RemoveEmptyEntries);
+ // Skip "api" prefix if present, take the first meaningful segment
+ foreach (var seg in segments)
+ {
+ if (seg.StartsWith("{") || string.Equals(seg, "api", System.StringComparison.OrdinalIgnoreCase))
+ continue;
+ // Capitalize first letter
+ return char.ToUpper(seg[0]) + seg.Substring(1);
+ }
+ return "Other";
+ }
+
+ private void RebuildGroupedSwaggerOperations()
+ {
+ GroupedSwaggerOperations.Clear();
+ var groups = SwaggerOperations
+ .GroupBy(op => string.IsNullOrEmpty(op.SwaggerGroup) ? "Other" : op.SwaggerGroup)
+ .OrderBy(g => g.Key);
+
+ foreach (var group in groups)
+ {
+ GroupedSwaggerOperations.Add(new SwaggerGroupViewModel
+ {
+ GroupName = group.Key,
+ Operations = new NodifyObservableCollection(group.ToList())
+ });
+ }
+ }
+
private void SaveSwaggerNodesToDb(List nodes, string swaggerFileName)
{
using var db = new LiteDbHelper("SwaggerNodes");
@@ -535,7 +587,8 @@ namespace Nodify.Calculator
OPType = node.OPType ?? string.Empty,
InputNames = new List(node.Input),
SwaggerFileName = swaggerFileName,
- ResponseModelClassName = node.ResponseModelClassName ?? string.Empty
+ ResponseModelClassName = node.ResponseModelClassName ?? string.Empty,
+ SwaggerGroup = node.SwaggerGroup ?? string.Empty
});
}
}
@@ -553,7 +606,8 @@ namespace Nodify.Calculator
{
Title = saved.Title,
OPType = saved.OPType,
- Type = OperationType.API
+ Type = OperationType.API,
+ SwaggerGroup = saved.SwaggerGroup ?? string.Empty
};
foreach (var inputName in saved.InputNames)
diff --git a/Examples/Nodify.Calculator/UpperCaseConverter.cs b/Examples/Nodify.Calculator/UpperCaseConverter.cs
new file mode 100644
index 0000000..a994502
--- /dev/null
+++ b/Examples/Nodify.Calculator/UpperCaseConverter.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace Nodify.Calculator
+{
+ public class UpperCaseConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return value is string s ? s.ToUpper() : value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}