Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Independent Connections

Use body connector: options for the usual original-mark-to-body connection. Use deixis-connect when a line needs its own source selection or configuration. The small editable example draws two independently styled connections to one body.

For custom objects, deixis-attach(id: <card>)[...] supplies a (node: <card>) endpoint. A routing wrapper collects individual connections and plans them together, using attached obstacles. Existing selectors below remain available inside that wrapper.

Source

#deixis-inset-note(id: <shared>, connector: none,
  layer: "foreground", width: 100pt, dx: 120pt, dy: 50pt,
)[Original passage][One explanation.]
#deixis-mark(of: <shared>, occurrence: <again>)[Another passage]

#deixis-connect(from: <shared>, to: <shared>)
#deixis-connect(from: (mark: <shared>, occurrence: <again>), to: <shared>,
  connector: (route: "curve", heads: (body: "triangle")))

Complete source and preview setup

Output · Click to zoom

A label/string in from selects the annotation’s original mark; one in to selects its body frame. Explicit forms are (mark: id) and (body: id). Add occurrence: name to the mark selector to choose a named repeat. Label-valued IDs resolve to the same annotation as strings of the same spelling; occurrence names stay scoped to that annotation. Neither reference labels nor unnamed repeats become geometric endpoints. Named repeats may be inline frames, region spans, or phantom points. Regions use their fragment on the body’s page, clamped to the first/last fragment when the body is outside the span. A named phantom supplies an intentional zero-size semantic point; a hidden inline repeat still has no drawable frame. See shared notes. To choose a live body content boundary instead of a frame, use (body: id, point: "start") or "end".

Connect Pins Without Creating Notes

If coordinates are already known, numeric routing builds the same native geometry without setup or document declarations. Pins remain useful for measuring actual placed content and selecting its page.

Pin connections also support a control polygon, without declaring any note:

Fragment · Requires surrounding code

#deixis-connect(from: (pin: <source>), to: (pin: <destination>),
  connector: (route: (kind: "b-spline", controls: ((pin: <bend>),))))

Controls bend the curve without promising an exact visit. Use via for exact knots and explicit sections to combine them; see influence curves and the small comparison.

The routing core is also useful for diagrams made entirely of your own content. Attach pins to cards, figures, table cells, or other native elements, then use deixis-connect for the edges. This shares routes, heads, manual cuts, optional crossing gaps, layers, and custom painters with note connectors. The card example is a small complete drawing. No note mark or body is required; setup supplies the page overlays and lookup.

Fragment · Requires surrounding code

#deixis-connect(from: (pin: <out>), to: (pin: <in>),
  connector: (route: "curve", via: ((pin: <bend>),)))

Pins can be either endpoint, including pin-to-pin, mark-to-pin, or pin-to-body connections. A bare string/label keeps its existing note-ID meaning; (pin: ...) makes pin selection explicit. Define the pins directly with deixis-pin or attach them to arbitrary content with deixis-attach. Declarations may precede the pins, and pins inside deferred body renderers work too. No phantom annotation, body, counter, or reference origin is created. See the small editable example.

pin takes a name or (name:, scope:, select:, page:) record. Names/scopes accept equivalent strings/labels; native location scopes isolate component instances. The default requires one unique occurrence. Use first/last or a page restriction to select deliberately; select: "all" must still yield just one point here. Connections use the native pin position, ignoring its region-padding preset. An unconfigured pin is a zero-size, direction-free endpoint. Existing ports.mark/body sides/gaps can constrain departure/arrival. The callback’s endpoint identity contains (pin:, scope:, location:); it is not a note ID.

A pin in ports instead replaces a note’s attachment point while preserving its full bounds. Use (pin: target, side: left, gap: 2pt) there; see custom ports. This requires the node’s page, whereas a pin endpoint chooses its own page and needs no body frame.

Pin waypoints must belong to the route’s page. For a cross-page route, place source-page pins before the existing "split" control and destination-page pins after it, for example via: ((pin: "out-bend"), "split", (pin: "in-bend")). Without a split, waypoints belong to the source side. Wrong-page or intermediate pins are diagnosed instead of reusing their coordinates on another page. The connection retains its existing endpoint-only page-continuation policy.

Ownership And Defaults

Each declaration draws one additional edge. It does not replace, deduplicate, or restyle the body’s implicit connection. Use connector: none on the body when declaring all lines yourself. Several edges can share nodes without narrowing their bounds or candidate ports.

The 0.2 API keeps shorthand on bodies. Creating or reusing a mark never inherits an edge, and there is no mark-side connector: option. Name an occurrence and use deixis-connect to add its connection, including before the original/body. This keeps the simple note call short and makes additional edges explicit.

An explicit declaration defaults to a straight line and foreground. Its connector dictionary accepts the same route, ports, waypoints/segments, heads, stroke, radius, renderer, and layer settings as body connectors. The mark and body port/head names retain their endpoint roles. A head alone works here because the explicit default route is enabled; body shorthand retains its usual disabled route default.

Settings and typography come from the connection declaration, independently of the destination body. Configure explicit connections through the connection type in deixis-set, for example:

Fragment · Requires surrounding code

#deixis-set(
  connector: (connection: (route: "curve", heads: (body: "circle"))),
  stroke: (connection: teal + 0.8pt),
)

Each declaration captures its settings. Later changes do not restyle earlier edges. Explicit calls have no note counter, body, reservation, placement anchor, outline entry, or PDF return-link origin. They are metadata-only in the document flow. connector: none disables the declaration and skips endpoint resolution; its selector shapes are still checked.

