reladraw 0.1.0 → 0.3.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/README.md +45 -35
- package/SYNTAX.md +498 -193
- package/dist/ast.d.ts +257 -38
- package/dist/ast.js +250 -18
- package/dist/constants.d.ts +60 -42
- package/dist/constants.js +78 -63
- package/dist/grammar.d.ts +24 -11
- package/dist/grammar.js +61 -15
- package/dist/icons.d.ts +55 -37
- package/dist/icons.js +83 -45
- package/dist/lexer.d.ts +1 -1
- package/dist/lexer.js +7 -6
- package/dist/measure.d.ts +3 -3
- package/dist/measure.js +3 -3
- package/dist/model.d.ts +86 -16
- package/dist/parser.js +529 -179
- package/dist/render.d.ts +1 -1
- package/dist/render.js +401 -320
- package/dist/resolve.js +1287 -249
- package/dist/text.d.ts +48 -0
- package/dist/text.js +196 -0
- package/package.json +2 -2
package/dist/icons.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* the SVG. Inline paths cost a few hundred bytes each and always arrive.
|
|
9
9
|
*
|
|
10
10
|
* A name here says what the thing *is*, never what the picture looks like. The
|
|
11
|
-
* same discipline as `
|
|
11
|
+
* same discipline as `muted` over a hex value:
|
|
12
12
|
* the word is the whole interface, so it has to carry meaning rather than
|
|
13
13
|
* geometry, and naming the meaning is what lets the drawing be improved later
|
|
14
14
|
* without every diagram that uses it changing sense.
|
|
@@ -26,7 +26,7 @@ export const ICON_STROKE = 1.1;
|
|
|
26
26
|
function circle(cx, cy, r) {
|
|
27
27
|
return `M${cx - r} ${cy} a${r} ${r} 0 1 0 ${r * 2} 0 a${r} ${r} 0 1 0 ${-r * 2} 0 Z`;
|
|
28
28
|
}
|
|
29
|
-
/** The three visible faces of an isometric cube, top face
|
|
29
|
+
/** The three visible faces of an isometric cube, top face centered on `cx, cy`. */
|
|
30
30
|
function cube(cx, cy, s) {
|
|
31
31
|
const half = s / 2;
|
|
32
32
|
return [
|
|
@@ -83,14 +83,23 @@ export const ICONS = {
|
|
|
83
83
|
* than any particular number: this is the symbol for "several", and at two
|
|
84
84
|
* line-heights square a literal count turns to mush. Where the count carries
|
|
85
85
|
* meaning — where one of them is the end of an arrow — they are nodes, and
|
|
86
|
-
* `
|
|
86
|
+
* `icon: cube` is how you draw them.
|
|
87
87
|
*/
|
|
88
88
|
cubes: {
|
|
89
89
|
grid: GRID,
|
|
90
90
|
paths: [...cube(6.6, 2.6, 4.4), ...cube(17.4, 2.6, 4.4), ...cube(12, 11.6, 4.4)],
|
|
91
91
|
},
|
|
92
|
-
/**
|
|
93
|
-
|
|
92
|
+
/**
|
|
93
|
+
* One unit of the kind `cubes` shows several of.
|
|
94
|
+
*
|
|
95
|
+
* Called `instance` until 0.3.0, under the rule that a name says what the
|
|
96
|
+
* thing is rather than what the picture looks like. That rule works where a
|
|
97
|
+
* picture has one conventional meaning — a disk, a laptop, a folded corner —
|
|
98
|
+
* and it misfires here, because a cube has none: it stands for a container in
|
|
99
|
+
* one diagram, a VM in another, a service in a third, and the diagram assigns
|
|
100
|
+
* the meaning. `instance` picked one of those and hid the picture.
|
|
101
|
+
*/
|
|
102
|
+
cube: {
|
|
94
103
|
grid: GRID,
|
|
95
104
|
paths: cube(12, 2.6, 8.4),
|
|
96
105
|
},
|
|
@@ -107,60 +116,89 @@ export const ICONS = {
|
|
|
107
116
|
};
|
|
108
117
|
export const ICON_NAMES = Object.keys(ICONS);
|
|
109
118
|
/**
|
|
110
|
-
* The
|
|
111
|
-
*
|
|
112
|
-
* about whether there is an icon at all.
|
|
113
|
-
*
|
|
114
|
-
* An unknown name is refused rather than dropped. That is the same rule
|
|
115
|
-
* `DIAGRAM_KEYS` follows and it is here for the same reason: a misspelt
|
|
116
|
-
* `icon: laptp` that quietly draws nothing is indistinguishable from the tool
|
|
117
|
-
* being broken, and an author will stare at the file looking for the mistake in
|
|
118
|
-
* the wrong place. Since the vocabulary is closed and short, the error can list
|
|
119
|
-
* the whole of it.
|
|
120
|
-
*/
|
|
121
|
-
/**
|
|
122
|
-
* The outlines a box can take. `box` is the plain rectangle and needs no word.
|
|
119
|
+
* The outlines a node's body can take. `rectangle` is the plain one and needs no
|
|
120
|
+
* word, since it is what a node is when it says nothing.
|
|
123
121
|
*
|
|
124
122
|
* Named for what a node *is*, never for the geometry, which is the same rule the
|
|
125
123
|
* icon names follow: `document` and not `folded-corner`. A shape carrying a
|
|
126
|
-
* conventional meaning is a second channel alongside
|
|
124
|
+
* conventional meaning is a second channel alongside color, and a stronger one
|
|
127
125
|
* — a fill is whatever the author assigned and has to be learnt from the
|
|
128
126
|
* diagram, while a folded corner has meant "an artifact, not a process" in
|
|
129
127
|
* flowcharts for decades and reads with no legend at all.
|
|
128
|
+
*
|
|
129
|
+
* `circle` and `diamond` join these when a diagram asks. The set being short is
|
|
130
|
+
* a fact about what has been drawn, not about the key.
|
|
130
131
|
*/
|
|
131
|
-
export const
|
|
132
|
-
|
|
132
|
+
export const OUTLINES = ['rectangle', 'document'];
|
|
133
|
+
/** Every value `shape:` accepts, `none` included. */
|
|
134
|
+
export const SHAPE_WORDS = [...OUTLINES, 'none'];
|
|
135
|
+
const PLAIN = { kind: 'shape', outline: 'rectangle' };
|
|
133
136
|
/**
|
|
134
|
-
*
|
|
135
|
-
* renderer, which draws it.
|
|
137
|
+
* Which body a node has, from what it wrote and what its style carried.
|
|
136
138
|
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
139
|
+
* Called once per node while the tree is built, and the answer is kept on the
|
|
140
|
+
* node — so the resolver, which sizes the body, and the renderer, which draws
|
|
141
|
+
* it, cannot disagree about what it is.
|
|
142
|
+
*
|
|
143
|
+
* A key the node wrote itself overrules the other key coming from a style. That
|
|
144
|
+
* is the same permissiveness `checkAttrs` already grants a style: a style is a
|
|
145
|
+
* bundle meant to be shared across kinds, and a key it carries that this node
|
|
146
|
+
* has overridden is unused rather than wrong. Both keys from one level is a
|
|
147
|
+
* genuine contradiction and is refused by name.
|
|
142
148
|
*/
|
|
143
|
-
export function
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
export function bodyFor(attrs, appearance, line) {
|
|
150
|
+
const wroteShape = attrs['shape'] !== undefined;
|
|
151
|
+
const wroteIcon = attrs['icon'] !== undefined;
|
|
152
|
+
let shape = wroteIcon && !wroteShape ? undefined : appearance['shape'];
|
|
153
|
+
let icon = wroteShape && !wroteIcon ? undefined : appearance['icon'];
|
|
154
|
+
if (shape !== undefined && icon !== undefined) {
|
|
155
|
+
throw new SourceError(`shape: ${shape} and icon: ${icon} both say what this node is drawn as, and it has one ` +
|
|
156
|
+
'body — `shape:` is the outline it is drawn with, `icon:` is the picture it is drawn as', line);
|
|
157
|
+
}
|
|
158
|
+
if (icon !== undefined)
|
|
159
|
+
return { kind: 'icon', icon: iconNamed(icon, line) };
|
|
160
|
+
if (shape === undefined)
|
|
148
161
|
return PLAIN;
|
|
149
|
-
if (
|
|
150
|
-
return {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
162
|
+
if (shape === 'none')
|
|
163
|
+
return { kind: 'none' };
|
|
164
|
+
if (OUTLINES.includes(shape)) {
|
|
165
|
+
return { kind: 'shape', outline: shape };
|
|
166
|
+
}
|
|
167
|
+
// Until 0.3.0 `shape:` also took an icon name and drew the node as that
|
|
168
|
+
// picture. The two jobs are two parts and now have two keys, so an older file
|
|
169
|
+
// is told which one it wanted rather than being drawn as a plain rectangle.
|
|
170
|
+
if (ICONS[shape] !== undefined) {
|
|
171
|
+
const now = shape === 'instance' ? 'cube' : shape;
|
|
172
|
+
return refuse(`\`shape: ${shape}\` drew the node as a picture, and the picture is now \`icon:\` — ` +
|
|
173
|
+
`try \`icon: ${now}\``, line);
|
|
174
|
+
}
|
|
175
|
+
if (shape === 'instance') {
|
|
176
|
+
return refuse('`shape: instance` is now `icon: cube` — the icon was renamed with it', line);
|
|
177
|
+
}
|
|
178
|
+
if (shape === 'box') {
|
|
179
|
+
return refuse('`shape: box` is now `shape: rectangle`, since nothing else in the vocabulary is abbreviated', line);
|
|
180
|
+
}
|
|
181
|
+
return refuse(`there is no shape called "${shape}". The shapes are ${SHAPE_WORDS.join(', ')}, and a ` +
|
|
182
|
+
`picture is \`icon:\` rather than \`shape:\`: ${ICON_NAMES.join(', ')}`, line);
|
|
156
183
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
184
|
+
function refuse(message, line) {
|
|
185
|
+
throw new SourceError(message, line);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The icon of that name, or an error listing the set.
|
|
189
|
+
*
|
|
190
|
+
* An unknown name is refused rather than dropped. That is the same rule
|
|
191
|
+
* `DIAGRAM_KEYS` follows and it is here for the same reason: a misspelt
|
|
192
|
+
* `icon: laptp` that quietly draws nothing is indistinguishable from the tool
|
|
193
|
+
* being broken, and an author will stare at the file looking for the mistake in
|
|
194
|
+
* the wrong place. Since the vocabulary is closed and short, the error can list
|
|
195
|
+
* the whole of it.
|
|
196
|
+
*/
|
|
197
|
+
export function iconNamed(named, line) {
|
|
161
198
|
const icon = ICONS[named];
|
|
162
199
|
if (icon === undefined) {
|
|
163
|
-
|
|
200
|
+
const was = named === 'instance' ? ' — the cube was called `instance` until 0.3.0' : '';
|
|
201
|
+
throw new SourceError(`there is no icon called "${named}". The icons are ${ICON_NAMES.join(', ')}${was}`, line);
|
|
164
202
|
}
|
|
165
203
|
return icon;
|
|
166
204
|
}
|
package/dist/lexer.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface Token {
|
|
|
9
9
|
* runs to the end of the line, so a comment may trail a statement.
|
|
10
10
|
*
|
|
11
11
|
* A lone `/` is an ordinary character, which keeps a path or a ratio writable
|
|
12
|
-
* unquoted. `#` is ordinary too: it opens a hex
|
|
12
|
+
* unquoted. `#` is ordinary too: it opens a hex color, which is why comments
|
|
13
13
|
* are spelled `//` rather than the `#` an earlier version used.
|
|
14
14
|
*
|
|
15
15
|
* Parentheses group the modifiers on a placement — `left of hub (gap: wide)` —
|
package/dist/lexer.js
CHANGED
|
@@ -5,7 +5,7 @@ import { SourceError } from './errors.js';
|
|
|
5
5
|
* runs to the end of the line, so a comment may trail a statement.
|
|
6
6
|
*
|
|
7
7
|
* A lone `/` is an ordinary character, which keeps a path or a ratio writable
|
|
8
|
-
* unquoted. `#` is ordinary too: it opens a hex
|
|
8
|
+
* unquoted. `#` is ordinary too: it opens a hex color, which is why comments
|
|
9
9
|
* are spelled `//` rather than the `#` an earlier version used.
|
|
10
10
|
*
|
|
11
11
|
* Parentheses group the modifiers on a placement — `left of hub (gap: wide)` —
|
|
@@ -48,11 +48,12 @@ export function tokenizeLine(line, lineNumber) {
|
|
|
48
48
|
const c = line[i];
|
|
49
49
|
if (c === '\\' && i + 1 < line.length) {
|
|
50
50
|
const next = line[i + 1];
|
|
51
|
-
// `\/`
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
51
|
+
// `\/` and `\[` are the two escapes that must survive tokenizing.
|
|
52
|
+
// Neither of the things they escape is resolved here — the line break
|
|
53
|
+
// waits for `splitRuns` and the markup tag for `parseMarkup` — so
|
|
54
|
+
// collapsing either to a bare character now would lose the fact that
|
|
55
|
+
// the author asked for a literal. Every other escape resolves here.
|
|
56
|
+
text += next === '/' || next === '[' ? `\\${next}` : next;
|
|
56
57
|
i += 2;
|
|
57
58
|
continue;
|
|
58
59
|
}
|
package/dist/measure.d.ts
CHANGED
|
@@ -20,16 +20,16 @@ export interface Measurer {
|
|
|
20
20
|
lineHeight(fontSize: number): number;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* A slash with whitespace on both sides marks a line break, so a
|
|
23
|
+
* A slash with whitespace on both sides marks a line break, so a text is
|
|
24
24
|
* really a short stack of lines. Each line is trimmed; empty ones are dropped.
|
|
25
25
|
*
|
|
26
26
|
* The whitespace is what makes the marker safe. Splitting on a bare `/` meant
|
|
27
|
-
* no
|
|
27
|
+
* no text could contain one, so `TCP/IP` came out as two lines, and so did
|
|
28
28
|
* `16/9`, `I/O` and every path or URL. Requiring the spaces keeps the marker
|
|
29
29
|
* legible where it is meant — `"Computer 1 / Ubuntu"` — while a
|
|
30
30
|
* slash inside a word stays an ordinary character.
|
|
31
31
|
*
|
|
32
|
-
* That leaves the
|
|
32
|
+
* That leaves the text that wants a spaced slash and no break — `Before / After`
|
|
33
33
|
* — which writes it `\/`. The lexer preserves that escape rather than resolving
|
|
34
34
|
* it, so the backslash is still here to suppress the split, and is dropped once
|
|
35
35
|
* the splitting is done.
|
package/dist/measure.js
CHANGED
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
* measures exactly.
|
|
10
10
|
*/
|
|
11
11
|
/**
|
|
12
|
-
* A slash with whitespace on both sides marks a line break, so a
|
|
12
|
+
* A slash with whitespace on both sides marks a line break, so a text is
|
|
13
13
|
* really a short stack of lines. Each line is trimmed; empty ones are dropped.
|
|
14
14
|
*
|
|
15
15
|
* The whitespace is what makes the marker safe. Splitting on a bare `/` meant
|
|
16
|
-
* no
|
|
16
|
+
* no text could contain one, so `TCP/IP` came out as two lines, and so did
|
|
17
17
|
* `16/9`, `I/O` and every path or URL. Requiring the spaces keeps the marker
|
|
18
18
|
* legible where it is meant — `"Computer 1 / Ubuntu"` — while a
|
|
19
19
|
* slash inside a word stays an ordinary character.
|
|
20
20
|
*
|
|
21
|
-
* That leaves the
|
|
21
|
+
* That leaves the text that wants a spaced slash and no break — `Before / After`
|
|
22
22
|
* — which writes it `\/`. The lexer preserves that escape rather than resolving
|
|
23
23
|
* it, so the backslash is still here to suppress the split, and is dropped once
|
|
24
24
|
* the splitting is done.
|
package/dist/model.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { Attrs, Axis, Placement } from './ast.js';
|
|
1
|
+
import type { Attrs, Axis, Kind, Placement } from './ast.js';
|
|
2
|
+
import type { Line } from './text.js';
|
|
3
|
+
import type { Body } from './icons.js';
|
|
2
4
|
/** A `between` clause with its targets resolved. Mirrors `Passage` in `ast.ts`. */
|
|
3
5
|
export interface LayoutPassage {
|
|
4
6
|
nodes: [LayoutNode, LayoutNode];
|
|
@@ -6,13 +8,26 @@ export interface LayoutPassage {
|
|
|
6
8
|
axis?: Axis;
|
|
7
9
|
}
|
|
8
10
|
/** A node with its geometry solved. Coordinates are absolute, origin top-left. */
|
|
11
|
+
/** A distance past each side of a box. */
|
|
12
|
+
export interface Reach {
|
|
13
|
+
left: number;
|
|
14
|
+
top: number;
|
|
15
|
+
right: number;
|
|
16
|
+
bottom: number;
|
|
17
|
+
}
|
|
9
18
|
export interface LayoutNode {
|
|
10
19
|
name: string;
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
/** Which set of attributes this node answers to, decided by its body. */
|
|
21
|
+
kind: Exclude<Kind, 'edge'>;
|
|
22
|
+
/**
|
|
23
|
+
* What the node is drawn as, resolved once while the tree is built — so the
|
|
24
|
+
* resolver, which sizes it, and the renderer, which draws it, cannot disagree.
|
|
25
|
+
*/
|
|
26
|
+
body: Body;
|
|
27
|
+
/** The text as written, before line splitting. */
|
|
13
28
|
text: string;
|
|
14
|
-
/** The
|
|
15
|
-
lines:
|
|
29
|
+
/** The text split into the lines that will be drawn, each as its runs. */
|
|
30
|
+
lines: Line[];
|
|
16
31
|
parent?: LayoutNode;
|
|
17
32
|
children: LayoutNode[];
|
|
18
33
|
x: number;
|
|
@@ -24,15 +39,60 @@ export interface LayoutNode {
|
|
|
24
39
|
* except a deck, where the offset copies sit in that margin.
|
|
25
40
|
*/
|
|
26
41
|
inset: number;
|
|
27
|
-
/** One
|
|
28
|
-
|
|
42
|
+
/** One text per copy behind this node, back to front. Empty for most nodes. */
|
|
43
|
+
deckTexts: string[];
|
|
29
44
|
/**
|
|
30
|
-
* Vertical space this node's own
|
|
31
|
-
* the box `
|
|
45
|
+
* Vertical space this node's own text and icon occupy, at whichever end of
|
|
46
|
+
* the box `text.at` puts them. Zero for leaves.
|
|
32
47
|
*/
|
|
33
48
|
headerHeight: number;
|
|
34
|
-
/**
|
|
35
|
-
|
|
49
|
+
/**
|
|
50
|
+
* How far this node's children stick out past its own box on each side —
|
|
51
|
+
* a child placed `outside` it, or one further down that does. Everything
|
|
52
|
+
* kept clear of this node keeps clear of those too, since they are part of
|
|
53
|
+
* it; alignments still read the node's own box. Zero on every side for a
|
|
54
|
+
* node whose children all sit inside it.
|
|
55
|
+
*/
|
|
56
|
+
reach: Reach;
|
|
57
|
+
/**
|
|
58
|
+
* Whether things stack beside this node's text — below it, or above it when
|
|
59
|
+
* the text is at the bottom. That is what a title band *is*, so it is what
|
|
60
|
+
* decides a container's look and its text's defaults, rather than whether
|
|
61
|
+
* the node has children: a node whose only child sits beside its text or in
|
|
62
|
+
* a corner has nothing stacked below the text, and draws as a leaf.
|
|
63
|
+
*/
|
|
64
|
+
banded: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* The rectangle this node's own text occupies, as an offset from the node's
|
|
67
|
+
* outer top-left. The *ink* box, not the room it ranges in: for a container
|
|
68
|
+
* that is the title itself, not the width of the band.
|
|
69
|
+
*
|
|
70
|
+
* Worked out by the resolver rather than the renderer because `hub text` is
|
|
71
|
+
* a placement target, so it has to be a number before anything is solved —
|
|
72
|
+
* and because the resolver reserving the room and the renderer filling it
|
|
73
|
+
* must not be able to disagree about where it ended up. The renderer draws
|
|
74
|
+
* from this rather than recomputing it.
|
|
75
|
+
*
|
|
76
|
+
* All zeroes where the node has no text.
|
|
77
|
+
*/
|
|
78
|
+
textBox: {
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
width: number;
|
|
82
|
+
height: number;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Which way the text ranges in the room it was given, kept because a node
|
|
86
|
+
* can be widened *after* it was sized — `contents: (widths: match)` and
|
|
87
|
+
* `(widths: fill)` both do — and a centered or right-ranged text has to move
|
|
88
|
+
* with the new width. Without it the box grows and the words stay put.
|
|
89
|
+
*/
|
|
90
|
+
textSide: 'left' | 'center' | 'right';
|
|
91
|
+
/**
|
|
92
|
+
* What the text's brackets said, with anything a style's `text: (…)`
|
|
93
|
+
* contributed underneath it. Usually empty.
|
|
94
|
+
*/
|
|
95
|
+
textAttrs: Attrs;
|
|
36
96
|
attrs: Attrs;
|
|
37
97
|
/** Style attributes merged in from a named style, then overridden by the node's own. */
|
|
38
98
|
appearance: Attrs;
|
|
@@ -40,15 +100,19 @@ export interface LayoutNode {
|
|
|
40
100
|
placements: Placement[];
|
|
41
101
|
line: number;
|
|
42
102
|
}
|
|
43
|
-
export interface
|
|
103
|
+
export interface LayoutEdge {
|
|
44
104
|
from: LayoutNode;
|
|
45
105
|
to: LayoutNode;
|
|
46
106
|
both: boolean;
|
|
47
|
-
|
|
107
|
+
text?: string;
|
|
108
|
+
/** The text split into the lines that will be drawn, each as its runs. */
|
|
109
|
+
lines?: Line[];
|
|
110
|
+
/** The text's bracketed modifiers, with a style's `text: (…)` underneath. */
|
|
111
|
+
textAttrs: Attrs;
|
|
48
112
|
/**
|
|
49
113
|
* The gap a `between` clause named, with its two nodes resolved. Nothing in
|
|
50
114
|
* the resolver uses this — a corridor is measured off the solved layout
|
|
51
|
-
* rather than solved for, so
|
|
115
|
+
* rather than solved for, so edges stay out of the constraint system entirely.
|
|
52
116
|
*/
|
|
53
117
|
between?: LayoutPassage;
|
|
54
118
|
attrs: Attrs;
|
|
@@ -60,7 +124,13 @@ export interface Layout {
|
|
|
60
124
|
nodes: LayoutNode[];
|
|
61
125
|
/** Top-level nodes only, in declaration order. */
|
|
62
126
|
roots: LayoutNode[];
|
|
63
|
-
|
|
127
|
+
edges: LayoutEdge[];
|
|
128
|
+
/**
|
|
129
|
+
* Every style the file's markup names, resolved to the color it lends. The
|
|
130
|
+
* renderer needs it because a marked run borrows its color from a style
|
|
131
|
+
* rather than stating one, and styles are otherwise merged away by here.
|
|
132
|
+
*/
|
|
133
|
+
markup: Record<string, string>;
|
|
64
134
|
/**
|
|
65
135
|
* What the `diagram` statement said, as written. Nothing here affects
|
|
66
136
|
* geometry; it rides along so the renderer sees the whole compiled document
|
|
@@ -71,7 +141,7 @@ export interface Layout {
|
|
|
71
141
|
height: number;
|
|
72
142
|
/**
|
|
73
143
|
* The clear band left around the drawing. Kept so the renderer can hold the
|
|
74
|
-
* same band open around
|
|
144
|
+
* same band open around an edge that leaves the boxes' bounds — a curve out of
|
|
75
145
|
* a `top` side does exactly that, and the canvas has to grow to hold it.
|
|
76
146
|
*/
|
|
77
147
|
margin: number;
|