Skip to content

Latest commit

 

History

906 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

⚡ VeloxDev

Build modern, AI-controllable workflow editors on any .NET GUI — WPF, Avalonia, WinUI, MAUI, WinForms, Razor, or Jalium.

WPF Avalonia WinUI MAUI WinForms Razor Jalium

NuGet NuGet License: MIT GitHub


📖 Wiki — Online (WASM) · Local — the online Wiki is a WebAssembly app, so its load speed depends on your network.


What this is — a node editor / node-graph / workflow-editor framework for .NET / C#. Drag nodes, wire slots into links on a zoomable, virtualized canvas, drive the graph with a compiled, pull-based execution engine, gate structural edits behind undo/redo, and control it all through an AI agent (function calling + MCP). One model → 7 GUIs: WPF · Avalonia · WinUI · MAUI · WinForms · Blazor · Jalium.

✨ What is VeloxDev?

VeloxDev gives .NET developers a complete foundation for building interactive workflow editors — the kind where users drag nodes, wire slots together, and watch data flow through a graph at runtime.

Three ideas hold the whole project together:

  1. One model, every GUI. The workflow model, compile-time identity, runtime engine, serialization and the command/undo-redo stack live in VeloxDev.Core with zero UI dependencies. Platform adapters (WPF, Avalonia, WinUI, MAUI, WinForms, Razor, Jalium) supply the views plus the platform glue only they can provide — timers and frame pacers, thread marshalling, native value conversion. Your graph data and its execution semantics behave identically on every platform.
  2. A real execution engine, not just a canvas. Besides the drag-and-drop surface, CompilerEx compiles any reachable sub-graph into a plan (linear chain / router branch / fan-out group) and drives it deterministically — including reverse (Terminal) compilation: ask "what would this node output?" and it computes just the ancestor cone that feeds it, with no controller needed. Fan-out branches run concurrently as interleaved async operations, not as threads — every branch starts on the caller's context and each await inside it yields to its siblings, so I/O-bound branches (a node invoking a process, say) overlap while the group stays on the host's SynchronizationContext. A branch that burns CPU still occupies the thread in turn: a compiled run shares one runtime blackboard, which is deliberately not thread-safe.
  3. AI is a first-class controller. A 60+ function-calling Workflow Agent lets an LLM inspect, build and mutate graphs at runtime through natural language — with the same undo/redo, validation and lifecycle the GUI uses, plus optional MCP tool connectivity.

Why not just a WPF node editor? Libraries like Nodify and NodeNetwork are excellent, but they are WPF-only canvases — they draw the graph and stop there. VeloxDev runs the same node graph on Avalonia, WinUI, MAUI, WinForms and Blazor, and layers on what a canvas alone cannot give you: a compiled forward + reverse execution engine, and an AI agent that edits and runs the graph through the same commands the GUI uses.

The workflow system

Layer What it provides Dependency
⛓️ Workflow Tree / Node / Slot / Link templates with undo-redo, spatial indexing, deep-zoom canvas math, serialization, and a compiled execution engine (forward + reverse)
🤖 Workflow Agent 60+ Function Calling tools — an AI can create nodes, wire slots, patch properties and run chains at runtime via natural language. Supports MCP (Model Context Protocol) for external tools/data. Ships bilingual (en/zh) embedded prompt docs so the agent knows exact tool semantics. VeloxDev.Core.Extension

Other building blocks you can reuse

Layer What it provides Dependency
🪶 MVVM Source generators for observable properties and async, cancellable commands — keeps node ViewModels lightweight
🎞️ Transition Cross-platform interpolation animation with easing & Fluent API — smooth visual feedback for workflow state changes Platform Adapter Package
🎨 Theme Runtime theme switching with animated transitions — instant visual identity for your editor Platform Adapter Package
🌀 AOP Generated aspect interfaces with runtime proxies — intercept node members for logging or validation without modifying business logic
⚙️ Tickable Frame-driven lifecycle loop — tick-based node simulation or real-time graph execution

