reladraw 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.claude/skills/reladraw/SKILL.md +7 -5
  2. package/.claude/skills/reladraw/reference/syntax.md +44 -5
  3. package/README.md +2 -2
  4. package/SYNTAX.md +44 -5
  5. package/dist/cli.js +15 -1
  6. package/dist/examples.d.ts +26 -0
  7. package/dist/examples.js +56 -0
  8. package/dist/page-data.d.ts +130 -0
  9. package/dist/page-data.js +226 -0
  10. package/dist/page.d.ts +46 -2
  11. package/dist/page.js +1239 -241
  12. package/dist/serve.d.ts +18 -0
  13. package/dist/serve.js +544 -0
  14. package/examples/arch.reladraw +95 -0
  15. package/examples/around.reladraw +101 -0
  16. package/examples/borders.reladraw +28 -0
  17. package/examples/bundles.reladraw +49 -0
  18. package/examples/circles.reladraw +37 -0
  19. package/examples/coincident.reladraw +66 -0
  20. package/examples/contents.reladraw +52 -0
  21. package/examples/corridors.reladraw +48 -0
  22. package/examples/deck.reladraw +26 -0
  23. package/examples/flowchart.reladraw +23 -0
  24. package/examples/frames.reladraw +79 -0
  25. package/examples/gap.reladraw +21 -0
  26. package/examples/gaps.reladraw +25 -0
  27. package/examples/icons.reladraw +35 -0
  28. package/examples/lanes.reladraw +54 -0
  29. package/examples/lines.reladraw +73 -0
  30. package/examples/linetexts.reladraw +48 -0
  31. package/examples/marks.reladraw +39 -0
  32. package/examples/names.reladraw +23 -0
  33. package/examples/outlines.reladraw +56 -0
  34. package/examples/overhang.reladraw +24 -0
  35. package/examples/overlays.reladraw +75 -0
  36. package/examples/parts.reladraw +31 -0
  37. package/examples/passes.reladraw +56 -0
  38. package/examples/points.reladraw +28 -0
  39. package/examples/regions.reladraw +14 -0
  40. package/examples/routing.reladraw +88 -0
  41. package/examples/shapes.reladraw +38 -0
  42. package/examples/snug.reladraw +8 -0
  43. package/examples/text.reladraw +69 -0
  44. package/examples/themes.reladraw +16 -0
  45. package/examples/urls.reladraw +35 -0
  46. package/examples/venn.reladraw +15 -0
  47. package/package.json +2 -1
