@rse/gradia 1.1.9 → 1.2.1
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 +89 -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 +10 -1
- package/dst/gradia-api-render-node.js +35 -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 +280 -135
- package/dst/gradia-api-type-grid.d.ts +3 -2
- package/dst/gradia-api-type-grid.js +10 -5
- 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 +58 -54
- 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
|
+
- Seven 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,47 @@ 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,
|
|
217
|
+
- `order: <number>` places the node explicitly (see below).
|
|
218
|
+
|
|
219
|
+
- A `graph` (a `graph` container level, too) with at least one node
|
|
220
|
+
carrying an `order` attribute is laid out in that explicit order
|
|
221
|
+
instead of by the layered algorithm: the distinct order values become
|
|
222
|
+
the rows, top-down in ascending order, the nodes of one value fill
|
|
223
|
+
their row left-to-right in declaration order (wrapped into further
|
|
224
|
+
rows beyond `graph-columns-max` nodes), and the nodes without an order
|
|
225
|
+
follow in the trailing rows. A `grid` places its tiles in the order of
|
|
226
|
+
the attribute, too, the tiles without one trailing. An invalid value
|
|
227
|
+
is ignored.
|
|
208
228
|
|
|
209
229
|
- A node cannot be a member of more than one group. As soon as at least
|
|
210
230
|
one node carries a `group` attribute, all nodes without one implicitly
|
|
211
231
|
belong to the group `default`, and every edge has to stay within a
|
|
212
|
-
single group.
|
|
232
|
+
single group. A nested node belongs to the group of its outermost
|
|
233
|
+
container.
|
|
234
|
+
|
|
235
|
+
- A node referenced as the `parent` of another node is a container: it
|
|
236
|
+
is rendered as a dashed grey box surrounding its members instead of as
|
|
237
|
+
a node box, with its label, `type`, and `url` in the box head (its
|
|
238
|
+
remaining attributes are not rendered). Containers nest to any depth,
|
|
239
|
+
but a node cannot be a member of more than one container, and cannot
|
|
240
|
+
be nested into itself.
|
|
241
|
+
|
|
242
|
+
- The members of a container are laid out as a diagram of their own,
|
|
243
|
+
with the diagram type of the enclosing level unless overridden by
|
|
244
|
+
`container: <type>`, and the container then takes part in the layout
|
|
245
|
+
of the enclosing level as a single box. Edges may connect any two
|
|
246
|
+
nodes, across container boundaries and to container nodes themselves,
|
|
247
|
+
except a container with one of its own members. An edge crossing the
|
|
248
|
+
boundary of a container laid out as `graph` is routed through the box
|
|
249
|
+
border to the inner node (entering on the west and leaving on the east
|
|
250
|
+
side), while an edge crossing the boundary of a container laid out as
|
|
251
|
+
`hub` or `grid` ends at the box border instead.
|
|
213
252
|
|
|
214
253
|
- All remaining attributes are displayed as `<key>: <val>` lines inside
|
|
215
254
|
the node box.
|
|
@@ -241,10 +280,14 @@ placement is rendered as a dashed "ghost" box, colored by the
|
|
|
241
280
|
The diagram type `grid` rejects the input with an error as soon as the
|
|
242
281
|
graph contains at least one edge.
|
|
243
282
|
|
|
244
|
-
As the groups of a grouped graph
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
283
|
+
As the groups of a grouped graph, and the members of every container,
|
|
284
|
+
are laid out individually with the selected (or, for a container, the
|
|
285
|
+
annotated) diagram type, these constraints apply to every single group
|
|
286
|
+
and container level, with the edges crossing a container boundary
|
|
287
|
+
counting at the level they are lifted to (the boundary-crossing edges of
|
|
288
|
+
a `graph` container additionally count inside it, from the boundary to
|
|
289
|
+
the inner node). For the type `hub` this especially means that every
|
|
290
|
+
group and every `hub` container needs its own primary node.
|
|
248
291
|
|
|
249
292
|
### Directives
|
|
250
293
|
|
|
@@ -259,35 +302,38 @@ command-line options (`font-embed` being command-line only, see below).
|
|
|
259
302
|
The recognized options, and their default values, are:
|
|
260
303
|
|
|
261
304
|
```
|
|
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-
|
|
305
|
+
font-family Helvetica size-edge-corner-radius 20
|
|
306
|
+
font-embed false size-edge-hop-radius 8
|
|
307
|
+
color-node-regular-name #336699 size-edge-track-gap 12
|
|
308
|
+
color-node-regular-box #e0f0ff size-edge-port-gap 24
|
|
309
|
+
color-node-regular-border #c0d0e0 group-box-padding 30
|
|
310
|
+
color-node-primary-name #ffffff group-box-gap 40
|
|
311
|
+
color-node-primary-box #336699 container-box-padding 30
|
|
312
|
+
color-node-primary-border #003366 graph-columns-max 4
|
|
313
|
+
color-node-ghost-name #666666 graph-channel-width-max 140
|
|
314
|
+
color-node-ghost-box #f0f0f0 graph-channel-width-min 24
|
|
315
|
+
color-node-ghost-border #a0a0a0 graph-gutter-height-max 90
|
|
316
|
+
color-group-name #6699cc graph-gutter-height-min 20
|
|
317
|
+
color-group-box #f4f8fc graph-node-separation 30
|
|
318
|
+
color-group-border #c0d0e0 graph-rank-separation 60
|
|
319
|
+
color-container-name #666666 graph-node-degree-max 3
|
|
320
|
+
color-container-box #f6f6f6 hub-channel-width-max 340
|
|
321
|
+
color-container-border #a0a0a0 hub-channel-width-min 240
|
|
322
|
+
color-edge-line #999999 hub-node-gap 20
|
|
323
|
+
color-edge-name #333333 hub-node-degree-max 3
|
|
324
|
+
color-edge-arity #333333 grid-columns-max 4
|
|
325
|
+
color-edge-halo #ffffff grid-columns-min 3
|
|
326
|
+
size-canvas-margin 40 grid-gap-horizontal 40
|
|
327
|
+
size-node-width-min 220 grid-gap-vertical 20
|
|
328
|
+
size-node-width-max 0 grid-node-width-equal true
|
|
329
|
+
size-node-height-scale 2.25
|
|
285
330
|
```
|
|
286
331
|
|
|
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
|
|
332
|
+
The `size-*`, `group-*`, `container-*`, `graph-*`, `hub-*`, and
|
|
333
|
+
`grid-*` options control the rendering geometry (canvas margin, node
|
|
334
|
+
box sizing, edge routing, group and container box spacing, and the
|
|
335
|
+
per-diagram-type layout) and take non-negative numbers, except the
|
|
336
|
+
boolean `grid-node-width-equal`.
|
|
291
337
|
|
|
292
338
|
The `size-node-width-max` option additionally enables the word-wrapping
|
|
293
339
|
of the node box texts: given a positive value, the node name, its type,
|
|
@@ -297,12 +343,19 @@ value `0` disables the wrapping entirely and hence lets a node box
|
|
|
297
343
|
become as wide as its longest text. A single word wider than the
|
|
298
344
|
maximum is never broken and still widens the box beyond the maximum.
|
|
299
345
|
|
|
346
|
+
The `group-box-padding` and `container-box-padding` options control the
|
|
347
|
+
inner padding of the group and container boxes around their content.
|
|
348
|
+
For a `graph` container, the content additionally includes the
|
|
349
|
+
boundary channels along its west and east sides, through which the
|
|
350
|
+
edges crossing the container boundary reach the inner nodes, sized
|
|
351
|
+
like all channels by the edges routed through them.
|
|
352
|
+
|
|
300
353
|
The `size-edge-port-gap` option controls the distance of the edge
|
|
301
354
|
attachment ports along a node side, and hence the vertical distance of
|
|
302
355
|
the parallel edges and their labels. It is a *maximum*: a node box too
|
|
303
356
|
small to hold all its ports at that distance packs them closer together
|
|
304
357
|
instead. A node side carrying more edges than `graph-node-degree-max`
|
|
305
|
-
|
|
358
|
+
or `hub-node-degree-max` grows its box height by this very
|
|
306
359
|
distance per additional edge.
|
|
307
360
|
|
|
308
361
|
The `graph-channel-width-*` and `graph-gutter-height-*` options control
|
|
@@ -351,7 +404,7 @@ $ gradia -c font-family="Source Sans 3" -c font-embed=true -o graph.svg graph.tx
|
|
|
351
404
|
|
|
352
405
|
The option `font-embed`, and a `font-family` value pointing to a WOFF2
|
|
353
406
|
file, are intentionally rejected in directives, as the input is treated
|
|
354
|
-
as untrusted. Both are available on the command
|
|
407
|
+
as untrusted. Both are available on the command line only.
|
|
355
408
|
|
|
356
409
|
### Example
|
|
357
410
|
|
|
@@ -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>;
|