reladraw 0.26.1 → 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.
- package/.claude/skills/reladraw/SKILL.md +7 -5
- package/.claude/skills/reladraw/reference/syntax.md +55 -7
- package/README.md +2 -2
- package/SYNTAX.md +55 -7
- package/dist/cli.js +15 -1
- package/dist/examples.d.ts +26 -0
- package/dist/examples.js +56 -0
- package/dist/page-data.d.ts +130 -0
- package/dist/page-data.js +226 -0
- package/dist/page.d.ts +46 -2
- package/dist/page.js +1992 -252
- package/dist/serve.d.ts +18 -0
- package/dist/serve.js +544 -0
- package/dist/viewer.js +1 -1
- package/examples/arch.reladraw +95 -0
- package/examples/around.reladraw +101 -0
- package/examples/borders.reladraw +28 -0
- package/examples/bundles.reladraw +49 -0
- package/examples/circles.reladraw +37 -0
- package/examples/coincident.reladraw +66 -0
- package/examples/contents.reladraw +52 -0
- package/examples/corridors.reladraw +48 -0
- package/examples/deck.reladraw +26 -0
- package/examples/flowchart.reladraw +23 -0
- package/examples/frames.reladraw +79 -0
- package/examples/gap.reladraw +21 -0
- package/examples/gaps.reladraw +25 -0
- package/examples/icons.reladraw +35 -0
- package/examples/lanes.reladraw +54 -0
- package/examples/lines.reladraw +73 -0
- package/examples/linetexts.reladraw +48 -0
- package/examples/marks.reladraw +39 -0
- package/examples/names.reladraw +23 -0
- package/examples/outlines.reladraw +56 -0
- package/examples/overhang.reladraw +24 -0
- package/examples/overlays.reladraw +75 -0
- package/examples/parts.reladraw +31 -0
- package/examples/passes.reladraw +56 -0
- package/examples/points.reladraw +28 -0
- package/examples/regions.reladraw +14 -0
- package/examples/routing.reladraw +88 -0
- package/examples/shapes.reladraw +38 -0
- package/examples/snug.reladraw +8 -0
- package/examples/text.reladraw +69 -0
- package/examples/themes.reladraw +16 -0
- package/examples/urls.reladraw +35 -0
- package/examples/venn.reladraw +15 -0
- package/package.json +2 -1
- package/dist/search.d.ts +0 -104
- package/dist/search.js +0 -490
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// What a node is drawn as. `shape:` is the outline it is drawn with; `icon:` is
|
|
2
|
+
// a picture it is drawn as, with no box at all.
|
|
3
|
+
|
|
4
|
+
style artifact fill: theme-secondary-subtle border: theme-border shape: document
|
|
5
|
+
|
|
6
|
+
// The fold is a fixed size rather than a fraction of the box, so it looks the
|
|
7
|
+
// same on all three of these.
|
|
8
|
+
node narrow "Report" style: artifact
|
|
9
|
+
node medium "Nightly database dump" right of narrow (gap: tight) style: artifact
|
|
10
|
+
node wide "A very much longer file name than either of the other two" right of medium (gap: tight) style: artifact
|
|
11
|
+
|
|
12
|
+
// A picture has no outline, no fill and no padding. The text goes underneath.
|
|
13
|
+
node services "" below narrow shape: none
|
|
14
|
+
node services.web "web" icon: cube
|
|
15
|
+
node services.api "api" right of services.web (gap: tight) icon: cube
|
|
16
|
+
node services.worker "worker" right of services.api (gap: tight) icon: cube
|
|
17
|
+
|
|
18
|
+
// No text at all: a picture usually is the statement, so it gets no caption.
|
|
19
|
+
node bare1 below services icon: cube
|
|
20
|
+
node bare2 below bare1 (gap: tight) icon: cube
|
|
21
|
+
|
|
22
|
+
// A picture body is an ordinary node, so it takes edges and sides like any
|
|
23
|
+
// other. That is what it buys over a badge, which cannot be the end of an arrow.
|
|
24
|
+
node sink "collector" right of bare1 and bare2 (gap: wide)
|
|
25
|
+
edge bare1 -> sink from: right to: left
|
|
26
|
+
edge bare2 -> sink from: right to: left
|
|
27
|
+
|
|
28
|
+
// Any icon works as a body, not just the cube.
|
|
29
|
+
node store below bare2 icon: database
|
|
30
|
+
node drive right of store (gap: tight) icon: disk
|
|
31
|
+
|
|
32
|
+
// `(inside bottom)` puts the title under the contents, centered along that side.
|
|
33
|
+
// The lower cube is centered under the upper pair by naming both of them and
|
|
34
|
+
// nothing else.
|
|
35
|
+
node pool "Worker pool" (inside bottom) right of store and drive (gap: wide)
|
|
36
|
+
node pool.one icon: cube
|
|
37
|
+
node pool.two right of pool.one (gap: tight) icon: cube
|
|
38
|
+
node pool.three below pool.one and pool.two (gap: tight) icon: cube
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// A node sits exactly where its placements say: a gap is the distance asked
|
|
2
|
+
// for, not a minimum.
|
|
3
|
+
|
|
4
|
+
node anchor "anchor"
|
|
5
|
+
node far "far" right of anchor
|
|
6
|
+
node near "near" right of far
|
|
7
|
+
|
|
8
|
+
node trailing "one tight gap from near" left of near (gap: tight) below anchor (gap: tight) overlap: allow shape: none
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// An edge's text takes no room of its own: the gap it sits in is the one the
|
|
2
|
+
// placements say, so a text that needs room is given it by stating the gap.
|
|
3
|
+
|
|
4
|
+
style plain fill: theme-fill border: theme-border
|
|
5
|
+
|
|
6
|
+
// A `normal` gap is sized for two nodes to breathe, not to hold a word, so a
|
|
7
|
+
// word that needs more is given it by stating the gap, here in units.
|
|
8
|
+
node parser "Parser" style: plain
|
|
9
|
+
node resolver "Resolver" right of parser (gap: 10) style: plain
|
|
10
|
+
edge parser -> resolver "statements" from: right to: left
|
|
11
|
+
|
|
12
|
+
// Going downward the text is only a line deep, and it hangs out either side.
|
|
13
|
+
//
|
|
14
|
+
// Written with `<-`, which draws the same picture as `writer -> disk`. It just
|
|
15
|
+
// puts the node the line is about first. `from:` is still the first name
|
|
16
|
+
// written, so it names Disk's side.
|
|
17
|
+
node writer "Writer" right of resolver (gap: wide) style: plain
|
|
18
|
+
node disk "Disk" below writer (gap: wide) style: plain
|
|
19
|
+
edge disk <- writer "flushed to disk" from: top to: bottom
|
|
20
|
+
|
|
21
|
+
// Corner to corner, the text sits at the middle of the line, so the gap is
|
|
22
|
+
// stated on both axes.
|
|
23
|
+
node high "High" right of writer (gap: wide) style: plain
|
|
24
|
+
node low "Low" below high (gap: wide) right of high (gap: wide) style: plain
|
|
25
|
+
edge high -> low "diagonal"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
// --- What the brackets after a text say ------------------------------------
|
|
29
|
+
// A container's title says which side it sits against, and `align` where along
|
|
30
|
+
// that side, which also ranges its lines against each other.
|
|
31
|
+
|
|
32
|
+
style dim text: (color: theme-muted)
|
|
33
|
+
|
|
34
|
+
node shelf "Shelf" (inside bottom, align: right) below disk (gap: wide) left even with parser style: plain
|
|
35
|
+
node shelf.one "first" style: plain
|
|
36
|
+
node shelf.two "second" below shelf.one (gap: tight) style: plain
|
|
37
|
+
|
|
38
|
+
// A box is usually the size of its own text. A picture beside the text is two
|
|
39
|
+
// lines tall, and lining the picture's top or bottom up with the text's puts
|
|
40
|
+
// the text at the top or bottom of the box. Joined to the text, the picture and
|
|
41
|
+
// the text are centered in the box together.
|
|
42
|
+
node tall "Top" right of shelf (gap: wide) style: plain
|
|
43
|
+
node tall.disk icon: disk 1 right of tall text top even with tall text join-with: tall text
|
|
44
|
+
node deep "Bottom" below tall (gap: tight) style: plain
|
|
45
|
+
node deep.disk icon: disk 1 right of deep text bottom even with deep text join-with: deep text
|
|
46
|
+
|
|
47
|
+
// Marked words take their style's size as well as its color. A line that is
|
|
48
|
+
// all small is spaced as a small line, and a small word mid-line sits on the
|
|
49
|
+
// same baseline as its neighbors.
|
|
50
|
+
style note text: (size: small, color: theme-muted)
|
|
51
|
+
node synced "Shared folder / [note]synced 2 min ago[/note] / 3 [note]files[/note] waiting" below deep (gap: tight) style: plain
|
|
52
|
+
|
|
53
|
+
// `size` and `wrap` are properties of the text too. The wrap is a count of
|
|
54
|
+
// characters, not a distance: it says how much fits on a line and nothing
|
|
55
|
+
// about where anything sits.
|
|
56
|
+
node aside "A quiet remark that folds itself, with [dim]one word[/dim] quieter than the rest." (size: small, wrap: 28) right of tall (gap: wide) shape: none
|
|
57
|
+
|
|
58
|
+
// A literal bracket is written \[, since [ opens markup.
|
|
59
|
+
node literal "sizes \[small, normal, large]" below aside (gap: tight) shape: none
|
|
60
|
+
|
|
61
|
+
// An edge's text takes the same brackets. A size may be a multiple of normal,
|
|
62
|
+
// such as 1.2, or pixels, such as 12px, as well as a name.
|
|
63
|
+
edge tall -> aside "carried" (color: theme-muted, size: 12px) from: right to: left
|
|
64
|
+
|
|
65
|
+
// `reads: up` or `down` turns a text a quarter turn, so a title on a left or
|
|
66
|
+
// right side takes a column one line wide rather than one word wide.
|
|
67
|
+
node rack "Rack" (inside left, reads: up) right of aside (gap: wide) top even with tall layout: column (gap: tight) style: plain
|
|
68
|
+
node rack.web "web" style: plain
|
|
69
|
+
node rack.db "db" style: plain
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// A diagram that states no color of its own, so every color in it is the
|
|
2
|
+
// theme's. Render it in each theme to compare them.
|
|
3
|
+
|
|
4
|
+
style quiet text: (color: theme-muted, size: small)
|
|
5
|
+
|
|
6
|
+
node web "Web app" badge: laptop
|
|
7
|
+
node api "API server" 10 right of web
|
|
8
|
+
node cluster "Workers" (inside top, align: left) 6 below api
|
|
9
|
+
node cluster.a "worker a"
|
|
10
|
+
node cluster.b "worker b" right of cluster.a (gap: tight)
|
|
11
|
+
node db "Records" icon: database 10 right of api
|
|
12
|
+
node note "Backed up nightly / to cold storage" below db shape: none style: quiet
|
|
13
|
+
|
|
14
|
+
edge web -> api "HTTPS"
|
|
15
|
+
edge api -> cluster "jobs"
|
|
16
|
+
edge api -> db "queries"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// `url:` makes a node or an edge clickable. The whole thing is the target — the
|
|
2
|
+
// box and everything drawn in it, or an edge's text — and nothing about it
|
|
3
|
+
// looks any different, because a destination is content rather than appearance
|
|
4
|
+
// and nothing else in the language lets content reach the ink.
|
|
5
|
+
//
|
|
6
|
+
// The value is quoted. Without the quotes everything from the `//` onwards is a
|
|
7
|
+
// comment, which is why the error for the bare form talks about punctuation.
|
|
8
|
+
|
|
9
|
+
style plain fill: theme-fill border: theme-border
|
|
10
|
+
|
|
11
|
+
node docs "Documentation" style: plain url: "https://example.com/docs"
|
|
12
|
+
|
|
13
|
+
// A container's destination reaches its children: `intro` has none of its own
|
|
14
|
+
// and follows the container, while `deep` overrules it inside its own box.
|
|
15
|
+
node site "Handbook" (inside top, align: left) right of docs (gap: wide) layout: column (align-children: left) url: "https://example.com/"
|
|
16
|
+
node site.intro "Introduction" style: plain
|
|
17
|
+
node site.deep "Deep dive" style: plain url: "https://example.com/deep"
|
|
18
|
+
|
|
19
|
+
// A picture and a bodiless node take one too. There is no kind that cannot.
|
|
20
|
+
node store icon: database below docs (gap: wide) url: "https://example.com/store"
|
|
21
|
+
node aside "and this line is a link as well" shape: none right of store url: "https://example.com/aside"
|
|
22
|
+
|
|
23
|
+
// An edge's text is what carries the destination. An edge with no text may
|
|
24
|
+
// have one too: an unseen wider copy of the line takes the click.
|
|
25
|
+
edge docs -> store "read first" from: bottom to: top url: "https://example.com/order"
|
|
26
|
+
|
|
27
|
+
// A long address is declared once under a name, with its name and badge, and
|
|
28
|
+
// used by that name. A declaration may also list several links, a bundle, and
|
|
29
|
+
// a `url:` mixes names and quoted addresses in the order written.
|
|
30
|
+
url orders-docs "https://docs.example.com/orders/v2/reference/endpoints" (name: "Docs", icon: "D" (fill: #3a6ea5, text: white))
|
|
31
|
+
url orders-code "https://github.com/example/orders/tree/main/services/api" (name: "Code", icon: "C")
|
|
32
|
+
url orders-links orders-docs orders-code "https://wiki.example.com/teams/orders" (name: "Wiki")
|
|
33
|
+
|
|
34
|
+
node api "Orders API" style: plain below store (gap: wide) url: orders-links
|
|
35
|
+
node worker "Worker" style: plain right of api url: orders-docs "https://example.com/runbooks/worker" (name: "Runbook")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// A PDF template is made at design time and used at runtime, so it sits where the two overlap.
|
|
2
|
+
|
|
3
|
+
node phases "" layout: venn (gap: 12)
|
|
4
|
+
node phases.design "Design time" (inside top, align: left)
|
|
5
|
+
node phases.both ""
|
|
6
|
+
node phases.runtime "Runtime" (inside top, align: right)
|
|
7
|
+
|
|
8
|
+
node phases.design.editor "Editor"
|
|
9
|
+
node phases.both.template "PDF template"
|
|
10
|
+
node phases.runtime.python "Python Lib" even with phases.both.template
|
|
11
|
+
node phases.runtime.report "Report PDF" below phases.runtime.python (gap: wide)
|
|
12
|
+
|
|
13
|
+
edge phases.design.editor -> phases.both.template "generate"
|
|
14
|
+
edge phases.both.template -> phases.runtime.python "import"
|
|
15
|
+
edge phases.runtime.python -> phases.runtime.report "generate"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reladraw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "A diagram language where you say where things go.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
".claude/skills",
|
|
13
13
|
"dist",
|
|
14
|
+
"examples/*.reladraw",
|
|
14
15
|
"NOTICE",
|
|
15
16
|
"SYNTAX.md"
|
|
16
17
|
],
|
package/dist/search.d.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Finding a line's way through the open space between boxes.
|
|
3
|
-
*
|
|
4
|
-
* Every box is a wall. A line may turn only where two tracks cross, and the
|
|
5
|
-
* tracks are few: a line a little way out from each side of each wall, one
|
|
6
|
-
* down the middle of each gap between walls, and the lines through the two
|
|
7
|
-
* ends. That is the whole of the open space a right-angled line ever needs,
|
|
8
|
-
* reduced to a grid of a few hundred points for a diagram of ordinary size.
|
|
9
|
-
*
|
|
10
|
-
* The line leaves its start heading straight out and arrives at its end heading
|
|
11
|
-
* straight in, with a straight stretch at each end at least `stub` long, so an
|
|
12
|
-
* arrowhead always lands on a straight piece. Between those two stretches it
|
|
13
|
-
* takes the cheapest way through the grid, where cost is length plus a charge
|
|
14
|
-
* for every turn and for every line already placed that it crosses. On a
|
|
15
|
-
* right-angled grid many ways tie on length — a staircase is exactly as long as
|
|
16
|
-
* one bend — and the turn charge breaks those ties toward the fewest bends,
|
|
17
|
-
* which is the line a person would draw. What still ties after that goes over
|
|
18
|
-
* the top rather than under, and round the right rather than the left.
|
|
19
|
-
*
|
|
20
|
-
* A way through a box is not a losing option here. It is not a way at all.
|
|
21
|
-
*/
|
|
22
|
-
export interface SearchPoint {
|
|
23
|
-
x: number;
|
|
24
|
-
y: number;
|
|
25
|
-
}
|
|
26
|
-
/** A box the line may not enter, as its extent. Touching its border is allowed. */
|
|
27
|
-
export interface SearchWall {
|
|
28
|
-
minX: number;
|
|
29
|
-
minY: number;
|
|
30
|
-
maxX: number;
|
|
31
|
-
maxY: number;
|
|
32
|
-
}
|
|
33
|
-
/** One end of the line: a point on a box's side, and the unit direction out of it. */
|
|
34
|
-
export interface SearchEnd {
|
|
35
|
-
x: number;
|
|
36
|
-
y: number;
|
|
37
|
-
dx: number;
|
|
38
|
-
dy: number;
|
|
39
|
-
}
|
|
40
|
-
export interface SearchOptions {
|
|
41
|
-
/** How far the line runs straight out of each end before it may turn. */
|
|
42
|
-
stub: number;
|
|
43
|
-
/** How far outside a wall a track runs beside it, where there is room. */
|
|
44
|
-
clear: number;
|
|
45
|
-
/** What a turn costs, in the same units as length. */
|
|
46
|
-
turn: number;
|
|
47
|
-
/**
|
|
48
|
-
* Walls the stretches at the two ends may pass through: the line's own boxes,
|
|
49
|
-
* which it has to cross the margin of to leave, and any box holding an end.
|
|
50
|
-
*/
|
|
51
|
-
ownStart: SearchWall[];
|
|
52
|
-
ownEnd: SearchWall[];
|
|
53
|
-
/**
|
|
54
|
-
* Lines already placed. A line may cross one but not run along it within
|
|
55
|
-
* `lane` of it; tracks are added a lane either side of each, so a later line
|
|
56
|
-
* sharing a stretch takes the next lane out.
|
|
57
|
-
*/
|
|
58
|
-
taken: SearchPoint[][];
|
|
59
|
-
lane: number;
|
|
60
|
-
/**
|
|
61
|
-
* For each line in `taken`, for each of its stretches, half the depth across
|
|
62
|
-
* it of a text riding there, and 0 where none does. A line running beside a
|
|
63
|
-
* stretch carrying a text keeps clear of the text, not just of the line.
|
|
64
|
-
*/
|
|
65
|
-
takenHalves?: number[][];
|
|
66
|
-
/**
|
|
67
|
-
* A gap the line must pass through: somewhere it crosses the line at `at`
|
|
68
|
-
* on the `run` axis, travelling along `run`, at a level between `lo` and `hi`.
|
|
69
|
-
*/
|
|
70
|
-
gate?: SearchGate;
|
|
71
|
-
/**
|
|
72
|
-
* Half the depth of this line's own text, which rides in the gate: inside it,
|
|
73
|
-
* the line keeps that much further from the lines beside it.
|
|
74
|
-
*/
|
|
75
|
-
ownHalf?: number;
|
|
76
|
-
/**
|
|
77
|
-
* How far the line may go at all: the diagram with room round it. A wall
|
|
78
|
-
* reaching past this — a clause's fence, which runs to the edge of the page —
|
|
79
|
-
* cannot be gone round at its far end.
|
|
80
|
-
*/
|
|
81
|
-
bounds?: SearchWall;
|
|
82
|
-
/**
|
|
83
|
-
* A cost the line is no use at unless it comes in under: the cheapest way
|
|
84
|
-
* already found between another pair of sides. The search gives up as soon
|
|
85
|
-
* as it cannot.
|
|
86
|
-
*/
|
|
87
|
-
limit?: number;
|
|
88
|
-
}
|
|
89
|
-
/** A gap a line must pass through. See `SearchOptions.gate`. */
|
|
90
|
-
export interface SearchGate {
|
|
91
|
-
run: 'x' | 'y';
|
|
92
|
-
at: number;
|
|
93
|
-
lo: number;
|
|
94
|
-
hi: number;
|
|
95
|
-
}
|
|
96
|
-
export interface SearchResult {
|
|
97
|
-
points: SearchPoint[];
|
|
98
|
-
cost: number;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* The cheapest right-angled line from `start` to `end` that enters no wall, or
|
|
102
|
-
* undefined when there is none.
|
|
103
|
-
*/
|
|
104
|
-
export declare function searchRoute(start: SearchEnd, end: SearchEnd, walls: SearchWall[], options: SearchOptions): SearchResult | undefined;
|