@nowline/layout 0.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/LICENSE +190 -0
- package/README.md +103 -0
- package/dist/band-scale.d.ts +56 -0
- package/dist/band-scale.d.ts.map +1 -0
- package/dist/band-scale.js +86 -0
- package/dist/band-scale.js.map +1 -0
- package/dist/calendar.d.ts +79 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +210 -0
- package/dist/calendar.js.map +1 -0
- package/dist/capacity.d.ts +72 -0
- package/dist/capacity.d.ts.map +1 -0
- package/dist/capacity.js +163 -0
- package/dist/capacity.js.map +1 -0
- package/dist/dsl-utils.d.ts +5 -0
- package/dist/dsl-utils.d.ts.map +1 -0
- package/dist/dsl-utils.js +28 -0
- package/dist/dsl-utils.js.map +1 -0
- package/dist/edge-routing.d.ts +89 -0
- package/dist/edge-routing.d.ts.map +1 -0
- package/dist/edge-routing.js +435 -0
- package/dist/edge-routing.js.map +1 -0
- package/dist/frame-tab-geometry.d.ts +78 -0
- package/dist/frame-tab-geometry.d.ts.map +1 -0
- package/dist/frame-tab-geometry.js +115 -0
- package/dist/frame-tab-geometry.js.map +1 -0
- package/dist/header-card-geometry.d.ts +29 -0
- package/dist/header-card-geometry.d.ts.map +1 -0
- package/dist/header-card-geometry.js +41 -0
- package/dist/header-card-geometry.js.map +1 -0
- package/dist/i18n.d.ts +48 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +114 -0
- package/dist/i18n.js.map +1 -0
- package/dist/include-chrome-geometry.d.ts +86 -0
- package/dist/include-chrome-geometry.d.ts.map +1 -0
- package/dist/include-chrome-geometry.js +104 -0
- package/dist/include-chrome-geometry.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/item-bar-geometry.d.ts +127 -0
- package/dist/item-bar-geometry.d.ts.map +1 -0
- package/dist/item-bar-geometry.js +173 -0
- package/dist/item-bar-geometry.js.map +1 -0
- package/dist/lane-utilization.d.ts +90 -0
- package/dist/lane-utilization.d.ts.map +1 -0
- package/dist/lane-utilization.js +206 -0
- package/dist/lane-utilization.js.map +1 -0
- package/dist/layout-context.d.ts +143 -0
- package/dist/layout-context.d.ts.map +1 -0
- package/dist/layout-context.js +8 -0
- package/dist/layout-context.js.map +1 -0
- package/dist/layout.d.ts +18 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +1213 -0
- package/dist/layout.js.map +1 -0
- package/dist/nodes/anchor-node.d.ts +16 -0
- package/dist/nodes/anchor-node.d.ts.map +1 -0
- package/dist/nodes/anchor-node.js +68 -0
- package/dist/nodes/anchor-node.js.map +1 -0
- package/dist/nodes/footnote-node.d.ts +10 -0
- package/dist/nodes/footnote-node.d.ts.map +1 -0
- package/dist/nodes/footnote-node.js +41 -0
- package/dist/nodes/footnote-node.js.map +1 -0
- package/dist/nodes/group-node.d.ts +29 -0
- package/dist/nodes/group-node.d.ts.map +1 -0
- package/dist/nodes/group-node.js +187 -0
- package/dist/nodes/group-node.js.map +1 -0
- package/dist/nodes/include-node.d.ts +16 -0
- package/dist/nodes/include-node.d.ts.map +1 -0
- package/dist/nodes/include-node.js +117 -0
- package/dist/nodes/include-node.js.map +1 -0
- package/dist/nodes/item-node.d.ts +51 -0
- package/dist/nodes/item-node.d.ts.map +1 -0
- package/dist/nodes/item-node.js +108 -0
- package/dist/nodes/item-node.js.map +1 -0
- package/dist/nodes/marker-geometry.d.ts +22 -0
- package/dist/nodes/marker-geometry.d.ts.map +1 -0
- package/dist/nodes/marker-geometry.js +38 -0
- package/dist/nodes/marker-geometry.js.map +1 -0
- package/dist/nodes/milestone-node.d.ts +48 -0
- package/dist/nodes/milestone-node.d.ts.map +1 -0
- package/dist/nodes/milestone-node.js +210 -0
- package/dist/nodes/milestone-node.js.map +1 -0
- package/dist/nodes/parallel-node.d.ts +21 -0
- package/dist/nodes/parallel-node.d.ts.map +1 -0
- package/dist/nodes/parallel-node.js +80 -0
- package/dist/nodes/parallel-node.js.map +1 -0
- package/dist/nodes/roadmap-node.d.ts +76 -0
- package/dist/nodes/roadmap-node.d.ts.map +1 -0
- package/dist/nodes/roadmap-node.js +788 -0
- package/dist/nodes/roadmap-node.js.map +1 -0
- package/dist/nodes/swimlane-node.d.ts +38 -0
- package/dist/nodes/swimlane-node.d.ts.map +1 -0
- package/dist/nodes/swimlane-node.js +308 -0
- package/dist/nodes/swimlane-node.js.map +1 -0
- package/dist/renderable.d.ts +44 -0
- package/dist/renderable.d.ts.map +1 -0
- package/dist/renderable.js +21 -0
- package/dist/renderable.js.map +1 -0
- package/dist/row-packer.d.ts +125 -0
- package/dist/row-packer.d.ts.map +1 -0
- package/dist/row-packer.js +169 -0
- package/dist/row-packer.js.map +1 -0
- package/dist/style-resolution.d.ts +14 -0
- package/dist/style-resolution.d.ts.map +1 -0
- package/dist/style-resolution.js +191 -0
- package/dist/style-resolution.js.map +1 -0
- package/dist/themes/dark.d.ts +4 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +241 -0
- package/dist/themes/dark.js.map +1 -0
- package/dist/themes/index.d.ts +15 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +30 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/light.d.ts +4 -0
- package/dist/themes/light.d.ts.map +1 -0
- package/dist/themes/light.js +248 -0
- package/dist/themes/light.js.map +1 -0
- package/dist/themes/shape.d.ts +194 -0
- package/dist/themes/shape.d.ts.map +1 -0
- package/dist/themes/shape.js +6 -0
- package/dist/themes/shape.js.map +1 -0
- package/dist/themes/shared.d.ts +145 -0
- package/dist/themes/shared.d.ts.map +1 -0
- package/dist/themes/shared.js +310 -0
- package/dist/themes/shared.js.map +1 -0
- package/dist/time-scale.d.ts +39 -0
- package/dist/time-scale.d.ts.map +1 -0
- package/dist/time-scale.js +62 -0
- package/dist/time-scale.js.map +1 -0
- package/dist/types.d.ts +483 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/view-preset.d.ts +23 -0
- package/dist/view-preset.d.ts.map +1 -0
- package/dist/view-preset.js +146 -0
- package/dist/view-preset.js.map +1 -0
- package/dist/working-calendar.d.ts +14 -0
- package/dist/working-calendar.d.ts.map +1 -0
- package/dist/working-calendar.js +74 -0
- package/dist/working-calendar.js.map +1 -0
- package/package.json +37 -0
- package/src/band-scale.ts +115 -0
- package/src/calendar.ts +244 -0
- package/src/capacity.ts +191 -0
- package/src/dsl-utils.ts +30 -0
- package/src/edge-routing.ts +550 -0
- package/src/frame-tab-geometry.ts +165 -0
- package/src/header-card-geometry.ts +48 -0
- package/src/i18n.ts +124 -0
- package/src/include-chrome-geometry.ts +156 -0
- package/src/index.ts +116 -0
- package/src/item-bar-geometry.ts +222 -0
- package/src/lane-utilization.ts +259 -0
- package/src/layout-context.ts +182 -0
- package/src/layout.ts +1446 -0
- package/src/nodes/anchor-node.ts +77 -0
- package/src/nodes/footnote-node.ts +60 -0
- package/src/nodes/group-node.ts +252 -0
- package/src/nodes/include-node.ts +168 -0
- package/src/nodes/item-node.ts +171 -0
- package/src/nodes/marker-geometry.ts +43 -0
- package/src/nodes/milestone-node.ts +263 -0
- package/src/nodes/parallel-node.ts +101 -0
- package/src/nodes/roadmap-node.ts +957 -0
- package/src/nodes/swimlane-node.ts +423 -0
- package/src/renderable.ts +68 -0
- package/src/row-packer.ts +271 -0
- package/src/style-resolution.ts +243 -0
- package/src/themes/dark.ts +244 -0
- package/src/themes/index.ts +36 -0
- package/src/themes/light.ts +251 -0
- package/src/themes/shape.ts +230 -0
- package/src/themes/shared.ts +369 -0
- package/src/time-scale.ts +78 -0
- package/src/types.ts +607 -0
- package/src/view-preset.ts +180 -0
- package/src/working-calendar.ts +91 -0
package/dist/calendar.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Calendar and duration resolution. An item's `duration:` is a raw literal
|
|
2
|
+
// (`1d`, `2w`, `3m`, `1q`, `1y`); `size:NAME` references a `size NAME
|
|
3
|
+
// effort:<literal>` declaration whose effort literal is calendar-resolved
|
|
4
|
+
// once into a `ResolvedSize`. The calendar block controls how literal units
|
|
5
|
+
// translate into absolute days.
|
|
6
|
+
import { parseCapacityValue } from './capacity.js';
|
|
7
|
+
import { propValue } from './dsl-utils.js';
|
|
8
|
+
const BUSINESS = {
|
|
9
|
+
mode: 'business',
|
|
10
|
+
daysPerWeek: 5,
|
|
11
|
+
daysPerMonth: 22,
|
|
12
|
+
daysPerQuarter: 65,
|
|
13
|
+
daysPerYear: 260,
|
|
14
|
+
};
|
|
15
|
+
const FULL = {
|
|
16
|
+
mode: 'full',
|
|
17
|
+
daysPerWeek: 7,
|
|
18
|
+
daysPerMonth: 30,
|
|
19
|
+
daysPerQuarter: 91,
|
|
20
|
+
daysPerYear: 365,
|
|
21
|
+
};
|
|
22
|
+
export function resolveCalendar(file, customBlock) {
|
|
23
|
+
const calProp = file.roadmapDecl?.properties.find((p) => stripColon(p.key) === 'calendar');
|
|
24
|
+
const mode = calProp?.value ?? 'business';
|
|
25
|
+
if (mode === 'business')
|
|
26
|
+
return { ...BUSINESS };
|
|
27
|
+
if (mode === 'full')
|
|
28
|
+
return { ...FULL };
|
|
29
|
+
if (customBlock) {
|
|
30
|
+
const get = (key, fallback) => {
|
|
31
|
+
const p = customBlock.properties.find((x) => stripColon(x.key) === key);
|
|
32
|
+
const n = p ? parseInt(p.value, 10) : NaN;
|
|
33
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
mode: 'custom',
|
|
37
|
+
daysPerWeek: get('days-per-week', BUSINESS.daysPerWeek),
|
|
38
|
+
daysPerMonth: get('days-per-month', BUSINESS.daysPerMonth),
|
|
39
|
+
daysPerQuarter: get('days-per-quarter', BUSINESS.daysPerQuarter),
|
|
40
|
+
daysPerYear: get('days-per-year', BUSINESS.daysPerYear),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return { ...BUSINESS };
|
|
44
|
+
}
|
|
45
|
+
// Decimal-aware so `size xs effort:0.5d` and `duration:1.5w` round-trip cleanly.
|
|
46
|
+
const DURATION_RE = /^(\d+(?:\.\d+)?)([dwmqy])$/;
|
|
47
|
+
export function literalToDays(literal, cal) {
|
|
48
|
+
const m = DURATION_RE.exec(literal);
|
|
49
|
+
if (!m)
|
|
50
|
+
return 0;
|
|
51
|
+
const n = parseFloat(m[1]);
|
|
52
|
+
switch (m[2]) {
|
|
53
|
+
case 'd':
|
|
54
|
+
return n;
|
|
55
|
+
case 'w':
|
|
56
|
+
return n * cal.daysPerWeek;
|
|
57
|
+
case 'm':
|
|
58
|
+
return n * cal.daysPerMonth;
|
|
59
|
+
case 'q':
|
|
60
|
+
return n * cal.daysPerQuarter;
|
|
61
|
+
case 'y':
|
|
62
|
+
return n * cal.daysPerYear;
|
|
63
|
+
default:
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Convert a `duration:` literal or a `size:NAME` reference into a calendar-
|
|
69
|
+
* resolved day count. Capacity-agnostic — used both for the duration
|
|
70
|
+
* literal lookup in `deriveItemDurationDays` and for the
|
|
71
|
+
* `remaining:` literal normalization in `sequenceItem`. Returns 0 for
|
|
72
|
+
* missing or unresolvable values; callers substitute their own minimum
|
|
73
|
+
* width when the result is zero.
|
|
74
|
+
*/
|
|
75
|
+
export function resolveDuration(value, sizes, cal) {
|
|
76
|
+
if (!value)
|
|
77
|
+
return 0;
|
|
78
|
+
if (DURATION_RE.test(value))
|
|
79
|
+
return literalToDays(value, cal);
|
|
80
|
+
return sizes.get(value)?.effortDays ?? 0;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Derive an item's calendar duration in days from its properties.
|
|
84
|
+
* Precedence (matches specs/dsl.md § "Sizing precedence"):
|
|
85
|
+
*
|
|
86
|
+
* 1. Explicit `duration:LITERAL` wins. The literal IS the calendar
|
|
87
|
+
* duration the bar paints; `size:NAME` (if also present) collapses
|
|
88
|
+
* to a pure annotation rendered as the size chip.
|
|
89
|
+
* 2. Otherwise, `size:NAME` resolves to its size declaration's
|
|
90
|
+
* `effort:` (single-engineer days) and we divide by the item's
|
|
91
|
+
* capacity (default 1) to get the team's calendar duration.
|
|
92
|
+
* 3. With neither, returns 0 — the validator already errors on items
|
|
93
|
+
* missing both `size:` and `duration:`, so this only happens in
|
|
94
|
+
* transient malformed inputs.
|
|
95
|
+
*/
|
|
96
|
+
export function deriveItemDurationDays(props, sizes, cal) {
|
|
97
|
+
const durationRaw = propValue(props, 'duration');
|
|
98
|
+
if (durationRaw && DURATION_RE.test(durationRaw)) {
|
|
99
|
+
return literalToDays(durationRaw, cal);
|
|
100
|
+
}
|
|
101
|
+
const sizeRef = propValue(props, 'size');
|
|
102
|
+
const size = sizeRef ? sizes.get(sizeRef) : undefined;
|
|
103
|
+
if (!size)
|
|
104
|
+
return 0;
|
|
105
|
+
const capacity = parseCapacityValue(propValue(props, 'capacity')) ?? 1;
|
|
106
|
+
return size.effortDays / capacity;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Total work for the item in single-engineer days. Used to normalize a
|
|
110
|
+
* literal `remaining:` value (also single-engineer days per spec) into a
|
|
111
|
+
* 0..1 progress fraction.
|
|
112
|
+
*
|
|
113
|
+
* - sized: `size.effortDays` directly (already per-engineer).
|
|
114
|
+
* - duration-literal'd: `duration_days × capacity`. The literal sets
|
|
115
|
+
* calendar duration; multiplying by the engineer count recovers the
|
|
116
|
+
* equivalent single-engineer effort the lane is consuming.
|
|
117
|
+
*
|
|
118
|
+
* Returns 0 when neither `size:` nor `duration:` is set so callers can
|
|
119
|
+
* skip normalization safely.
|
|
120
|
+
*/
|
|
121
|
+
export function deriveTotalEffortDays(props, sizes, cal) {
|
|
122
|
+
const sizeRef = propValue(props, 'size');
|
|
123
|
+
const size = sizeRef ? sizes.get(sizeRef) : undefined;
|
|
124
|
+
if (size)
|
|
125
|
+
return size.effortDays;
|
|
126
|
+
const durationRaw = propValue(props, 'duration');
|
|
127
|
+
if (durationRaw && DURATION_RE.test(durationRaw)) {
|
|
128
|
+
const capacity = parseCapacityValue(propValue(props, 'capacity')) ?? 1;
|
|
129
|
+
return literalToDays(durationRaw, cal) * capacity;
|
|
130
|
+
}
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Format a calendar day count back into the largest unit literal that
|
|
135
|
+
* divides cleanly under the active calendar. Used by the meta line for
|
|
136
|
+
* sized items so the displayed duration reflects the *derived* calendar
|
|
137
|
+
* duration rather than the raw effort literal — `size:m capacity:5`
|
|
138
|
+
* with `effort:1w` paints a 1d bar and the meta should read `M 1d`,
|
|
139
|
+
* not `M 1w`.
|
|
140
|
+
*
|
|
141
|
+
* - Whole-unit folds happen in descending order (`y` → `q` → `m` →
|
|
142
|
+
* `w`); the first one that yields an integer wins so `5d` →
|
|
143
|
+
* `"1w"` and `22d` (business calendar) → `"1m"`.
|
|
144
|
+
* - Anything left over collapses to a `Nd` literal with up to two
|
|
145
|
+
* decimal places, trailing zeros trimmed (`"3d"`, `"1.5d"`,
|
|
146
|
+
* `"1.67d"`). Matches the `DURATION_LITERAL` shape the parser
|
|
147
|
+
* accepts so round-tripping through the renderer reads naturally.
|
|
148
|
+
* - `0` (or anything sub-1d) returns `"0d"` rather than an empty
|
|
149
|
+
* string so callers always get a renderable token.
|
|
150
|
+
*/
|
|
151
|
+
export function formatDurationDays(days, cal) {
|
|
152
|
+
const EPSILON = 1e-6;
|
|
153
|
+
const isWhole = (n) => n >= 1 && Math.abs(n - Math.round(n)) < EPSILON;
|
|
154
|
+
const folds = [
|
|
155
|
+
[cal.daysPerYear, 'y'],
|
|
156
|
+
[cal.daysPerQuarter, 'q'],
|
|
157
|
+
[cal.daysPerMonth, 'm'],
|
|
158
|
+
[cal.daysPerWeek, 'w'],
|
|
159
|
+
];
|
|
160
|
+
for (const [unitDays, suffix] of folds) {
|
|
161
|
+
const n = days / unitDays;
|
|
162
|
+
if (isWhole(n))
|
|
163
|
+
return `${Math.round(n)}${suffix}`;
|
|
164
|
+
}
|
|
165
|
+
if (Math.abs(days - Math.round(days)) < EPSILON) {
|
|
166
|
+
return `${Math.round(days)}d`;
|
|
167
|
+
}
|
|
168
|
+
// parseFloat strips trailing zeros: `1.50` → `1.5`, `1.67` → `1.67`.
|
|
169
|
+
return `${parseFloat(days.toFixed(2))}d`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Build the layout's `Map<string, ResolvedSize>` once the calendar is
|
|
173
|
+
* known. Skips sizes whose `effort:` literal is missing or unparseable —
|
|
174
|
+
* the validator already errors on those, so layout silently drops them
|
|
175
|
+
* rather than emitting NaN-laden positions.
|
|
176
|
+
*/
|
|
177
|
+
export function resolveSizes(decls, cal) {
|
|
178
|
+
const out = new Map();
|
|
179
|
+
for (const [name, decl] of decls) {
|
|
180
|
+
const effortProp = decl.properties.find((p) => stripColon(p.key) === 'effort');
|
|
181
|
+
const effortLiteral = effortProp?.value;
|
|
182
|
+
if (!effortLiteral)
|
|
183
|
+
continue;
|
|
184
|
+
const effortDays = literalToDays(effortLiteral, cal);
|
|
185
|
+
if (effortDays <= 0)
|
|
186
|
+
continue;
|
|
187
|
+
out.set(name, {
|
|
188
|
+
name,
|
|
189
|
+
title: decl.title || undefined,
|
|
190
|
+
effortDays,
|
|
191
|
+
effortLiteral,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
function stripColon(key) {
|
|
197
|
+
return key.endsWith(':') ? key.slice(0, -1) : key;
|
|
198
|
+
}
|
|
199
|
+
// Date arithmetic in day units. All coordinates are relative to the roadmap
|
|
200
|
+
// start date; today is computed in days since start.
|
|
201
|
+
export function daysBetween(from, to) {
|
|
202
|
+
const ONE_DAY = 86400 * 1000;
|
|
203
|
+
return Math.round((to.getTime() - from.getTime()) / ONE_DAY);
|
|
204
|
+
}
|
|
205
|
+
export function addDays(base, days) {
|
|
206
|
+
const out = new Date(base.getTime());
|
|
207
|
+
out.setUTCDate(out.getUTCDate() + days);
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=calendar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar.js","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SymbolDeclaration } from '@nowline/core';
|
|
2
|
+
import type { ResolvedCapacityIconRef } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parse a `capacity:` value. Accepts the three forms the validator allows on
|
|
5
|
+
* items (positive int, positive decimal, positive percent) — swimlanes only
|
|
6
|
+
* allow int/decimal but the validator already rejects percent on lanes, so a
|
|
7
|
+
* single parser is fine here.
|
|
8
|
+
*
|
|
9
|
+
* Returns `null` when the value is missing, malformed, or non-positive. The
|
|
10
|
+
* renderer must not draw a capacity suffix / badge in that case (per spec
|
|
11
|
+
* "the suffix appears only when the resolved capacity is `> 0`").
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseCapacityValue(raw: string | undefined): number | null;
|
|
14
|
+
/**
|
|
15
|
+
* Format a parsed capacity number for display per specs/rendering.md §
|
|
16
|
+
* "Number formatting": integers render as integers (`5`, not `5.0`); decimals
|
|
17
|
+
* render with trailing zeros trimmed (`0.5`, `1.25`).
|
|
18
|
+
*
|
|
19
|
+
* The `toFixed(6)` cap guards against `0.1 + 0.2`-style float noise creeping
|
|
20
|
+
* into the rendered string. Six is more than the DSL grammar admits anyway —
|
|
21
|
+
* `1.234567%` lexes but is far below the granularity any roadmap author cares
|
|
22
|
+
* about, so trimming there is safe.
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatCapacityNumber(value: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Resolved capacity-icon ready for the renderer. Re-exported from
|
|
27
|
+
* `./types.js` so callers can `import { ResolvedCapacityIcon } from
|
|
28
|
+
* './capacity.js'` without reaching into the positioned-model module.
|
|
29
|
+
*
|
|
30
|
+
* Two flavors:
|
|
31
|
+
*
|
|
32
|
+
* - `kind: 'builtin'` — the renderer looks up its SVG (person/people/
|
|
33
|
+
* points/time) or text representation (multiplier) via its icon library.
|
|
34
|
+
* `'none'` is collapsed to `null` upstream (no glyph rendered).
|
|
35
|
+
* - `kind: 'literal'` — the renderer paints `text` as a `<text>` node.
|
|
36
|
+
* Covers inline Unicode literals (`capacity-icon:"💰"`) and dereferenced
|
|
37
|
+
* custom `symbol` declarations.
|
|
38
|
+
*/
|
|
39
|
+
export type ResolvedCapacityIcon = ResolvedCapacityIconRef;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve a `capacity-icon:` style value into a `ResolvedCapacityIcon` (or
|
|
42
|
+
* `null` for `'none'`).
|
|
43
|
+
*
|
|
44
|
+
* Resolution order matches specs/dsl.md § Style Properties for `icon:` and
|
|
45
|
+
* `capacity-icon:`:
|
|
46
|
+
*
|
|
47
|
+
* 1. `'none'` → `null` (renderer emits no glyph).
|
|
48
|
+
* 2. Built-in name → `{ kind: 'builtin', name }`.
|
|
49
|
+
* 3. Custom symbol id present in `symbols` → `{ kind: 'literal', text:
|
|
50
|
+
* <unicode:> }`. The author wrote an identifier; we hand the
|
|
51
|
+
* renderer the underlying Unicode payload.
|
|
52
|
+
* 4. Anything else → `{ kind: 'literal', text: icon }`. This is the inline
|
|
53
|
+
* Unicode literal form (`capacity-icon:"💰"`) — Langium's
|
|
54
|
+
* ValueConverter has already stripped the surrounding quotes, so the
|
|
55
|
+
* raw payload arrives here.
|
|
56
|
+
*
|
|
57
|
+
* Validator rule 17 already rejects malformed combinations (unknown built-in
|
|
58
|
+
* with no matching symbol, symbol id collision with built-ins, etc.), so this
|
|
59
|
+
* function trusts its input shape.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveCapacityIcon(icon: string, symbols: Map<string, SymbolDeclaration>): ResolvedCapacityIcon | null;
|
|
62
|
+
/**
|
|
63
|
+
* Estimate the on-screen width (px) the capacity suffix will occupy at the
|
|
64
|
+
* given font size, including the leading separator gap before the glyph.
|
|
65
|
+
*
|
|
66
|
+
* The renderer paints the suffix as `<num>{gap}<glyph>` (no leading space
|
|
67
|
+
* before the number — callers handle that as an outer separator). Width
|
|
68
|
+
* estimates are intentionally pessimistic so borderline-fitting captions
|
|
69
|
+
* trigger spill rather than clip.
|
|
70
|
+
*/
|
|
71
|
+
export declare function estimateCapacitySuffixWidth(text: string, icon: ResolvedCapacityIcon | null, fontSizePx: number): number;
|
|
72
|
+
//# sourceMappingURL=capacity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity.d.ts","sourceRoot":"","sources":["../src/capacity.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAuB1D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAezE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAmB3D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAC/B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,GACxC,oBAAoB,GAAG,IAAI,CAc7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACvC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,oBAAoB,GAAG,IAAI,EACjC,UAAU,EAAE,MAAM,GACnB,MAAM,CAcR"}
|
package/dist/capacity.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Capacity parsing, number formatting, and capacity-icon resolution helpers.
|
|
2
|
+
//
|
|
3
|
+
// Layout owns the *contract* the renderer reads:
|
|
4
|
+
//
|
|
5
|
+
// - `parseCapacityValue` turns the DSL's three numeric forms (`5`, `0.5`,
|
|
6
|
+
// `50%`) into a single positive number — percent literals are syntactic
|
|
7
|
+
// sugar for decimals (`50%` → `0.5`) per specs/dsl.md § Capacity.
|
|
8
|
+
// - `formatCapacityNumber` produces the spec's display string: integers
|
|
9
|
+
// stay integers (`5`, not `5.0`); decimals trim trailing zeros (`0.5`,
|
|
10
|
+
// `1.25`).
|
|
11
|
+
// - `resolveCapacityIcon` collapses the three syntactic forms of the
|
|
12
|
+
// `capacity-icon:` style property (built-in name, custom `symbol` id,
|
|
13
|
+
// inline Unicode literal) into either a built-in name the renderer
|
|
14
|
+
// recognizes OR a literal string the renderer paints as text. Custom
|
|
15
|
+
// symbol ids are dereferenced via `ResolvedConfig.symbols` here so the
|
|
16
|
+
// renderer never has to walk the config map.
|
|
17
|
+
//
|
|
18
|
+
// All three helpers are pure — no AST, no theme, no side effects — so they
|
|
19
|
+
// can be tested in isolation and reused by future capacity consumers (e.g.
|
|
20
|
+
// the lane badge in m7 and the overload sweep in m8).
|
|
21
|
+
/**
|
|
22
|
+
* Built-in `capacity-icon:` names the renderer understands directly. Stays in
|
|
23
|
+
* sync with `BUILTIN_CAPACITY_ICONS` in `packages/core/.../nowline-validator.ts`
|
|
24
|
+
* — the validator uses this set to decide whether a value is a known built-in;
|
|
25
|
+
* the layout uses it to decide whether to forward the value as-is or
|
|
26
|
+
* dereference it through the glyph map. Layout-side and validator-side can
|
|
27
|
+
* diverge briefly during refactors but should converge before each release.
|
|
28
|
+
*/
|
|
29
|
+
const BUILTIN_CAPACITY_ICONS = new Set([
|
|
30
|
+
'none',
|
|
31
|
+
'multiplier',
|
|
32
|
+
'person',
|
|
33
|
+
'people',
|
|
34
|
+
'points',
|
|
35
|
+
'time',
|
|
36
|
+
]);
|
|
37
|
+
const POSITIVE_INT_RE = /^\d+$/;
|
|
38
|
+
const POSITIVE_DECIMAL_RE = /^\d+\.\d+$/;
|
|
39
|
+
const POSITIVE_PERCENT_RE = /^\d+(?:\.\d+)?%$/;
|
|
40
|
+
/**
|
|
41
|
+
* Parse a `capacity:` value. Accepts the three forms the validator allows on
|
|
42
|
+
* items (positive int, positive decimal, positive percent) — swimlanes only
|
|
43
|
+
* allow int/decimal but the validator already rejects percent on lanes, so a
|
|
44
|
+
* single parser is fine here.
|
|
45
|
+
*
|
|
46
|
+
* Returns `null` when the value is missing, malformed, or non-positive. The
|
|
47
|
+
* renderer must not draw a capacity suffix / badge in that case (per spec
|
|
48
|
+
* "the suffix appears only when the resolved capacity is `> 0`").
|
|
49
|
+
*/
|
|
50
|
+
export function parseCapacityValue(raw) {
|
|
51
|
+
if (!raw)
|
|
52
|
+
return null;
|
|
53
|
+
if (POSITIVE_INT_RE.test(raw)) {
|
|
54
|
+
const n = parseInt(raw, 10);
|
|
55
|
+
return n > 0 ? n : null;
|
|
56
|
+
}
|
|
57
|
+
if (POSITIVE_DECIMAL_RE.test(raw)) {
|
|
58
|
+
const n = parseFloat(raw);
|
|
59
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
60
|
+
}
|
|
61
|
+
if (POSITIVE_PERCENT_RE.test(raw)) {
|
|
62
|
+
const n = parseFloat(raw.slice(0, -1)) / 100;
|
|
63
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Format a parsed capacity number for display per specs/rendering.md §
|
|
69
|
+
* "Number formatting": integers render as integers (`5`, not `5.0`); decimals
|
|
70
|
+
* render with trailing zeros trimmed (`0.5`, `1.25`).
|
|
71
|
+
*
|
|
72
|
+
* The `toFixed(6)` cap guards against `0.1 + 0.2`-style float noise creeping
|
|
73
|
+
* into the rendered string. Six is more than the DSL grammar admits anyway —
|
|
74
|
+
* `1.234567%` lexes but is far below the granularity any roadmap author cares
|
|
75
|
+
* about, so trimming there is safe.
|
|
76
|
+
*/
|
|
77
|
+
export function formatCapacityNumber(value) {
|
|
78
|
+
if (Number.isInteger(value))
|
|
79
|
+
return String(value);
|
|
80
|
+
let s = value.toFixed(6);
|
|
81
|
+
s = s.replace(/0+$/, '');
|
|
82
|
+
s = s.replace(/\.$/, '');
|
|
83
|
+
return s;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Read a property value off a Langium-shaped EntityProperty, normalizing the
|
|
87
|
+
* trailing-colon form. Validator-side uses the same trick — the grammar
|
|
88
|
+
* stores `key` as the *raw* token, including the colon for `unicode:`-style
|
|
89
|
+
* property keys.
|
|
90
|
+
*/
|
|
91
|
+
function propKey(prop) {
|
|
92
|
+
return prop.key.endsWith(':') ? prop.key.slice(0, -1) : prop.key;
|
|
93
|
+
}
|
|
94
|
+
function symbolUnicode(decl) {
|
|
95
|
+
for (const p of decl.properties) {
|
|
96
|
+
if (propKey(p) === 'unicode' && p.value)
|
|
97
|
+
return p.value;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a `capacity-icon:` style value into a `ResolvedCapacityIcon` (or
|
|
103
|
+
* `null` for `'none'`).
|
|
104
|
+
*
|
|
105
|
+
* Resolution order matches specs/dsl.md § Style Properties for `icon:` and
|
|
106
|
+
* `capacity-icon:`:
|
|
107
|
+
*
|
|
108
|
+
* 1. `'none'` → `null` (renderer emits no glyph).
|
|
109
|
+
* 2. Built-in name → `{ kind: 'builtin', name }`.
|
|
110
|
+
* 3. Custom symbol id present in `symbols` → `{ kind: 'literal', text:
|
|
111
|
+
* <unicode:> }`. The author wrote an identifier; we hand the
|
|
112
|
+
* renderer the underlying Unicode payload.
|
|
113
|
+
* 4. Anything else → `{ kind: 'literal', text: icon }`. This is the inline
|
|
114
|
+
* Unicode literal form (`capacity-icon:"💰"`) — Langium's
|
|
115
|
+
* ValueConverter has already stripped the surrounding quotes, so the
|
|
116
|
+
* raw payload arrives here.
|
|
117
|
+
*
|
|
118
|
+
* Validator rule 17 already rejects malformed combinations (unknown built-in
|
|
119
|
+
* with no matching symbol, symbol id collision with built-ins, etc.), so this
|
|
120
|
+
* function trusts its input shape.
|
|
121
|
+
*/
|
|
122
|
+
export function resolveCapacityIcon(icon, symbols) {
|
|
123
|
+
if (icon === 'none')
|
|
124
|
+
return null;
|
|
125
|
+
if (BUILTIN_CAPACITY_ICONS.has(icon)) {
|
|
126
|
+
return {
|
|
127
|
+
kind: 'builtin',
|
|
128
|
+
name: icon,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const custom = symbols.get(icon);
|
|
132
|
+
if (custom) {
|
|
133
|
+
const unicode = symbolUnicode(custom);
|
|
134
|
+
return { kind: 'literal', text: unicode ?? icon };
|
|
135
|
+
}
|
|
136
|
+
return { kind: 'literal', text: icon };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Estimate the on-screen width (px) the capacity suffix will occupy at the
|
|
140
|
+
* given font size, including the leading separator gap before the glyph.
|
|
141
|
+
*
|
|
142
|
+
* The renderer paints the suffix as `<num>{gap}<glyph>` (no leading space
|
|
143
|
+
* before the number — callers handle that as an outer separator). Width
|
|
144
|
+
* estimates are intentionally pessimistic so borderline-fitting captions
|
|
145
|
+
* trigger spill rather than clip.
|
|
146
|
+
*/
|
|
147
|
+
export function estimateCapacitySuffixWidth(text, icon, fontSizePx) {
|
|
148
|
+
if (!icon)
|
|
149
|
+
return text.length * fontSizePx * 0.58;
|
|
150
|
+
if (icon.kind === 'builtin' && icon.name === 'multiplier') {
|
|
151
|
+
// Multiplier is a typographic operator with built-in side bearing —
|
|
152
|
+
// no separator gap, glyph width approx. one character.
|
|
153
|
+
return (text.length + 1) * fontSizePx * 0.58;
|
|
154
|
+
}
|
|
155
|
+
// 0.1em separator + 1em glyph. Estimating the glyph as 1em (rather than
|
|
156
|
+
// 0.58em, the per-character width) is intentional: SVG icons render at
|
|
157
|
+
// their full font-size square, and most Unicode literals authors use for
|
|
158
|
+
// capacity (★, 💰, ⚙) similarly read at near-em widths.
|
|
159
|
+
const glyphWidthEm = 1.0;
|
|
160
|
+
const gapEm = 0.1;
|
|
161
|
+
return text.length * fontSizePx * 0.58 + (gapEm + glyphWidthEm) * fontSizePx;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=capacity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity.js","sourceRoot":"","sources":["../src/capacity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { EntityProperty } from '@nowline/core';
|
|
2
|
+
export declare function propValue(props: EntityProperty[], key: string): string | undefined;
|
|
3
|
+
export declare function propValues(props: EntityProperty[], key: string): string[];
|
|
4
|
+
export declare function parseDate(raw: string | undefined): Date | null;
|
|
5
|
+
//# sourceMappingURL=dsl-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsl-utils.d.ts","sourceRoot":"","sources":["../src/dsl-utils.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAElF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAIzE;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAM9D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Tiny DSL utilities shared between `layout.ts` and the per-entity nodes
|
|
2
|
+
// under `nodes/`. Kept intentionally trivial and pure: a `:`-trim,
|
|
3
|
+
// property lookup helpers against the AST's `EntityProperty[]`, and an
|
|
4
|
+
// ISO-date parser. Anything that needs configuration or non-trivial
|
|
5
|
+
// resolution (durations, calendars, styles) stays in the modules that
|
|
6
|
+
// own those concerns.
|
|
7
|
+
function stripColon(key) {
|
|
8
|
+
return key.endsWith(':') ? key.slice(0, -1) : key;
|
|
9
|
+
}
|
|
10
|
+
export function propValue(props, key) {
|
|
11
|
+
return props.find((p) => stripColon(p.key) === key)?.value;
|
|
12
|
+
}
|
|
13
|
+
export function propValues(props, key) {
|
|
14
|
+
const p = props.find((x) => stripColon(x.key) === key);
|
|
15
|
+
if (!p)
|
|
16
|
+
return [];
|
|
17
|
+
return p.value !== undefined ? [p.value] : [...p.values];
|
|
18
|
+
}
|
|
19
|
+
export function parseDate(raw) {
|
|
20
|
+
if (!raw)
|
|
21
|
+
return null;
|
|
22
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(raw);
|
|
23
|
+
if (!m)
|
|
24
|
+
return null;
|
|
25
|
+
const d = new Date(Date.UTC(parseInt(m[1], 10), parseInt(m[2], 10) - 1, parseInt(m[3], 10)));
|
|
26
|
+
return Number.isNaN(d.getTime()) ? null : d;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=dsl-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsl-utils.js","sourceRoot":"","sources":["../src/dsl-utils.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,mEAAmE;AACnE,uEAAuE;AACvE,oEAAoE;AACpE,sEAAsE;AACtE,sBAAsB;AAItB,SAAS,UAAU,CAAC,GAAW;IAC3B,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAuB,EAAE,GAAW;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAuB,EAAE,GAAW;IAC3D,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IACvD,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAuB;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,CAAC,GAAG,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7F,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { BoundingBox, Point, PositionedIncludeRegion, PositionedSwimlane } from './types.js';
|
|
2
|
+
/** A visible parallel or group bracket stroke. Used by the
|
|
3
|
+
* bracket-clearance nudge: arrows whose chosen elbow X falls within
|
|
4
|
+
* `BRACKET_NUDGE_PX` of one of these lines are shifted away. Brackets
|
|
5
|
+
* are NOT obstacles — they're aesthetic preferences, not collisions. */
|
|
6
|
+
export interface BracketLine {
|
|
7
|
+
x: number;
|
|
8
|
+
yTop: number;
|
|
9
|
+
yBottom: number;
|
|
10
|
+
}
|
|
11
|
+
/** Per-edge routing request. `from` and `to` are the (visualEdge, midY)
|
|
12
|
+
* attach points the rest of the layout already computed; the router
|
|
13
|
+
* produces the orthogonal polyline that connects them. */
|
|
14
|
+
export interface EdgeRouteRequest {
|
|
15
|
+
fromId: string;
|
|
16
|
+
toId: string;
|
|
17
|
+
from: Point;
|
|
18
|
+
to: Point;
|
|
19
|
+
/** Marker → item edges (anchor / milestone source) skip the channel
|
|
20
|
+
* router entirely — the cut line is the visible stem, the path is
|
|
21
|
+
* always a short horizontal stub. Set true to bypass routing. */
|
|
22
|
+
isMarkerSource: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface EdgeRouteResult {
|
|
25
|
+
fromId: string;
|
|
26
|
+
toId: string;
|
|
27
|
+
waypoints: Point[];
|
|
28
|
+
/** True when the chosen channel intersected an item bar. The
|
|
29
|
+
* renderer paints these edges BEFORE bar fills with a thinner
|
|
30
|
+
* stroke so the bar still reads as the foreground. */
|
|
31
|
+
underBar: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** Distance (px) the elbow X must keep from any visible bracket. Picked
|
|
34
|
+
* to leave a small visible gap between the arrow's vertical leg and
|
|
35
|
+
* the bracket stroke without forcing the arrow far from its natural
|
|
36
|
+
* mid-gutter line. */
|
|
37
|
+
export declare const BRACKET_NUDGE_PX = 4;
|
|
38
|
+
/** Minimum horizontal source stub (px) — distance from the source's
|
|
39
|
+
* exit point to the vertical-leg elbow. Below this, the source-side
|
|
40
|
+
* arrow body collapses into the bar's right edge with no visible
|
|
41
|
+
* horizontal segment. Treated as a hard constraint when picking
|
|
42
|
+
* the channel X for left-to-right edges. */
|
|
43
|
+
export declare const MIN_SOURCE_STUB_PX = 6;
|
|
44
|
+
/** Minimum horizontal target stub (px) — distance from the vertical-leg
|
|
45
|
+
* elbow to the target's left edge. Below this, the arrowhead has no
|
|
46
|
+
* horizontal lead-in: the leg appears to plunge directly into the bar
|
|
47
|
+
* without a visible target-side stub. Treated as a hard constraint
|
|
48
|
+
* when picking the channel X for left-to-right edges; conflicts with
|
|
49
|
+
* obstacle / bracket clearance trigger the under-bar fallback. */
|
|
50
|
+
export declare const MIN_TARGET_STUB_PX = 6;
|
|
51
|
+
export interface ChannelGridInput {
|
|
52
|
+
/** Every painted item box (visual edges, including chip-spill
|
|
53
|
+
* growth). Treated as hard obstacles: a vertical leg crossing one
|
|
54
|
+
* triggers under-bar fallback. */
|
|
55
|
+
itemBars: BoundingBox[];
|
|
56
|
+
/** Every visible bracket stroke (parallel `bracket:solid|dashed`,
|
|
57
|
+
* filled-style group chiclets are NOT brackets). Used by the
|
|
58
|
+
* clearance nudge only — not obstacles. */
|
|
59
|
+
brackets: BracketLine[];
|
|
60
|
+
}
|
|
61
|
+
export declare class ChannelGrid {
|
|
62
|
+
private readonly items;
|
|
63
|
+
private readonly brackets;
|
|
64
|
+
constructor(input: ChannelGridInput);
|
|
65
|
+
/** True when a vertical line at `x` intersects any item bar within
|
|
66
|
+
* the Y span `[yMin, yMax]`. */
|
|
67
|
+
hasObstacle(x: number, yMin: number, yMax: number): boolean;
|
|
68
|
+
/** Visible brackets within `radius` px of `x` whose Y span overlaps
|
|
69
|
+
* `[yMin, yMax]`. The clearance nudge uses these to shift the
|
|
70
|
+
* elbow X away from the bracket. */
|
|
71
|
+
bracketsNear(x: number, yMin: number, yMax: number, radius: number): BracketLine[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Walk a positioned swimlane tree and collect every painted item bar
|
|
75
|
+
* (visual edges) AND every visible bracket stroke. Output feeds
|
|
76
|
+
* `ChannelGrid` so the router knows what to avoid.
|
|
77
|
+
*
|
|
78
|
+
* Swimlanes contained in include regions count too — items inside an
|
|
79
|
+
* isolated region share the parent timeline so cross-region arrows
|
|
80
|
+
* still need the obstacle/bracket data to route cleanly.
|
|
81
|
+
*/
|
|
82
|
+
export declare function collectRoutingObstacles(swimlanes: PositionedSwimlane[], includes: PositionedIncludeRegion[]): ChannelGridInput;
|
|
83
|
+
/**
|
|
84
|
+
* Route every dependency edge in one batch so slot assignment can
|
|
85
|
+
* coordinate across edges that share a channel. Marker → item edges
|
|
86
|
+
* route as direct stubs and bypass the channel router.
|
|
87
|
+
*/
|
|
88
|
+
export declare function routeChannelEdges(requests: EdgeRouteRequest[], grid: ChannelGrid): EdgeRouteResult[];
|
|
89
|
+
//# sourceMappingURL=edge-routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-routing.d.ts","sourceRoot":"","sources":["../src/edge-routing.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EACR,WAAW,EACX,KAAK,EACL,uBAAuB,EACvB,kBAAkB,EAErB,MAAM,YAAY,CAAC;AAEpB;;;yEAGyE;AACzE,MAAM,WAAW,WAAW;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;2DAE2D;AAC3D,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;IACZ,EAAE,EAAE,KAAK,CAAC;IACV;;sEAEkE;IAClE,cAAc,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,KAAK,EAAE,CAAC;IACnB;;2DAEuD;IACvD,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED;;;uBAGuB;AACvB,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC;;;;6CAI6C;AAC7C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC;;;;;mEAKmE;AACnE,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAmBpC,MAAM,WAAW,gBAAgB;IAC7B;;uCAEmC;IACnC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB;;gDAE4C;IAC5C,QAAQ,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED,qBAAa,WAAW;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;gBAE7B,KAAK,EAAE,gBAAgB;IAKnC;qCACiC;IACjC,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAW3D;;yCAEqC;IACrC,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,EAAE;CAWrF;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACnC,SAAS,EAAE,kBAAkB,EAAE,EAC/B,QAAQ,EAAE,uBAAuB,EAAE,GACpC,gBAAgB,CAqElB;AAyPD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC7B,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,IAAI,EAAE,WAAW,GAClB,eAAe,EAAE,CAkDnB"}
|