@rse/gradia 1.1.8 → 1.2.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 +78 -36
- package/dst/gradia-api-config.d.ts +7 -0
- package/dst/gradia-api-config.js +54 -24
- package/dst/gradia-api-parser.js +10 -4
- package/dst/gradia-api-render-base.d.ts +28 -0
- package/dst/gradia-api-render-base.js +3 -2
- package/dst/gradia-api-render-container.d.ts +31 -0
- package/dst/gradia-api-render-container.js +300 -0
- package/dst/gradia-api-render-edge.d.ts +1 -1
- package/dst/gradia-api-render-edge.js +106 -37
- package/dst/gradia-api-render-group.d.ts +3 -2
- package/dst/gradia-api-render-group.js +16 -27
- package/dst/gradia-api-render-node.d.ts +9 -1
- package/dst/gradia-api-render-node.js +28 -14
- package/dst/gradia-api-render-svg.d.ts +1 -21
- package/dst/gradia-api-render-svg.js +82 -20
- package/dst/gradia-api-type-graph.d.ts +3 -2
- package/dst/gradia-api-type-graph.js +282 -126
- package/dst/gradia-api-type-grid.d.ts +3 -2
- package/dst/gradia-api-type-grid.js +5 -3
- package/dst/gradia-api-type-hub.d.ts +3 -2
- package/dst/gradia-api-type-hub.js +20 -10
- package/dst/gradia-api.d.ts +12 -4
- package/dst/gradia-api.js +33 -13
- package/dst/gradia-api.umd.js +2 -2
- package/dst/gradia-cli.js +4 -21
- package/dst/gradia-mcp.js +47 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,10 @@ Here are examples of the three graph diagram renderings:
|
|
|
41
41
|
|
|
42
42
|

