jellyflow-layout 0.2.0

Optional headless graph layout adapters for Jellyflow.
Documentation

Jellyflow Layout

Optional headless layout adapters for Jellyflow graphs.

The crate keeps layout engines outside jellyflow-core and returns normal Jellyflow transactions so hosts can decide when to apply or animate layout results.

Engine Boundary

jellyflow-layout exposes a small engine protocol:

  • LayoutEngine is implemented by built-in or host-provided engines.
  • LayoutEngineRegistry maps stable LayoutEngineId values to engines.
  • LayoutEngineRequest selects one engine and carries a LayoutRequest.
  • LayoutContext carries runtime-only facts such as measured node sizes and fallback node origin.
  • LayoutFamilyMetadata and LayoutEngineMetadata expose discovery-only family/capability data without changing the stable engine-id dispatch contract.

The built-in dugong engine is registered under DUGONG_LAYOUT_ENGINE_ID ("dugong"). The built-in tidy tree engine is registered under TIDY_TREE_LAYOUT_ENGINE_ID ("tidy_tree"). The built-in radial mind-map engine is registered under MIND_MAP_RADIAL_LAYOUT_ENGINE_ID ("mind_map_radial"). The built-in freeform mind-map engine is registered under MIND_MAP_FREEFORM_LAYOUT_ENGINE_ID ("mind_map_freeform"). It currently keeps the input canvas feel and only resolves overlaps while preserving pinned nodes. Compatibility helpers such as layout_graph_with_dugong remain available, but new host code should prefer the registry path when it needs runtime selection or custom engines. The built-in dugong and tidy tree engines are grouped under the layered_dag family, while the radial and freeform mind-map engines are grouped under the mind_map family.

Presets

For common editor flows, start with LayoutPresetBuilder. Presets only choose an engine and options; they still produce ordinary LayoutEngineRequest values, so hosts can pass them through the registry/runtime path or customize them before execution.

use jellyflow_layout::{LayoutDirection, LayoutPresetBuilder};

let workflow = LayoutPresetBuilder::workflow()
    .with_direction(LayoutDirection::LeftToRight)
    .build();
let tree = LayoutPresetBuilder::tree().build();
let mind_map = LayoutPresetBuilder::mind_map().build();

assert_eq!(workflow.engine.as_str(), "dugong");
assert_eq!(tree.engine.as_str(), "tidy_tree");
assert_eq!(mind_map.engine.as_str(), "mind_map_radial");
use jellyflow_core::Graph;
use jellyflow_layout::{
    LayoutContext, LayoutPresetBuilder, builtin_layout_engine_registry,
    layout_graph_to_transaction_with_engine,
};

fn plan_builtin_layout(graph: &Graph) -> Result<(), jellyflow_layout::LayoutError> {
    let registry = builtin_layout_engine_registry();
    let request = LayoutPresetBuilder::workflow().build();
    let context = LayoutContext::new();

    let tx = layout_graph_to_transaction_with_engine(graph, &request, &registry, &context)?;
    assert_eq!(tx.label(), Some("Layout graph"));
    Ok(())
}

For interactive editors, keep product-specific behavior in custom engines. A radial, freeform, or mind-map engine can implement LayoutEngine, use LayoutContext for measured sizes or pinned nodes, and still return the same LayoutResult / GraphTransaction shape as the built-in engine.