Platform adapter packages

Platform Package NuGet
WPF VeloxDev.WPF NuGet
Avalonia VeloxDev.Avalonia NuGet
WinUI VeloxDev.WinUI NuGet
MAUI VeloxDev.MAUI NuGet
WinForms VeloxDev.WinForms NuGet
Razor VeloxDev.Razor NuGet
Jalium VeloxDev.Jalium NuGet

Adapter API docs: WinForms · Razor


🧠 Core concepts (60 seconds)

  • A workflow is a Tree of Nodes. Every Node is a ViewModel + a Helper (component/helper pattern). Nodes own Slots; slots are wired into Links. A slot has a channel (one/many, sender/receiver/both) that governs how connections are validated.
  • Views are templates over the model. The adapter's WorkflowSurface hosts Node/Slot/Link views (plus grid decorator, minimap, ruler and tree views), virtualized against a spatial index so graphs with thousands of nodes stay fluid.
  • Structural edits are undoable commands. Create/delete node or slot, connect/disconnect, selector changes and their cascades all flow through IVeloxCommand with a redo/undo pair — and the Agent tool layer uses the same commands the GUI does. Position and size are outside that history: SetAnchor/SetSize are commands with no undo entry, in the GUI and in the Agent alike.
  • Execution is compiled, then driven. Nodes expose one receive entry ReceiveCommand → Helper.ReceiveAsync(ITaskContext, ct). The engine (RuntimeEngine) compiles a sub-graph into segments once and then pulls data through them — nodes do not broadcast to each other on their own during a compiled run.
  • Phases are explicit. Compile time assigns every node a fixed identity (Order/ChainIndex/Offset) and runs dataflow validation without data; runtime reuses the same AccessAsync gate with real payloads. A context hierarchy (IContext → IAccessContext → ITaskContext, plus ICompileContext / IRuntimeContext) describes every hand-off.

⚙️ Execution model — compile once, run deterministically

CompilerViewModel has one API, CompileAsync(node, role):

public enum CompileRole { Root, Terminal }   // the node is a starter, or the result you want
Role Meaning What it compiles
Root The node starts a run (e.g. a controller) Its reachable sub-graph, walking downstream along Targets
Terminal You want this node's result Its ancestor cone — the producers feeding it, walked backward along Sources — starting automatically from the cone's entry frontier
var compiler = new CompilerViewModel();

// 1) Forward: run the whole chain from a controller
var graph = (await compiler.CompileAsync(controller, CompileRole.Root))[0];
var session = new RuntimeContext();
await new RuntimeEngine().RunAsync(graph, session);
Console.WriteLine(session.Data);            // the chain's final payload

// 2) Reverse: compute just one node's value — no start node needed
var cone = (await compiler.CompileAsync(someNode, CompileRole.Terminal))[0];
var probe = new RuntimeContext { Target = someNode };
await new RuntimeEngine().RunAsync(cone, probe);

if (probe.TargetReached) Console.WriteLine(probe.Data);   // that node's output
else Console.WriteLine("target NOT reached — no value fabricated");

The plan it produces is a small tree of segments: a linear chain, a router branch (a node implementing ICompileTimeRouter, Static or Dynamic), and fan-out groups (branches off the same source, driven concurrently as interleaved async operations) — and it is always acyclic. Looping is expressed as runtime redirects instead of graph cycles: when a node signals an error, the engine checks for IRedirectable and, if the node implements it, re-runs the graph toward the returned target under an internal retry limit. Redirects are therefore an extension point you implement on your own nodes — the engine supports them end to end, and the demo's Python node is the reference implementation (its script names the node to fall back to, by title, and the node resolves that to a compile order).

