turbometro 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 +80 -0
- package/bin/turbometro.js +2 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +107 -0
- package/dist/find-root.d.ts +2 -0
- package/dist/find-root.js +14 -0
- package/dist/graph.d.ts +14 -0
- package/dist/graph.js +74 -0
- package/dist/layout.d.ts +19 -0
- package/dist/layout.js +15 -0
- package/dist/parse-turbo.d.ts +5 -0
- package/dist/parse-turbo.js +29 -0
- package/dist/parse-workspace.d.ts +4 -0
- package/dist/parse-workspace.js +100 -0
- package/dist/render-html.d.ts +8 -0
- package/dist/render-html.js +699 -0
- package/dist/render-map.d.ts +29 -0
- package/dist/render-map.js +287 -0
- package/dist/replay.d.ts +9 -0
- package/dist/replay.js +74 -0
- package/dist/scale.d.ts +7 -0
- package/dist/scale.js +19 -0
- package/dist/scene-data.d.ts +40 -0
- package/dist/scene-data.js +146 -0
- package/dist/theme.d.ts +15 -0
- package/dist/theme.js +15 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/fixtures/mini-mono/apps/web/package.json +11 -0
- package/fixtures/mini-mono/package.json +5 -0
- package/fixtures/mini-mono/packages/ui/package.json +11 -0
- package/fixtures/mini-mono/packages/utils/package.json +8 -0
- package/fixtures/mini-mono/pnpm-workspace.yaml +3 -0
- package/fixtures/mini-mono/turbo.json +10 -0
- package/package.json +44 -0
- package/vendor/dag-map/LICENSE +201 -0
- package/vendor/dag-map/NOTICE +6 -0
- package/vendor/dag-map/src/color-scales.js +61 -0
- package/vendor/dag-map/src/dag-map.css +63 -0
- package/vendor/dag-map/src/events.js +108 -0
- package/vendor/dag-map/src/graph-utils.js +185 -0
- package/vendor/dag-map/src/hasse.css +61 -0
- package/vendor/dag-map/src/index.js +55 -0
- package/vendor/dag-map/src/layout-flow.js +1066 -0
- package/vendor/dag-map/src/layout-hasse.js +485 -0
- package/vendor/dag-map/src/layout-metro.js +542 -0
- package/vendor/dag-map/src/occupancy.js +138 -0
- package/vendor/dag-map/src/render-flow-station.js +132 -0
- package/vendor/dag-map/src/render.js +360 -0
- package/vendor/dag-map/src/route-angular.js +137 -0
- package/vendor/dag-map/src/route-bezier.js +51 -0
- package/vendor/dag-map/src/route-metro.js +122 -0
- package/vendor/dag-map/src/themes.js +49 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ================================================================
|
|
2
|
+
// route-metro.js — Metro-style right-angle routing with rounded elbows
|
|
3
|
+
// ================================================================
|
|
4
|
+
// Produces clean H-V-H or V-H-V paths with quadratic bezier corners.
|
|
5
|
+
// Designed for parallel-line layouts where visual clarity is paramount.
|
|
6
|
+
//
|
|
7
|
+
// For LTR layouts: horizontal → rounded elbow → vertical → rounded elbow → horizontal
|
|
8
|
+
// For TTB layouts: vertical → rounded elbow → horizontal → rounded elbow → vertical
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Rounded elbow path between two points using right-angle bends.
|
|
12
|
+
*
|
|
13
|
+
* @param {number} px - source x
|
|
14
|
+
* @param {number} py - source y
|
|
15
|
+
* @param {number} qx - destination x
|
|
16
|
+
* @param {number} qy - destination y
|
|
17
|
+
* @param {number} routeIdx - route index (for deterministic midpoint variation)
|
|
18
|
+
* @param {number} segIdx - segment index within route
|
|
19
|
+
* @param {number} refY - reference Y (not used, kept for API compat)
|
|
20
|
+
* @param {object} [options]
|
|
21
|
+
* @param {number} [options.cornerRadius=8] - corner radius in px (before scale)
|
|
22
|
+
* @param {'h-first'|'v-first'|'auto'} [options.bendStyle='auto'] - which axis to run first
|
|
23
|
+
* @returns {string} SVG path data (without leading M)
|
|
24
|
+
*/
|
|
25
|
+
export function metroPath(px, py, qx, qy, routeIdx, segIdx, refY, options = {}) {
|
|
26
|
+
const cornerRadius = options.cornerRadius ?? 8;
|
|
27
|
+
const bendStyle = options.bendStyle ?? 'auto';
|
|
28
|
+
const dx = qx - px, dy = qy - py;
|
|
29
|
+
|
|
30
|
+
// Straight horizontal or vertical — no bend needed
|
|
31
|
+
if (Math.abs(dy) < 1) return `L ${qx} ${qy}`;
|
|
32
|
+
if (Math.abs(dx) < 1) return `L ${qx} ${qy}`;
|
|
33
|
+
|
|
34
|
+
// Clamp radius so it doesn't exceed available space
|
|
35
|
+
const r = Math.min(cornerRadius, Math.abs(dx) / 2, Math.abs(dy) / 2);
|
|
36
|
+
|
|
37
|
+
if (r < 1) return `L ${qx} ${qy}`; // too small for rounded corners
|
|
38
|
+
|
|
39
|
+
// Determine bend direction
|
|
40
|
+
const useHFirst = bendStyle === 'h-first' ||
|
|
41
|
+
(bendStyle === 'auto' && Math.abs(dx) >= Math.abs(dy));
|
|
42
|
+
|
|
43
|
+
if (useHFirst) {
|
|
44
|
+
// H-V path: horizontal run → rounded corner → vertical run
|
|
45
|
+
const midFrac = options.midFrac ?? (0.45 + (((routeIdx * 7 + segIdx * 13) % 17) / 17) * 0.10);
|
|
46
|
+
const midX = px + dx * midFrac;
|
|
47
|
+
|
|
48
|
+
return hvPath(px, py, midX, qx, qy, r);
|
|
49
|
+
} else {
|
|
50
|
+
// V-H path: vertical run → rounded corner → horizontal run
|
|
51
|
+
const midFrac = options.midFrac ?? (0.45 + (((routeIdx * 7 + segIdx * 13) % 17) / 17) * 0.10);
|
|
52
|
+
const midY = py + dy * midFrac;
|
|
53
|
+
|
|
54
|
+
return vhPath(px, py, midY, qx, qy, r);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* H-V-H path: horizontal → corner → vertical → corner → horizontal
|
|
60
|
+
*/
|
|
61
|
+
function hvPath(px, py, midX, qx, qy, r) {
|
|
62
|
+
const dy = qy - py;
|
|
63
|
+
const sy = Math.sign(dy); // +1 down, -1 up
|
|
64
|
+
|
|
65
|
+
// First elbow: at (midX, py) turning toward qy
|
|
66
|
+
const e1x = midX - r; // approach point
|
|
67
|
+
const e1cx = midX; // corner control point
|
|
68
|
+
const e1cy = py;
|
|
69
|
+
const e1ex = midX; // exit point
|
|
70
|
+
const e1ey = py + sy * r;
|
|
71
|
+
|
|
72
|
+
// Second elbow: at (midX, qy) turning toward qx
|
|
73
|
+
const e2x = midX; // approach point
|
|
74
|
+
const e2y = qy - sy * r;
|
|
75
|
+
const e2cx = midX; // corner control point
|
|
76
|
+
const e2cy = qy;
|
|
77
|
+
const e2ex = midX + r * Math.sign(qx - midX); // exit toward qx
|
|
78
|
+
const e2ey = qy;
|
|
79
|
+
|
|
80
|
+
let d = '';
|
|
81
|
+
d += `L ${e1x.toFixed(1)} ${py} `; // horizontal to first elbow approach
|
|
82
|
+
d += `Q ${e1cx.toFixed(1)} ${e1cy} ${e1ex.toFixed(1)} ${e1ey.toFixed(1)} `; // rounded corner
|
|
83
|
+
d += `L ${e2x.toFixed(1)} ${e2y.toFixed(1)} `; // vertical run
|
|
84
|
+
d += `Q ${e2cx.toFixed(1)} ${e2cy} ${e2ex.toFixed(1)} ${e2ey.toFixed(1)} `; // rounded corner
|
|
85
|
+
d += `L ${qx} ${qy}`; // horizontal to destination
|
|
86
|
+
|
|
87
|
+
return d;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* V-H-V path: vertical → corner → horizontal → corner → vertical
|
|
92
|
+
*/
|
|
93
|
+
function vhPath(px, py, midY, qx, qy, r) {
|
|
94
|
+
const dx = qx - px;
|
|
95
|
+
const sx = Math.sign(dx); // +1 right, -1 left
|
|
96
|
+
const dy = qy - py;
|
|
97
|
+
const sy = Math.sign(dy);
|
|
98
|
+
|
|
99
|
+
// First elbow: at (px, midY) turning toward qx
|
|
100
|
+
const e1y = midY - sy * r;
|
|
101
|
+
const e1cx = px;
|
|
102
|
+
const e1cy = midY;
|
|
103
|
+
const e1ex = px + sx * r;
|
|
104
|
+
const e1ey = midY;
|
|
105
|
+
|
|
106
|
+
// Second elbow: at (qx, midY) turning toward qy
|
|
107
|
+
const e2x = qx - sx * r;
|
|
108
|
+
const e2y = midY;
|
|
109
|
+
const e2cx = qx;
|
|
110
|
+
const e2cy = midY;
|
|
111
|
+
const e2ex = qx;
|
|
112
|
+
const e2ey = midY + sy * r;
|
|
113
|
+
|
|
114
|
+
let d = '';
|
|
115
|
+
d += `L ${px} ${e1y.toFixed(1)} `; // vertical to first elbow
|
|
116
|
+
d += `Q ${e1cx.toFixed(1)} ${e1cy.toFixed(1)} ${e1ex.toFixed(1)} ${e1ey.toFixed(1)} `; // rounded corner
|
|
117
|
+
d += `L ${e2x.toFixed(1)} ${e2y.toFixed(1)} `; // horizontal run
|
|
118
|
+
d += `Q ${e2cx.toFixed(1)} ${e2cy.toFixed(1)} ${e2ex.toFixed(1)} ${e2ey.toFixed(1)} `; // rounded corner
|
|
119
|
+
d += `L ${qx} ${qy}`; // vertical to destination
|
|
120
|
+
|
|
121
|
+
return d;
|
|
122
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// ================================================================
|
|
2
|
+
// themes.js — Theme definitions for dag-map
|
|
3
|
+
// ================================================================
|
|
4
|
+
// Five built-in themes plus custom theme support via resolveTheme().
|
|
5
|
+
//
|
|
6
|
+
// The class names (pure, recordable, side_effecting, gate) are defaults.
|
|
7
|
+
// Users can use any string keys they want — just pass matching keys in
|
|
8
|
+
// the node `cls` field and in a custom theme's `classes` object.
|
|
9
|
+
|
|
10
|
+
export const THEMES = {
|
|
11
|
+
cream: {
|
|
12
|
+
paper: '#F5F0E8', ink: '#2C2C2C', muted: '#8C8680', border: '#D4CFC7',
|
|
13
|
+
classes: { pure: '#2B8A8E', recordable: '#E8846B', side_effecting: '#D4944C', gate: '#C45B4A', pending: '#B0AAA0' }
|
|
14
|
+
},
|
|
15
|
+
light: {
|
|
16
|
+
paper: '#FAFAFA', ink: '#333333', muted: '#999999', border: '#E0E0E0',
|
|
17
|
+
classes: { pure: '#0077B6', recordable: '#E36414', side_effecting: '#6C757D', gate: '#DC3545', pending: '#BDBDBD' }
|
|
18
|
+
},
|
|
19
|
+
dark: {
|
|
20
|
+
paper: '#1E1E2E', ink: '#CDD6F4', muted: '#6C7086', border: '#313244',
|
|
21
|
+
classes: { pure: '#94E2D5', recordable: '#F38BA8', side_effecting: '#F9E2AF', gate: '#EBA0AC', pending: '#45475A' }
|
|
22
|
+
},
|
|
23
|
+
blueprint: {
|
|
24
|
+
paper: '#1B2838', ink: '#E0E8F0', muted: '#5A7A9A', border: '#2A4060',
|
|
25
|
+
classes: { pure: '#4FC3F7', recordable: '#FF8A65', side_effecting: '#FFD54F', gate: '#EF5350', pending: '#3D5A7A' }
|
|
26
|
+
},
|
|
27
|
+
mono: {
|
|
28
|
+
paper: '#FFFFFF', ink: '#1A1A1A', muted: '#888888', border: '#CCCCCC',
|
|
29
|
+
classes: { pure: '#444444', recordable: '#777777', side_effecting: '#999999', gate: '#333333', pending: '#AAAAAA' }
|
|
30
|
+
},
|
|
31
|
+
metro: {
|
|
32
|
+
paper: '#FFFFFF', ink: '#1A1A1A', muted: '#9E9E9E', border: '#D4EAF7',
|
|
33
|
+
classes: { pure: '#0078C8', recordable: '#E3242B', side_effecting: '#F5A623', gate: '#6A2D8E', pending: '#BDBDBD' },
|
|
34
|
+
lineOpacity: 1.4
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resolve a theme option to a full theme object.
|
|
40
|
+
*
|
|
41
|
+
* @param {string|object} [themeOption] - theme name string, custom theme object, or undefined
|
|
42
|
+
* @returns {object} resolved theme with paper, ink, muted, border, and classes
|
|
43
|
+
*/
|
|
44
|
+
export function resolveTheme(themeOption) {
|
|
45
|
+
if (!themeOption || themeOption === 'cream') return THEMES.cream;
|
|
46
|
+
if (typeof themeOption === 'string') return THEMES[themeOption] || THEMES.cream;
|
|
47
|
+
// Custom theme object — merge with cream defaults
|
|
48
|
+
return { ...THEMES.cream, ...themeOption, classes: { ...THEMES.cream.classes, ...(themeOption.classes || {}) } };
|
|
49
|
+
}
|