@@ -0,0 +1,54 @@
1
+ // Several lines between the same two sides are copies of one line, and fan out
2
+ // side by side instead of lying on top of each other. Copies never cross.
3
+
4
+ style plain fill: theme-fill border: theme-border
5
+
6
+ // A reciprocal pair around a corner. Each copy is shifted to the left of the
7
+ // way it travels, so the order reverses between the two ends: further left
8
+ // along Gateway's bottom is further down Queue's left.
9
+ node gateway "Gateway" style: plain
10
+ node queue "Queue" 5 below gateway 22 right of gateway style: plain
11
+ edge gateway -> queue "publishes" (at: 2/3) from: bottom to: left line: (path: square, corners: rounded)
12
+ edge queue -> gateway "receives" (at: 2/3) from: left to: bottom line: (path: square, corners: rounded)
13
+
14
+ // Two lines facing each other, where the order runs the same way at both ends.
15
+ node client "Client" below queue (gap: wide) left even with gateway style: plain
16
+ node server "Server" 10 right of client style: plain
17
+ edge client -> server "request" (below edge) from: right to: left
18
+ edge server -> client "response" (above edge) from: left to: right
19
+
20
+ // Five copies down a vertical run, spread evenly along the two sides. The
21
+ // texts read along their lines, since level they would be wider than a lane.
22
+ node fan "Fan of five, spread wide" below client (gap: wide) left even with gateway style: plain
23
+ node hub "Hub of the same five" below fan (gap: wide) style: plain
24
+ edge fan -> hub "one" (reads: along) from: bottom to: top
25
+ edge fan -> hub "two" (reads: along) from: bottom to: top
26
+ edge fan -> hub "three" (reads: along) from: bottom to: top
27
+ edge fan -> hub "four" (reads: along) from: bottom to: top
28
+ edge fan -> hub "five" (reads: along) from: bottom to: top
29
+
30
+ // Two lines pointing the same way. Nothing tells them apart, so the order they
31
+ // are written in decides. Swap these two lines and the texts swap lanes.
32
+ node left_wall "Producer" below hub (gap: wide) left even with gateway style: plain
33
+ node right_wall "Consumer" 9 right of left_wall style: plain
34
+ edge left_wall -> right_wall "bulk" (below edge) from: right to: left
35
+ edge left_wall -> right_wall "control" (above edge) from: right to: left
36
+
37
+ // Sharing one side only doesn't make copies. The far ends differ, and their
38
+ // order along the side decides which end is which.
39
+ node spoke "Spoke" below left_wall (gap: 16) left even with gateway style: plain
40
+ node up "Up" above spoke right of spoke (gap: wide) style: plain
41
+ node down "Down" below spoke right of spoke (gap: wide) style: plain
42
+ edge spoke -> up from: right to: left
43
+ edge spoke -> down from: right to: left
44
+
45
+ // Five copies need sides long enough to hold them. Nothing grows a box to
46
+ // make room, so these two are given the height; at their own height the ends
47
+ // would crowd together, drawn and reported.
48
+ node narrow "Narrow" below down (gap: wide) left even with gateway height: 10 style: plain
49
+ node also "Also" 9 right of narrow height: 10 style: plain
50
+ edge narrow -> also "first" from: right to: left
51
+ edge narrow -> also "second" from: right to: left
52
+ edge narrow -> also "third" from: right to: left
53
+ edge narrow -> also "fourth" from: right to: left
54
+ edge narrow -> also "fifth" from: right to: left
@@ -0,0 +1,73 @@
1
+ // Everything about how a line is drawn, in its bracket: line: (…). A line is
2
+ // straight unless the bracket says otherwise. A shape is the rule for joining
3
+ // what the file fixed — where the line leaves and which way it heads, where it
4
+ // arrives, and any sides it passes.
5
+
6
+ // Out of A's top and into B's left, three ways.
7
+ node a1 "A"
8
+ node b1 "B" right of a1 above a1 (gap: wide)
9
+ edge a1 -> b1 "curved" from: top to: left line: (path: curved)
10
+
11
+ node a2 "A" right of a1 (gap: 18)
12
+ node b2 "B" right of a2 above a2 (gap: wide)
13
+ edge a2 -> b2 "square" from: top to: left line: (path: square)
14
+
15
+ node a3 "A" right of a2 (gap: 18)
16
+ node b3 "B" right of a3 above a3 (gap: wide)
17
+ edge a3 -> b3 "straight" from: top to: left line: (path: straight)
18
+
19
+ // Rounded corners, and a square line naming no sides: across first, then down.
20
+ node c1 "C" below a1 (gap: 8)
21
+ node d1 "D" right of c1 below c1 (gap: wide)
22
+ edge c1 -> d1 "rounded" from: bottom to: left line: (path: square, corners: rounded)
23
+
24
+ node c2 "C" right of c1 (gap: 18)
25
+ node d2 "D" right of c2 below c2 (gap: wide)
26
+ edge c2 -> d2 "no sides" (at: 1/5, right of edge) line: (path: square)
27
+
28
+ // A stop: the line runs along Middle's bottom. Square turns where its shape
29
+ // says; straight joins the stop's two ends to its own with straight pieces.
30
+ // Both run a normal gap under Middle, twice the default, to leave the text room.
31
+ node p1 "P" below c1 (gap: 18)
32
+ node q1 "Q" right of p1 (gap: 18)
33
+ node m1 "Middle" right of p1 left of q1 even with p1
34
+ edge p1 -> q1 "square" from: right to: left via: m1 bottom (gap: normal) line: (path: square)
35
+
36
+ node p2 "P" below p1 (gap: 8)
37
+ node q2 "Q" right of p2 (gap: 18)
38
+ node m2 "Middle" right of p2 left of q2 even with p2
39
+ edge p2 -> q2 "straight" from: right to: left via: m2 bottom (gap: normal) line: (path: straight, corners: rounded)
40
+
41
+ // Patterns and thicknesses.
42
+ node f1 "F" below p2 (gap: 8)
43
+ node g1 "G" right of f1 (gap: 18)
44
+ edge f1 -> g1 "dashed" line: (pattern: dashed)
45
+ node f2 "F" below f1 (gap: normal)
46
+ node g2 "G" right of f2 (gap: 18)
47
+ edge f2 -> g2 "dotted" line: (pattern: dotted)
48
+ node f3 "F" below f2 (gap: normal)
49
+ node g3 "G" right of f3 (gap: 18)
50
+ edge f3 -> g3 "dash-dot" line: (pattern: dash-dot)
51
+ node h1 "H" right of g1 (gap: 12)
52
+ node k1 "K" right of h1 (gap: 18)
53
+ edge h1 -> k1 "thin" line: (thickness: thin)
54
+ node h2 "H" below h1 (gap: normal)
55
+ node k2 "K" right of h2 (gap: 18)
56
+ edge h2 -> k2 "thick" line: (thickness: thick, color: #d2904e)
57
+ node h3 "H" below h2 (gap: normal)
58
+ node k3 "K" right of h3 (gap: 18)
59
+ edge h3 -> k3 "4px, dashed" line: (thickness: 4px, pattern: dashed)
60
+
61
+ // Crossings: the later line jumps the earlier one.
62
+ node w "W" below f3 (gap: 12)
63
+ node e "E" right of w (gap: 35)
64
+ node n1 "N1" below g3 (gap: 8)
65
+ node n2 "N2" right of n1 (gap: 2.5)
66
+ node n3 "N3" right of n2 (gap: 2.5)
67
+ node s1 "S1" below n1 (gap: 13)
68
+ node s2 "S2" right of s1 (gap: 2.5)
69
+ node s3 "S3" right of s2 (gap: 2.5)
70
+ edge w -> e
71
+ edge n1 -> s1 "arc" line: (crossing: arc)
72
+ edge n2 -> s2 "square" line: (crossing: square)
73
+ edge n3 -> s3 "gap" line: (crossing: gap)
@@ -0,0 +1,48 @@
1
+ // Texts on lines. With nothing said a text sits at the middle of its line,
2
+ // on it, and several spread evenly. Its bracket says where else: a fraction
3
+ // along the line, along one named leg or along its longest level piece, a gap from either end, where a way
4
+ // straight out from a box meets the line, a gap off a box, or beside the
5
+ // line on a stated side. The line is drawn first and never moves for a text.
6
+
7
+ style plain fill: theme-fill border: theme-border
8
+
9
+ // Near each end, clear of the arrowhead, above the line; one in the middle.
10
+ node customer "Customer" style: plain
11
+ node order "Order" right of customer (gap: 20) style: plain
12
+ edge customer -> order "1" (at: start, above edge) "places" "0..*" (at: end, above edge)
13
+
14
+ // On a named leg, level with a box: the text goes where a way straight right
15
+ // from Confirm meets the climb into Broker, then right of it.
16
+ node accept "Accept order" below customer (gap: wide) style: plain
17
+ node broker "Broker" right of accept (gap: 12) style: plain
18
+ node confirm "Confirm" below accept (gap: tight) left of accept (gap: wide) style: plain
19
+ edge accept -> broker "publish message" (on: climb, straight right from confirm, right of edge)
20
+ from: bottom to: bottom line: (corners: rounded)
21
+ via: 7 below accept bottom, 7 below broker bottom, leg climb
22
+
23
+ // Resting on a box: the text's bottom a gap above Service, left of the
24
+ // line coming down into it.
25
+ node service "Service" below confirm (gap: 16) style: plain
26
+ node queue "Queue" right of service (gap: 10) 6 above service style: plain
27
+ edge queue -> service "ack" (above service, on the line, left of edge) from: left to: top line: (path: square)
28
+
29
+ // Which way a text reads: turned up beside a tall line, and along a slope.
30
+ node top "Top" right of queue (gap: wide) style: plain
31
+ node bottom "Bottom" below top (gap: 10) style: plain
32
+ edge top -> bottom "ten units" (left of edge, reads: up)
33
+ node low "Low" right of top (gap: wide) 8 below top style: plain
34
+ node high "High" right of low (gap: 12) above low (gap: 4) style: plain
35
+ edge low -> high "climbing" (reads: along)
36
+
37
+ // On the longest level piece: the middle of this line would land by the
38
+ // corner, where the text would stand off in open space.
39
+ node ask "Ask" below service (gap: 12) style: plain
40
+ node answer "Answer" below ask (gap: tight) right of ask (gap: 10) style: plain
41
+ edge answer -> ask "retry" (on: longest level) from: left to: left line: (path: square, corners: rounded)
42
+
43
+ // How far off its line: "1 above edge" rises one unit from its spot on the
44
+ // line and may still cross it, "1/2 clear above edge" goes half the way
45
+ // that would clear it. The text hides the line where they cross.
46
+ node near "Near" right of answer (gap: wide) above answer (gap: 2) style: plain
47
+ node far "Far" 10 below and 16 right of near style: plain
48
+ edge near -> far "one up" (at: 1/4, 1 above edge) "half clear" (at: 3/4, 1/2 clear above edge)
@@ -0,0 +1,39 @@
1
+ // What can be drawn at each end of a line. The arrow is a mark, a line, a mark:
2
+ // a word or a glyph at either end of one or two dashes, either end left off.
3
+
4
+ style label text: (color: theme-muted)
5
+
6
+ node plain "plain"
7
+ node plain2 "a -- b" right of plain (gap: wide) style: label
8
+ edge plain -- plain2
9
+
10
+ node arrows "arrows" below plain
11
+ node arrows2 "a <-> b" right of arrows (gap: wide) style: label
12
+ edge arrows <-> arrows2
13
+
14
+ node uml "inherits" below arrows
15
+ node uml2 "a --|> b" right of uml (gap: wide) style: label
16
+ edge uml --|> uml2
17
+
18
+ node whole "composed" below uml
19
+ node whole2 "a diamond--> b" right of whole (gap: wide) style: label
20
+ edge whole diamond--> whole2
21
+
22
+ node part "aggregates" below whole
23
+ node part2 "a odiamond-- b" right of part (gap: wide) style: label
24
+ edge part odiamond-- part2
25
+
26
+ node dots "dots" below part
27
+ node dots2 "a *--o b" right of dots (gap: wide) style: label
28
+ edge dots *--o dots2
29
+
30
+ node bars "bars" below dots
31
+ node bars2 "a |--| b" right of bars (gap: wide) style: label
32
+ edge bars |--| bars2
33
+
34
+ // A style can carry marks too. It fills only the ends the arrow leaves blank,
35
+ // so here the arrow's own head stays and the style adds a dot at the start.
36
+ style tracked from-mark: dot to-mark: oarrow
37
+ node styled "styled" below bars
38
+ node styled2 "a --> b, with a style" right of styled (gap: wide) style: label
39
+ edge styled --> styled2 style: tracked
@@ -0,0 +1,23 @@
1
+ // A node with no text of its own takes its name as its text.
2
+
3
+ node parser
4
+ node resolver 12 right of parser
5
+ node renderer 12 right of resolver
6
+ edge parser -> resolver "statements"
7
+ edge resolver -> renderer "geometry"
8
+
9
+ // A dotted name shows only its last segment. The containment is already drawn.
10
+
11
+ node server "My Server Machine" (inside top, align: left) below parser (gap: wide)
12
+ node server.docker
13
+ node server.backup right of server.docker (gap: tight)
14
+
15
+ // Empty quotes are the other half: a node that is deliberately blank.
16
+
17
+ node pool "" right of server (gap: wide) shape: none
18
+ node pool.one icon: cube
19
+ node pool.two right of pool.one (gap: tight) icon: cube
20
+
21
+ // A stated text still wins.
22
+
23
+ node store "Records" below pool.one and pool.two badge: database
@@ -0,0 +1,56 @@
1
+ // Every outline `shape:` names. A shape geometry has a word for is named by
2
+ // it, and takes settings in a bracket for what its box doesn't decide; a
3
+ // flowchart shape with no geometric word is `flow-` and what it means, drawn
4
+ // as the standard draws it. Each grows until its text fits at its middle.
5
+
6
+ node diamond "valid?" shape: diamond
7
+ node flat "valid?" right of diamond (gap: tight) shape: diamond width: 18 height: 5
8
+ node pill "start" right of flat (gap: tight) shape: pill
9
+ node para "read input" right of pill (gap: tight) shape: parallelogram
10
+ node left "read input" right of para (gap: tight) shape: parallelogram (slant: 3, lean: left)
11
+
12
+ node hexagon "set up" below diamond (gap: wide) shape: hexagon
13
+ node pointy "set up" right of hexagon (gap: tight) shape: hexagon (point: 3)
14
+ node trap "sign off" right of pointy (gap: tight) shape: trapezoid
15
+ node trapup "sign off" right of trap (gap: tight) shape: trapezoid (taper: 3, narrow: top)
16
+ // The text sits at a triangle's middle, a third of the way from its base.
17
+ node up "split" right of trapup (gap: tight) shape: triangle (aim: up)
18
+ node down "merge" right of up (gap: tight) shape: triangle
19
+ node aimed "next" right of down (gap: tight) shape: triangle (aim: right)
20
+
21
+ node db "orders" below hexagon (gap: wide) shape: cylinder
22
+ node queue "queue" right of db (gap: tight) shape: cylinder (cap: 1.5, lying)
23
+ node oval "cache" right of queue (gap: tight) shape: circle width: 16 height: 6
24
+ node doc "report" right of oval (gap: tight) shape: document (fold: 1.5)
25
+
26
+ node sub "handle" below db (gap: wide) shape: flow-subprocess
27
+ node wavy "report" right of sub (gap: tight) shape: flow-document
28
+ node page "p. 2" right of wavy (gap: tight) shape: flow-off-page
29
+ node delay "wait" right of page (gap: tight) shape: flow-delay
30
+ node manual "type code" right of delay (gap: tight) shape: flow-manual-input
31
+ node stored "store" right of manual (gap: tight) shape: flow-stored-data
32
+
33
+ node loop "each file" below sub (gap: wide) shape: flow-loop-start
34
+ node next "next" right of loop (gap: tight) shape: flow-loop-end
35
+ node card "card" right of next (gap: tight) shape: flow-card
36
+ node screen "screen" right of card (gap: tight) shape: flow-display
37
+ node tape "T" right of screen (gap: tight) shape: flow-tape
38
+ node paper "tape" right of tape (gap: tight) shape: flow-punched-tape
39
+ // Collate's middle is where its two halves meet, so its text sits on a patch.
40
+ node collate "collate" right of paper (gap: tight) shape: flow-collate
41
+
42
+ // Lines on a shape's sides and corners. A corner is where the line from the
43
+ // shape's middle to that corner of its box meets the outline.
44
+ node hub "route?" below loop (gap: wide) shape: diamond
45
+ node north "in" above hub (gap: normal) shape: none
46
+ node east "no" right of hub (gap: wide)
47
+ node south1 "a" below hub (gap: wide)
48
+ node south2 "b" right of south1 (gap: tight)
49
+ node south3 "c" right of south2 (gap: tight)
50
+ node note "see policy" right of north (gap: wide) shape: none
51
+ edge north -> hub
52
+ edge hub -> east from: right
53
+ edge hub -> south1 from: bottom
54
+ edge hub -> south2 from: bottom
55
+ edge hub -> south3 from: bottom
56
+ edge note -> hub to: top-right
@@ -0,0 +1,24 @@
1
+ // Lines that leave the area the nodes cover. The page grows to hold them, on
2
+ // every side a line goes past.
3
+
4
+ default edge line: (path: square, corners: rounded)
5
+
6
+ style plain fill: theme-fill border: theme-border
7
+
8
+ node a "A" style: plain
9
+ node b "B" right of a (gap: wide) width: 5 style: plain
10
+ node c "C" right of b style: plain
11
+
12
+ node d "D" below a and c style: plain
13
+
14
+ // A long arch over everything, with the text at the top of it.
15
+ edge a -> b "over the top" from: top to: top
16
+
17
+ // The same shape across a narrow gap.
18
+ edge b -> c from: top to: top
19
+
20
+ // Out the left and back into the left, so the loop hangs off the left edge.
21
+ edge d -> a "round the side" (at: 1/4) from: left to: left
22
+
23
+ // Out the right and round into the bottom, under the bottom row.
24
+ edge c -> d "under" (at: 2/3) from: right to: bottom
@@ -0,0 +1,75 @@
1
+ // A placement's target may be a *part* of a node — its text, one of its four
2
+ // sides, or one of its nine points — written as a separate word after the
3
+ // node's name. Three direction words go with a part:
4
+ //
5
+ // inside wholly within, against that part
6
+ // outside wholly beyond it
7
+ // on centered on it, so a node on a corner straddles it
8
+ //
9
+ // `inside` and `outside` are shorthands whose expansion is derived rather than
10
+ // listed: inside is the direction from the named part toward the box's center,
11
+ // outside is away from it. `inside right` is `left of`, `inside top-right` is
12
+ // `below-left of`, and nobody writes a table.
13
+
14
+ style mark shape: none text: (color: theme-muted)
15
+ style spacer fill: none border: none
16
+ style link shape: none text: (color: #6fa8dc)
17
+
18
+ // All nine points at once. The board is held open by six invisible children
19
+ // with nothing in them, which is also the point being made: a box is sized by
20
+ // what it *holds*, and the nine marks stamped on it are not held by anything.
21
+ node board "" fill: theme-fill border: theme-border
22
+ node board.nw "" style: spacer
23
+ node board.n "" style: spacer right of board.nw (gap: 10)
24
+ node board.ne "" style: spacer right of board.n (gap: 10)
25
+ node board.sw "" style: spacer below board.nw (gap: wide)
26
+ node board.s "" style: spacer right of board.sw (gap: 10) below board.n (gap: wide)
27
+ node board.se "" style: spacer right of board.s (gap: 10) below board.ne (gap: wide)
28
+
29
+ node p1 "top-left" inside board top-left style: mark
30
+ node p2 "top-center" inside board top-center style: mark
31
+ node p3 "top-right" inside board top-right style: mark
32
+ node p4 "left-center" inside board left-center style: mark
33
+ node p5 "center" on board center style: mark
34
+ node p6 "right-center" inside board right-center style: mark
35
+ node p7 "bottom-left" inside board bottom-left style: mark
36
+ node p8 "bottom-center" inside board bottom-center style: mark
37
+ node p9 "bottom-right" inside board bottom-right style: mark
38
+
39
+ // The case the design record names: an ordinary box with its text in the middle
40
+ // and, centered at the bottom inside it, a small link.
41
+ node bob "Bob the builder" below board
42
+ node bob.card "" style: spacer
43
+ node bob_link "bob.example.com" inside bob bottom-center style: link url: "https://bob.example.com"
44
+
45
+ // The inset is a named gap on the placement, and `none` puts the node hard
46
+ // against the edge, the same as the gap on any other placement.
47
+ node plate "A wide plate to stamp on" right of bob (gap: wide)
48
+ node flush "flush" inside plate top-right (gap: none) style: mark
49
+
50
+ // `on` is the thing the old dedicated overlay word could not say: the node's
51
+ // center on the point, so it straddles the corner instead of tucking inside.
52
+ node dot "A box with a dot on its corner" below plate (gap: wide)
53
+ node straddle "*" on dot top-right style: mark
54
+
55
+ // A side is a segment rather than a point, so the axis it does not name is
56
+ // left free — this sits against the right edge and centers down it. The box
57
+ // is made wide enough that the mark clears the children.
58
+ node column "A tall box" right of dot (gap: wide) layout: column width: 25
59
+ node column.one "one"
60
+ node column.two "two"
61
+ node column.three "three"
62
+ node column_mark "inside right" inside column right style: mark
63
+
64
+ // `outside` is the same rule read the other way: wholly beyond the named
65
+ // part, which is how a caption sits clear of one edge and no other.
66
+ node under "outside bottom" outside column bottom style: mark
67
+
68
+ // A part target works on a picture as readily as on a box. The count is
69
+ // bigger than the small picture, so it sits on the corner rather than inside.
70
+ node unit icon: cube right of plate
71
+ node tag "x3" on unit bottom-right style: mark
72
+
73
+ // Saying `inside` or `on` says the overlap is meant, for that pair and nothing
74
+ // else: anything else `flush` lands on is still reported.
75
+ edge bob -> plate from: right to: left
@@ -0,0 +1,31 @@
1
+ // A color names the part it colors: fill, border, text, line. A text says its
2
+ // own in the brackets after it; a style, having no text of its own, says it as
3
+ // `text: (color: …)`.
4
+
5
+ // All three parts of a node.
6
+ style dim text: (color: theme-muted)
7
+
8
+ node ledger "Ledger / [dim]append only[/dim]" fill: theme-primary-subtle border: theme-primary
9
+
10
+ // Text on its own.
11
+ node plain "Plain" (color: #ff9d4d) right of ledger (gap: wide)
12
+
13
+ // A node with no body is text and nothing else, so its text is its only color.
14
+ // `border:` here is an error rather than a no-op.
15
+ node remark "no body, so text is the only part" (color: #66ccff) below ledger shape: none
16
+
17
+ // A style shared between nodes and edges writes one key per kind. The node takes
18
+ // the border, the edge takes the line, and neither sees the other's word.
19
+ style traced border: #d2904e line: #d2904e
20
+
21
+ // A picture body is a picture and the text under it.
22
+ node store "store" (color: #ff9d4d) right of plain (gap: wide) icon: database
23
+
24
+ // An edge's text takes the line's color unless its own brackets say otherwise.
25
+ node worker "Worker" below remark (gap: wide) style: traced
26
+ node queue "Queue" 18 right of worker
27
+ edge worker -> queue "inherits the line" style: traced from: right to: left
28
+
29
+ node reader "Reader" below worker (gap: wide)
30
+ node cache "Cache" 18 right of reader
31
+ edge reader -> cache "says its own" (color: theme-text) line: #ff9d4d from: right to: left
@@ -0,0 +1,56 @@
1
+ // Saying where a line passes, with via:. A side is a run along it, just
2
+ // outside, from the end nearer the line; a point is one spot the line goes
3
+ // through. Between the stops it names, a curved line takes one sweep.
4
+
5
+ default edge line: (path: curved)
6
+
7
+ // A row, with the line sent underneath it: a run under each end box.
8
+ node parser "Parser"
9
+ node resolver "Resolver" right of parser
10
+ node renderer "Renderer" right of resolver
11
+
12
+ edge parser -> renderer "underneath" from: left to: right via: parser bottom, renderer bottom
13
+
14
+ // Two runs on opposite sides: down A's left, then down B's right, so the line
15
+ // crosses over in the gap between.
16
+ node a "A" below parser (gap: 14)
17
+ node b "B" below a (gap: wide)
18
+
19
+ edge a -> b "an S" from: top to: bottom via: a left, b right
20
+
21
+ // Out of the top and round the left.
22
+ node p "P" right of a (gap: 18)
23
+ node q "Q" below p (gap: normal)
24
+
25
+ edge p -> q "the left" from: top to: top via: p left (gap: normal)
26
+
27
+ // One point, over the middle of Peak's top: the line rises to it and falls.
28
+ // A curve swings in below the point on either side of it, so the point is
29
+ // a normal gap up, clear of Peak's corners, rather than the default tight.
30
+ node t1 "T1" below b (gap: 16)
31
+ node t2 "T2" right of t1 (gap: 13)
32
+ node peak "Peak" right of t1 left of t2 above t1 (gap: tight)
33
+
34
+ edge t1 -> t2 "over a point" from: right to: left via: peak top-center (gap: normal)
35
+
36
+ // Runs under both ends, so the line stays low past Peak2 between them.
37
+ node u1 "U1" right of t2 (gap: wide)
38
+ node u2 "U2" right of u1 (gap: 13)
39
+ node peak2 "Peak2" right of u1 left of u2 above u1 (gap: tight)
40
+
41
+ edge u1 -> u2 "under both" from: left to: right via: u1 bottom, u2 bottom
42
+
43
+ // An end naming no side leaves from the one facing the other box.
44
+ node x "X" below t1 (gap: 12)
45
+ node z "Z" right of x
46
+ node y "Y" right of z
47
+
48
+ edge x -> y "no sides named" via: z bottom
49
+
50
+ // The point between two boxes: the line threads the gap between LN and LM.
51
+ node la "LA" right of y (gap: wide)
52
+ node ln "LN" below la (gap: wide)
53
+ node lm "LM" right of ln (gap: wide)
54
+ node lb "LB" below lm (gap: wide)
55
+
56
+ edge la -> lb "between" from: bottom to: top via: between ln and lm
@@ -0,0 +1,28 @@
1
+ // Points and regions: a place said in words, worked out from named things, so
2
+ // moving them moves it. Render with --guides to see the construction.
3
+
4
+ node gw "Gateway" height: 12
5
+
6
+ // A fraction of the way along the side the direction faces.
7
+ node users "Users" right of gw 1/4 down (gap: wide)
8
+ node orders "Orders" right of gw 3/4 down (gap: wide)
9
+
10
+ // A named point, with a text of its own: above the dot unless it says which way.
11
+ point split "by path" (below) between gw and orders
12
+
13
+ // Part against part: centers exactly 5 apart, as on a timeline.
14
+ node v1 "v1" center 5 below gw bottom-center
15
+ node v2 "v2" center 5 right of v1 center
16
+ node v3 "v3" center 5 right of v2 center
17
+
18
+ // Where a line straight down from one thing meets one straight right from another.
19
+ node tag "latest" shape: none on down from orders and right from v3
20
+
21
+ // A region is drawn in front of the boxes it doesn't name and behind the ones it does.
22
+ // Its text takes a title's words, and inside, the region makes room for it.
23
+ region services "services" (inside top, align: left) users top-left to orders bottom-right padding: 1
24
+
25
+ // A guide does everything but draw: a rail the versions hang from, and a line
26
+ // shown only while working.
27
+ guide region rail v1 top-left to v3 top-right
28
+ guide edge gw -> users
@@ -0,0 +1,14 @@
1
+ // Naming several targets places a node against the region that bounds them all.
2
+
3
+ node one "first"
4
+ node two "second" below one
5
+ node three "third with a longer label" below two
6
+
7
+ // A direction clears the furthest of them. This one says nothing about its own
8
+ // vertical position, so it centers on the region, as a lone direction does.
9
+ node spans "even with all three, and clear of the widest" (wrap: 24) right of one and three (gap: wide) shape: none
10
+
11
+ // An alignment uses the region as a position. "top" is its top edge, not its middle.
12
+ node tops "top even with the second box" (wrap: 20) right of two (gap: tight) top even with two shape: none
13
+
14
+ node beside "centered on the top two" right of spans (gap: wide) even with one and two
@@ -0,0 +1,88 @@
1
+ // Every routing bug reported so far, one row each, the line's text saying
2
+ // where it was reported. Each of these was drawn through a box, on top of
3
+ // another line, or hard against a box's edge. Lines no longer find their own
4
+ // way round anything: each row states its course, and the line follows it.
5
+
6
+ default edge line: (path: square, corners: rounded)
7
+
8
+ // Hacker News, 2026-09-26: ends facing away across a row. The line ran
9
+ // straight through the middle node, its text sitting on it. It now runs along
10
+ // the tops of the two end boxes, so it passes over the row.
11
+ node parser "Parser"
12
+ node resolver "Resolver" right of parser
13
+ node renderer "Renderer" right of resolver
14
+ edge parser -> renderer "Hacker News" from: left to: right via: parser top, renderer top
15
+
16
+ // GitHub issue #2: stacked, sides at right angles. The line came straight up
17
+ // through the lower node. Leaving the bottom and arriving at the left, it now
18
+ // steps out and comes round.
19
+ node p "P" below parser (gap: wide)
20
+ node q "Q" below p (gap: 7)
21
+ edge p -> q "issue #2" (at: 5/8, left of edge) from: bottom to: left
22
+
23
+ // GitHub issue #2: both ends on the same side, one node behind the other. The
24
+ // line went straight through the node behind.
25
+ node r "R" right of p (gap: wide)
26
+ node s "S" right of r (gap: 19)
27
+ edge r -> s "issue #2, same side" (above edge) from: left to: left via: r top
28
+
29
+ node v "V" right of s (gap: wide)
30
+ node w "W" below v
31
+ edge v -> w from: bottom to: bottom via: w right
32
+
33
+ // A fan of three, 2026-09-27. The lines to the top and bottom nodes ran level
34
+ // with the middle one, straight through it, out past the column and back.
35
+ // All three now leave from one point and fan out, written as one statement so
36
+ // the stretch they share is said rather than happened on.
37
+ node a "A" below q (gap: wide)
38
+ node c "C" right of a (gap: wide)
39
+ node b "B" above c (gap: 1)
40
+ node d "D" below c (gap: 1)
41
+ edge a -> b, c, d "fan" (at: start, above edge) from: right-center to: left
42
+
43
+ // The Cache case, 2026-09-27. The curved line dipped through Cache, while the
44
+ // same line drawn square happened to clear it. It now runs under Cache.
45
+ node web "Web" right of c (gap: wide)
46
+ node api "API" right of web
47
+ node db "Postgres" right of api
48
+ node cache "Cache" below api
49
+ edge web -> db "cache case" from: bottom to: bottom via: cache bottom
50
+
51
+ // A line back round the side of a row, 2026-09-26: it crossed the node it
52
+ // was going round.
53
+ node e "E" below a (gap: 22)
54
+ node f "F" right of e (gap: 16)
55
+ node g "G" below e and f
56
+ edge g -> e "round the side" (at: 1/4) from: left to: left
57
+
58
+ // Two lines into one box, 2026-09-27. The longer one's curve swung across
59
+ // the other's twice. The longer one now comes in from above.
60
+ node x1 "X" right of z (gap: wide)
61
+ node a1 "A" below x1
62
+ node b1 "B" right of a1 (gap: normal) below a1 (gap: tight)
63
+ edge a1 -> b1 from: right to: left
64
+ edge x1 -> b1 from: right to: top
65
+
66
+ // No side named, with a box in the way. The line used to be straight from
67
+ // center to center, through whatever lay between.
68
+ node x "X" right of f (gap: wide)
69
+ node y "Y" right of x
70
+ node z "Z" right of y
71
+ edge x -> z "no sides named" (above edge) via: y top
72
+
73
+ // Three lines told the same thing, 2026-09-27. They were drawn on one path,
74
+ // so the three read as one line with three arrowheads. Copies of one line now
75
+ // fan out, nested so that none crosses another.
76
+ node ra "A" below g (gap: wide) width: 6
77
+ node rb "B" right of ra width: 6
78
+ edge ra -> rb from: bottom to: bottom
79
+ edge ra -> rb from: bottom to: bottom
80
+ edge ra -> rb from: bottom to: bottom
81
+
82
+ // Through a named gap, 2026-09-28. The line ran about 2px from Lower, then
83
+ // came into Lowest's right side down Lowest's own edge. It now passes the
84
+ // point between the two and comes into the side from outside.
85
+ node west "West" right of rb (gap: wide)
86
+ node lower "Lower" below west (gap: tight)
87
+ node lowest "Lowest" below lower (gap: normal)
88
+ edge west -> lowest "between" from: left to: right via: between lower and lowest