Three properties worth calling out, because they keep reverse compilation honest:

  • Branches are real, never bypassed. Terminal compilation keeps a router's true BranchSegment behavior; it only compiles the branch that leads into the target's cone. If the router actually selects a sibling branch at runtime, the target is not reached — you get an explicit "… was NOT reached … No result was produced." outcome, never a fabricated value. (Agent tools surface this as a specific error message; set the router's selection to the right branch and retry.)
  • Joins aggregate by source. A multi-input node receives an IGroupData — a read-only map keyed by its upstream node — so a join "waits for all inputs" however the fan-out's branches interleaved (the shared runtime session is intentionally not thread-safe, which is why a branch that burns CPU still takes the thread in turn rather than running beside its siblings).
  • TargetReached means "the target was driven", not "a value was produced". The flag is set as the engine enters the target node, so read it together with the run status: a target that throws surfaces as a run error (Run failed: …), not as a reached target.
// Minimal "node" — the generator wires INotifyPropertyChanged, slot lifecycle and commands.
[WorkflowBuilder.Node<MyNodeHelper>]
public partial class MyNodeViewModel
{
    public MyNodeViewModel() => InitializeWorkflow();

    [AgentContext(AgentLanguages.English, "Input slot (receiver)")]
    [VeloxProperty] public partial MySlotViewModel InputSlot { get; set; }

    [AgentContext(AgentLanguages.English, "Output slot (sender)")]
    [VeloxProperty] public partial MySlotViewModel OutputSlot { get; set; }

    [VeloxProperty] private string title = "My Node";
}

🤖 AI control

The Workflow Agent turns a workflow Tree into a tool surface for any IChatClient (Microsoft.Extensions.AI), so an LLM can inspect the graph, build nodes, connect slots, patch properties, and run — or reverse-compute — compiled chains:

var scope = tree.AsAgentScope()
    .WithAutoDiscovery(assemblyName: "MyApp")
    .WithInteractionSafety(3)          // confirm before destructive ops; present choices via tool
    .WithSelectionHandler(ShowDialog)
    .WithConfirmationHandler(ShowDialog);

var agent = chatClient.AsAIAgent(
    instructions: scope.ProvideProgressiveContextPrompt(),
    tools: scope.ProvideTools());

Highlights of the tool surface:

  • Inspect & mutate like the GUI — ListNodes, GetFullTopology, CreateNode, ConnectByProperty, PatchNodeProperties, SetEnumSlotCollection, Undo/Redo, MoveNode, … every mutation dispatches the same component command the GUI dispatches, so the Agent and the GUI share one edit path — including the same undo semantics, i.e. the moves and property patches that create no undo entry.
  • Execute at three levels — node-level (ExecuteNode), chain-level (RunCompiledWorkflow, Root role), and result-level (GetNodeResult, Terminal role). Plans can be read without running via CompileWorkflow / CompileNodeResult.
  • Gated by policy, not just prose — node-execution tools are disabled until the host calls WithAllowNodeExecution(true); generic command execution is allow-listed; interaction tools appear only when a selection/confirmation handler is wired. MaxToolCalls, MaxReadToolCalls and MaxWriteToolCalls bound a session.
  • Precision is baked into the prompt, in the host's language — embedded (en/zh) prompt docs describe tool semantics, error/rejection handling, mount-before-operate and the exact "target not reached" contract, so the agent knows before calling what each tool does and what errors mean.

🔌 Connect MCP servers for external tooling

var mcp = new McpScope()
    .WithMcpRoot(".evn/mcp")
    .WithSynchronizationContext(SynchronizationContext.Current);

var configs = new[]
{
    // Local stdio server (npx)
    new McpServerConfiguration
    {
        Name = "Filesystem",
        RunMode = McpServerRunMode.Npx,
        Package = "@modelcontextprotocol/server-filesystem",
        Arguments = ["C:/data"],
    },
    // Remote server over Streamable HTTP (SSE fallback for legacy servers)
    new McpServerConfiguration
    {
        Name = "Microsoft Learn",
        RunMode = McpServerRunMode.Http,
        Endpoint = "https://learn.microsoft.com/api/mcp",
        Options = new { connectionTimeout = 30 },
        // Header auth:  Options = new { headers = new { Authorization = "Bearer <token>" } }
        // OAuth 2.0:    Options = new { oauth = new { clientId = "...", redirectUri = "...", scopes = new[] { "read" } } }
    },
};

var mcpTools = await mcp.LoadAsync(configs);
var allTools = scope.ProvideTools().Concat(mcpTools).ToArray();   // merge into the agent

McpScope installs npm packages idempotently, manages stdio/HTTP transports, reports per-server failures without blocking the rest, and supports OAuth via WithOAuthAuthorizationRedirect(...).


📦 Installation

Install one of the platform adapter packages listed above and you get everything — workflow, execution engine, agent, animations and theming — wired up for that GUI.

Generate a view suite from templates

Each adapter ships a dotnet new template pack that generates the full view suite — Node, Slot, Link, Tree, template selector, grid decorator and minimap — pre-wired to the model. WPF example (replace MyApp with your root namespace):

dotnet new install VeloxDev.WPF.Templates
dotnet add package VeloxDev.WPF

dotnet new wpf-v-slot -n SlotView -ns MyApp.Views -o Views
dotnet new wpf-v-node -n NodeView -ns MyApp.Views -o Views
dotnet new wpf-v-link -n LinkView -ns MyApp.Views -o Views
dotnet new wpf-v-selector -n TemplateSelector -ns MyApp.Views -o Views
dotnet new wpf-v-decorator -n GridDecorator -ns MyApp.Views -o Views
dotnet new wpf-v-minimap -n MinimapOverlay -ns MyApp.Views -o Views
dotnet new wpf-v-tree -n TreeView -ns MyApp.Views -o Views

dotnet build

Avalonia, WinUI, MAUI, WinForms and Jalium suites expose the same style options (jalium-v-* for Jalium). Common style aliases:

Template Style aliases
Node -bg background, -fg foreground, -bb border brush, -bt border thickness, -cr corner radius
Slot -bg background, -sc standby color, -bc border color, -sp SVG path data
Link -lc line color, -lt line thickness
Tree -bg background, -bb border brush, -bt border thickness, -cr corner radius
Grid decorator -bg background, -mic minor color, -mac major color, -ac axis color, -gs spacing, -mle major interval, -rb ruler background, -rtc ruler tick color, -rlc ruler label color, -rdc ruler divider color
Minimap overlay -bg background, -bdr border, -nf node fill, -vs viewport stroke

All templates use -ns for the generated namespace.


🗂️ Repository Layout

VeloxDev/
├── VeloxDev.slnx
├── Src/
│   ├── Core/
│   │   ├── VeloxDev.Core                 # Workflow model, generators, canvas math, CompilerEx engine
│   │   ├── VeloxDev.Core.Extension       # Workflow Agent tools, MCP scope, runtime extensions
│   │   ├── VeloxDev.Core.Test            # Unit tests (model / canvas / execution)
│   │   └── VeloxDev.Core.Extension.Test  # Unit tests (agent tools / serialization / lifecycle)
│   ├── Adapters/
│   │   ├── VeloxDev.WPF · VeloxDev.Avalonia · VeloxDev.WinUI · VeloxDev.MAUI
│   │   ├── VeloxDev.WinForms · VeloxDev.Razor · VeloxDev.Jalium   # one view layer per platform
│   ├── Generators/
│   │   └── VeloxDev.Core.Generator       # Roslyn source generators (netstandard2.0)
│   └── Templates/                         # dotnet new item template packs per GUI adapter
├── Examples/
│   ├── Workflow/     # WPF · Avalonia · WinUI · WinForms · MAUI · Razor · Jalium
│   │                #   (+ "Trimmed" variants that compile no extraneous generator code)
│   │                #    Common/Lib holds the shared demo node library (Controller, routers, python workers)
│   ├── MVVM/         # WPF · Avalonia
│   ├── Transition/   # WPF · Avalonia · WinUI · WinForms · MAUI · Razor · Jalium
│   ├── Theme/ · AOP/ # WPF · Avalonia
│   └── Tickable/        # WPF
└── Docs/
    └── VeloxDev.Docs # Documentation site (WebAssembly: online/local wiki)

🧪 Tests & coverage

Two test suites live beside the source (VeloxDev.Core.Test, VeloxDev.Core.Extension.Test) and are deliberately fast (~500+ assertions with no I/O in the hot path):

dotnet test                          # or open VeloxDev.slnx and run in Visual Studio
# targeted:
dotnet test Src/Core/VeloxDev.Core.Test --filter "FullyQualifiedName~CompilerEx"

Coverage is collected with coverlet (--collect:"XPlat Code Coverage"). The CompilerEx execution engine — compile decomposition, runtime driving, redirects, joins, and reverse compilation — is covered end-to-end with self-contained contract tests (probe nodes, no UI/no demo dependency). A full XML-comment/language pass keeps the public API documented in English.


⚠️ Scope & status

Worth knowing before you depend on them:

  • Not AOT- or trim-safe, and it says so. VeloxDev.Core declares IsTrimmable=false, and the animation path compiles expression trees at runtime (Expression.Lambda(...).Compile()). There are no RequiresUnreferencedCode / DynamicallyAccessedMembers annotations anywhere in it. (The Examples/*/"Trimmed" folders are minimal demos — the name is not about trimming configuration.)
  • Redirects are a contract, and one node ships with it. The engine drives IRedirectable end to end; the demo's Python node is the reference implementation (its script names the node to fall back to and the node resolves that name to a compile order), and the demo graph uses it to send a run back when the data is too thin to trust. A redirect target must still be strictly backward, and a target inside a nested branch is out of reach — see the limitation noted with the demo graph.
  • Undo coverage is structural. Node/slot create and delete, connect/disconnect, selector changes and their cascades are undoable; position, size and direct property patches are not.
  • Fan-out overlaps, but it is not thread parallelism — branches are interleaved async operations on the host's SynchronizationContext (see above). A compiled run shares one runtime blackboard, which is intentionally not thread-safe, so a node that starts its own Task.Run is on its own.
  • Waiting, sleeping and offloading CPU work are node-body decisions, not engine features. ReceiveAsync is async, so a timer is await Task.Delay(...) inside the node, an external signal is awaiting a TaskCompletionSource, and real parallelism for one branch is Task.Run — subject to the caveat above about not touching the shared session from that thread. Timing and threading therefore look absent from the engine's feature list rather than missing from it. What does not follow from this is a wait that outlives the process: a run in flight lives in memory, so a durable wait rides on the checkpoint — save the place, reload the graph, carry on — and ExecutionCheckpoint.Rekey is what makes the reload legal, since a restored graph has fresh node identities.
  • A run can be paused, watched, retried, checkpointed and resumed — off by default. RuntimeContext takes an IExecutionGate, an IExecutionObserver, an INodeRetryPolicy, an IExecutionErrorSink, an IExecutionCompensation and an IExecutionCheckpointStore; with none configured a run behaves exactly as it did before they existed, down to the log lines and the number of node drives. What still has no shipped implementation is IRedirectable (a node's own fallback contract) — see the note above.
  • Animation overshoot is per-type today. When an ease overshoots (Back/Elastic), numeric samplers extrapolate past the endpoint while the remaining samplers pin to it. Unifying the two is an open pass.
  • The one model / 7 GUIs seam is at data and geometry, not rendering. The transition engine and the workflow surface math live in Core and each adapter contributes only a small pacer subclass, but the view layer (surface behaviour, virtualization window, pooling, link rendering) is implemented per platform.

📄 License

Released under the MIT License. © 2025 Axvser

About

Node editor & dataflow workflow framework for .NET/C#: one model, seven GUIs — WPF, Avalonia, WinUI, MAUI, WinForms, Blazor, Jalium. Zoomable node-graph canvas, spatial-index virtualization, compiled deterministic execution (forward + reverse), undo/redo, and an AI workflow agent with MCP.

Topics

Resources

Stars

40 stars

Watchers

0 watching

Forks

Contributors

Languages