@steffolino/diagram-kit-core 0.1.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/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/cssPalette.d.ts +15 -0
- package/dist/cssPalette.d.ts.map +1 -0
- package/dist/cssPalette.js +37 -0
- package/dist/cssPalette.js.map +1 -0
- package/dist/cycleLayout.d.ts +13 -0
- package/dist/cycleLayout.d.ts.map +1 -0
- package/dist/cycleLayout.js +174 -0
- package/dist/cycleLayout.js.map +1 -0
- package/dist/detailText.d.ts +5 -0
- package/dist/detailText.d.ts.map +1 -0
- package/dist/detailText.js +34 -0
- package/dist/detailText.js.map +1 -0
- package/dist/graph.d.ts +52 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +30 -0
- package/dist/graph.js.map +1 -0
- package/dist/graph.test.d.ts +2 -0
- package/dist/graph.test.d.ts.map +1 -0
- package/dist/graph.test.js +33 -0
- package/dist/graph.test.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +46 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +57 -0
- package/dist/layout.js.map +1 -0
- package/dist/layout.test.d.ts +2 -0
- package/dist/layout.test.d.ts.map +1 -0
- package/dist/layout.test.js +72 -0
- package/dist/layout.test.js.map +1 -0
- package/dist/path.d.ts +25 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +60 -0
- package/dist/path.js.map +1 -0
- package/dist/presets.d.ts +15 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +211 -0
- package/dist/presets.js.map +1 -0
- package/dist/stackLayout.d.ts +22 -0
- package/dist/stackLayout.d.ts.map +1 -0
- package/dist/stackLayout.js +47 -0
- package/dist/stackLayout.js.map +1 -0
- package/dist/stackLayout.test.d.ts +2 -0
- package/dist/stackLayout.test.d.ts.map +1 -0
- package/dist/stackLayout.test.js +48 -0
- package/dist/stackLayout.test.js.map +1 -0
- package/dist/structuralLayout.d.ts +30 -0
- package/dist/structuralLayout.d.ts.map +1 -0
- package/dist/structuralLayout.js +117 -0
- package/dist/structuralLayout.js.map +1 -0
- package/dist/structuralLayout.test.d.ts +2 -0
- package/dist/structuralLayout.test.d.ts.map +1 -0
- package/dist/structuralLayout.test.js +74 -0
- package/dist/structuralLayout.test.js.map +1 -0
- package/dist/textWidth.d.ts +18 -0
- package/dist/textWidth.d.ts.map +1 -0
- package/dist/textWidth.js +50 -0
- package/dist/textWidth.js.map +1 -0
- package/dist/theme.d.ts +68 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +94 -0
- package/dist/theme.js.map +1 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stefan Stretz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @steffolino/diagram-kit-core
|
|
2
|
+
|
|
3
|
+
Normalized `{ nodes, edges }` graph model, `dagre`-based auto-layout, and
|
|
4
|
+
light/dark theme tokens shared by every diagram-kit renderer and adapter.
|
|
5
|
+
Framework-agnostic — no React, no DOM.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { createGraph, layoutGraph, defaultTheme } from '@steffolino/diagram-kit-core'
|
|
9
|
+
|
|
10
|
+
const graph = createGraph(
|
|
11
|
+
[
|
|
12
|
+
{ id: 'api', label: 'API', category: 'presentation' },
|
|
13
|
+
{ id: 'domain', label: 'Domain', category: 'core' },
|
|
14
|
+
],
|
|
15
|
+
[{ id: 'api->domain', source: 'api', target: 'domain' }],
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const positioned = layoutGraph(graph, { direction: 'TB' })
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Part of the diagram-kit monorepo. See the repo root README for the full
|
|
22
|
+
picture (adapters, React renderer, static export).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ExtractedColor {
|
|
2
|
+
name: string;
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Pulls color values out of pasted CSS or a Tailwind-style JS/TS object —
|
|
7
|
+
* CSS custom properties (`--brand-500: #3b82f6;`, including Tailwind v4's
|
|
8
|
+
* `@theme { --color-primary: oklch(...); }`) and simple
|
|
9
|
+
* `key: '#hex'` object entries (a `tailwind.config.js` `colors: { ... }`
|
|
10
|
+
* block, or any similar JS/TS object literal). Deliberately not a full CSS
|
|
11
|
+
* or JS parser — just regex over color-shaped values, so it works on a
|
|
12
|
+
* pasted fragment without needing complete, valid syntax around it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractCssColors(source: string): ExtractedColor[];
|
|
15
|
+
//# sourceMappingURL=cssPalette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cssPalette.d.ts","sourceRoot":"","sources":["../src/cssPalette.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAQD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,CAyBjE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const COLOR_VALUE = /^(#[0-9a-fA-F]{3,8}|rgba?\([^)]*\)|hsla?\([^)]*\)|oklch\([^)]*\)|oklab\([^)]*\)|lch\([^)]*\)|lab\([^)]*\))$/;
|
|
2
|
+
function isColorValue(value) {
|
|
3
|
+
return COLOR_VALUE.test(value.trim());
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Pulls color values out of pasted CSS or a Tailwind-style JS/TS object —
|
|
7
|
+
* CSS custom properties (`--brand-500: #3b82f6;`, including Tailwind v4's
|
|
8
|
+
* `@theme { --color-primary: oklch(...); }`) and simple
|
|
9
|
+
* `key: '#hex'` object entries (a `tailwind.config.js` `colors: { ... }`
|
|
10
|
+
* block, or any similar JS/TS object literal). Deliberately not a full CSS
|
|
11
|
+
* or JS parser — just regex over color-shaped values, so it works on a
|
|
12
|
+
* pasted fragment without needing complete, valid syntax around it.
|
|
13
|
+
*/
|
|
14
|
+
export function extractCssColors(source) {
|
|
15
|
+
const results = [];
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
const cssVarPattern = /--([\w-]+)\s*:\s*([^;]+);/g;
|
|
18
|
+
for (const match of source.matchAll(cssVarPattern)) {
|
|
19
|
+
const name = match[1];
|
|
20
|
+
const value = match[2].trim();
|
|
21
|
+
if (isColorValue(value) && !seen.has(name)) {
|
|
22
|
+
results.push({ name, value });
|
|
23
|
+
seen.add(name);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const jsObjectPattern = /['"]?([\w-]+)['"]?\s*:\s*['"]([^'"]+)['"]/g;
|
|
27
|
+
for (const match of source.matchAll(jsObjectPattern)) {
|
|
28
|
+
const name = match[1];
|
|
29
|
+
const value = match[2].trim();
|
|
30
|
+
if (isColorValue(value) && !seen.has(name)) {
|
|
31
|
+
results.push({ name, value });
|
|
32
|
+
seen.add(name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=cssPalette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cssPalette.js","sourceRoot":"","sources":["../src/cssPalette.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,GAAG,6GAA6G,CAAA;AAEjI,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,OAAO,GAAqB,EAAE,CAAA;IACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAE9B,MAAM,aAAa,GAAG,4BAA4B,CAAA;IAClD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;QAC9B,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChB,CAAC;IACH,CAAC;IAED,MAAM,eAAe,GAAG,4CAA4C,CAAA;IACpE,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAA;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;QAC9B,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAChB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DiagramGraph } from './graph.js';
|
|
2
|
+
import { type PositionedGraph } from './layout.js';
|
|
3
|
+
/**
|
|
4
|
+
* Process diagram with a primary repeat loop and optional smaller repeat loops.
|
|
5
|
+
* The first self-edge identifies the primary loop; further self-edges identify
|
|
6
|
+
* satellites. Ancestors feed in from the left, remaining steps leave to the
|
|
7
|
+
* right. Steps within each lane follow source order. Without a self-edge this
|
|
8
|
+
* falls back to a left-to-right graph.
|
|
9
|
+
*/
|
|
10
|
+
export declare function layoutCycle(graph: DiagramGraph, options?: {
|
|
11
|
+
showDetails?: boolean;
|
|
12
|
+
}): PositionedGraph;
|
|
13
|
+
//# sourceMappingURL=cycleLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycleLayout.d.ts","sourceRoot":"","sources":["../src/cycleLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAe,KAAK,eAAe,EAAuB,MAAM,aAAa,CAAA;AAKpF;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,eAAe,CA4JzG"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { layoutGraph } from './layout.js';
|
|
2
|
+
import { estimateNodeWidth } from './textWidth.js';
|
|
3
|
+
import { blockArrowPoints } from './path.js';
|
|
4
|
+
import { detailHeight } from './detailText.js';
|
|
5
|
+
/**
|
|
6
|
+
* Process diagram with a primary repeat loop and optional smaller repeat loops.
|
|
7
|
+
* The first self-edge identifies the primary loop; further self-edges identify
|
|
8
|
+
* satellites. Ancestors feed in from the left, remaining steps leave to the
|
|
9
|
+
* right. Steps within each lane follow source order. Without a self-edge this
|
|
10
|
+
* falls back to a left-to-right graph.
|
|
11
|
+
*/
|
|
12
|
+
export function layoutCycle(graph, options = {}) {
|
|
13
|
+
const loops = graph.edges.filter((edge) => edge.source === edge.target);
|
|
14
|
+
const primary = loops[0]?.source;
|
|
15
|
+
if (!primary)
|
|
16
|
+
return layoutGraph(graph, { direction: 'LR', showDetails: options.showDetails });
|
|
17
|
+
const satellites = new Set(loops.slice(1).map((edge) => edge.source).filter((id) => id !== primary));
|
|
18
|
+
const ancestors = new Set();
|
|
19
|
+
const queue = [primary];
|
|
20
|
+
while (queue.length) {
|
|
21
|
+
const target = queue.pop();
|
|
22
|
+
for (const edge of graph.edges) {
|
|
23
|
+
if (edge.target !== target || edge.source === primary || satellites.has(edge.source) || ancestors.has(edge.source))
|
|
24
|
+
continue;
|
|
25
|
+
ancestors.add(edge.source);
|
|
26
|
+
queue.push(edge.source);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const inputs = graph.nodes.filter((node) => ancestors.has(node.id));
|
|
30
|
+
const outputs = graph.nodes.filter((node) => node.id !== primary && !ancestors.has(node.id) && !satellites.has(node.id));
|
|
31
|
+
const widths = new Map(graph.nodes.map((node) => [node.id, estimateNodeWidth(node.label, node.badge, { minWidth: 132, maxWidth: 240 })]));
|
|
32
|
+
const gap = 28;
|
|
33
|
+
const extraHeight = (id) => detailHeight(graph.nodes.find((node) => node.id === id), widths.get(id), options.showDetails);
|
|
34
|
+
// All entities share one detail-text allowance (the tallest any single
|
|
35
|
+
// node needs) so every box comes out the same height instead of each
|
|
36
|
+
// ragged with its own detail length.
|
|
37
|
+
const detailExtra = Math.max(0, ...graph.nodes.map((node) => extraHeight(node.id)));
|
|
38
|
+
const inputWidth = inputs.reduce((sum, node) => sum + widths.get(node.id) + gap, 0);
|
|
39
|
+
const radius = Math.max(118, Math.hypot(widths.get(primary) / 2, (64 + detailExtra) / 2) + 30);
|
|
40
|
+
const cx = inputWidth + radius + 20;
|
|
41
|
+
// Satellite loops always read as clearly smaller than the primary: capped
|
|
42
|
+
// to a fraction of its radius rather than scaling freely with label width.
|
|
43
|
+
const satelliteRadius = (id) => {
|
|
44
|
+
const width = widths.get(id);
|
|
45
|
+
const height = 44 + detailExtra;
|
|
46
|
+
return Math.min(radius * 0.58, Math.max(56, Math.hypot(width, height) / 2 + 16));
|
|
47
|
+
};
|
|
48
|
+
const maxSatelliteRadius = Math.max(0, ...[...satellites].map(satelliteRadius));
|
|
49
|
+
const cy = Math.max(210, radius + maxSatelliteRadius + 50);
|
|
50
|
+
const baseline = cy + radius;
|
|
51
|
+
const nodes = [];
|
|
52
|
+
let cursor = 0;
|
|
53
|
+
for (const node of inputs) {
|
|
54
|
+
const width = widths.get(node.id);
|
|
55
|
+
nodes.push({ id: node.id, x: cursor + width / 2, y: baseline, width, height: 54 + detailExtra });
|
|
56
|
+
cursor += width + gap;
|
|
57
|
+
}
|
|
58
|
+
nodes.push({ id: primary, x: cx, y: cy, width: widths.get(primary), height: 64 + detailExtra });
|
|
59
|
+
let right = cx + radius + 42;
|
|
60
|
+
for (const node of outputs) {
|
|
61
|
+
const width = widths.get(node.id);
|
|
62
|
+
nodes.push({ id: node.id, x: right + width / 2, y: baseline, width, height: 54 + detailExtra });
|
|
63
|
+
right += width + gap;
|
|
64
|
+
}
|
|
65
|
+
// Fan satellites out above the primary loop, each ring only lightly
|
|
66
|
+
// overlapping the primary so the size difference stays legible.
|
|
67
|
+
let satelliteAngle = -Math.PI * 0.75;
|
|
68
|
+
for (const id of satellites) {
|
|
69
|
+
const width = widths.get(id);
|
|
70
|
+
const height = 44 + detailExtra;
|
|
71
|
+
const satRadius = satelliteRadius(id);
|
|
72
|
+
const dist = radius + satRadius * 1.1;
|
|
73
|
+
nodes.push({
|
|
74
|
+
id,
|
|
75
|
+
x: cx + Math.cos(satelliteAngle) * dist,
|
|
76
|
+
y: cy + Math.sin(satelliteAngle) * dist,
|
|
77
|
+
width,
|
|
78
|
+
height,
|
|
79
|
+
});
|
|
80
|
+
satelliteAngle -= 0.55;
|
|
81
|
+
}
|
|
82
|
+
const byId = new Map(nodes.map((node) => [node.id, node]));
|
|
83
|
+
const edges = graph.edges.map((edge) => {
|
|
84
|
+
const source = byId.get(edge.source);
|
|
85
|
+
const target = byId.get(edge.target);
|
|
86
|
+
let points;
|
|
87
|
+
let arrow;
|
|
88
|
+
if (source === target) {
|
|
89
|
+
const loopRadius = source.id === primary ? radius : satelliteRadius(source.id);
|
|
90
|
+
// A nearly complete loop, with a gap at the lower right. The primary
|
|
91
|
+
// loop runs counterclockwise; satellite loops run clockwise. Each has
|
|
92
|
+
// its own gap angles so they can be tuned independently.
|
|
93
|
+
const [start, end] = source.id === primary
|
|
94
|
+
? [Math.PI * 0.45, Math.PI * 2.4]
|
|
95
|
+
: [Math.PI * 0.35, Math.PI * 2.25];
|
|
96
|
+
const arc = Array.from({ length: 65 }, (_, i) => {
|
|
97
|
+
const angle = start + (end - start) * i / 64;
|
|
98
|
+
return { x: source.x + loopRadius * Math.cos(angle), y: source.y + loopRadius * Math.sin(angle) };
|
|
99
|
+
});
|
|
100
|
+
points = source.id === primary ? arc.slice().reverse() : arc;
|
|
101
|
+
const half = source.id === primary ? 12 : 8;
|
|
102
|
+
const tip = points[points.length - 1];
|
|
103
|
+
const base = points[points.length - 4];
|
|
104
|
+
const dx = tip.x - base.x;
|
|
105
|
+
const dy = tip.y - base.y;
|
|
106
|
+
const len = Math.hypot(dx, dy);
|
|
107
|
+
const nx = -dy / len;
|
|
108
|
+
const ny = dx / len;
|
|
109
|
+
const shaft = points.slice(0, -3);
|
|
110
|
+
const side = (sign) => shaft.map((p, i) => {
|
|
111
|
+
const next = points[i + 1];
|
|
112
|
+
const length = Math.hypot(next.x - p.x, next.y - p.y);
|
|
113
|
+
return { x: p.x - sign * half * (next.y - p.y) / length, y: p.y + sign * half * (next.x - p.x) / length };
|
|
114
|
+
});
|
|
115
|
+
// Round off the open end of the ring (opposite the arrowhead) instead
|
|
116
|
+
// of leaving it as a flat cut, like a round SVG line cap.
|
|
117
|
+
const start0 = shaft[0];
|
|
118
|
+
const next0 = points[1];
|
|
119
|
+
const startLen = Math.hypot(next0.x - start0.x, next0.y - start0.y);
|
|
120
|
+
const startNx = -(next0.y - start0.y) / startLen;
|
|
121
|
+
const startNy = (next0.x - start0.x) / startLen;
|
|
122
|
+
const capSteps = 12;
|
|
123
|
+
const startCap = Array.from({ length: capSteps }, (_, i) => {
|
|
124
|
+
const theta = Math.PI - (Math.PI * i) / capSteps;
|
|
125
|
+
const cos = Math.cos(theta);
|
|
126
|
+
const sin = Math.sin(theta);
|
|
127
|
+
return {
|
|
128
|
+
x: start0.x + half * (startNx * cos - startNy * sin),
|
|
129
|
+
y: start0.y + half * (startNx * sin + startNy * cos),
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
arrow = [...side(1), { x: base.x + nx * half * 2, y: base.y + ny * half * 2 }, tip,
|
|
133
|
+
{ x: base.x - nx * half * 2, y: base.y - ny * half * 2 }, ...side(-1).reverse(), ...startCap];
|
|
134
|
+
}
|
|
135
|
+
else if (satellites.has(source.id) || satellites.has(target.id)) {
|
|
136
|
+
const dx = target.x - source.x;
|
|
137
|
+
const dy = target.y - source.y;
|
|
138
|
+
const boundary = (node, sign) => {
|
|
139
|
+
const ratio = Math.min(node.width / 2 / Math.abs(dx), node.height / 2 / Math.abs(dy));
|
|
140
|
+
return { x: node.x + sign * dx * ratio, y: node.y + sign * dy * ratio };
|
|
141
|
+
};
|
|
142
|
+
points = [boundary(source, 1), boundary(target, -1)];
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const start = source.id === primary
|
|
146
|
+
? { x: cx + 12, y: baseline }
|
|
147
|
+
: { x: source.x + source.width / 2, y: source.y };
|
|
148
|
+
const end = target.id === primary
|
|
149
|
+
? { x: cx - 18, y: baseline }
|
|
150
|
+
: { x: target.x - target.width / 2, y: target.y };
|
|
151
|
+
points = [start, end];
|
|
152
|
+
if (edge.style === 'block')
|
|
153
|
+
arrow = blockArrowPoints(points, { shaftWidth: 8, headWidth: 15, headLength: 18 });
|
|
154
|
+
}
|
|
155
|
+
return { id: edge.id, points, arrow };
|
|
156
|
+
});
|
|
157
|
+
const bounds = [
|
|
158
|
+
...nodes.flatMap((node) => [
|
|
159
|
+
{ x: node.x - node.width / 2, y: node.y - node.height / 2 },
|
|
160
|
+
{ x: node.x + node.width / 2, y: node.y + node.height / 2 },
|
|
161
|
+
]),
|
|
162
|
+
...edges.flatMap((edge) => [...edge.points, ...(edge.arrow ?? [])]),
|
|
163
|
+
];
|
|
164
|
+
const offsetX = -Math.min(0, ...bounds.map((p) => p.x));
|
|
165
|
+
const offsetY = -Math.min(0, ...bounds.map((p) => p.y));
|
|
166
|
+
const shift = (p) => ({ x: p.x + offsetX, y: p.y + offsetY });
|
|
167
|
+
return {
|
|
168
|
+
nodes: nodes.map((node) => ({ ...node, ...shift(node) })),
|
|
169
|
+
edges: edges.map((edge) => ({ ...edge, points: edge.points.map(shift), arrow: edge.arrow?.map(shift) })),
|
|
170
|
+
width: Math.max(...bounds.map((p) => p.x)) + offsetX,
|
|
171
|
+
height: Math.max(...bounds.map((p) => p.y)) + offsetY,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=cycleLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cycleLayout.js","sourceRoot":"","sources":["../src/cycleLayout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DiagramNode } from './graph.js';
|
|
2
|
+
/** Shared line breaking keeps browser and headless exports the same height. */
|
|
3
|
+
export declare function detailLines(detail: string | undefined, width: number): string[];
|
|
4
|
+
export declare function detailHeight(node: DiagramNode, width: number, showDetails?: boolean): number;
|
|
5
|
+
//# sourceMappingURL=detailText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detailText.d.ts","sourceRoot":"","sources":["../src/detailText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAwB/E;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,UAAQ,GAAG,MAAM,CAG1F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { estimateTextWidth } from './textWidth.js';
|
|
2
|
+
/** Shared line breaking keeps browser and headless exports the same height. */
|
|
3
|
+
export function detailLines(detail, width) {
|
|
4
|
+
if (!detail?.trim())
|
|
5
|
+
return [];
|
|
6
|
+
const lines = [];
|
|
7
|
+
for (const paragraph of detail.split('\n')) {
|
|
8
|
+
let line = '';
|
|
9
|
+
for (const word of paragraph.trim().split(/\s+/)) {
|
|
10
|
+
const candidate = line ? `${line} ${word}` : word;
|
|
11
|
+
if (estimateTextWidth(candidate, 11) <= Math.max(11, width)) {
|
|
12
|
+
line = candidate;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (line)
|
|
16
|
+
lines.push(line);
|
|
17
|
+
line = '';
|
|
18
|
+
for (const char of word) {
|
|
19
|
+
if (line && estimateTextWidth(line + char, 11) > Math.max(11, width)) {
|
|
20
|
+
lines.push(line);
|
|
21
|
+
line = '';
|
|
22
|
+
}
|
|
23
|
+
line += char;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
lines.push(line);
|
|
27
|
+
}
|
|
28
|
+
return lines;
|
|
29
|
+
}
|
|
30
|
+
export function detailHeight(node, width, showDetails = false) {
|
|
31
|
+
const count = showDetails ? detailLines(node.detail, width - 24).length : 0;
|
|
32
|
+
return count ? count * 15 + 8 : 0;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=detailText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detailText.js","sourceRoot":"","sources":["../src/detailText.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,+EAA+E;AAC/E,MAAM,UAAU,WAAW,CAAC,MAA0B,EAAE,KAAa;IACnE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,CAAA;IAC9B,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,IAAI,IAAI,GAAG,EAAE,CAAA;QACb,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;YACjD,IAAI,iBAAiB,CAAC,SAAS,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC5D,IAAI,GAAG,SAAS,CAAA;gBAChB,SAAQ;YACV,CAAC;YACD,IAAI,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC1B,IAAI,GAAG,EAAE,CAAA;YACT,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,IAAI,IAAI,iBAAiB,CAAC,IAAI,GAAG,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;oBACrE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBAChB,IAAI,GAAG,EAAE,CAAA;gBACX,CAAC;gBACD,IAAI,IAAI,IAAI,CAAA;YACd,CAAC;QACH,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAClB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAiB,EAAE,KAAa,EAAE,WAAW,GAAG,KAAK;IAChF,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3E,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACnC,CAAC"}
|
package/dist/graph.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized diagram model. Every input adapter (YAML, directory walker,
|
|
3
|
+
* Mermaid, AST extractors) produces this shape; every renderer (React,
|
|
4
|
+
* static SVG/PNG) consumes it. Adapters and renderers never talk to each
|
|
5
|
+
* other directly.
|
|
6
|
+
*/
|
|
7
|
+
export interface DiagramNode {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
/** Longer text shown in a detail panel/drawer on selection. */
|
|
11
|
+
detail?: string;
|
|
12
|
+
/** Free-form grouping key used for color coding (e.g. "domain", "external"). Not a fixed enum — adapters define their own vocabulary. */
|
|
13
|
+
category?: string;
|
|
14
|
+
/** Small pill shown next to the label, e.g. "planned", "deprecated". */
|
|
15
|
+
badge?: string;
|
|
16
|
+
/** Nodes this node logically contains, for compound/grouped layout. */
|
|
17
|
+
parentId?: string;
|
|
18
|
+
/** Visual shape for a leaf node. Default "rect". */
|
|
19
|
+
shape?: 'rect' | 'cylinder';
|
|
20
|
+
/**
|
|
21
|
+
* Border style when this node is rendered as a structural-mode container
|
|
22
|
+
* (i.e. other nodes have it as `parentId`) — solid for a real system
|
|
23
|
+
* boundary, dashed for a looser logical grouping. Default "solid".
|
|
24
|
+
*/
|
|
25
|
+
borderStyle?: 'solid' | 'dashed';
|
|
26
|
+
metadata?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface DiagramEdge {
|
|
29
|
+
id: string;
|
|
30
|
+
source: string;
|
|
31
|
+
target: string;
|
|
32
|
+
label?: string;
|
|
33
|
+
/** Relative strength, e.g. import count. Renderers may map this to stroke width. */
|
|
34
|
+
weight?: number;
|
|
35
|
+
/** Free-form kind, e.g. "calls", "imports", "contains". */
|
|
36
|
+
kind?: string;
|
|
37
|
+
/** Marks an edge as aspirational/not-yet-built, mirroring the "future" edges pattern. */
|
|
38
|
+
projected?: boolean;
|
|
39
|
+
/** "line" (default): a thin stroked path. "block": a chunky filled flow-arrow, for a process/pipeline diagram's step-to-step transitions. */
|
|
40
|
+
style?: 'line' | 'block';
|
|
41
|
+
}
|
|
42
|
+
export interface DiagramGraph {
|
|
43
|
+
nodes: DiagramNode[];
|
|
44
|
+
edges: DiagramEdge[];
|
|
45
|
+
metadata?: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createGraph(nodes?: DiagramNode[], edges?: DiagramEdge[], metadata?: Record<string, unknown>): DiagramGraph;
|
|
48
|
+
export declare class GraphValidationError extends Error {
|
|
49
|
+
}
|
|
50
|
+
/** Throws if edges reference missing nodes or ids collide. Adapters should call this before returning. */
|
|
51
|
+
export declare function validateGraph(graph: DiagramGraph): void;
|
|
52
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yIAAyI;IACzI,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oFAAoF;IACpF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yFAAyF;IACzF,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,6IAA6I;IAC7I,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,wBAAgB,WAAW,CACzB,KAAK,GAAE,WAAW,EAAO,EACzB,KAAK,GAAE,WAAW,EAAO,EACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,YAAY,CAEd;AAED,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAElD,0GAA0G;AAC1G,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAgBvD"}
|
package/dist/graph.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized diagram model. Every input adapter (YAML, directory walker,
|
|
3
|
+
* Mermaid, AST extractors) produces this shape; every renderer (React,
|
|
4
|
+
* static SVG/PNG) consumes it. Adapters and renderers never talk to each
|
|
5
|
+
* other directly.
|
|
6
|
+
*/
|
|
7
|
+
export function createGraph(nodes = [], edges = [], metadata) {
|
|
8
|
+
return { nodes, edges, metadata };
|
|
9
|
+
}
|
|
10
|
+
export class GraphValidationError extends Error {
|
|
11
|
+
}
|
|
12
|
+
/** Throws if edges reference missing nodes or ids collide. Adapters should call this before returning. */
|
|
13
|
+
export function validateGraph(graph) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
for (const node of graph.nodes) {
|
|
16
|
+
if (seen.has(node.id)) {
|
|
17
|
+
throw new GraphValidationError(`Duplicate node id "${node.id}"`);
|
|
18
|
+
}
|
|
19
|
+
seen.add(node.id);
|
|
20
|
+
}
|
|
21
|
+
for (const edge of graph.edges) {
|
|
22
|
+
if (!seen.has(edge.source)) {
|
|
23
|
+
throw new GraphValidationError(`Edge "${edge.id}" references missing source node "${edge.source}"`);
|
|
24
|
+
}
|
|
25
|
+
if (!seen.has(edge.target)) {
|
|
26
|
+
throw new GraphValidationError(`Edge "${edge.id}" references missing target node "${edge.target}"`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6CH,MAAM,UAAU,WAAW,CACzB,QAAuB,EAAE,EACzB,QAAuB,EAAE,EACzB,QAAkC;IAElC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,OAAO,oBAAqB,SAAQ,KAAK;CAAG;AAElD,0GAA0G;AAC1G,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,oBAAoB,CAAC,sBAAsB,IAAI,CAAC,EAAE,GAAG,CAAC,CAAA;QAClE,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACnB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,oBAAoB,CAAC,SAAS,IAAI,CAAC,EAAE,qCAAqC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACrG,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,oBAAoB,CAAC,SAAS,IAAI,CAAC,EAAE,qCAAqC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QACrG,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.test.d.ts","sourceRoot":"","sources":["../src/graph.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { createGraph, validateGraph, GraphValidationError } from './graph.js';
|
|
3
|
+
describe('createGraph', () => {
|
|
4
|
+
it('defaults to empty nodes/edges', () => {
|
|
5
|
+
const graph = createGraph();
|
|
6
|
+
expect(graph.nodes).toEqual([]);
|
|
7
|
+
expect(graph.edges).toEqual([]);
|
|
8
|
+
});
|
|
9
|
+
it('carries through provided nodes/edges/metadata', () => {
|
|
10
|
+
const graph = createGraph([{ id: 'a', label: 'A' }], [], { source: 'test' });
|
|
11
|
+
expect(graph.nodes).toHaveLength(1);
|
|
12
|
+
expect(graph.metadata).toEqual({ source: 'test' });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('validateGraph', () => {
|
|
16
|
+
it('accepts a graph with consistent nodes/edges', () => {
|
|
17
|
+
const graph = createGraph([{ id: 'a', label: 'A' }, { id: 'b', label: 'B' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
|
|
18
|
+
expect(() => validateGraph(graph)).not.toThrow();
|
|
19
|
+
});
|
|
20
|
+
it('rejects duplicate node ids', () => {
|
|
21
|
+
const graph = createGraph([{ id: 'a', label: 'A' }, { id: 'a', label: 'A2' }]);
|
|
22
|
+
expect(() => validateGraph(graph)).toThrow(GraphValidationError);
|
|
23
|
+
});
|
|
24
|
+
it('rejects an edge referencing a missing source node', () => {
|
|
25
|
+
const graph = createGraph([{ id: 'b', label: 'B' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
|
|
26
|
+
expect(() => validateGraph(graph)).toThrow(/missing source node "a"/);
|
|
27
|
+
});
|
|
28
|
+
it('rejects an edge referencing a missing target node', () => {
|
|
29
|
+
const graph = createGraph([{ id: 'a', label: 'A' }], [{ id: 'a->b', source: 'a', target: 'b' }]);
|
|
30
|
+
expect(() => validateGraph(graph)).toThrow(/missing target node "b"/);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=graph.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.test.js","sourceRoot":"","sources":["../src/graph.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type { DiagramNode, DiagramEdge, DiagramGraph } from './graph.js';
|
|
2
|
+
export { createGraph, validateGraph, GraphValidationError } from './graph.js';
|
|
3
|
+
export type { LayoutOptions, PositionedNode, PositionedEdge, PositionedGraph } from './layout.js';
|
|
4
|
+
export { layoutGraph } from './layout.js';
|
|
5
|
+
export { layoutCycle } from './cycleLayout.js';
|
|
6
|
+
export { detailLines, detailHeight } from './detailText.js';
|
|
7
|
+
export type { StructuralLayoutOptions } from './structuralLayout.js';
|
|
8
|
+
export { layoutStructural } from './structuralLayout.js';
|
|
9
|
+
export type { StackLayoutOptions } from './stackLayout.js';
|
|
10
|
+
export { layoutStack } from './stackLayout.js';
|
|
11
|
+
export type { ThemeColors, DiagramTheme, GenerateThemeOptions } from './theme.js';
|
|
12
|
+
export { defaultTheme, generateTheme, categoryColor, withAlpha } from './theme.js';
|
|
13
|
+
export type { Point, BlockArrowOptions } from './path.js';
|
|
14
|
+
export { pointsToSvgPath, blockArrowPoints, pointsToPolygon } from './path.js';
|
|
15
|
+
export type { ThemePresetName } from './presets.js';
|
|
16
|
+
export { themePresets } from './presets.js';
|
|
17
|
+
export type { ExtractedColor } from './cssPalette.js';
|
|
18
|
+
export { extractCssColors } from './cssPalette.js';
|
|
19
|
+
export type { NodeWidthOptions } from './textWidth.js';
|
|
20
|
+
export { estimateTextWidth, estimateNodeWidth, truncateLabel } from './textWidth.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAC7E,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC3D,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAClF,YAAY,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC9E,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { createGraph, validateGraph, GraphValidationError } from './graph.js';
|
|
2
|
+
export { layoutGraph } from './layout.js';
|
|
3
|
+
export { layoutCycle } from './cycleLayout.js';
|
|
4
|
+
export { detailLines, detailHeight } from './detailText.js';
|
|
5
|
+
export { layoutStructural } from './structuralLayout.js';
|
|
6
|
+
export { layoutStack } from './stackLayout.js';
|
|
7
|
+
export { defaultTheme, generateTheme, categoryColor, withAlpha } from './theme.js';
|
|
8
|
+
export { pointsToSvgPath, blockArrowPoints, pointsToPolygon } from './path.js';
|
|
9
|
+
export { themePresets } from './presets.js';
|
|
10
|
+
export { extractCssColors } from './cssPalette.js';
|
|
11
|
+
export { estimateTextWidth, estimateNodeWidth, truncateLabel } from './textWidth.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE7E,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAElF,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9E,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAElD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/layout.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { DiagramGraph } from './graph.js';
|
|
2
|
+
export interface LayoutOptions {
|
|
3
|
+
showDetails?: boolean;
|
|
4
|
+
/** Layout flow direction, dagre's rankdir. Default "TB" (top-to-bottom). */
|
|
5
|
+
direction?: 'TB' | 'BT' | 'LR' | 'RL';
|
|
6
|
+
/** Minimum node width — nodes with longer labels grow wider to fit, up to `maxNodeWidth`, when `nodeSizing` is "responsive". Default 160. */
|
|
7
|
+
nodeWidth?: number;
|
|
8
|
+
/** Cap on how wide a single node can grow to fit its label. Default 420. */
|
|
9
|
+
maxNodeWidth?: number;
|
|
10
|
+
/** "responsive" (default): nodes grow to fit their label, up to `maxNodeWidth`. "fixed": every node is exactly `nodeWidth` wide, and a label too long to fit gets truncated with an ellipsis instead. */
|
|
11
|
+
nodeSizing?: 'responsive' | 'fixed';
|
|
12
|
+
nodeHeight?: number;
|
|
13
|
+
/** Horizontal gap between nodes on the same rank. */
|
|
14
|
+
nodeSpacing?: number;
|
|
15
|
+
/** Vertical gap between ranks. */
|
|
16
|
+
rankSpacing?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface PositionedNode {
|
|
19
|
+
id: string;
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
}
|
|
25
|
+
export interface PositionedEdge {
|
|
26
|
+
id: string;
|
|
27
|
+
/** Optional filled arrow outline supplied by a specialized layout. */
|
|
28
|
+
arrow?: Array<{
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
}>;
|
|
32
|
+
/** Full point list dagre routed the edge through, including endpoints. */
|
|
33
|
+
points: Array<{
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
export interface PositionedGraph {
|
|
39
|
+
nodes: PositionedNode[];
|
|
40
|
+
edges: PositionedEdge[];
|
|
41
|
+
width: number;
|
|
42
|
+
height: number;
|
|
43
|
+
}
|
|
44
|
+
/** Runs dagre's layered/DAG layout over a normalized graph and returns absolute pixel positions. */
|
|
45
|
+
export declare function layoutGraph(graph: DiagramGraph, options?: LayoutOptions): PositionedGraph;
|
|
46
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAI9C,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;IACrC,6IAA6I;IAC7I,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yMAAyM;IACzM,UAAU,CAAC,EAAE,YAAY,GAAG,OAAO,CAAA;IACnC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,sEAAsE;IACtE,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvC,0EAA0E;IAC1E,MAAM,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAaD,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,aAAkB,GAAG,eAAe,CAiD7F"}
|