Skip to main content
Capability report version: 0.4.5. Generated from CapabilityReport(). Do not edit by hand — run go run ./tools/gen-docs to regenerate after a code change. Same content as isotopo capabilities JSON, but markdown for skim-reading.

Input formats

Layout engines (.d2 input)

Shapes

Each row is one iso shape with every DSL alias accepted. The height hint is the default extrusion multiplier — agents can override per-part via geom.h.

Composition primitives

group

Where: node.parts[*] Syntax: {shape: group, layout: {mode: row|column|grid|ring}, parts: [...], label: "…", geom: {h}} Wrap N parts in a translucent labeled iso substrate. Position children with layout (or per-child place) — the substrate then auto-sizes around them, so geom.w/d may be omitted. Child offsets are fine-tune deltas relative to the group.

stack

Where: node.parts[*].stack Syntax: stack: {count: N, gap: g} Replicate a part N times vertically with a Z-gap. Bottom copy keeps the id; the rest are suffixed “~k” so connectors can target a specific layer.

canvas-grid

Where: document.canvas Syntax: canvas: {background: "#FAFBFC", grid: iso, gridColor: "#E2E6EE", gridStep: 36} Paint an iso-aware backdrop under the scene. grid: iso draws a diamond rhombus lattice that aligns with the part projection.

annotation

Where: document.annotations[*] | node.annotations[*] (v3.0) Syntax: {anchor: <part-id>, text: "…", side: top|right|bottom|left, distance: 60} Screen-space callout pinned to a composite part. Multi-line text is supported via \n. Placement is collision-managed: the box never crosses or touches any part projection (6px clearance) and never overlaps other text; side/distance are the FIRST candidate, and the box slides to the nearest free peripheral position when occupied.

connector

Where: node.connectors[*] Syntax: {from: <part-id>, to: <part-id>, routing: straight|orthogonal|orthogonal2|bezier, arrow: none|triangle, label: "…"} Directed line between two parts, optionally labeled and orthogonal-routed. Coplanarity contract: the two endpoints should share the same base height (wz / tier) — orthogonal connectors hug the ground plane, so same-tier links render as clean 2D L-shapes. Connecting endpoints at DIFFERENT heights forces a vertical drop segment (riser); do that only to express a deliberate cross-tier call (e.g. gateway layer → data layer), never as a side effect of giving peer nodes different wz.

layout

Where: node.layout | node.parts[*].layout (groups) Syntax: layout: {mode: row|column|grid|ring|auto, engine: elk|dagre, cols: N, gap: 1, padding: 1, align: start|center|end} Auto-arrange a container’s parts along the iso ground axes — the preferred way to position parts. No hand-computed coordinates: row marches along world +x, column along +y, grid wraps row-major after cols, ring puts the FIRST child at the centre and distributes the rest on a circle around it (hub-and-spoke in one rule). A layout group’s geom.w/d may be omitted; the substrate auto-sizes around the arranged content.

place

Where: node.parts[*].place Syntax: place: {rightOf: <sibling-id>, inFrontOf: <sibling-id>, above: <sibling-id>, gap: 1, gapX: 2, gapY: 0, align: start|center|end} Position a part relative to a SIBLING — the preferred way to compose free-standing scenes. One constraint per axis: rightOf/leftOf pins world x, inFrontOf/behind pins world y (front = toward viewer), above pins world z. above is for DECORATIVE stacking — plinths, ghost volumes, toppers (the part sits flush ON TOP of the sibling, centred on its footprint). If an elevated part also needs a connector, keep it on the same tier (wz) as what it connects to, or the link will hang in a vertical drop; reserve above for parts with no connectors. Unpinned ground axes align to the referenced sibling per align. Chains are solved topologically (a stair = each tile rightOf+inFrontOf its predecessor). offset degrades to a fine-tune delta.

preset

Where: theme.presets + node.parts[*].preset Syntax: theme: {presets: {<name>: <Style>}} → part: {preset: <name>} Named design-system style presets — define a Style once in the theme, reference it by name from any part. Merges between the per-shape defaults and the part’s own style block, so parts can still override individual fields. The portable replacement for YAML anchors (works in JSON, validates with suggestions). Typical systems: hero / satellite / ghost.

icon

Where: node.parts[*].icon Syntax: icon: "iso://glyph/<name>[/light|/RRGGBB]" | "iso://si/<slug>[/light|/RRGGBB]" | "iso://brand/<name>" Put an icon on a part’s top face instead of text — the preferred look for showcase scenes. REAL brand logos (iso://si/<slug>, ~150 vendored from Simple Icons, CC0): mysql, postgresql, redis, apachekafka, docker, kubernetes, terraform, openai, anthropic, pytorch, grafana, …; same /light and /RRGGBB variants as glyphs; full list in docs/agent/ICONS.md. Glyphs (generic, stroke style): cloud, database, bolt, chart, globe, shield, lock, gear, cpu, code, layers, rocket, user, mobile, browser, search, bell, queue; default ink, /light = white (dark tops), /RRGGBB = custom. Brand aliases (iso://brand/<name>, resolve to the real logo in its brand color): spark, hadoop, mysql, postgresql, hive, pulsar, kafka, redis, mongo, kubernetes, docker, github, gcp, vite, rolldown, oxc. Unknown iso:// icons are validation errors with nearest-name suggestions. Any other icon value is treated as an external resource: an http(s) URL, a data: URI, OR a LOCAL IMAGE FILE PATH (svg/png/jpg/gif/webp, absolute or relative, ~ and file:// accepted) — local paths are read and inlined as a data URI at render time so the output stays self-contained. Browsable index with previews: docs/agent/ICONS.md.

Style keys

Every field under each style.* sub-block.

Enums

Closed vocabularies the engine accepts verbatim — a value outside the set silently falls back, so emit these exactly. isotopo validate rejects deviations with a nearest-match suggestion.

See also