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 field | Meaning |
|---|---|
| id | Name used to connect the shape (a -> b) and to anchor others to it. |
| class | Style classes — text and SVG paint (fill, stroke, …) via the class system. |
| anchor_left / anchor_right / anchor_top / anchor_bottom | Fractional anchors (0–1) that pin an edge of the shape to the parent box. |
| connect_points | Which 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
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
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
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
}