Primitive Shapes

Primitive shapes are what everything is broken down into for rendering. Higher-level blocks (flowchart nodes, charts, cards, …) all lower to these, so targeting a new backend only means re-implementing the primitives.

Every shape shares a few common fields, summarised here; the per-shape tables below reflect the full field set of each shape:

Shared fieldMeaning
idName used to connect the shape (a -> b) and to anchor others to it.
classStyle classes — text and SVG paint (fill, stroke, …) via the class system.
anchor_left / anchor_right / anchor_top / anchor_bottomFractional anchors (0–1) that pin an edge of the shape to the parent box.
connect_pointsWhich sides (:left/:right/:top/:bottom) edges attach to.

The box-like shapes — rect, circle, and container — additionally accept an icon badge: icon (a pack.name), icon_size, icon_pos (:center / :top_left / …), and icon_class.

rect

An axis-aligned rectangle — the workhorse box.

diagram { width = 200  height = 90
  rect { x = 20.0  y = 15.0  width = 120.0  height = 60.0  fill = "#cce"  stroke = "#333" }
}

circle

A circle, positioned by its centre.

diagram { width = 200  height = 100
  circle { cx = 100.0  cy = 50.0  r = 36.0  fill = "#8fbcbb"  stroke = "#333" }
}

line

A straight line segment between two points.

diagram { width = 200  height = 80
  line { x1 = 20.0  y1 = 60.0  x2 = 180.0  y2 = 20.0  stroke = "#bf616a" }
}

label

Hello

An SVG text label. Named label (not text) to avoid clashing with the paragraph block.

diagram { width = 200  height = 60
  label "Hello" { x = 100.0  y = 36.0  font_size = 22.0  fill = "#5e81ac" }
}

polygon

An arbitrary closed shape from a list of points.

diagram { width = 200  height = 100
  polygon { points = "40,80 100,15 160,80"  fill = "#ebcb8b"  stroke = "#333" }
}

container

A grouping box that holds and lays out child shapes (@children). Optional chrome makes the group visible; a layout arranges the children automatically.

diagram { width = 240  height = 130
  container {
    anchor_left = 10.0  anchor_top = 10.0
    fill = "#eef"  stroke = "#88a"  padding = 10.0
    layout = :grid  columns = 2  cell_width = 90.0  cell_height = 44.0  gap = 10.0
    rect { fill = "#88c0d0" }
    rect { fill = "#a3be8c" }
    rect { fill = "#ebcb8b" }
    rect { fill = "#b48ead" }
  }
}

image

A raster image placed as an SVG <image>. The source (the inline label) is a doc-relative path — copied into _wdoc/ — or a URL / data: URI passed through unchanged. (The preview above uses an inline data: URI so it renders here.)

diagram { width = 200  height = 90
  image "logo.png" { x = 50.0  y = 15.0  width = 100.0  height = 60.0 }
}

See Images for the page-level <img> form and asset handling.

card

Note

Rich text inside a diagram.

A box whose body is arbitrary wdoc content (paragraphs, callouts, lists, even nested diagrams), wrapped in an SVG <foreignObject> so it scales with the diagram. Timelines accept cards too, pinned to a date with on.

diagram { width = 260  height = 110
  card { x = 20.0  y = 15.0  width = 220.0  height = 80.0
    title = "Note"
    p "Rich **text** inside a diagram."
  }
}

node_table

users

id: int

email: text

orders

id: int

user_id: int

A titled box built from a stack of rows, each holding arbitrary wdoc content (like a card) and exposing its own connection point — so an edge attaches to a single row (a foreign-key column, a class field) rather than the whole box. Ideal for database / ER tables and UML class diagrams.

An edge targets a row by that row's id (ordersuserid -> users_id); the marker beside each row shows where edges land. Rows are a fixed row_height (the renderer can't measure HTML), so the table's height is derived from its rows. A row attaches on [:west, :east] by default — set a row's connect_points to change which sides expose a port.

diagram { width = 420  height = 170  routing = :straight
  node_table { id = users
    x = 20.0  y = 20.0  width = 150.0
    title = "users"
    node_row { id = users_id    p "id: int" }
    node_row { id = users_email p "email: text" }
  }
  node_table { id = orders
    x = 250.0  y = 20.0  width = 150.0
    title = "orders"
    node_row { id = orders_id      p "id: int" }
    node_row { id = orders_user_id p "user_id: int" }
  }
  orders_user_id -> users_id :data
}