11 Extending typograph
11.1 Where things live
| Concern | Module |
|---|---|
| Diagram item tags, vector math, direction parsing | src/utility.typ |
| Coordinate flip, inset resolution, rotation/flip math, outline-radius geometry | src/geometry.typ |
| Style dictionaries: defaults, merging, validation | src/style.typ |
| Shape builders and the custom-builder contract | src/shape.typ |
Node constructors, node-type(), make-node() |
src/node.typ |
Edge parsing, curve evaluation/splitting, edge-type() |
src/edge.typ |
| Silhouette intersection, endpoint clipping, deferred resolution | src/diagram.typ |
| Theme values and validation | src/theme.typ |
Scoped non-theme defaults (config()) |
src/config.typ |
place(), group() |
src/content.typ |
| Orchestration: passes, bounds, final layout | src/diagram.typ |
| Public facade | src/lib.typ |
| Optional ZX interpretation | Separate typograph-zx companion package |
11.2 Adding a new theme kind
Almost never requires touching the package at all — see Theming. node-type()/ edge-type() plus a node-presets/edge-presets entry covers the large majority of “add a new kind of node/wire” requests.
11.3 Adding a new shape builder
Also doesn’t require touching the package, as long as the geometry reduces to one of the six outline kinds the renderer already understands (empty, bare, circle, ellipse, rectangle, polygon) — see Custom shape builders. Test a new builder in isolation with typ.shapes.build-outline before wiring it into a node-type() constructor; it’s the same validator the renderer itself calls at that boundary. Validation checks the outline protocol, not every visual property: also test label containment, bounds, clipping, and any ports on a real diagram. Polygon simplicity is the builder author’s responsibility.
11.4 Adding a genuinely new outline primitive
This is the one case that does require package changes — an open path, a compound shape, or a silhouette with a hole isn’t expressible as any existing outline kind. It needs, at minimum:
- A new
kindvalue in the outline schema (shape.typ), plus its required fields. typ.shapes.build-outlinevalidation for the new kind’s fields.- Drawing logic wherever outlines become Typst elements (
node.typ’sdraw-outlinehelpers). - Bounds logic — how the new kind’s extent folds into the diagram’s overall bounds.
- Clipping logic — how an edge finds its exit point against the new silhouette (
diagram.typ’s outline-intersection code), including whether an analytic intersection is possible or a sampled search is required (see Performance Model). - A port-projection rule, if the new kind should support gate-style ports.
Each of these is a real, separate piece of work — they don’t share an abstraction today, because each one answers a different question about the same outline (how does it draw, how big is it, where does a ray exit it, where do ports sit on it). Before adding a new kind, check whether the shape is actually expressible as an existing one first (a “hole” is sometimes better modeled as two overlapping nodes than as a true compound outline, for instance) — extending the outline schema is a last resort, not the first tool to reach for.
11.5 Testing changes
Whatever you change, bash tests/run.sh before and after — see Testing for what the suite actually checks and how to add a new case to it.