@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.
Files changed (183) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +103 -0
  3. package/dist/band-scale.d.ts +56 -0
  4. package/dist/band-scale.d.ts.map +1 -0
  5. package/dist/band-scale.js +86 -0
  6. package/dist/band-scale.js.map +1 -0
  7. package/dist/calendar.d.ts +79 -0
  8. package/dist/calendar.d.ts.map +1 -0
  9. package/dist/calendar.js +210 -0
  10. package/dist/calendar.js.map +1 -0
  11. package/dist/capacity.d.ts +72 -0
  12. package/dist/capacity.d.ts.map +1 -0
  13. package/dist/capacity.js +163 -0
  14. package/dist/capacity.js.map +1 -0
  15. package/dist/dsl-utils.d.ts +5 -0
  16. package/dist/dsl-utils.d.ts.map +1 -0
  17. package/dist/dsl-utils.js +28 -0
  18. package/dist/dsl-utils.js.map +1 -0
  19. package/dist/edge-routing.d.ts +89 -0
  20. package/dist/edge-routing.d.ts.map +1 -0
  21. package/dist/edge-routing.js +435 -0
  22. package/dist/edge-routing.js.map +1 -0
  23. package/dist/frame-tab-geometry.d.ts +78 -0
  24. package/dist/frame-tab-geometry.d.ts.map +1 -0
  25. package/dist/frame-tab-geometry.js +115 -0
  26. package/dist/frame-tab-geometry.js.map +1 -0
  27. package/dist/header-card-geometry.d.ts +29 -0
  28. package/dist/header-card-geometry.d.ts.map +1 -0
  29. package/dist/header-card-geometry.js +41 -0
  30. package/dist/header-card-geometry.js.map +1 -0
  31. package/dist/i18n.d.ts +48 -0
  32. package/dist/i18n.d.ts.map +1 -0
  33. package/dist/i18n.js +114 -0
  34. package/dist/i18n.js.map +1 -0
  35. package/dist/include-chrome-geometry.d.ts +86 -0
  36. package/dist/include-chrome-geometry.d.ts.map +1 -0
  37. package/dist/include-chrome-geometry.js +104 -0
  38. package/dist/include-chrome-geometry.js.map +1 -0
  39. package/dist/index.d.ts +11 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +10 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/item-bar-geometry.d.ts +127 -0
  44. package/dist/item-bar-geometry.d.ts.map +1 -0
  45. package/dist/item-bar-geometry.js +173 -0
  46. package/dist/item-bar-geometry.js.map +1 -0
  47. package/dist/lane-utilization.d.ts +90 -0
  48. package/dist/lane-utilization.d.ts.map +1 -0
  49. package/dist/lane-utilization.js +206 -0
  50. package/dist/lane-utilization.js.map +1 -0
  51. package/dist/layout-context.d.ts +143 -0
  52. package/dist/layout-context.d.ts.map +1 -0
  53. package/dist/layout-context.js +8 -0
  54. package/dist/layout-context.js.map +1 -0
  55. package/dist/layout.d.ts +18 -0
  56. package/dist/layout.d.ts.map +1 -0
  57. package/dist/layout.js +1213 -0
  58. package/dist/layout.js.map +1 -0
  59. package/dist/nodes/anchor-node.d.ts +16 -0
  60. package/dist/nodes/anchor-node.d.ts.map +1 -0
  61. package/dist/nodes/anchor-node.js +68 -0
  62. package/dist/nodes/anchor-node.js.map +1 -0
  63. package/dist/nodes/footnote-node.d.ts +10 -0
  64. package/dist/nodes/footnote-node.d.ts.map +1 -0
  65. package/dist/nodes/footnote-node.js +41 -0
  66. package/dist/nodes/footnote-node.js.map +1 -0
  67. package/dist/nodes/group-node.d.ts +29 -0
  68. package/dist/nodes/group-node.d.ts.map +1 -0
  69. package/dist/nodes/group-node.js +187 -0
  70. package/dist/nodes/group-node.js.map +1 -0
  71. package/dist/nodes/include-node.d.ts +16 -0
  72. package/dist/nodes/include-node.d.ts.map +1 -0
  73. package/dist/nodes/include-node.js +117 -0
  74. package/dist/nodes/include-node.js.map +1 -0
  75. package/dist/nodes/item-node.d.ts +51 -0
  76. package/dist/nodes/item-node.d.ts.map +1 -0
  77. package/dist/nodes/item-node.js +108 -0
  78. package/dist/nodes/item-node.js.map +1 -0
  79. package/dist/nodes/marker-geometry.d.ts +22 -0
  80. package/dist/nodes/marker-geometry.d.ts.map +1 -0
  81. package/dist/nodes/marker-geometry.js +38 -0
  82. package/dist/nodes/marker-geometry.js.map +1 -0
  83. package/dist/nodes/milestone-node.d.ts +48 -0
  84. package/dist/nodes/milestone-node.d.ts.map +1 -0
  85. package/dist/nodes/milestone-node.js +210 -0
  86. package/dist/nodes/milestone-node.js.map +1 -0
  87. package/dist/nodes/parallel-node.d.ts +21 -0
  88. package/dist/nodes/parallel-node.d.ts.map +1 -0
  89. package/dist/nodes/parallel-node.js +80 -0
  90. package/dist/nodes/parallel-node.js.map +1 -0
  91. package/dist/nodes/roadmap-node.d.ts +76 -0
  92. package/dist/nodes/roadmap-node.d.ts.map +1 -0
  93. package/dist/nodes/roadmap-node.js +788 -0
  94. package/dist/nodes/roadmap-node.js.map +1 -0
  95. package/dist/nodes/swimlane-node.d.ts +38 -0
  96. package/dist/nodes/swimlane-node.d.ts.map +1 -0
  97. package/dist/nodes/swimlane-node.js +308 -0
  98. package/dist/nodes/swimlane-node.js.map +1 -0
  99. package/dist/renderable.d.ts +44 -0
  100. package/dist/renderable.d.ts.map +1 -0
  101. package/dist/renderable.js +21 -0
  102. package/dist/renderable.js.map +1 -0
  103. package/dist/row-packer.d.ts +125 -0
  104. package/dist/row-packer.d.ts.map +1 -0
  105. package/dist/row-packer.js +169 -0
  106. package/dist/row-packer.js.map +1 -0
  107. package/dist/style-resolution.d.ts +14 -0
  108. package/dist/style-resolution.d.ts.map +1 -0
  109. package/dist/style-resolution.js +191 -0
  110. package/dist/style-resolution.js.map +1 -0
  111. package/dist/themes/dark.d.ts +4 -0
  112. package/dist/themes/dark.d.ts.map +1 -0
  113. package/dist/themes/dark.js +241 -0
  114. package/dist/themes/dark.js.map +1 -0
  115. package/dist/themes/index.d.ts +15 -0
  116. package/dist/themes/index.d.ts.map +1 -0
  117. package/dist/themes/index.js +30 -0
  118. package/dist/themes/index.js.map +1 -0
  119. package/dist/themes/light.d.ts +4 -0
  120. package/dist/themes/light.d.ts.map +1 -0
  121. package/dist/themes/light.js +248 -0
  122. package/dist/themes/light.js.map +1 -0
  123. package/dist/themes/shape.d.ts +194 -0
  124. package/dist/themes/shape.d.ts.map +1 -0
  125. package/dist/themes/shape.js +6 -0
  126. package/dist/themes/shape.js.map +1 -0
  127. package/dist/themes/shared.d.ts +145 -0
  128. package/dist/themes/shared.d.ts.map +1 -0
  129. package/dist/themes/shared.js +310 -0
  130. package/dist/themes/shared.js.map +1 -0
  131. package/dist/time-scale.d.ts +39 -0
  132. package/dist/time-scale.d.ts.map +1 -0
  133. package/dist/time-scale.js +62 -0
  134. package/dist/time-scale.js.map +1 -0
  135. package/dist/types.d.ts +483 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/types.js +6 -0
  138. package/dist/types.js.map +1 -0
  139. package/dist/view-preset.d.ts +23 -0
  140. package/dist/view-preset.d.ts.map +1 -0
  141. package/dist/view-preset.js +146 -0
  142. package/dist/view-preset.js.map +1 -0
  143. package/dist/working-calendar.d.ts +14 -0
  144. package/dist/working-calendar.d.ts.map +1 -0
  145. package/dist/working-calendar.js +74 -0
  146. package/dist/working-calendar.js.map +1 -0
  147. package/package.json +37 -0
  148. package/src/band-scale.ts +115 -0
  149. package/src/calendar.ts +244 -0
  150. package/src/capacity.ts +191 -0
  151. package/src/dsl-utils.ts +30 -0
  152. package/src/edge-routing.ts +550 -0
  153. package/src/frame-tab-geometry.ts +165 -0
  154. package/src/header-card-geometry.ts +48 -0
  155. package/src/i18n.ts +124 -0
  156. package/src/include-chrome-geometry.ts +156 -0
  157. package/src/index.ts +116 -0
  158. package/src/item-bar-geometry.ts +222 -0
  159. package/src/lane-utilization.ts +259 -0
  160. package/src/layout-context.ts +182 -0
  161. package/src/layout.ts +1446 -0
  162. package/src/nodes/anchor-node.ts +77 -0
  163. package/src/nodes/footnote-node.ts +60 -0
  164. package/src/nodes/group-node.ts +252 -0
  165. package/src/nodes/include-node.ts +168 -0
  166. package/src/nodes/item-node.ts +171 -0
  167. package/src/nodes/marker-geometry.ts +43 -0
  168. package/src/nodes/milestone-node.ts +263 -0
  169. package/src/nodes/parallel-node.ts +101 -0
  170. package/src/nodes/roadmap-node.ts +957 -0
  171. package/src/nodes/swimlane-node.ts +423 -0
  172. package/src/renderable.ts +68 -0
  173. package/src/row-packer.ts +271 -0
  174. package/src/style-resolution.ts +243 -0
  175. package/src/themes/dark.ts +244 -0
  176. package/src/themes/index.ts +36 -0
  177. package/src/themes/light.ts +251 -0
  178. package/src/themes/shape.ts +230 -0
  179. package/src/themes/shared.ts +369 -0
  180. package/src/time-scale.ts +78 -0
  181. package/src/types.ts +607 -0
  182. package/src/view-preset.ts +180 -0
  183. package/src/working-calendar.ts +91 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"include-node.js","sourceRoot":"","sources":["../../src/nodes/include-node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { IntrinsicSize, MeasureContext, PlaceContext, Point, Renderable } from '../renderable.js';
2
+ import type { BoundingBox } from '../types.js';
3
+ export interface ItemNodeInput {
4
+ id: string;
5
+ title: string;
6
+ /** Logical left x of the column the bar lives in. */
7
+ logicalLeftX: number;
8
+ /** Logical right x of the column the bar lives in. */
9
+ logicalRightX: number;
10
+ /** Caption text shown under the title (e.g. "1w - 50% remaining"). */
11
+ metaText?: string;
12
+ /**
13
+ * Extra horizontal width (px) appended to the meta line for spill
14
+ * detection — covers content the renderer paints after `metaText` but
15
+ * that the layout assembles as a structured trailing element rather
16
+ * than a string (currently: the capacity suffix). The layout adds this
17
+ * to the meta-line's measured width before deciding whether the
18
+ * caption block fits inside the bar.
19
+ *
20
+ * Defaults to 0. Title spill checks ignore this value (the suffix
21
+ * never renders on the title line).
22
+ */
23
+ metaTrailingWidth?: number;
24
+ /**
25
+ * True when the bar shows a link icon in its upper-left corner.
26
+ * Caption text indents past the icon column so the title doesn't
27
+ * collide with the icon.
28
+ */
29
+ hasLinkIcon?: boolean;
30
+ }
31
+ export interface PlacedItemGeometry {
32
+ id: string;
33
+ box: BoundingBox;
34
+ /**
35
+ * X for the title/meta text. Equal to `box.x + TEXT_INSET_PX`
36
+ * when text fits inside the bar; otherwise positioned just past
37
+ * the bar's right edge so the caption reads as belonging to the
38
+ * item rather than being clipped.
39
+ */
40
+ textX: number;
41
+ /** True when text spills past the bar's right edge. */
42
+ textSpills: boolean;
43
+ }
44
+ export declare class ItemNode implements Renderable<PlacedItemGeometry> {
45
+ readonly input: ItemNodeInput;
46
+ constructor(input: ItemNodeInput);
47
+ get id(): string;
48
+ measure(ctx: MeasureContext): IntrinsicSize;
49
+ place(origin: Point, ctx: PlaceContext): PlacedItemGeometry;
50
+ }
51
+ //# sourceMappingURL=item-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-node.d.ts","sourceRoot":"","sources":["../../src/nodes/item-node.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACR,aAAa,EACb,cAAc,EACd,YAAY,EACZ,KAAK,EACL,UAAU,EACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAqB/C,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,WAAW,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;CACvB;AAWD,qBAAa,QAAS,YAAW,UAAU,CAAC,kBAAkB,CAAC;aAC/B,KAAK,EAAE,aAAa;gBAApB,KAAK,EAAE,aAAa;IAEhD,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,aAAa;IAW3C,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,GAAG,kBAAkB;CAiD9D"}
@@ -0,0 +1,108 @@
1
+ // ItemNode — first Renderable entity in the m2.5c port. Owns the
2
+ // geometry of a single item bar: its visual box (insets + height) and
3
+ // its caption x (inside vs. spilled past the bar).
4
+ //
5
+ // Constructed from already-resolved inputs (start/end x, status,
6
+ // remaining, meta text) so this node stays small and pure. The
7
+ // dependency-resolution logic in layout.ts (after/before chains,
8
+ // cursor.x, footnote indexing) remains where it is for now; m2.5c's
9
+ // remaining work is to migrate the rest into sibling node files.
10
+ //
11
+ // `measure(ctx)` returns the bar's intrinsic size. `place(origin,
12
+ // ctx)` returns a `PositionedItem`-shaped fragment with the box, row,
13
+ // and `textX`. The shape is byte-stable with the legacy sequenceItem
14
+ // arithmetic when fed the same inputs.
15
+ import { ITEM_LINK_ICON_INSET_PX, ITEM_LINK_ICON_TILE_SIZE_PX } from '../item-bar-geometry.js';
16
+ import { ITEM_INSET_PX, MIN_ITEM_WIDTH } from '../themes/shared.js';
17
+ /**
18
+ * Inner padding applied on each side of the title text — the bar's
19
+ * inner-padded text area is `box.width - 2 * TEXT_INSET_PX` wide.
20
+ * Text spills past the bar when either the title or the meta line
21
+ * exceeds that area.
22
+ */
23
+ const TEXT_INSET_PX = 12;
24
+ /**
25
+ * Gap (px) between the bar's right edge and overflow text. Smaller
26
+ * than `TEXT_INSET_PX` so the text reads as belonging to this bar —
27
+ * adjacent bars are at least `2 * ITEM_INSET_PX = 12` away, so the
28
+ * text still has a clear visual home.
29
+ */
30
+ const TEXT_OUTSIDE_GAP_PX = 4;
31
+ const TITLE_FONT_SIZE_PX = 13;
32
+ const META_FONT_SIZE_PX = 11;
33
+ /**
34
+ * Approx. rendered width of `text` at `fontSizePx`. Matches the legacy
35
+ * `sequenceItem` heuristic (intentionally pessimistic at ~0.58 em/char so
36
+ * borderline-fitting captions trigger spill rather than clip).
37
+ */
38
+ function estimateTextWidth(text, fontSizePx) {
39
+ return text.length * fontSizePx * 0.58;
40
+ }
41
+ export class ItemNode {
42
+ input;
43
+ constructor(input) {
44
+ this.input = input;
45
+ }
46
+ get id() {
47
+ return this.input.id;
48
+ }
49
+ measure(ctx) {
50
+ const naturalWidth = Math.max(MIN_ITEM_WIDTH, this.input.logicalRightX - this.input.logicalLeftX);
51
+ return {
52
+ width: naturalWidth,
53
+ height: ctx.bands.bandwidth(),
54
+ };
55
+ }
56
+ place(origin, ctx) {
57
+ const intrinsic = this.measure(ctx);
58
+ const visualWidth = Math.max(MIN_ITEM_WIDTH, intrinsic.width - 2 * ITEM_INSET_PX);
59
+ const boxX = origin.x + ITEM_INSET_PX;
60
+ const box = {
61
+ x: boxX,
62
+ y: origin.y,
63
+ width: visualWidth,
64
+ height: intrinsic.height,
65
+ };
66
+ // The link icon (when present) lives in the bar's upper-left
67
+ // and shares the title's vertical band. The caption indents
68
+ // past the icon so the title doesn't render on top of it.
69
+ const linkColumn = this.input.hasLinkIcon
70
+ ? ITEM_LINK_ICON_INSET_PX +
71
+ ITEM_LINK_ICON_TILE_SIZE_PX +
72
+ LINK_ICON_TO_CAPTION_GAP_PX -
73
+ TEXT_INSET_PX
74
+ : 0;
75
+ const captionLeftInset = TEXT_INSET_PX + Math.max(0, linkColumn);
76
+ const innerWidth = Math.max(0, visualWidth - captionLeftInset - TEXT_INSET_PX);
77
+ const titleStr = this.input.title;
78
+ const titleWidth = titleStr ? estimateTextWidth(titleStr, TITLE_FONT_SIZE_PX) : 0;
79
+ const metaTextWidth = this.input.metaText
80
+ ? estimateTextWidth(this.input.metaText, META_FONT_SIZE_PX)
81
+ : 0;
82
+ const trailingWidth = this.input.metaTrailingWidth ?? 0;
83
+ // Trailing decoration (capacity suffix) renders to the right of
84
+ // metaText. When metaText is empty the suffix sits at the
85
+ // caption's leading edge, so its width is the entire meta-line
86
+ // budget; when both exist the suffix needs a small separator gap
87
+ // (rendered via `<tspan dx>` later) included in trailingWidth.
88
+ const metaWidth = metaTextWidth + trailingWidth;
89
+ const hasMetaContent = this.input.metaText !== undefined || trailingWidth > 0;
90
+ const textSpills = (titleStr.length > 0 && titleWidth > innerWidth) ||
91
+ (hasMetaContent && metaWidth > innerWidth);
92
+ const textX = textSpills
93
+ ? boxX + visualWidth + TEXT_OUTSIDE_GAP_PX
94
+ : boxX + captionLeftInset;
95
+ return {
96
+ id: this.input.id,
97
+ box,
98
+ textX,
99
+ textSpills,
100
+ };
101
+ }
102
+ }
103
+ /**
104
+ * Horizontal gap (px) between the link-icon tile's right edge and the
105
+ * start of the caption text when both render inside the bar.
106
+ */
107
+ const LINK_ICON_TO_CAPTION_GAP_PX = 4;
108
+ //# sourceMappingURL=item-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-node.js","sourceRoot":"","sources":["../../src/nodes/item-node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /** Height (px) of a single marker row in the timeline header band. */
2
+ export declare const MARKER_ROW_PITCH_PX = 26;
3
+ /**
4
+ * Vertical center offset of a marker row from the top of its band.
5
+ * Always half of `MARKER_ROW_PITCH_PX` — exposed as a derived constant
6
+ * so call sites read as "row 0 center" instead of magic `13`.
7
+ */
8
+ export declare const MARKER_ROW_CENTER_OFFSET_PX: number;
9
+ /** Half-width of the diamond glyph drawn at every marker's centerX. */
10
+ export declare const MARKER_DIAMOND_RADIUS_PX = 6;
11
+ /** Horizontal gap between the diamond's edge and its label box. */
12
+ export declare const MARKER_LABEL_GAP_PX = 6;
13
+ /** Height of the label box (one line of marker label text). */
14
+ export declare const MARKER_LABEL_HEIGHT_PX = 12;
15
+ /**
16
+ * Width-estimate surcharge for bold sans-serif marker labels (milestone
17
+ * titles). `estimateTextWidth` is intentionally pessimistic at ~0.58
18
+ * em/char so this small surcharge keeps overlap detection on the safe
19
+ * side without overshooting layout space.
20
+ */
21
+ export declare const MARKER_BOLD_WIDTH_FACTOR = 1.05;
22
+ //# sourceMappingURL=marker-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker-geometry.d.ts","sourceRoot":"","sources":["../../src/nodes/marker-geometry.ts"],"names":[],"mappings":"AAiBA,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAA0B,CAAC;AAEnE,uEAAuE;AACvE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,mEAAmE;AACnE,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,+DAA+D;AAC/D,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC"}
@@ -0,0 +1,38 @@
1
+ // Marker-row geometry shared across the marker stack: anchors,
2
+ // milestones, and the roadmap-level row packer all read from here so a
3
+ // font, gap, or row-height change flows from one place.
4
+ //
5
+ // Two row-band types live in this file:
6
+ //
7
+ // - `MARKER_ROW_PITCH_PX` (26 px): height of a single marker row in
8
+ // the timeline header. The marker band stacks N rows tall when
9
+ // anchors and milestones can't fit on a single row without colliding.
10
+ // - The bare `13` (= PITCH / 2) midpoint that used to sprinkle
11
+ // `roadmap-node.ts` is derived from this constant — bumping the
12
+ // pitch automatically re-centers labels.
13
+ //
14
+ // Diamond + label box dimensions are reused by every marker (anchor +
15
+ // milestone), and the `MARKER_BOLD_WIDTH_FACTOR` corrects the
16
+ // pessimistic 0.58 em width estimate for bold milestone labels.
17
+ /** Height (px) of a single marker row in the timeline header band. */
18
+ export const MARKER_ROW_PITCH_PX = 26;
19
+ /**
20
+ * Vertical center offset of a marker row from the top of its band.
21
+ * Always half of `MARKER_ROW_PITCH_PX` — exposed as a derived constant
22
+ * so call sites read as "row 0 center" instead of magic `13`.
23
+ */
24
+ export const MARKER_ROW_CENTER_OFFSET_PX = MARKER_ROW_PITCH_PX / 2;
25
+ /** Half-width of the diamond glyph drawn at every marker's centerX. */
26
+ export const MARKER_DIAMOND_RADIUS_PX = 6;
27
+ /** Horizontal gap between the diamond's edge and its label box. */
28
+ export const MARKER_LABEL_GAP_PX = 6;
29
+ /** Height of the label box (one line of marker label text). */
30
+ export const MARKER_LABEL_HEIGHT_PX = 12;
31
+ /**
32
+ * Width-estimate surcharge for bold sans-serif marker labels (milestone
33
+ * titles). `estimateTextWidth` is intentionally pessimistic at ~0.58
34
+ * em/char so this small surcharge keeps overlap detection on the safe
35
+ * side without overshooting layout space.
36
+ */
37
+ export const MARKER_BOLD_WIDTH_FACTOR = 1.05;
38
+ //# sourceMappingURL=marker-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marker-geometry.js","sourceRoot":"","sources":["../../src/nodes/marker-geometry.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,uEAAuE;AACvE,wDAAwD;AACxD,EAAE;AACF,wCAAwC;AACxC,EAAE;AACF,sEAAsE;AACtE,mEAAmE;AACnE,0EAA0E;AAC1E,iEAAiE;AACjE,oEAAoE;AACpE,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,8DAA8D;AAC9D,gEAAgE;AAEhE,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAEtC;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,GAAG,CAAC,CAAC;AAEnE,uEAAuE;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C,mEAAmE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,+DAA+D;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC"}
@@ -0,0 +1,48 @@
1
+ import type { MilestoneDeclaration } from '@nowline/core';
2
+ import type { LayoutContext } from '../layout-context.js';
3
+ import type { PositionedMilestone } from '../types.js';
4
+ /**
5
+ * One predecessor of a milestone with everything the slack-arrow
6
+ * pipeline needs: its source x (visual right edge for items, marker
7
+ * centerX for anchors / other milestones), its attach y (bar bottom
8
+ * strip when text spills, row mid otherwise), and its flow key (used
9
+ * to dedupe so a chained-flow's siblings collapse to the last entry).
10
+ */
11
+ export interface MilestonePredecessor {
12
+ ref: string;
13
+ x: number;
14
+ y: number;
15
+ flowKey: string;
16
+ }
17
+ /**
18
+ * Resolve each `after:` reference into a `MilestonePredecessor`.
19
+ * Items use their VISUAL right edge as the slack source so the
20
+ * arrow leaves the painted bar instead of landing in the inter-
21
+ * column gutter; markers (anchors / other milestones) fall back
22
+ * to their cut-line centerX. Refs whose target is unknown drop
23
+ * silently — matches the legacy continue path.
24
+ */
25
+ export declare function collectMilestonePredecessors(refs: string[], ctx: LayoutContext): MilestonePredecessor[];
26
+ /**
27
+ * Keep only the rightmost predecessor per flow key. Two
28
+ * predecessors share a flow when they sit in the same deepest
29
+ * single-track container (swimlane root, sequential group, or one
30
+ * sub-track of a parallel) — file order already encodes their
31
+ * ordering, so only the latest entry contributes a slack arrow.
32
+ * Predecessors in different flows (e.g. two parallel sub-tracks)
33
+ * each survive as their flow's last entry.
34
+ */
35
+ export declare function lastPredecessorPerFlow(preds: MilestonePredecessor[]): MilestonePredecessor[];
36
+ export declare class MilestoneNode {
37
+ readonly id: string;
38
+ readonly milestone: MilestoneDeclaration;
39
+ constructor(id: string, milestone: MilestoneDeclaration);
40
+ /**
41
+ * Returns null when the milestone is neither date-pinned nor has a
42
+ * resolvable `after:` predecessor (skipped silently — matches the
43
+ * legacy `buildMilestones` continue-paths).
44
+ */
45
+ place(ctx: LayoutContext): PositionedMilestone | null;
46
+ }
47
+ export declare function buildMilestones(milestones: Map<string, MilestoneDeclaration>, ctx: LayoutContext): PositionedMilestone[];
48
+ //# sourceMappingURL=milestone-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-node.d.ts","sourceRoot":"","sources":["../../src/nodes/milestone-node.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAsB,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQ3E;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CACxC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,aAAa,GACnB,oBAAoB,EAAE,CAaxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,oBAAoB,EAAE,CAO5F;AAyBD,qBAAa,aAAa;aAEF,EAAE,EAAE,MAAM;aACV,SAAS,EAAE,oBAAoB;gBAD/B,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,oBAAoB;IAGnD;;;;OAIG;IACH,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,mBAAmB,GAAG,IAAI;CAoIxD;AAED,wBAAgB,eAAe,CAC3B,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC7C,GAAG,EAAE,aAAa,GACnB,mBAAmB,EAAE,CAOvB"}
@@ -0,0 +1,210 @@
1
+ // MilestoneNode — Renderable for a single milestone + a
2
+ // `buildMilestones` loop helper. Each milestone sits in the marker row
3
+ // and either pins to a fixed `date:` or floats to the rightmost `after:`
4
+ // predecessor. A non-binding (second-latest) predecessor drives the
5
+ // "slack" arrow when present. Date-pinned milestones whose predecessors
6
+ // would push past the date are flagged `isOverrun`.
7
+ //
8
+ // Marker-row placement (row + label box + left/right side) is decided
9
+ // once by `roadmap-node.ts::packMarkerRow`. Date-pinned entities are
10
+ // pre-packed before swimlanes run; after-only milestones get a
11
+ // PROVISIONAL row=0 placement here at build time which the unified
12
+ // re-pack in `RoadmapNode.place` overwrites with the final tick-order
13
+ // bottom-first slot once every centerX is known.
14
+ import { parseDate, propValue, propValues } from '../dsl-utils.js';
15
+ import { resolveStyle } from '../style-resolution.js';
16
+ import { MARKER_BOLD_WIDTH_FACTOR, MARKER_DIAMOND_RADIUS_PX, MARKER_LABEL_GAP_PX, MARKER_LABEL_HEIGHT_PX, } from './marker-geometry.js';
17
+ /**
18
+ * Resolve each `after:` reference into a `MilestonePredecessor`.
19
+ * Items use their VISUAL right edge as the slack source so the
20
+ * arrow leaves the painted bar instead of landing in the inter-
21
+ * column gutter; markers (anchors / other milestones) fall back
22
+ * to their cut-line centerX. Refs whose target is unknown drop
23
+ * silently — matches the legacy continue path.
24
+ */
25
+ export function collectMilestonePredecessors(refs, ctx) {
26
+ const out = [];
27
+ for (const ref of refs) {
28
+ const visualRight = ctx.entityVisualRightX.get(ref);
29
+ const x = visualRight ?? ctx.entityRightEdges.get(ref);
30
+ if (x === undefined)
31
+ continue;
32
+ const y = ctx.itemSlackAttachY.get(ref) ?? ctx.entityMidpoints.get(ref)?.y ?? 0;
33
+ // Markers don't share a flow with anything, so use their id
34
+ // as a unique flow key — every marker stands on its own.
35
+ const flowKey = ctx.itemFlowKey.get(ref) ?? `marker:${ref}`;
36
+ out.push({ ref, x, y, flowKey });
37
+ }
38
+ return out;
39
+ }
40
+ /**
41
+ * Keep only the rightmost predecessor per flow key. Two
42
+ * predecessors share a flow when they sit in the same deepest
43
+ * single-track container (swimlane root, sequential group, or one
44
+ * sub-track of a parallel) — file order already encodes their
45
+ * ordering, so only the latest entry contributes a slack arrow.
46
+ * Predecessors in different flows (e.g. two parallel sub-tracks)
47
+ * each survive as their flow's last entry.
48
+ */
49
+ export function lastPredecessorPerFlow(preds) {
50
+ const m = new Map();
51
+ for (const p of preds) {
52
+ const existing = m.get(p.flowKey);
53
+ if (!existing || p.x > existing.x)
54
+ m.set(p.flowKey, p);
55
+ }
56
+ return Array.from(m.values());
57
+ }
58
+ function decideLabelBoxForCanvas(centerX, centerY, radius, title, fontSize, bold, chartLeftX, chartRightX) {
59
+ const labelWidth = title.length * fontSize * 0.58 * (bold ? MARKER_BOLD_WIDTH_FACTOR : 1);
60
+ const naturalRightX = centerX + radius + MARKER_LABEL_GAP_PX;
61
+ const naturalLeftX = centerX - radius - MARKER_LABEL_GAP_PX - labelWidth;
62
+ const fitsRight = naturalRightX + labelWidth <= chartRightX;
63
+ const fitsLeft = naturalLeftX >= chartLeftX;
64
+ const side = fitsRight ? 'right' : fitsLeft ? 'left' : 'right';
65
+ const xLeft = side === 'right' ? naturalRightX : naturalLeftX;
66
+ return {
67
+ box: { x: xLeft, y: centerY - 4, width: labelWidth, height: MARKER_LABEL_HEIGHT_PX },
68
+ side,
69
+ };
70
+ }
71
+ export class MilestoneNode {
72
+ id;
73
+ milestone;
74
+ constructor(id, milestone) {
75
+ this.id = id;
76
+ this.milestone = milestone;
77
+ }
78
+ /**
79
+ * Returns null when the milestone is neither date-pinned nor has a
80
+ * resolvable `after:` predecessor (skipped silently — matches the
81
+ * legacy `buildMilestones` continue-paths).
82
+ */
83
+ place(ctx) {
84
+ const m = this.milestone;
85
+ const style = resolveStyle('milestone', m.properties, ctx.styleCtx);
86
+ const dateRaw = propValue(m.properties, 'date');
87
+ const afterRaw = propValues(m.properties, 'after');
88
+ const date = parseDate(dateRaw);
89
+ const inRowY = ctx.timeline.markerRow.y;
90
+ const radius = MARKER_DIAMOND_RADIUS_PX;
91
+ const title = m.title ?? this.id;
92
+ let centerX = null;
93
+ let centerY = inRowY;
94
+ let labelBox = null;
95
+ let labelSide = 'right';
96
+ let fixed = false;
97
+ let slackArrows;
98
+ let isOverrun = false;
99
+ if (date) {
100
+ const x = ctx.scale.forwardWithinDomain(date);
101
+ if (x === null)
102
+ return null;
103
+ centerX = x;
104
+ fixed = true;
105
+ const placement = ctx.markerRowPlacements.get(this.id);
106
+ if (placement) {
107
+ centerY = placement.centerY;
108
+ labelBox = placement.labelBox;
109
+ labelSide = placement.labelSide;
110
+ }
111
+ // Date-pinned milestones whose `after:` predecessors finish
112
+ // past the pinned date are flagged `isOverrun`; the latest
113
+ // last-of-flow predecessor draws a single overrun arrow
114
+ // pointing back from its visual right edge to the
115
+ // milestone's column. Flow-dedupe avoids stacking redundant
116
+ // arrows from sibling items in one chained flow.
117
+ const preds = collectMilestonePredecessors(afterRaw, ctx);
118
+ const dedupedPreds = lastPredecessorPerFlow(preds);
119
+ let maxPred = null;
120
+ for (const p of dedupedPreds) {
121
+ if (!maxPred || p.x > maxPred.x)
122
+ maxPred = p;
123
+ }
124
+ if (maxPred && maxPred.x > x) {
125
+ isOverrun = true;
126
+ slackArrows = [
127
+ {
128
+ x: maxPred.x,
129
+ y: maxPred.y > 0 ? maxPred.y : centerY,
130
+ },
131
+ ];
132
+ }
133
+ }
134
+ else if (afterRaw.length > 0) {
135
+ // Float to the rightmost (binding) predecessor; every
136
+ // last-of-flow predecessor that finishes EARLIER than the
137
+ // binding contributes one slack arrow. Predecessors in the
138
+ // same single-track flow (sequential group, swimlane root,
139
+ // one parallel sub-track) collapse to just their last
140
+ // entry — file order encodes the dependency chain, so only
141
+ // the rightmost matters. Predecessors in different flows
142
+ // (e.g. two parallel sub-tracks) each contribute their own
143
+ // last-of-flow arrow.
144
+ const preds = collectMilestonePredecessors(afterRaw, ctx);
145
+ const dedupedPreds = lastPredecessorPerFlow(preds);
146
+ dedupedPreds.sort((a, b) => b.x - a.x);
147
+ const maxEnd = dedupedPreds[0]?.x ?? ctx.timeline.originX;
148
+ centerX = maxEnd;
149
+ fixed = false;
150
+ // Provisional placement — RoadmapNode runs a unified
151
+ // tick-order pack across all markers (date-pinned anchors,
152
+ // date-pinned milestones, after-only milestones) once
153
+ // everyone's centerX is known. That pass overwrites the
154
+ // rowIndex / centerY / labelBox / labelSide we set here.
155
+ const provisional = decideLabelBoxForCanvas(centerX, inRowY, radius, title, 10, true, ctx.timeline.box.x, ctx.chartRightX);
156
+ centerY = inRowY;
157
+ labelBox = provisional.box;
158
+ labelSide = provisional.side;
159
+ ctx.markerRowPlacements.set(this.id, {
160
+ rowIndex: 0,
161
+ centerY,
162
+ labelBox,
163
+ labelSide,
164
+ });
165
+ const arrows = [];
166
+ for (let i = 1; i < dedupedPreds.length; i++) {
167
+ const p = dedupedPreds[i];
168
+ if (p.x < maxEnd && p.y > 0)
169
+ arrows.push({ x: p.x, y: p.y });
170
+ }
171
+ if (arrows.length > 0)
172
+ slackArrows = arrows;
173
+ }
174
+ if (centerX === null)
175
+ return null;
176
+ if (!labelBox) {
177
+ const fallback = decideLabelBoxForCanvas(centerX, centerY, radius, title, 10, true, ctx.timeline.box.x, ctx.chartRightX);
178
+ labelBox = fallback.box;
179
+ labelSide = fallback.side;
180
+ }
181
+ const center = { x: centerX, y: centerY };
182
+ ctx.entityLeftEdges.set(this.id, center.x);
183
+ ctx.entityRightEdges.set(this.id, center.x);
184
+ ctx.entityMidpoints.set(this.id, center);
185
+ return {
186
+ id: this.id,
187
+ title,
188
+ center,
189
+ radius,
190
+ fixed,
191
+ slackArrows,
192
+ isOverrun,
193
+ style,
194
+ cutTopY: ctx.chartTopY,
195
+ cutBottomY: ctx.swimlaneBottomY,
196
+ labelBox,
197
+ labelSide,
198
+ };
199
+ }
200
+ }
201
+ export function buildMilestones(milestones, ctx) {
202
+ const out = [];
203
+ for (const [id, m] of milestones) {
204
+ const positioned = new MilestoneNode(id, m).place(ctx);
205
+ if (positioned)
206
+ out.push(positioned);
207
+ }
208
+ return out;
209
+ }
210
+ //# sourceMappingURL=milestone-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"milestone-node.js","sourceRoot":"","sources":["../../src/nodes/milestone-node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { GroupBlock, ItemDeclaration, ParallelBlock } from '@nowline/core';
2
+ import type { LayoutContext, TrackCursor } from '../layout-context.js';
3
+ import type { PositionedParallel, PositionedTrackChild } from '../types.js';
4
+ export interface ParallelNodeDeps {
5
+ sequenceOne: (child: ItemDeclaration | GroupBlock | ParallelBlock, cursor: TrackCursor, ctx: LayoutContext) => PositionedTrackChild;
6
+ newCursor: (x: number, y: number) => TrackCursor;
7
+ }
8
+ export declare class ParallelNode {
9
+ readonly node: ParallelBlock;
10
+ private readonly deps;
11
+ constructor(node: ParallelBlock, deps: ParallelNodeDeps);
12
+ get id(): string;
13
+ /**
14
+ * Sequence children into stacked sub-tracks, advance the parent
15
+ * `cursor` past the parallel's right edge plus
16
+ * `TRACK_BLOCK_TAIL_GUTTER_PX` of breathing room, and return a
17
+ * `PositionedParallel`.
18
+ */
19
+ place(cursor: TrackCursor, ctx: LayoutContext): PositionedParallel;
20
+ }
21
+ //# sourceMappingURL=parallel-node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel-node.d.ts","sourceRoot":"","sources":["../../src/nodes/parallel-node.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,KAAK,EAAe,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEzF,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,CACT,KAAK,EAAE,eAAe,GAAG,UAAU,GAAG,aAAa,EACnD,MAAM,EAAE,WAAW,EACnB,GAAG,EAAE,aAAa,KACjB,oBAAoB,CAAC;IAC1B,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,WAAW,CAAC;CACpD;AAED,qBAAa,YAAY;aAED,IAAI,EAAE,aAAa;IAEnC,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAFL,IAAI,EAAE,aAAa,EAElB,IAAI,EAAE,gBAAgB;IAG3C,IAAI,EAAE,IAAI,MAAM,CAEf;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,GAAG,kBAAkB;CA6DrE"}
@@ -0,0 +1,80 @@
1
+ // ParallelNode — Renderable for a `parallel { ... }` block. Stacks
2
+ // children top-to-bottom (each child owns a fresh sub-track) and reports
3
+ // the union bounding box. Each child is sequenced via the injected
4
+ // `deps.sequenceOne` callback, which dispatches to the appropriate
5
+ // per-entity Renderable (or, transitionally, the legacy sequencer
6
+ // helpers in `layout.ts`).
7
+ import { resolveStyle } from '../style-resolution.js';
8
+ import { TRACK_BLOCK_TAIL_GUTTER_PX } from '../themes/shared.js';
9
+ export class ParallelNode {
10
+ node;
11
+ deps;
12
+ constructor(node,
13
+ // biome-ignore lint/correctness/noUnusedPrivateClassMembers: accessed via `const { deps } = this` destructuring inside methods, which the analyzer does not detect.
14
+ deps) {
15
+ this.node = node;
16
+ this.deps = deps;
17
+ }
18
+ get id() {
19
+ return this.node.name ?? '';
20
+ }
21
+ /**
22
+ * Sequence children into stacked sub-tracks, advance the parent
23
+ * `cursor` past the parallel's right edge plus
24
+ * `TRACK_BLOCK_TAIL_GUTTER_PX` of breathing room, and return a
25
+ * `PositionedParallel`.
26
+ */
27
+ place(cursor, ctx) {
28
+ const { node } = this;
29
+ const { deps } = this;
30
+ const style = resolveStyle('parallel', node.properties, ctx.styleCtx);
31
+ const startX = cursor.x;
32
+ const startY = cursor.y;
33
+ const children = [];
34
+ let maxRight = startX;
35
+ let accumulatedHeight = 0;
36
+ // Each child of a parallel block lives on its own sub-track,
37
+ // so each child starts a fresh flow segment under the parent's
38
+ // path. Two predecessors that sit on different parallel
39
+ // sub-tracks therefore stay in different flows for milestone
40
+ // slack-arrow dedupe.
41
+ const previousFlowKey = ctx.currentFlowKey;
42
+ const parId = node.name ?? 'p';
43
+ let childIndex = 0;
44
+ for (const child of node.content) {
45
+ if (child.$type === 'DescriptionDirective')
46
+ continue;
47
+ ctx.currentFlowKey = `${previousFlowKey}/par:${parId}#${childIndex}`;
48
+ const subCursor = deps.newCursor(startX, startY + accumulatedHeight);
49
+ const positioned = deps.sequenceOne(child, subCursor, ctx);
50
+ children.push(positioned);
51
+ accumulatedHeight += Math.max(ctx.bandScale.step(), subCursor.height);
52
+ maxRight = Math.max(maxRight, subCursor.maxX);
53
+ childIndex++;
54
+ }
55
+ ctx.currentFlowKey = previousFlowKey;
56
+ const box = {
57
+ x: startX,
58
+ y: startY,
59
+ width: maxRight - startX,
60
+ height: accumulatedHeight,
61
+ };
62
+ cursor.x = maxRight + TRACK_BLOCK_TAIL_GUTTER_PX;
63
+ cursor.maxX = Math.max(cursor.maxX, cursor.x);
64
+ cursor.height = Math.max(cursor.height, accumulatedHeight);
65
+ const id = node.name;
66
+ if (id) {
67
+ ctx.entityLeftEdges.set(id, box.x);
68
+ ctx.entityRightEdges.set(id, box.x + box.width);
69
+ }
70
+ return {
71
+ kind: 'parallel',
72
+ id,
73
+ title: node.title ?? node.name,
74
+ box,
75
+ children,
76
+ style,
77
+ };
78
+ }
79
+ }
80
+ //# sourceMappingURL=parallel-node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel-node.js","sourceRoot":"","sources":["../../src/nodes/parallel-node.ts"],"names":[],"mappings":"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"}