Supported Endpoints And Pages

  • Sources: original inline, phantom, or region marks, plus visible named repeated inline frames. Original inline marks keep the existing typographic marker-bound approximation. Use a named repeat to route from a complete short passage box.
  • Destinations: one painted margin or inset body, in any body layer, or an inline body declared with breakable: false. Standalone bodies can be destinations through their ID/public label, but have no original mark to use as a source. Source and destination may belong to different notes.
  • Point destinations: start/end locations from any supported body adapter, including printed endnotes, native page/block footnotes, and unframed inline bodies. The selected point determines the destination page.
  • Forward declarations work for originals, body labels, and named repeats. The declaration can occur anywhere in the document; its position does not anchor the edge. Actual endpoint geometry determines routing.
  • Cross-page routes paint on the source and destination pages in either selected overlay. They retain continuation/head ownership and do not paint intermediate pages or create new pages. A region source uses the fragment on the body’s page when available, otherwise the closest endpoint page of that region, matching the existing inset policy.

Destinations use prepared margin bounds or actual inset corner probes, without measuring or replaying their bodies per edge. Missing/hidden sources, unknown occurrences, unsupported body types, and missing/fragmented/duplicated body frames produce diagnostics outside the painting callback. Hidden named repeats keep their return-link identity but deliberately have no routing ports. Every frame also needs one complete live body span and destination on its page. An inset whose live boundaries fall outside its proposed frame cannot be selected as a whole body; this includes continuation into another column on the same page. Choose an explicit start/end point for such a body.

Connect To A Collected Or Paginated Body

Choose a body’s live content boundary to connect to an endnote or footnote, including a long body that continues across pages:

Source

#deixis-endnote(id: <detail>)[An explanation printed later.]
#deixis-connect(from: <detail>,
  to: (body: <detail>, point: "end"))
#deixis-print-endnotes()

Complete source and preview setup

Output · Click to zoom

point: "start" selects the native location before the live body payload; "end" selects the location after it, on its final printed page. These are zero-size points. They do not enclose the marker, container, or backlinks and do not measure glyphs. For block content, Typst commonly reports them at the leading edge of the first/last content line, rather than after the final glyph. Use directional ports.body and its gap to keep arrowheads clear of text. Body-relative waypoints use the selected point as their reference position.

Native, aligned, grid, and column endnote layouts retain their own pagination. Native page/block footnotes keep their reservations and allocation. The printer must actually print the body; a connection does not trigger printing or create a reservation. Declarations can precede the original, body, or printer. Repeated or overlapping printers still emit one body. See the small editable example.

Points also work on margin/inset and ordinary inline bodies without imposing a single-frame layout. Their native start/end positions, recorded by the live renderer, determine routing. Exactly one complete start/end span and one live body destination are required. Missing/unprinted, suppressed, or duplicated body output is diagnosed outside painting; an incomplete span is never silently replaced with a link destination.

The default point: none, or an omitted field, keeps the complete-frame contract below. A point selection does not add a reference origin, change a note number, move a body, or change native wrapping. Start/end points are supported now; rectangular continuation fragments and selection by arbitrary page remain deferred. Start/end points also follow bodies that continue between columns on the same page, in either text direction. A matching start/end page alone does not mean the body fits one frame.

Connect To A Body In Normal Flow

Inline bodies can supply a complete frame without changing their declaration’s placement role. Opt into one native nonbreaking frame with breakable: false:

Source

#deixis-mark(id: <idea>)[A new idea]
#deixis-inline-note-body(of: <idea>, inline: true,
  breakable: false)[A short explanation]
#deixis-connect(from: <idea>, to: <idea>)

Complete source and preview setup

Output · Click to zoom

With inline: true, the frame is an inline box, so the complete note stays together on one line. With inline: false, it occupies a nonbreaking block. It may move to the next line/page when needed. Its native frame includes the rendered marker, body, backlinks, and container. Ports use the four live corners; surrounding native transforms produce their page-space envelope. A child that paints outside its native frame remains outside those bounds.

The default breakable: true keeps the existing rendering path; selecting it as a complete-frame destination reports that a single frame is required. Explicit start/end point targets remain available. Adding or disabling edges does not change whether/how the body is framed. No new body or reference origin is created per incoming edge. Forward IDs, named sources, standalone bodies, and both connection layers work as for margin/inset targets.

A custom single renderer must preserve exactly one live rendered span and body destination, as built-in renderers do. Suppressing or duplicating its output cannot produce a valid frame merely because an outer box exists. Long bodies should keep native pagination; a nonbreaking body can overflow an undersized page, as native nonbreaking containers do. Fragment selection remains separate work. See the small editable example.

Routing Limits

Explicit edges use the generic port/waypoint router. Margin body shorthand keeps its existing lane staggering and spillover relay heuristics; extra independent edges do not automatically join those lanes. Ports and waypoints can constrain routes, but do not provide general obstacle avoidance.

Complete footnote/endnote frames and arbitrary continuation rectangles need separate fragment/geometry contracts; explicit start/end points are supported. The declaration cannot force an endnote printer or reserve a native footnote at another location. Custom renderers must preserve the single-frame contract and rendered-span metadata; rendering the passed body zero or several times is not a selectable destination. Immediate body transforms retain the placement limits described in rendering. Use connector.z-index for independent ordering within that overlay; paint order explains ties, atomic groups, and native state/layer boundaries.

See connectors for route/head/renderer contracts and body ownership for page allocation and placement rules. Modern painters receive canonical source/target identities under request.route.document.connection; legacy painters receive connection in their request. Neither contains live body content. For a point destination, the target identity is (annotation: ..., point: "start" | "end") and the reported body bounds have zero width/height. Complete-frame target identities keep their existing form.