A high-performance, extensible node-based editor framework -- ferrum-flow

Hello everyone, I’m glad to introduce an open-source project I’ve been working on recently: ferrum-flow.

It’s a node editor framework built with gpui, featuring core capabilities such as node rendering, node dragging, connections, box selection, as well as viewport zooming and panning.

Beyond the most essential features, most functionalities are implemented as plugins. Our plugin system is both flexible and powerful—I’ll likely write a separate post to explain its architecture in detail.

In addition, the framework includes built-in support for real-time multi-user collaborative editing. The attached demo GIF showcases this feature.

ferrum-flow0427

Plugin System

Plugins are the primary extension mechanism:

pub trait Plugin {
    fn name(&self) -> &'static str;

    fn setup(&mut self, ctx: &mut InitPluginContext);

    fn on_event(&mut self, event: &FlowEvent, ctx: &mut PluginContext) -> EventResult;

    fn render(&mut self, ctx: &mut RenderContext) -> Option<AnyElement>;

    fn priority(&self) -> i32 {
        0
    }

    fn render_layer(&self) -> RenderLayer {
        RenderLayer::Overlay
    }
}

Responsibilities

A plugin can:

  • Handle input events
  • Start interactions
  • Render UI layers
  • Modify graph state

Starter example for custom plugin development:
crates/core/examples/plugin.rs

Interaction System

Interactions represent ongoing user actions, such as:

  • Node dragging
  • Box selection
  • Viewport panning
pub trait Interaction {
    fn on_mouse_move(&mut self, event: &MouseMoveEvent, ctx: &mut PluginContext) -> InteractionResult;

    fn on_mouse_up(&mut self, event: &MouseUpEvent, ctx: &mut PluginContext) -> InteractionResult;

    fn render(&self, ctx: &mut RenderContext) -> Option<AnyElement>;
}

Interaction lifecycle:

Start -> Update -> End / Replace
pub enum InteractionResult {
    Continue,
    End,
    Replace(Box<dyn Interaction>),
}

Command System (Undo / Redo)

Implements the Command Pattern:

pub trait Command {
    fn execute(&mut self, ctx: &mut CommandContext);
    fn undo(&mut self, ctx: &mut CommandContext);
}

Built-in features:

  • Undo / Redo stacks(using ⌘ + z / ⌘ + Shift + z or Ctrl + z / Ctrl + Shift + z)
  • Composite commands
  • Easy integration via PluginContext
ctx.execute_command(MyCommand { ... });

GitHub: GitHub - tu6ge/ferrum-flow: A high-performance, extensible node-based editor framework · GitHub

I’d really appreciate your feedback.

1 Like