The graph and system modelling toolkit

Compose
systems.Connect ideas.

Foldkit Zloz is a functional, extensible graph toolkit for interactive node editors, C4 architecture modelling, and Mermaid export.

Order platform / System contextC4
An order platform in contextA customer places an order. The order platform charges through an external payment system and dispatches through an external delivery system. Static preview of a real Zloz model.PersonCustomerPlaces an order.Software systemOrder platformCoordinates checkout.Software systemPaymentsCaptures payment.ExternalSoftware systemDeliveryShips the order.ExternalPlaces orderChargesDispatches
One model. People, systems and relationships.Static package-generated preview

Compose. Connect. Communicate.

More than a flow editor.
A system composition toolkit.

01 / Compose

Give ideas
a structure.

Build interactive graphs with nodes, edges, ports and connections. Shape the experience through composable renderers, policies and themes.

Try Compose
02 / Model

Make boundaries
visible.

Describe people, software systems and containers with built-in C4 semantics. Keep architectural meaning in the model, independent of its visual form.

Download this C4 model
03 / Communicate

Let your model
travel.

Export Mermaid for documentation and collaboration. Review conversion losses and preserve identity metadata alongside the diagram source.

Inspect the Mermaid export

Compose / Live Foldkit example

A small workflow.
Yours to connect.

Select a node, connect Review to Publish, and undo the change. The same package owns the graph, input handling and history.

This example resets on reload. Explore the full workbench ↗

Read the Model and update source
import {
  createEditor,
  updateEditor,
  type EditorEvent,
  type EditorState,
  type ZlozGraph,
} from '@opsydyn/foldkit-zloz';
import { Schema } from 'effect';
import { defineMessageUnion } from 'foldkit/message';
import type { Return } from 'foldkit/update';

const input = { id: 'in', label: 'Input', kind: 'input', side: 'top', offset: 0.5 } as const;
const output = { id: 'out', label: 'Output', kind: 'output', side: 'bottom', offset: 0.5 } as const;

/** Vertical workflow keeps node text legible at both desktop and narrow widths. */
const initialGraph: ZlozGraph = {
  nodes: [
    {
      id: 'draft',
      label: 'Draft',
      position: { x: 80, y: 30 },
      size: { width: 220, height: 80 },
      ports: [output],
    },
    {
      id: 'review',
      label: 'Review',
      position: { x: 80, y: 180 },
      size: { width: 220, height: 80 },
      ports: [input, output],
    },
    {
      id: 'publish',
      label: 'Publish',
      position: { x: 80, y: 330 },
      size: { width: 220, height: 80 },
      ports: [input],
    },
  ],
  edges: [
    {
      id: 'draft-review',
      source: { node: 'draft', port: 'out' },
      target: { node: 'review', port: 'in' },
      pathStyle: 'smooth',
    },
  ],
};

// Opaque package state is constructed only by createEditor/updateEditor, never decoded from props.
export const Model = Schema.Struct({ editor: Schema.Unknown });
export type Model = Readonly<{ editor: EditorState }>;
export const Message = defineMessageUnion({ ObservedZlozInput: { event: Schema.Unknown } });
export type Message = typeof Message.Type;

export const observe = (event: EditorEvent): Message => Message.ObservedZlozInput({ event });

export const init = (): Return<Model, Message> => ({
  model: {
    editor: createEditor(initialGraph, {
      fitViewPadding: 24,
      zoomLimits: { minZoom: 0.2, maxZoom: 1.25 },
    }),
  },
});

export const update = (model: Model, message: Message): Return<Model, Message> =>
  Message.match(message, {
    ObservedZlozInput: ({ event: raw }) => {
      // SAFETY: zlozView and the controls below emit the public, typed EditorEvent contract.
      const event = raw as EditorEvent;
      const next = updateEditor(model.editor, event);
      const editor =
        event.type === 'SurfaceMeasured' && model.editor.surface === null
          ? updateEditor(next, { type: 'FitViewClicked' })
          : next;
      return { model: { ...model, editor } };
    },
  });

Open by nature

From the canvas
to the conversation.

This is the actual export of the context model above. Mermaid is a projection: canonical JSON retains the full editable model.

Download source, identity and loss report
Read Mermaid source and conversion notes
flowchart LR
  n_63_75_73_74_6f_6d_65_72("Customer")
  n_6f_72_64_65_72_73["Order platform"]
  n_70_61_79_6d_65_6e_74_73["Payments"]
  n_64_65_6c_69_76_65_72_79["Delivery"]
  n_63_75_73_74_6f_6d_65_72 e_70_6c_61_63_65_73_2d_6f_72_64_65_72@-->|"Places order"| n_6f_72_64_65_72_73
  n_6f_72_64_65_72_73 e_63_68_61_72_67_65_73@-->|"Charges"| n_70_61_79_6d_65_6e_74_73
  n_6f_72_64_65_72_73 e_64_69_73_70_61_74_63_68_65_73@-->|"Dispatches"| n_64_65_6c_69_76_65_72_79

What changes in conversion

  • Not represented: architecture.
  • Port IDs/kinds remain in identity metadata. Port labels, ordering, offsets and anchors are omitted from source.
  • Not represented: architecture.
  • Port IDs/kinds remain in identity metadata. Port labels, ordering, offsets and anchors are omitted from source.
  • Not represented: architecture.
  • Port IDs/kinds remain in identity metadata. Port labels, ordering, offsets and anchors are omitted from source.
  • Not represented: architecture.
  • Port IDs/kinds remain in identity metadata. Port labels, ordering, offsets and anchors are omitted from source.
  • Not represented: pathStyle.
  • Not represented: pathStyle.
  • Not represented: pathStyle.
  • Authored positions and dimensions are not represented; Mermaid chooses layout.
  • Source uses safe aliases. Keep the bundle identity metadata to retain original node, edge and port IDs.

Functional at heart

Pure functions, composition and immutable data. State and changes stay explicit.

Headless by design

Separate the graph model from its visual form. Use the core on its own.

Extensible by default

Build your own nodes, edges, layouts and tools through public contracts.

Open by nature

Models should travel beyond the editor, not be trapped inside it.

A Foldkit family member

Fold. Assemble.
Put together.

Zloz comes from Złóż: fold, assemble, put together. What began as a node-and-edge editor has grown into a toolkit for composing, exploring and communicating complex systems.

Foldkit is the UI toolkit. Zloz is its graph and system modelling toolkit.

Start with the model