@rse/gradia 0.9.8 → 1.0.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 +82 -16
- package/dst/gradia-api-config.d.ts +2 -0
- package/dst/gradia-api-config.js +12 -5
- package/dst/gradia-api-parser.js +8 -5
- package/dst/gradia-api-render-base.js +5 -3
- package/dst/gradia-api-render-edge.js +3 -2
- package/dst/gradia-api-render-group.js +1 -1
- package/dst/gradia-api-render-node.js +5 -4
- package/dst/gradia-api-render-svg.d.ts +1 -1
- package/dst/gradia-api-render-svg.js +70 -54
- package/dst/gradia-api-type-graph.js +20 -10
- package/dst/gradia-api-type-grid.js +14 -7
- package/dst/gradia-api-type-hub.js +7 -9
- package/dst/gradia-api.js +20 -4
- package/dst/gradia-api.umd.js +2 -2
- package/dst/gradia-cli.js +5 -3
- package/dst/gradia-mcp.js +85 -14
- package/package.json +2 -2
|
@@ -81,6 +81,9 @@ export const render = async (graph, config) => {
|
|
|
81
81
|
const scale = config["size-node-height-scale"];
|
|
82
82
|
/* the fixed three-column layout assignment */
|
|
83
83
|
const colOf = (id) => id === center.id ? 1 : (inputSet.has(id) ? 0 : 2);
|
|
84
|
+
/* determine the attachment sides of every edge (east/west),
|
|
85
|
+
derived from the column relation of its endpoint nodes */
|
|
86
|
+
const sides = edges.map((edge) => colOf(edge.source) < colOf(edge.target) ? { s: "e", t: "w" } : { s: "w", t: "e" });
|
|
84
87
|
/* determine node box sizes from their textual content (all boxes at
|
|
85
88
|
half height scale), then grow every box whose edge attachments
|
|
86
89
|
exceed the configured per-side maximum, step-wise by one port
|
|
@@ -88,12 +91,10 @@ export const render = async (graph, config) => {
|
|
|
88
91
|
attachment room without a fixed height increase */
|
|
89
92
|
const { boxW, boxH, contentH } = measureNodes(nodes, config, () => scale / 2);
|
|
90
93
|
const portCnt = new Map();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
portCnt.set(`${tSide}:${edge.target}`, (portCnt.get(`${tSide}:${edge.target}`) ?? 0) + 1);
|
|
96
|
-
}
|
|
94
|
+
edges.forEach((edge, i) => {
|
|
95
|
+
portCnt.set(`${sides[i].s}:${edge.source}`, (portCnt.get(`${sides[i].s}:${edge.source}`) ?? 0) + 1);
|
|
96
|
+
portCnt.set(`${sides[i].t}:${edge.target}`, (portCnt.get(`${sides[i].t}:${edge.target}`) ?? 0) + 1);
|
|
97
|
+
});
|
|
97
98
|
for (const node of nodes) {
|
|
98
99
|
const cnt = Math.max(portCnt.get(`w:${node.id}`) ?? 0, portCnt.get(`e:${node.id}`) ?? 0);
|
|
99
100
|
if (cnt > config["hub-node-degree-max"])
|
|
@@ -148,9 +149,6 @@ export const render = async (graph, config) => {
|
|
|
148
149
|
return colLX[1] + colWidth[1] / 2;
|
|
149
150
|
};
|
|
150
151
|
const cy = (id) => nodeCY.get(id);
|
|
151
|
-
/* determine the attachment sides of every edge (east/west),
|
|
152
|
-
derived from the column relation of its endpoint nodes */
|
|
153
|
-
const sides = edges.map((edge) => colOf(edge.source) < colOf(edge.target) ? { s: "e", t: "w" } : { s: "w", t: "e" });
|
|
154
152
|
/* distribute the edge attachment ports along each node side */
|
|
155
153
|
const portPos = assignPorts(edges, sides, cx, cy, boxW, boxH);
|
|
156
154
|
/* assign the vertical tracks within each channel (see assignTracks
|
package/dst/gradia-api.js
CHANGED
|
@@ -39,7 +39,7 @@ export const diagramFormatDefault = "svg:standalone";
|
|
|
39
39
|
export class Gradia {
|
|
40
40
|
/* the rendering configuration options and their default values
|
|
41
41
|
(exposed so consumers can discover and validate the options) */
|
|
42
|
-
static config = configDefaults;
|
|
42
|
+
static config = Object.freeze({ ...configDefaults });
|
|
43
43
|
/* parse a graph description into the graph model */
|
|
44
44
|
static parse(spec) {
|
|
45
45
|
return parse(spec);
|
|
@@ -67,15 +67,31 @@ export class Gradia {
|
|
|
67
67
|
const layout = parts === null ?
|
|
68
68
|
await renderers[type](graph, config) :
|
|
69
69
|
composeGroups(parts, await Promise.all(parts.map((part) => renderers[type](part.graph, config))), config);
|
|
70
|
+
/* derive the identifier seed of the SVG document from everything
|
|
71
|
+
which determines the rendered output, so that regenerating an
|
|
72
|
+
unchanged diagram yields a byte-identical SVG (the explicitly
|
|
73
|
+
configured options enter as their sorted key list only, as
|
|
74
|
+
their values are already part of the layered configuration and
|
|
75
|
+
merely their presence decides between a hard-coded CSS value
|
|
76
|
+
and a CSS custom property lookup) */
|
|
77
|
+
const seed = JSON.stringify({
|
|
78
|
+
type,
|
|
79
|
+
config,
|
|
80
|
+
explicit: Object.keys(explicit).sort(),
|
|
81
|
+
nodes: Array.from(graph.nodes.values()),
|
|
82
|
+
edges: graph.edges
|
|
83
|
+
});
|
|
70
84
|
/* render the laid out graph into an SVG document */
|
|
71
|
-
const svg = renderSVG(layout, config, explicit);
|
|
72
|
-
/* convert the SVG document into the requested output format
|
|
85
|
+
const svg = renderSVG(layout, config, explicit, seed);
|
|
86
|
+
/* convert the SVG document into the requested output format
|
|
87
|
+
(the Base64 encoding uses the platform-neutral "btoa", as the
|
|
88
|
+
API is also shipped as a browser bundle without "Buffer") */
|
|
73
89
|
if (format === "svg:embedded")
|
|
74
90
|
return svg.replace(/^<\?xml[^?]*\?>\n/, "");
|
|
75
91
|
else if (format === "url:xml")
|
|
76
92
|
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
|
77
93
|
else if (format === "url:base64")
|
|
78
|
-
return `data:image/svg+xml;base64,${
|
|
94
|
+
return `data:image/svg+xml;base64,${btoa(Array.from(new TextEncoder().encode(svg), (byte) => String.fromCharCode(byte)).join(""))}`;
|
|
79
95
|
else
|
|
80
96
|
return svg;
|
|
81
97
|
}
|