- Bracket ink is part of the fit: \st@facebody drops -inkw/-inke extreme coordinates and \stface/\ststack register them with the enclosing group/col/row fit, so a group outline can no longer be crossed by a member's bracket arms - \stlink inside \stgroup or \stcol is now a package error: sub-flow members never terminate a pending connector, so the arrow was dropped silently while the label still rendered - \stgroup requires role= (explicit role=neutral for mixed groups) and must bind at least two members or one \stcol partition; a lone stack or face inside a group is a dirty-build warning - lint: default budget is one \stcallout per figure; the allow-multiple-callouts directive relaxes it to one per band - build.sh: clean-build hint no longer names hue budget (lint owns it) - tests/group-callout.tex reworked: multi-member group with a bracketed member as a regression probe, single callout; new negative fixtures group-link, group-norole, group-single, callout-budget - api.md, checklist.md, style.md, layout.md, SKILL.md updated to match
5.2 KiB
Layout invariants
Everything on the canvas is a bounding box: tensors, full offset stacks, brackets, operators, arrow labels, annotations, symbols, shape labels, stage headings, the meaning box, the signature. Tangency counts as collision.
Place by cursor, not by coordinate
The invariants below are statements about the finished figure; the flow layout in
api.md is how you get them without checking each one by hand. Use it by default:
\ststage / \strow … \strowend / \stcol … \stcolend, with an empty coordinate
argument on every face.
The failure it removes is specific. With hand-written offsets, each gap is a magic number
tuned against the current content, so the day a label grows by two characters it silently
lands on the next tensor — the figure still compiles and still looks clean. With the
cursor, every object reserves its own width, so growing one object can only push the rest
apart. And because a band declares its height, an object that does not fit becomes a
Package supertensor Warning, which build.sh turns into a failed build.
Hand placement remains available for the cases the cursor cannot express. When you use it,
wrap the node in \sttrack so the bounding box and the vertical cursor still see it.
Gutters
Define one base gutter g ≥ 1 em; that is what \stgutter (6 mm) is. Unrelated boxes stay
at least g apart; stage bands at least 1.5g (\strowgap, \stblockgap). Set them once
at the top of the figure rather than per call — a per-call gap= is for stating a
relationship (gap=0pt = these shards tile), not for nudging.
Overlap is allowed only inside one declared composite:
- tiles inside their own face,
- shards tiling a parent,
- outline sheets in one
\ststack, - a bracket around its own tensor,
- a
\stgroupoutline around its own members, - a connector endpoint touching its source/target border.
Every other intersection or occlusion is forbidden.
Lanes
Reserve separate vertical lanes and never put anything else in them:
stage heading
(connector annotations)
tensor / operator row
symbols <- \stcaption arg 2
shapes <- \stcaption arg 3
Faces of different heights would otherwise hang their captions at different depths. Fix it
with a shared baseline — \strowend arms one automatically:
\strow{rowA}{T}
...
\strowend
\stcaption{Q}{$\mathbf Q^{(i)}$}{$h\times T\times d_h$}
Under absolute placement, arm it yourself with \stlane{rowA} … \stnolane. Either way
every \stcaption inside hangs from the bottom of rowA, so symbols and shapes form two
flat lanes.
Stage headings share one left rail. \ststage puts them there: the rail is a single stored
x (\stleftrail to move it), and the y is derived from the lowest ink drawn so far, so
a heading can neither drift right nor collide with the row above it.
Explanatory prose belongs in the stage subtitle, the bottom box, above its own connector,
or on a \stcallout card hanging off the right edge of a band. Never drop a floating
commentary card between two operands unless it is a real operation node (st comm).
\stcallout is that rule made structural: it refuses to open inside a band, the linter
rejects it unless it is anchored to a \strow name, and a second card in the figure is an
error without an explicit exemption — a card per band is a dashboard, not a figure. A card that ends up much taller
than its band is telling you the same thing the overflow warning does: that text is not an
aside, it is the Mechanism row of \stmeaningbox.
Composites
\stcol and \stgroup are the two sub-flows, and both exist so that a relationship can
be stated rather than arranged for. A group wraps its members from the inside, which is
what makes an arrow attach to the outline rather than end inside it — a connector whose
endpoint is a member but which crosses the group border is the ordinary version of "a
connector may not cross a box that is not its endpoint".
Layers
The package declares three: stbg (connectors), main (tensors, operators), stfg
(text). \starrow and \starrowlabel route on stbg automatically, so a connector can
never cover a face. Two consequences you still own:
- A connector may not cross a box that is not one of its endpoints. Move the row, don't route over.
- A label's white underlay may cover only its own connector — never a tensor, never
another label. This is the single most common failure after a first draft, and
\stlinkis the fix: it makes the label itself a flow object and draws the arrow to whatever lands on either side of it, so the label cannot be wider than its connector.\starrowlabelbetween two hand-placed nodes still has the old failure mode.
Stacks
\ststack includes its offset sheets in the bounding box, so neighbours can be spaced
against the real extent. Back sheets are outline-only and must carry no semantic content
of their own. If individual slices need to be read, use separate panels instead of overlap.
When it does not fit
In this order:
- shorten or remove secondary annotation,
- widen the natural crop,
- increase row spacing,
- move the whole stage to another row.
Never solve crowding by shrinking below the type hierarchy, closing the gutter, or covering another object.