|
|
43
43
|
|
|
44
|
+
- [container-sample](smp/container-sample.txt) (nested containers):
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+
|
|
44
48
|
Playground
|
|
45
49
|
----------
|
|
46
50
|
|
|
@@ -193,7 +197,7 @@ separated by `|`.
|
|
|
193
197
|
- The `<edge-name>` is rendered as the label of the edge and the
|
|
194
198
|
`<edge-arity>` as its cardinality label.
|
|
195
199
|
|
|
196
|
-
-
|
|
200
|
+
- Six attribute keys are reserved and consumed by the renderer instead
|
|
197
201
|
of being displayed as a regular `<key>: <val>` line:
|
|
198
202
|
|
|
199
203
|
- `url: <url>` renders the node box as a hyperlink. Only relative URLs
|
|
@@ -204,12 +208,36 @@ separated by `|`.
|
|
|
204
208
|
- `primary: true` renders the node in the primary node colors
|
|
205
209
|
(and, for diagram type `hub`, marks the single central hub node),
|
|
206
210
|
- `group: <name>` places the node into the surrounding group box
|
|
207
|
-
`<name
|
|
211
|
+
`<name>`,
|
|
212
|
+
- `parent: <id>` nests the node into the container node `<id>` (a
|
|
213
|
+
node which is implicitly declared if never referenced otherwise),
|
|
214
|
+
- `container: <type>` marks the node as a container whose members
|
|
215
|
+
are laid out with the diagram type `<type>` (`graph`, `hub`, or
|
|
216
|
+
`grid`), even if it has no members at all.
|
|
208
217
|
|
|
209
218
|
- A node cannot be a member of more than one group. As soon as at least
|
|
210
219
|
one node carries a `group` attribute, all nodes without one implicitly
|
|
211
220
|
belong to the group `default`, and every edge has to stay within a
|
|
212
|
-
single group.
|
|
221
|
+
single group. A nested node belongs to the group of its outermost
|
|
222
|
+
container.
|
|
223
|
+
|
|
224
|
+
- A node referenced as the `parent` of another node is a container: it
|
|
225
|
+
is rendered as a dashed grey box surrounding its members instead of as
|
|
226
|
+
a node box, with its label, `type`, and `url` in the box head (its
|
|
227
|
+
remaining attributes are not rendered). Containers nest to any depth,
|
|
228
|
+
but a node cannot be a member of more than one container, and cannot
|
|
229
|
+
be nested into itself.
|
|
230
|
+
|
|
231
|
+
- The members of a container are laid out as a diagram of their own,
|
|
232
|
+
with the diagram type of the enclosing level unless overridden by
|
|
233
|
+
`container: <type>`, and the container then takes part in the layout
|
|
234
|
+
of the enclosing level as a single box. Edges may connect any two
|
|
235
|
+
nodes, across container boundaries and to container nodes themselves,
|
|
236
|
+
except a container with one of its own members. An edge crossing the
|
|
237
|
+
boundary of a container laid out as `graph` is routed through the box
|
|
238
|
+
border to the inner node (entering on the west and leaving on the east
|
|
239
|
+
side), while an edge crossing the boundary of a container laid out as
|
|
240
|
+
`hub` or `grid` ends at the box border instead.
|
|
213
241
|
|
|
214
242
|
- All remaining attributes are displayed as `<key>: <val>` lines inside
|
|
215
243
|
the node box.
|
|
@@ -241,10 +269,14 @@ placement is rendered as a dashed "ghost" box, colored by the
|
|
|
241
269
|
The diagram type `grid` rejects the input with an error as soon as the
|
|
242
270
|
graph contains at least one edge.
|
|
243
271
|
|
|
244
|
-
As the groups of a grouped graph
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
272
|
+
As the groups of a grouped graph, and the members of every container,
|
|
273
|
+
are laid out individually with the selected (or, for a container, the
|
|
274
|
+
annotated) diagram type, these constraints apply to every single group
|
|
275
|
+
and container level, with the edges crossing a container boundary
|
|
276
|
+
counting at the level they are lifted to (the boundary-crossing edges of
|
|
277
|
+
a `graph` container additionally count inside it, from the boundary to
|
|
278
|
+
the inner node). For the type `hub` this especially means that every
|
|
279
|
+
group and every `hub` container needs its own primary node.
|
|
248
280
|
|
|
249
281
|
### Directives
|
|
250
282
|
|
|
@@ -259,35 +291,38 @@ command-line options (`font-embed` being command-line only, see below).
|
|
|
259
291
|
The recognized options, and their default values, are:
|
|
260
292
|
|
|
261
293
|
```
|
|
262
|
-
font-family Helvetica size-edge-
|
|
263
|
-
font-embed false size-edge-
|
|
264
|
-
color-node-regular-name #336699 size-edge-
|
|
265
|
-
color-node-regular-box #e0f0ff
|
|
266
|
-
color-node-regular-border #c0d0e0 group-box-
|
|
267
|
-
color-node-primary-name #ffffff
|
|
268
|
-
color-node-primary-box #336699
|
|
269
|
-
color-node-primary-border #003366 graph-
|
|
270
|
-
color-node-ghost-name #666666 graph-
|
|
271
|
-
color-node-ghost-box #f0f0f0 graph-
|
|
272
|
-
color-node-ghost-border #a0a0a0 graph-
|
|
273
|
-
color-group-name #6699cc graph-
|
|
274
|
-
color-group-box #f4f8fc graph-node-
|
|
275
|
-
color-group-border #c0d0e0
|
|
276
|
-
color-
|
|
277
|
-
color-
|
|
278
|
-
color-
|
|
279
|
-
color-edge-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
size-
|
|
284
|
-
size-
|
|
294
|
+
font-family Helvetica size-edge-corner-radius 20
|
|
295
|
+
font-embed false size-edge-hop-radius 8
|
|
296
|
+
color-node-regular-name #336699 size-edge-track-gap 12
|
|
297
|
+
color-node-regular-box #e0f0ff size-edge-port-gap 24
|
|
298
|
+
color-node-regular-border #c0d0e0 group-box-padding 30
|
|
299
|
+
color-node-primary-name #ffffff group-box-gap 40
|
|
300
|
+
color-node-primary-box #336699 container-box-padding 30
|
|
301
|
+
color-node-primary-border #003366 graph-columns-max 4
|
|
302
|
+
color-node-ghost-name #666666 graph-channel-width-max 140
|
|
303
|
+
color-node-ghost-box #f0f0f0 graph-channel-width-min 24
|
|
304
|
+
color-node-ghost-border #a0a0a0 graph-gutter-height-max 90
|
|
305
|
+
color-group-name #6699cc graph-gutter-height-min 20
|
|
306
|
+
color-group-box #f4f8fc graph-node-separation 30
|
|
307
|
+
color-group-border #c0d0e0 graph-rank-separation 60
|
|
308
|
+
color-container-name #666666 graph-node-degree-max 3
|
|
309
|
+
color-container-box #f6f6f6 hub-channel-width-max 340
|
|
310
|
+
color-container-border #a0a0a0 hub-channel-width-min 240
|
|
311
|
+
color-edge-line #999999 hub-node-gap 20
|
|
312
|
+
color-edge-name #333333 hub-node-degree-max 3
|
|
313
|
+
color-edge-arity #333333 grid-columns-max 4
|
|
314
|
+
color-edge-halo #ffffff grid-columns-min 3
|
|
315
|
+
size-canvas-margin 40 grid-gap-horizontal 40
|
|
316
|
+
size-node-width-min 220 grid-gap-vertical 20
|
|
317
|
+
size-node-width-max 0 grid-node-width-equal true
|
|
318
|
+
size-node-height-scale 2.25
|
|
285
319
|
```
|
|
286
320
|
|
|
287
|
-
The `size-*`, `group-*`, `graph-*`, `hub-*`, and
|
|
288
|
-
control the rendering geometry (canvas margin, node
|
|
289
|
-
routing, group box spacing, and the
|
|
290
|
-
non-negative numbers, except the
|
|
321
|
+
The `size-*`, `group-*`, `container-*`, `graph-*`, `hub-*`, and
|
|
322
|
+
`grid-*` options control the rendering geometry (canvas margin, node
|
|
323
|
+
box sizing, edge routing, group and container box spacing, and the
|
|
324
|
+
per-diagram-type layout) and take non-negative numbers, except the
|
|
325
|
+
boolean `grid-node-width-equal`.
|
|
291
326
|
|
|
292
327
|
The `size-node-width-max` option additionally enables the word-wrapping
|
|
293
328
|
of the node box texts: given a positive value, the node name, its type,
|
|
@@ -297,12 +332,19 @@ value `0` disables the wrapping entirely and hence lets a node box
|
|
|
297
332
|
become as wide as its longest text. A single word wider than the
|
|
298
333
|
maximum is never broken and still widens the box beyond the maximum.
|
|
299
334
|
|
|
335
|
+
The `group-box-padding` and `container-box-padding` options control the
|
|
336
|
+
inner padding of the group and container boxes around their content.
|
|
337
|
+
For a `graph` container, the content additionally includes the
|
|
338
|
+
boundary channels along its west and east sides, through which the
|
|
339
|
+
edges crossing the container boundary reach the inner nodes, sized
|
|
340
|
+
like all channels by the edges routed through them.
|
|
341
|
+
|
|
300
342
|
The `size-edge-port-gap` option controls the distance of the edge
|
|
301
343
|
attachment ports along a node side, and hence the vertical distance of
|
|
302
344
|
the parallel edges and their labels. It is a *maximum*: a node box too
|
|
303
345
|
small to hold all its ports at that distance packs them closer together
|
|
304
346
|
instead. A node side carrying more edges than `graph-node-degree-max`
|
|
305
|
-
|
|
347
|
+
or `hub-node-degree-max` grows its box height by this very
|
|
306
348
|
distance per additional edge.
|
|
307
349
|
|
|
308
350
|
The `graph-channel-width-*` and `graph-gutter-height-*` options control
|
|
@@ -351,7 +393,7 @@ $ gradia -c font-family="Source Sans 3" -c font-embed=true -o graph.svg graph.tx
|
|
|
351
393
|
|
|
352
394
|
The option `font-embed`, and a `font-family` value pointing to a WOFF2
|
|
353
395
|
file, are intentionally rejected in directives, as the input is treated
|
|
354
|
-
as untrusted. Both are available on the command
|
|
396
|
+
as untrusted. Both are available on the command line only.
|
|
355
397
|
|
|
356
398
|
### Example
|
|
357
399
|
|
|
@@ -13,6 +13,9 @@ export declare const configDefaults: {
|
|
|
13
13
|
"color-group-name": string;
|
|
14
14
|
"color-group-box": string;
|
|
15
15
|
"color-group-border": string;
|
|
16
|
+
"color-container-name": string;
|
|
17
|
+
"color-container-box": string;
|
|
18
|
+
"color-container-border": string;
|
|
16
19
|
"color-edge-line": string;
|
|
17
20
|
"color-edge-name": string;
|
|
18
21
|
"color-edge-arity": string;
|
|
@@ -27,6 +30,7 @@ export declare const configDefaults: {
|
|
|
27
30
|
"size-edge-port-gap": number;
|
|
28
31
|
"group-box-padding": number;
|
|
29
32
|
"group-box-gap": number;
|
|
33
|
+
"container-box-padding": number;
|
|
30
34
|
"graph-columns-max": number;
|
|
31
35
|
"graph-channel-width-max": number;
|
|
32
36
|
"graph-channel-width-min": number;
|
|
@@ -50,6 +54,9 @@ export type ConfigEmbedded = {
|
|
|
50
54
|
[K in keyof Config]: Config[K] extends string ? K : never;
|
|
51
55
|
}[keyof Config];
|
|
52
56
|
export declare const cssValueOf: (explicit: Partial<Config>, key: ConfigEmbedded) => string;
|
|
57
|
+
export declare const validateConfig: (raw: Record<string, unknown>, options: {
|
|
58
|
+
trusted: boolean;
|
|
59
|
+
}) => Partial<Config>;
|
|
53
60
|
export declare const parseDirectives: (input: string) => Partial<Config>;
|
|
54
61
|
export declare const resolveFont: (config: Config) => {
|
|
55
62
|
family: string;
|
package/dst/gradia-api-config.js
CHANGED
|
@@ -23,6 +23,9 @@ export const configDefaults = {
|
|
|
23
23
|
"color-group-name": "#6699cc",
|
|
24
24
|
"color-group-box": "#f4f8fc",
|
|
25
25
|
"color-group-border": "#c0d0e0",
|
|
26
|
+
"color-container-name": "#666666",
|
|
27
|
+
"color-container-box": "#f6f6f6",
|
|
28
|
+
"color-container-border": "#a0a0a0",
|
|
26
29
|
"color-edge-line": "#999999",
|
|
27
30
|
"color-edge-name": "#333333",
|
|
28
31
|
"color-edge-arity": "#333333",
|
|
@@ -37,6 +40,7 @@ export const configDefaults = {
|
|
|
37
40
|
"size-edge-port-gap": 24, /* max separation of adjacent edge ports */
|
|
38
41
|
"group-box-padding": 30, /* inner padding of group boxes */
|
|
39
42
|
"group-box-gap": 40, /* vertical gap between group boxes */
|
|
43
|
+
"container-box-padding": 30, /* inner padding of container boxes */
|
|
40
44
|
"graph-columns-max": 4, /* graph: max side-by-side nodes */
|
|
41
45
|
"graph-channel-width-max": 140, /* graph: max width of column channels */
|
|
42
46
|
"graph-channel-width-min": 24, /* graph: min width of column channels */
|
|
@@ -64,37 +68,63 @@ export const configDefaults = {
|
|
|
64
68
|
export const cssValueOf = (explicit, key) => Object.hasOwn(explicit, key) ?
|
|
65
69
|
explicit[key].replace(/[;{}]/g, "") :
|
|
66
70
|
`var(--gradia-${key}, ${configDefaults[key]})`;
|
|
71
|
+
/* validate and coerce rendering configuration options into their
|
|
72
|
+
types (coercing strings, as given on the command line): an unknown
|
|
73
|
+
option or an invalid value is rejected, an option set to "undefined"
|
|
74
|
+
is dropped, and an untrusted source may neither embed fonts nor name
|
|
75
|
+
a WOFF2 file, as this would let it read arbitrary local files */
|
|
76
|
+
export const validateConfig = (raw, options) => {
|
|
77
|
+
const config = {};
|
|
78
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
79
|
+
if (!Object.hasOwn(configDefaults, key))
|
|
80
|
+
throw new Error(`unknown configuration option "${key}"`);
|
|
81
|
+
const k = key;
|
|
82
|
+
if (val === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
if (k === "font-embed" && !options.trusted)
|
|
85
|
+
throw new Error(`configuration option "${key}" is not available in this context`);
|
|
86
|
+
else if (typeof configDefaults[k] === "boolean") {
|
|
87
|
+
if (typeof val !== "boolean" && val !== "true" && val !== "false")
|
|
88
|
+
throw new Error(`invalid value "${String(val)}" for boolean configuration option "${key}" ` +
|
|
89
|
+
"(expected \"true\" or \"false\")");
|
|
90
|
+
config[k] = typeof val === "boolean" ? val : val === "true";
|
|
91
|
+
}
|
|
92
|
+
else if (typeof configDefaults[k] === "number") {
|
|
93
|
+
const num = Number(val);
|
|
94
|
+
if ((typeof val !== "number" && typeof val !== "string") || String(val).trim() === "" || !Number.isFinite(num) || num < 0)
|
|
95
|
+
throw new Error(`invalid value "${String(val)}" for numeric configuration option "${key}" ` +
|
|
96
|
+
"(expected a non-negative number)");
|
|
97
|
+
config[k] = num;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
if (typeof val !== "string")
|
|
101
|
+
throw new Error(`invalid value "${String(val)}" for string configuration option "${key}" (expected a string)`);
|
|
102
|
+
if (val === "")
|
|
103
|
+
throw new Error(`invalid empty value for string configuration option "${key}"`);
|
|
104
|
+
if (k === "font-family" && val.endsWith(".woff2") && !options.trusted)
|
|
105
|
+
throw new Error("configuration option \"font-family\" must not be the path to a WOFF2 file in this context");
|
|
106
|
+
config[k] = val;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return config;
|
|
110
|
+
};
|
|
67
111
|
/* parse "#config <option> <value>" configuration directives from a graph
|
|
68
|
-
description (lines which are otherwise treated as plain comments)
|
|
112
|
+
description (lines which are otherwise treated as plain comments),
|
|
113
|
+
validating them as untrusted and silently skipping an unknown option
|
|
114
|
+
or an invalid value, as directives never abort the rendering */
|
|
69
115
|
export const parseDirectives = (input) => {
|
|
70
116
|
const partial = {};
|
|
71
117
|
for (const line of input.split(/\r?\n/)) {
|
|
72
118
|
const m = /^[ \t]*#config[ \t]+([a-z][a-z0-9-]*)[ \t]+(?:"((?:[^"\\]|\\.)*)"|(\S+))[ \t]*$/.exec(line);
|
|
73
|
-
if (m === null
|
|
119
|
+
if (m === null)
|
|
74
120
|
continue;
|
|
75
|
-
const key = m[1];
|
|
76
121
|
const val = m[2] !== undefined ? m[2].replace(/\\(.)/g, "$1") : m[3];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (key === "font-embed" || (key === "font-family" && val.endsWith(".woff2")))
|
|
80
|
-
continue;
|
|
81
|
-
if (typeof configDefaults[key] === "boolean") {
|
|
82
|
-
/* silently skip invalid boolean values, as directives are
|
|
83
|
-
lines of an untrusted input and never abort the rendering */
|
|
84
|
-
if (val !== "true" && val !== "false")
|
|
85
|
-
continue;
|
|
86
|
-
partial[key] = val === "true";
|
|
122
|
+
try {
|
|
123
|
+
Object.assign(partial, validateConfig({ [m[1]]: val }, { trusted: false }));
|
|
87
124
|
}
|
|
88
|
-
|
|
89
|
-
/*
|
|
90
|
-
lines of an untrusted input and never abort the rendering */
|
|
91
|
-
const num = Number(val);
|
|
92
|
-
if (val === "" || !Number.isFinite(num) || num < 0)
|
|
93
|
-
continue;
|
|
94
|
-
partial[key] = num;
|
|
125
|
+
catch {
|
|
126
|
+
/* skip the invalid directive */
|
|
95
127
|
}
|
|
96
|
-
else
|
|
97
|
-
partial[key] = val;
|
|
98
128
|
}
|
|
99
129
|
return partial;
|
|
100
130
|
};
|
|
@@ -108,8 +138,8 @@ const fontsBuiltIn = {
|
|
|
108
138
|
};
|
|
109
139
|
/* resolve the configured font family: either a built-in font family, a
|
|
110
140
|
plain font family name, or a path to a WOFF2 file (the content of
|
|
111
|
-
the
|
|
112
|
-
weight range of
|
|
141
|
+
the built-in family or the WOFF2 file optionally embedded as base64,
|
|
142
|
+
the former together with the weight range of its variable font) */
|
|
113
143
|
export const resolveFont = (config) => {
|
|
114
144
|
const spec = config["font-family"];
|
|
115
145
|
if (Object.hasOwn(fontsBuiltIn, spec)) {
|
package/dst/gradia-api-parser.js
CHANGED
|
@@ -130,15 +130,21 @@ export const parse = (input) => {
|
|
|
130
130
|
if (idx === -1)
|
|
131
131
|
node.attrs.push(attr);
|
|
132
132
|
else {
|
|
133
|
-
/* a node has to stay in a single group, so
|
|
134
|
-
attempt to re-assign it to a different
|
|
135
|
-
if (attr.key === "group" && node.attrs[idx].val !== attr.val)
|
|
136
|
-
throw new Error(`node "${id}" cannot be a member of more than one
|
|
133
|
+
/* a node has to stay in a single group and container, so
|
|
134
|
+
reject any attempt to re-assign it to a different one */
|
|
135
|
+
if ((attr.key === "group" || attr.key === "parent") && node.attrs[idx].val !== attr.val)
|
|
136
|
+
throw new Error(`node "${id}" cannot be a member of more than one ` +
|
|
137
|
+
`${attr.key === "group" ? "group" : "container"} ` +
|
|
137
138
|
`("${node.attrs[idx].val}" vs "${attr.val}") ` +
|
|
138
139
|
`at line ${start.line}, column ${start.col}`);
|
|
139
140
|
node.attrs[idx] = attr;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
143
|
+
/* implicitly declare the container node a "parent" attribute
|
|
144
|
+
refers to, so a container needs no declaration of its own */
|
|
145
|
+
const parent = node.attrs.find((attr) => attr.key === "parent")?.val;
|
|
146
|
+
if (parent !== undefined && !graph.nodes.has(parent))
|
|
147
|
+
graph.nodes.set(parent, { id: parent, name: parent, attrs: [] });
|
|
142
148
|
graph.nodes.set(id, node);
|
|
143
149
|
return id;
|
|
144
150
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Node, Edge } from "./gradia-api-model.js";
|
|
1
2
|
import { ConfigEmbedded } from "./gradia-api-config.js";
|
|
2
3
|
export declare const FS_NAME = 30;
|
|
3
4
|
export declare const FS_TYPE = 16;
|
|
@@ -16,3 +17,30 @@ export interface NodeStyle {
|
|
|
16
17
|
text: ConfigEmbedded;
|
|
17
18
|
dash?: string;
|
|
18
19
|
}
|
|
20
|
+
export interface GroupBox {
|
|
21
|
+
name: string;
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
w: number;
|
|
25
|
+
h: number;
|
|
26
|
+
}
|
|
27
|
+
export interface ContainerBox {
|
|
28
|
+
node: Node;
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
w: number;
|
|
32
|
+
h: number;
|
|
33
|
+
}
|
|
34
|
+
export interface Layout {
|
|
35
|
+
nodes: Node[];
|
|
36
|
+
edges: Edge[];
|
|
37
|
+
cx: (id: string) => number;
|
|
38
|
+
cy: (id: string) => number;
|
|
39
|
+
boxW: Map<string, number>;
|
|
40
|
+
boxH: Map<string, number>;
|
|
41
|
+
contentH: Map<string, number>;
|
|
42
|
+
polys: Poly[];
|
|
43
|
+
styleOf?: (node: Node) => NodeStyle;
|
|
44
|
+
groups?: GroupBox[];
|
|
45
|
+
containers?: ContainerBox[];
|
|
46
|
+
}
|
|
@@ -22,8 +22,9 @@ export const textWidth = (text, size) => text.length * size * 0.52;
|
|
|
22
22
|
inside a word harms the readability more than an overlong box) */
|
|
23
23
|
export const textWrap = (text, size, maxWidth) => {
|
|
24
24
|
const words = text.split(/\s+/).filter((word) => word !== "");
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const norm = words.join(" ");
|
|
26
|
+
if (maxWidth <= 0 || words.length <= 1 || textWidth(norm, size) <= maxWidth)
|
|
27
|
+
return [norm];
|
|
27
28
|
const lines = [];
|
|
28
29
|
let line = words[0];
|
|
29
30
|
for (const word of words.slice(1)) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Node, Graph } from "./gradia-api-model.js";
|
|
2
|
+
import { Config } from "./gradia-api-config.js";
|
|
3
|
+
import { Poly, Layout } from "./gradia-api-render-base.js";
|
|
4
|
+
export type GateSide = "in" | "out";
|
|
5
|
+
export interface LevelContext {
|
|
6
|
+
fixedSize?: Map<string, {
|
|
7
|
+
w: number;
|
|
8
|
+
h: number;
|
|
9
|
+
}>;
|
|
10
|
+
fixedPort?: (edge: number, role: "s" | "t") => number | undefined;
|
|
11
|
+
gates?: Map<string, GateSide>;
|
|
12
|
+
}
|
|
13
|
+
export type Renderer = (graph: Graph, config: Config, level?: LevelContext) => Promise<Layout>;
|
|
14
|
+
export interface DiagramTypeSpec {
|
|
15
|
+
render: Renderer;
|
|
16
|
+
gated: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface Containment {
|
|
19
|
+
parentOf: Map<string, string>;
|
|
20
|
+
childrenOf: Map<string, Node[]>;
|
|
21
|
+
}
|
|
22
|
+
export declare const containmentOf: (graph: Graph, types: string[]) => Containment;
|
|
23
|
+
export declare const rootOf: (containment: Containment, id: string) => string;
|
|
24
|
+
export interface Bounds {
|
|
25
|
+
minX: number;
|
|
26
|
+
minY: number;
|
|
27
|
+
maxX: number;
|
|
28
|
+
maxY: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const boundsOf: (layout: Layout, extra?: Poly[]) => Bounds;
|
|
31
|
+
export declare const renderContained: (graph: Graph, containment: Containment, type: string, types: Record<string, DiagramTypeSpec>, config: Config) => Promise<Layout>;
|