Skip to content

araxis/nodely

Repository files navigation

Nodely

Package Downloads

Nodely is a native Avalonia toolkit for building interactive node / graph / diagram editors — a first-party port of the proven Blazor.Diagrams architecture to Avalonia. Pan/zoom canvas, custom nodes, interactive links, groups, an overview minimap, theming, read-only mode, serialization, undo/redo, and auto-layout — with no SVG, no JS, no WebView, just Avalonia's native rendering.

Status: v0.6.0. Engine + Avalonia UI are complete and tested on net8.0 and net10.0 (150 tests per runtime across the headless engine and Avalonia headless UI). See CHANGELOG.md and the design notes in memory/.

Why

  • Performance first — virtualized nodes, cached link geometry, immediate-mode grid/overview. A 2000-node / ~4000-link graph re-routes + generates all paths in ~15 ms.
  • Customizable — define a custom node by subclassing NodeModel and registering an Avalonia control.
  • Clean architecture — a UI-agnostic engine (Nodely.Core) + a thin Avalonia rendering/input layer.
  • MVVM-agnostic — works with CommunityToolkit.Mvvm, ReactiveUI, or plain objects.

Packages

Install the main Avalonia package:

dotnet add package Nodely.Avalonia

Optional packages:

dotnet add package Nodely.Algorithms
dotnet add package Nodely.Serialization

Use Nodely.Core directly for headless engine scenarios; it is included transitively by Nodely.Avalonia.

Package Targets What
Nodely.Core netstandard2.0, net8.0, net10.0 UI-agnostic engine: models, behaviors, geometry, routers, path generators, commands.
Nodely.Avalonia net8.0, net10.0 Avalonia controls: DiagramCanvas, DiagramNavigator, theming, adorners.
Nodely.Algorithms netstandard2.0, net8.0, net10.0 Optional: traversal, connected components, layered auto-layout.
Nodely.Serialization netstandard2.0, net8.0, net10.0 Optional: versioned JSON snapshots.

Getting started

using Nodely;
using Nodely.Avalonia.Controls;
using Nodely.Models;
using Point = Nodely.Geometry.Point;

var diagram = new NodelyDiagram();

var a = diagram.Nodes.Add(new NodeModel(new Point(80, 80))  { Title = "Start" });
var b = diagram.Nodes.Add(new NodeModel(new Point(360, 80)) { Title = "End" });
diagram.Links.Add(new LinkModel(a.AddPort(PortAlignment.Right), b.AddPort(PortAlignment.Left)));

var canvas = new DiagramCanvas { Diagram = diagram };   // drop into any Avalonia layout

Drag empty space to pan, scroll to zoom, drag a node to move, drag from a port to another to connect, Shift-drag to marquee-select, Delete to remove, Esc to clear selection.

Customizing nodes (the headline)

public sealed class TaskNode : NodeModel
{
    public TaskNode(Point p, string title) : base(p) => Title = title;
    public string Status { get; set; } = "Pending";
}

canvas.RegisterNode<TaskNode>(node => new Border
{
    Background = new SolidColorBrush(Color.FromRgb(0x2D, 0x4A, 0x6B)),
    Padding = new Thickness(14, 10),
    Child = new TextBlock { Text = $"{node.Title}{node.Status}", Foreground = Brushes.White },
});

diagram.Nodes.Add(new TaskNode(new Point(120, 200), "Build") { Status = "Running" });

Custom links are composed: set a per-link Router / PathGenerator, or change the defaults via diagram.Options.Links. Custom ports/anchors/behaviors are registered explicitly (no reflection scanning).

More features

// Theming
canvas.Palette = NodelyPalettes.Light;          // or NodelyPalettes.Dark (default)

// Read-only inspector (pan/zoom/select work; move/connect/delete blocked)
canvas.IsReadOnly = true;

// View controls
canvas.ZoomToFit();  canvas.ZoomIn();  canvas.ResetView();

// Overview minimap (bind to the same diagram, place anywhere)
var navigator = new DiagramNavigator { Diagram = diagram };

// Snap-to-grid
diagram.Options.GridSize = 24;

// Grouping
diagram.Options.Groups.Enabled = true;
diagram.Groups.Group(a, b);

// Auto-layout (Nodely.Algorithms)
Nodely.Algorithms.LayeredLayout.Arrange(diagram);

// Serialization (Nodely.Serialization)
string json = Nodely.Serialization.DiagramSerializer.Serialize(diagram);
Nodely.Serialization.DiagramSerializer.Deserialize(new NodelyDiagram(), json);

// Undo/redo (Nodely.Commands)
var history = new Nodely.Commands.UndoRedoStack();
history.Execute(new Nodely.Commands.AddNodeCommand(diagram, new NodeModel()));
history.Undo();  history.Redo();

// Toolbar state
canvas.CommandStateChanged += RefreshToolbar;
copyButton.IsEnabled = canvas.CanCopySelection;
pasteButton.IsEnabled = canvas.CanPasteClipboard;
groupButton.IsEnabled = canvas.CanGroupSelection;

Repository layout

src/        Nodely.Core, Nodely.Avalonia, Nodely.Algorithms, Nodely.Serialization
samples/    Nodely.Demo (Avalonia desktop gallery), Nodely.QuickStart (minimal copyable app)
tests/      Nodely.Core.Tests (xUnit), Nodely.Avalonia.Tests (Avalonia headless)
bench/      Nodely.Benchmarks (engine throughput)
memory/     Design decisions (ADRs), research, the development plan, progress, learnings

Build & run

Building the repository requires the .NET 10 SDK (pinned via global.json). Packages ship assets for both net8.0 and net10.0 Avalonia consumers; samples/Nodely.QuickStart targets net8.0.

dotnet build Nodely.slnx
dotnet test  Nodely.slnx
dotnet run --project samples/Nodely.Demo
dotnet run --project samples/Nodely.QuickStart
dotnet pack  Nodely.slnx -c Release   # produces the NuGet packages

License

MIT — see LICENSE and THIRD-PARTY-NOTICES.md.

About

Node-based diagramming toolkit.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages