@nowline/layout 0.0.0-dev.20260601071750.g04bdff9

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 (193) 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/inline-date-pin-geometry.d.ts +46 -0
  44. package/dist/inline-date-pin-geometry.d.ts.map +1 -0
  45. package/dist/inline-date-pin-geometry.js +149 -0
  46. package/dist/inline-date-pin-geometry.js.map +1 -0
  47. package/dist/item-bar-geometry.d.ts +157 -0
  48. package/dist/item-bar-geometry.d.ts.map +1 -0
  49. package/dist/item-bar-geometry.js +214 -0
  50. package/dist/item-bar-geometry.js.map +1 -0
  51. package/dist/lane-utilization.d.ts +90 -0
  52. package/dist/lane-utilization.d.ts.map +1 -0
  53. package/dist/lane-utilization.js +206 -0
  54. package/dist/lane-utilization.js.map +1 -0
  55. package/dist/layout-context.d.ts +143 -0
  56. package/dist/layout-context.d.ts.map +1 -0
  57. package/dist/layout-context.js +8 -0
  58. package/dist/layout-context.js.map +1 -0
  59. package/dist/layout.d.ts +18 -0
  60. package/dist/layout.d.ts.map +1 -0
  61. package/dist/layout.js +1298 -0
  62. package/dist/layout.js.map +1 -0
  63. package/dist/nodes/anchor-node.d.ts +16 -0
  64. package/dist/nodes/anchor-node.d.ts.map +1 -0
  65. package/dist/nodes/anchor-node.js +68 -0
  66. package/dist/nodes/anchor-node.js.map +1 -0
  67. package/dist/nodes/footnote-node.d.ts +10 -0
  68. package/dist/nodes/footnote-node.d.ts.map +1 -0
  69. package/dist/nodes/footnote-node.js +41 -0
  70. package/dist/nodes/footnote-node.js.map +1 -0
  71. package/dist/nodes/group-node.d.ts +29 -0
  72. package/dist/nodes/group-node.d.ts.map +1 -0
  73. package/dist/nodes/group-node.js +195 -0
  74. package/dist/nodes/group-node.js.map +1 -0
  75. package/dist/nodes/include-node.d.ts +16 -0
  76. package/dist/nodes/include-node.d.ts.map +1 -0
  77. package/dist/nodes/include-node.js +117 -0
  78. package/dist/nodes/include-node.js.map +1 -0
  79. package/dist/nodes/item-node.d.ts +51 -0
  80. package/dist/nodes/item-node.d.ts.map +1 -0
  81. package/dist/nodes/item-node.js +108 -0
  82. package/dist/nodes/item-node.js.map +1 -0
  83. package/dist/nodes/marker-geometry.d.ts +22 -0
  84. package/dist/nodes/marker-geometry.d.ts.map +1 -0
  85. package/dist/nodes/marker-geometry.js +38 -0
  86. package/dist/nodes/marker-geometry.js.map +1 -0
  87. package/dist/nodes/milestone-node.d.ts +48 -0
  88. package/dist/nodes/milestone-node.d.ts.map +1 -0
  89. package/dist/nodes/milestone-node.js +210 -0
  90. package/dist/nodes/milestone-node.js.map +1 -0
  91. package/dist/nodes/parallel-node.d.ts +21 -0
  92. package/dist/nodes/parallel-node.d.ts.map +1 -0
  93. package/dist/nodes/parallel-node.js +88 -0
  94. package/dist/nodes/parallel-node.js.map +1 -0
  95. package/dist/nodes/roadmap-node.d.ts +76 -0
  96. package/dist/nodes/roadmap-node.d.ts.map +1 -0
  97. package/dist/nodes/roadmap-node.js +788 -0
  98. package/dist/nodes/roadmap-node.js.map +1 -0
  99. package/dist/nodes/swimlane-node.d.ts +38 -0
  100. package/dist/nodes/swimlane-node.d.ts.map +1 -0
  101. package/dist/nodes/swimlane-node.js +308 -0
  102. package/dist/nodes/swimlane-node.js.map +1 -0
  103. package/dist/renderable.d.ts +44 -0
  104. package/dist/renderable.d.ts.map +1 -0
  105. package/dist/renderable.js +21 -0
  106. package/dist/renderable.js.map +1 -0
  107. package/dist/row-packer.d.ts +125 -0
  108. package/dist/row-packer.d.ts.map +1 -0
  109. package/dist/row-packer.js +169 -0
  110. package/dist/row-packer.js.map +1 -0
  111. package/dist/style-resolution.d.ts +14 -0
  112. package/dist/style-resolution.d.ts.map +1 -0
  113. package/dist/style-resolution.js +191 -0
  114. package/dist/style-resolution.js.map +1 -0
  115. package/dist/themes/dark.d.ts +4 -0
  116. package/dist/themes/dark.d.ts.map +1 -0
  117. package/dist/themes/dark.js +241 -0
  118. package/dist/themes/dark.js.map +1 -0
  119. package/dist/themes/grayscale.d.ts +4 -0
  120. package/dist/themes/grayscale.d.ts.map +1 -0
  121. package/dist/themes/grayscale.js +237 -0
  122. package/dist/themes/grayscale.js.map +1 -0
  123. package/dist/themes/index.d.ts +19 -0
  124. package/dist/themes/index.d.ts.map +1 -0
  125. package/dist/themes/index.js +57 -0
  126. package/dist/themes/index.js.map +1 -0
  127. package/dist/themes/light.d.ts +4 -0
  128. package/dist/themes/light.d.ts.map +1 -0
  129. package/dist/themes/light.js +248 -0
  130. package/dist/themes/light.js.map +1 -0
  131. package/dist/themes/shape.d.ts +194 -0
  132. package/dist/themes/shape.d.ts.map +1 -0
  133. package/dist/themes/shape.js +6 -0
  134. package/dist/themes/shape.js.map +1 -0
  135. package/dist/themes/shared.d.ts +145 -0
  136. package/dist/themes/shared.d.ts.map +1 -0
  137. package/dist/themes/shared.js +310 -0
  138. package/dist/themes/shared.js.map +1 -0
  139. package/dist/time-scale.d.ts +39 -0
  140. package/dist/time-scale.d.ts.map +1 -0
  141. package/dist/time-scale.js +62 -0
  142. package/dist/time-scale.js.map +1 -0
  143. package/dist/types.d.ts +516 -0
  144. package/dist/types.d.ts.map +1 -0
  145. package/dist/types.js +6 -0
  146. package/dist/types.js.map +1 -0
  147. package/dist/view-preset.d.ts +23 -0
  148. package/dist/view-preset.d.ts.map +1 -0
  149. package/dist/view-preset.js +146 -0
  150. package/dist/view-preset.js.map +1 -0
  151. package/dist/working-calendar.d.ts +14 -0
  152. package/dist/working-calendar.d.ts.map +1 -0
  153. package/dist/working-calendar.js +74 -0
  154. package/dist/working-calendar.js.map +1 -0
  155. package/package.json +43 -0
  156. package/src/band-scale.ts +115 -0
  157. package/src/calendar.ts +244 -0
  158. package/src/capacity.ts +191 -0
  159. package/src/dsl-utils.ts +30 -0
  160. package/src/edge-routing.ts +550 -0
  161. package/src/frame-tab-geometry.ts +165 -0
  162. package/src/header-card-geometry.ts +48 -0
  163. package/src/i18n.ts +124 -0
  164. package/src/include-chrome-geometry.ts +156 -0
  165. package/src/index.ts +118 -0
  166. package/src/inline-date-pin-geometry.ts +196 -0
  167. package/src/item-bar-geometry.ts +271 -0
  168. package/src/lane-utilization.ts +259 -0
  169. package/src/layout-context.ts +182 -0
  170. package/src/layout.ts +1530 -0
  171. package/src/nodes/anchor-node.ts +77 -0
  172. package/src/nodes/footnote-node.ts +60 -0
  173. package/src/nodes/group-node.ts +260 -0
  174. package/src/nodes/include-node.ts +168 -0
  175. package/src/nodes/item-node.ts +171 -0
  176. package/src/nodes/marker-geometry.ts +43 -0
  177. package/src/nodes/milestone-node.ts +263 -0
  178. package/src/nodes/parallel-node.ts +110 -0
  179. package/src/nodes/roadmap-node.ts +957 -0
  180. package/src/nodes/swimlane-node.ts +423 -0
  181. package/src/renderable.ts +68 -0
  182. package/src/row-packer.ts +271 -0
  183. package/src/style-resolution.ts +243 -0
  184. package/src/themes/dark.ts +244 -0
  185. package/src/themes/grayscale.ts +240 -0
  186. package/src/themes/index.ts +66 -0
  187. package/src/themes/light.ts +251 -0
  188. package/src/themes/shape.ts +230 -0
  189. package/src/themes/shared.ts +369 -0
  190. package/src/time-scale.ts +78 -0
  191. package/src/types.ts +641 -0
  192. package/src/view-preset.ts +180 -0
  193. package/src/working-calendar.ts +91 -0
@@ -0,0 +1,149 @@
1
+ // Inline-date pin glyph placement.
2
+ //
3
+ // `after:DATE` paints a calendar glyph in the entity's top-LEFT decoration
4
+ // slot; `before:DATE` paints it in the top-RIGHT slot. The two helpers
5
+ // below produce `Point` coordinates for the glyph's top-left corner, plus
6
+ // a `spilled` flag indicating whether the bar was too narrow to host the
7
+ // glyph inside (item case only — containers always have room).
8
+ //
9
+ // Slot interleaving rules (per specs/rendering.md "Inline-date glyph"):
10
+ //
11
+ // Item top-LEFT: glyph sits at the bar's leftmost slot when no link icon
12
+ // is present, otherwise one decoration step right of the link icon's
13
+ // right edge.
14
+ //
15
+ // Item top-RIGHT: glyph sits at the bar's rightmost slot when no status
16
+ // dot or footnotes are present, one step LEFT of the status dot when no
17
+ // footnotes, and one step LEFT of the LEFTMOST footnote indicator when
18
+ // footnotes are present (so the inline-date glyph inserts at the LEFT
19
+ // end of the existing badge cluster rather than reordering it).
20
+ //
21
+ // Container (group, parallel): glyph sits at the bounding box's top
22
+ // corners with the standard inset. Containers don't carry status dots
23
+ // or footnote indicators in their own decoration row, so no
24
+ // interleaving math is needed.
25
+ import { INLINE_DATE_GLYPH_GAP_PX, INLINE_DATE_GLYPH_INSET_LEFT_PX, INLINE_DATE_GLYPH_INSET_RIGHT_PX, INLINE_DATE_GLYPH_INSET_TOP_PX, INLINE_DATE_GLYPH_TILE_SIZE_PX, ITEM_CAPTION_SPILL_GAP_PX, ITEM_FOOTNOTE_INDICATOR_INSET_RIGHT_PX, ITEM_FOOTNOTE_INDICATOR_STEP_PX, ITEM_LINK_ICON_INSET_PX, ITEM_LINK_ICON_TILE_SIZE_PX, ITEM_STATUS_DOT_INSET_RIGHT_PX, ITEM_STATUS_DOT_RADIUS_PX, MIN_BAR_WIDTH_FOR_INLINE_DATE_PX, } from './item-bar-geometry.js';
26
+ /**
27
+ * Compute inline-date pin glyph placements for an item bar. Returns an
28
+ * empty array when neither `afterDate` nor `beforeDate` is set.
29
+ *
30
+ * Item bars participate in the narrow-bar spill family — when the bar is
31
+ * narrower than `MIN_BAR_WIDTH_FOR_INLINE_DATE_PX`, the `before:` glyph
32
+ * spills RIGHT of the bar (joining the status-dot / footnote spill
33
+ * column) and the `after:` glyph spills LEFT of the bar's leading edge
34
+ * so the side semantics stay readable.
35
+ */
36
+ export function computeItemInlineDatePins(opts) {
37
+ const { box, afterDate, beforeDate, hasLinkIcon, footnoteCount } = opts;
38
+ if (!afterDate && !beforeDate)
39
+ return [];
40
+ const pins = [];
41
+ const tileSize = INLINE_DATE_GLYPH_TILE_SIZE_PX;
42
+ const topY = box.y + INLINE_DATE_GLYPH_INSET_TOP_PX;
43
+ const spilled = box.width < MIN_BAR_WIDTH_FOR_INLINE_DATE_PX;
44
+ if (afterDate) {
45
+ const insideLeftX = hasLinkIcon
46
+ ? box.x +
47
+ ITEM_LINK_ICON_INSET_PX +
48
+ ITEM_LINK_ICON_TILE_SIZE_PX +
49
+ INLINE_DATE_GLYPH_GAP_PX
50
+ : box.x + INLINE_DATE_GLYPH_INSET_LEFT_PX;
51
+ const glyphLeft = spilled
52
+ ? {
53
+ x: box.x - ITEM_CAPTION_SPILL_GAP_PX - tileSize,
54
+ y: topY,
55
+ }
56
+ : { x: insideLeftX, y: topY };
57
+ pins.push({
58
+ side: 'after',
59
+ isoDate: afterDate,
60
+ glyphTopLeft: glyphLeft,
61
+ glyphSize: tileSize,
62
+ spilled,
63
+ });
64
+ }
65
+ if (beforeDate) {
66
+ // Walk LEFT from the rightmost top-decoration slot:
67
+ // - rightmost footnote anchors at (box.right - INSET_RIGHT_PX)
68
+ // - leftmost footnote sits one step further left per extra digit
69
+ // - status dot left edge sits at (box.right - INSET_RIGHT - DOT_RADIUS)
70
+ // - inline-date glyph sits one INLINE_DATE_GLYPH_GAP_PX further left
71
+ const rightEdge = box.x + box.width;
72
+ let anchorRightX;
73
+ if (footnoteCount > 0) {
74
+ const leftmostFootnoteCenter = rightEdge -
75
+ ITEM_FOOTNOTE_INDICATOR_INSET_RIGHT_PX -
76
+ (footnoteCount - 1) * ITEM_FOOTNOTE_INDICATOR_STEP_PX;
77
+ anchorRightX = leftmostFootnoteCenter - INLINE_DATE_GLYPH_GAP_PX;
78
+ }
79
+ else {
80
+ const dotLeftEdge = rightEdge - ITEM_STATUS_DOT_INSET_RIGHT_PX - ITEM_STATUS_DOT_RADIUS_PX;
81
+ anchorRightX = dotLeftEdge - INLINE_DATE_GLYPH_GAP_PX;
82
+ }
83
+ const insideRightX = anchorRightX - tileSize;
84
+ const glyphLeft = spilled
85
+ ? {
86
+ x: rightEdge + ITEM_CAPTION_SPILL_GAP_PX,
87
+ y: topY,
88
+ }
89
+ : { x: insideRightX, y: topY };
90
+ pins.push({
91
+ side: 'before',
92
+ isoDate: beforeDate,
93
+ glyphTopLeft: glyphLeft,
94
+ glyphSize: tileSize,
95
+ spilled,
96
+ });
97
+ }
98
+ return pins;
99
+ }
100
+ /**
101
+ * Compute inline-date pin glyph placements for a container (group or
102
+ * parallel). The glyphs sit flush to the box's top-LEFT (`after`) and
103
+ * top-RIGHT (`before`) corners with the standard inset; containers
104
+ * never spill (they always have room for a 12 px tile in their own
105
+ * top-decoration row).
106
+ */
107
+ export function computeContainerInlineDatePins(opts) {
108
+ const { box, afterDate, beforeDate } = opts;
109
+ if (!afterDate && !beforeDate)
110
+ return [];
111
+ const pins = [];
112
+ const tileSize = INLINE_DATE_GLYPH_TILE_SIZE_PX;
113
+ const topY = box.y + INLINE_DATE_GLYPH_INSET_TOP_PX;
114
+ if (afterDate) {
115
+ pins.push({
116
+ side: 'after',
117
+ isoDate: afterDate,
118
+ glyphTopLeft: { x: box.x + INLINE_DATE_GLYPH_INSET_LEFT_PX, y: topY },
119
+ glyphSize: tileSize,
120
+ spilled: false,
121
+ });
122
+ }
123
+ if (beforeDate) {
124
+ pins.push({
125
+ side: 'before',
126
+ isoDate: beforeDate,
127
+ glyphTopLeft: {
128
+ x: box.x + box.width - INLINE_DATE_GLYPH_INSET_RIGHT_PX - tileSize,
129
+ y: topY,
130
+ },
131
+ glyphSize: tileSize,
132
+ spilled: false,
133
+ });
134
+ }
135
+ return pins;
136
+ }
137
+ /**
138
+ * Returns the first ISO date literal in `values`, or undefined when none
139
+ * is present. The validator enforces "at most one inline date per
140
+ * direction" so this lookup is unambiguous.
141
+ */
142
+ export function pickInlineDate(values) {
143
+ for (const v of values) {
144
+ if (/^\d{4}-\d{2}-\d{2}$/.test(v))
145
+ return v;
146
+ }
147
+ return undefined;
148
+ }
149
+ //# sourceMappingURL=inline-date-pin-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-date-pin-geometry.js","sourceRoot":"","sources":["../src/inline-date-pin-geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ /** Horizontal inset (px) for the caption's left edge inside the bar. */
2
+ export declare const ITEM_CAPTION_INSET_X_PX = 12;
3
+ /**
4
+ * When the bar's title would overflow to the right, layout sets
5
+ * `textSpills` and the caption renders to the RIGHT of the bar; this
6
+ * is the gap (px) between the bar's right edge and the spilled
7
+ * caption.
8
+ */
9
+ export declare const ITEM_CAPTION_SPILL_GAP_PX = 6;
10
+ /** Baseline Y of the title text relative to the bar's top. */
11
+ export declare const ITEM_CAPTION_TITLE_BASELINE_OFFSET_PX = 20;
12
+ /** Baseline Y of the meta text (second line) relative to the bar's top. */
13
+ export declare const ITEM_CAPTION_META_BASELINE_OFFSET_PX = 38;
14
+ /** Font size (px) of the title text. */
15
+ export declare const ITEM_CAPTION_TITLE_FONT_SIZE_PX = 13;
16
+ /** Font size (px) of the meta text. */
17
+ export declare const ITEM_CAPTION_META_FONT_SIZE_PX = 11;
18
+ /** Distance (px) from the bar's right edge to the dot's center. */
19
+ export declare const ITEM_STATUS_DOT_INSET_RIGHT_PX = 12;
20
+ /** Distance (px) from the bar's top edge to the dot's center. */
21
+ export declare const ITEM_STATUS_DOT_INSET_TOP_PX = 12;
22
+ /** Radius (px) of the status dot. */
23
+ export declare const ITEM_STATUS_DOT_RADIUS_PX = 5;
24
+ /**
25
+ * X offset (px from bar's right edge) where the rightmost footnote
26
+ * digit's anchor sits. Tuned to leave room for the status dot.
27
+ */
28
+ export declare const ITEM_FOOTNOTE_INDICATOR_INSET_RIGHT_PX = 22;
29
+ /** Baseline Y (px from bar's top) for footnote indicator digits. */
30
+ export declare const ITEM_FOOTNOTE_INDICATOR_BASELINE_OFFSET_PX = 14;
31
+ /**
32
+ * Horizontal step (px) between consecutive footnote indicators when
33
+ * an item carries more than one footnote — they walk LEFT from the
34
+ * status dot.
35
+ */
36
+ export declare const ITEM_FOOTNOTE_INDICATOR_STEP_PX = 8;
37
+ /** Side length (px) of the square link-icon tile. */
38
+ export declare const ITEM_LINK_ICON_TILE_SIZE_PX = 14;
39
+ /** Distance (px) from the bar's right/bottom edges to the tile's edge. */
40
+ export declare const ITEM_LINK_ICON_INSET_PX = 6;
41
+ /**
42
+ * Horizontal gap (px) between consecutive decorations (status dot,
43
+ * link icon, footnote, title) when they spill into the column to
44
+ * the right of a bar that's too narrow to host them inside.
45
+ */
46
+ export declare const ITEM_DECORATION_SPILL_GAP_PX = 4;
47
+ /**
48
+ * Minimum bar width (px) needed to host the status dot inside the
49
+ * bar with its full inset. Below this, the dot would have to
50
+ * extend past the bar's left edge, so the dot spills into the
51
+ * caption column to the right of the bar instead.
52
+ */
53
+ export declare const MIN_BAR_WIDTH_FOR_DOT_PX: number;
54
+ /**
55
+ * Minimum bar width (px) needed to host the link-icon tile AND the
56
+ * status dot inside the bar with at least
57
+ * `ITEM_DECORATION_SPILL_GAP_PX` of breathing room between them.
58
+ * Below this, the link icon would visually collide with (or push
59
+ * into) the dot's column, so the icon spills out and renders ahead
60
+ * of the (also-spilled) title.
61
+ */
62
+ export declare const MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX: number;
63
+ /**
64
+ * Minimum bar width (px) needed to host the footnote indicator at
65
+ * its inset-right position without overshooting the bar's left
66
+ * edge or colliding with a leading link icon. Approximate width
67
+ * for one digit is 8px (font-size 10, bold).
68
+ */
69
+ export declare const MIN_BAR_WIDTH_FOR_FOOTNOTE_PX: number;
70
+ /** Side length (px) of the inline-date calendar glyph tile. */
71
+ export declare const INLINE_DATE_GLYPH_TILE_SIZE_PX = 12;
72
+ /** Distance (px) from the bar's left edge to the glyph's left edge
73
+ * when the glyph sits at the leftmost top-decoration slot (no link
74
+ * icon present). */
75
+ export declare const INLINE_DATE_GLYPH_INSET_LEFT_PX = 6;
76
+ /** Distance (px) from the bar's right edge to the glyph's right edge
77
+ * when the glyph sits at the rightmost top-decoration slot (no
78
+ * status dot or footnote present). */
79
+ export declare const INLINE_DATE_GLYPH_INSET_RIGHT_PX = 6;
80
+ /** Distance (px) from the bar's top edge to the glyph's top edge. */
81
+ export declare const INLINE_DATE_GLYPH_INSET_TOP_PX = 5;
82
+ /** Horizontal gap (px) between the inline-date glyph and an adjacent
83
+ * decoration (link icon on the left, status dot / footnote indicator
84
+ * on the right). Matches the existing decoration spill gap so the
85
+ * top-decoration row reads as one rhythm. */
86
+ export declare const INLINE_DATE_GLYPH_GAP_PX = 4;
87
+ /**
88
+ * Minimum bar width (px) needed to host the inline-date glyph inside
89
+ * the bar. Below this, the glyph spills:
90
+ * - `before:` glyph spills RIGHT into the column the status dot
91
+ * uses (same family of decorations, same step pitch).
92
+ * - `after:` glyph spills LEFT of the bar's leading edge so the
93
+ * side semantics (after vs before) stay readable in the spill.
94
+ *
95
+ * The threshold reserves room for the glyph itself plus a small
96
+ * clearance from the link-icon column (when present) so the two
97
+ * glyphs don't overlap inside narrow bars.
98
+ */
99
+ export declare const MIN_BAR_WIDTH_FOR_INLINE_DATE_PX: number;
100
+ /** Height (px) of a label chip rectangle. */
101
+ export declare const LABEL_CHIP_HEIGHT_PX = 13;
102
+ /**
103
+ * Vertical gap (px) between the top of the bottom progress strip and
104
+ * the BOTTOM of a label chip. Keeps chips from touching the strip and
105
+ * gives a clear visual rail.
106
+ */
107
+ export declare const LABEL_CHIP_GAP_ABOVE_PROGRESS_STRIP_PX = 3;
108
+ /** Horizontal gap (px) between consecutive chips in a row. */
109
+ export declare const LABEL_CHIP_GAP_BETWEEN_PX = 4;
110
+ /**
111
+ * Vertical gap (px) between two stacked chip rows when chips spill
112
+ * outside the bar and form a multi-row column to the right.
113
+ */
114
+ export declare const LABEL_CHIP_ROW_GAP_PX = 4;
115
+ /**
116
+ * Vertical row pitch (px) for a stacked chip column — chip height +
117
+ * inter-row gap.
118
+ */
119
+ export declare const LABEL_CHIP_ROW_STEP_PX: number;
120
+ /**
121
+ * Slack budget (fraction) applied ONCE per item when chips spill
122
+ * outside the bar. If a chip would overflow its row by less than
123
+ * `SPILL_ROW_SLACK_FRACTION × chip.width`, the row is allowed to
124
+ * stretch by the overflow amount and keep the chip on it instead of
125
+ * wrapping. This rescues "lonely chip" cases where one chip just
126
+ * barely overshoots; once the slack is consumed, no further row
127
+ * expansions happen for that item.
128
+ */
129
+ export declare const SPILL_ROW_SLACK_FRACTION = 0.25;
130
+ export interface ChipRowSample<T> {
131
+ id: T;
132
+ width: number;
133
+ }
134
+ export interface SpillChipPack<T> {
135
+ /** Rows ordered top-to-bottom — `rows[0]` sits at the chip
136
+ * column's top y, `rows[1]` one `LABEL_CHIP_ROW_STEP_PX` below,
137
+ * and so on. */
138
+ rows: ChipRowSample<T>[][];
139
+ /** True when the slack rule was used to keep an extra chip on
140
+ * row 0 (or whichever row was being filled when the overflow
141
+ * happened). At most one expansion per item. */
142
+ expanded: boolean;
143
+ }
144
+ /**
145
+ * Pack `chips` into rows for the SPILL column to the right of an
146
+ * item bar. Each row is capped at `barVisualWidth` (with gaps
147
+ * between chips). When a chip would overflow:
148
+ *
149
+ * - If the row is empty, place the chip anyway (a chip wider than
150
+ * the cap occupies its own row).
151
+ * - Else if the slack rule applies (`overflow ≤ 0.25 × chip.width`)
152
+ * AND the slack hasn't already been used for this item, expand
153
+ * the current row by `overflow` and keep the chip on it.
154
+ * - Else wrap the chip to a fresh row.
155
+ */
156
+ export declare function packSpillChips<T>(chips: ChipRowSample<T>[], barVisualWidth: number): SpillChipPack<T>;
157
+ //# sourceMappingURL=item-bar-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-bar-geometry.d.ts","sourceRoot":"","sources":["../src/item-bar-geometry.ts"],"names":[],"mappings":"AAkBA,wEAAwE;AACxE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,8DAA8D;AAC9D,eAAO,MAAM,qCAAqC,KAAK,CAAC;AAExD,2EAA2E;AAC3E,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD,uCAAuC;AACvC,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAIjD,mEAAmE;AACnE,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD,iEAAiE;AACjE,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C,qCAAqC;AACrC,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAI3C;;;GAGG;AACH,eAAO,MAAM,sCAAsC,KAAK,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,0CAA0C,KAAK,CAAC;AAE7D;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAIjD,qDAAqD;AACrD,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAIzC;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAA6D,CAAC;AAEnG;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,QAKjB,CAAC;AAE9B;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,QAA6C,CAAC;AAUxF,+DAA+D;AAC/D,eAAO,MAAM,8BAA8B,KAAK,CAAC;AAEjD;;qBAEqB;AACrB,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD;;uCAEuC;AACvC,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,qEAAqE;AACrE,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD;;;8CAG8C;AAC9C,eAAO,MAAM,wBAAwB,IAA+B,CAAC;AAErE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gCAAgC,QAKT,CAAC;AAIrC,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,IAAI,CAAC;AAExD,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAA+C,CAAC;AAEnF;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B,EAAE,EAAE,CAAC,CAAC;IACN,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC5B;;qBAEiB;IACjB,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC3B;;qDAEiD;IACjD,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC5B,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EACzB,cAAc,EAAE,MAAM,GACvB,aAAa,CAAC,CAAC,CAAC,CAkClB"}
@@ -0,0 +1,214 @@
1
+ // Item-bar internal geometry — the "rails" inside an item rectangle:
2
+ // caption text (title + meta), label chips along the bottom, status
3
+ // dot at the upper-right, footnote indicators just left of the dot,
4
+ // and the link icon tile at the bottom-right.
5
+ //
6
+ // These metrics are shared between layout (label-chip placement,
7
+ // future content-aware width estimation) and renderer (text baselines
8
+ // + glyph positions). Keeping them in one module means a single edit
9
+ // re-skins every item bar consistently — the alternative is hunting
10
+ // matching `+ 12`s across two packages.
11
+ //
12
+ // Coordinate convention: every offset is **relative to the item's
13
+ // `box`** (`box.x` for X, `box.y` for Y), with `box.height` /
14
+ // `box.width` used as the "right edge" / "bottom edge" anchor where
15
+ // noted.
16
+ // ---- Caption (title + meta text) ---------------------------------
17
+ /** Horizontal inset (px) for the caption's left edge inside the bar. */
18
+ export const ITEM_CAPTION_INSET_X_PX = 12;
19
+ /**
20
+ * When the bar's title would overflow to the right, layout sets
21
+ * `textSpills` and the caption renders to the RIGHT of the bar; this
22
+ * is the gap (px) between the bar's right edge and the spilled
23
+ * caption.
24
+ */
25
+ export const ITEM_CAPTION_SPILL_GAP_PX = 6;
26
+ /** Baseline Y of the title text relative to the bar's top. */
27
+ export const ITEM_CAPTION_TITLE_BASELINE_OFFSET_PX = 20;
28
+ /** Baseline Y of the meta text (second line) relative to the bar's top. */
29
+ export const ITEM_CAPTION_META_BASELINE_OFFSET_PX = 38;
30
+ /** Font size (px) of the title text. */
31
+ export const ITEM_CAPTION_TITLE_FONT_SIZE_PX = 13;
32
+ /** Font size (px) of the meta text. */
33
+ export const ITEM_CAPTION_META_FONT_SIZE_PX = 11;
34
+ // ---- Status dot (upper-right glyph) ------------------------------
35
+ /** Distance (px) from the bar's right edge to the dot's center. */
36
+ export const ITEM_STATUS_DOT_INSET_RIGHT_PX = 12;
37
+ /** Distance (px) from the bar's top edge to the dot's center. */
38
+ export const ITEM_STATUS_DOT_INSET_TOP_PX = 12;
39
+ /** Radius (px) of the status dot. */
40
+ export const ITEM_STATUS_DOT_RADIUS_PX = 5;
41
+ // ---- Footnote indicators (numbers next to status dot) ------------
42
+ /**
43
+ * X offset (px from bar's right edge) where the rightmost footnote
44
+ * digit's anchor sits. Tuned to leave room for the status dot.
45
+ */
46
+ export const ITEM_FOOTNOTE_INDICATOR_INSET_RIGHT_PX = 22;
47
+ /** Baseline Y (px from bar's top) for footnote indicator digits. */
48
+ export const ITEM_FOOTNOTE_INDICATOR_BASELINE_OFFSET_PX = 14;
49
+ /**
50
+ * Horizontal step (px) between consecutive footnote indicators when
51
+ * an item carries more than one footnote — they walk LEFT from the
52
+ * status dot.
53
+ */
54
+ export const ITEM_FOOTNOTE_INDICATOR_STEP_PX = 8;
55
+ // ---- Link icon tile (bottom-right) -------------------------------
56
+ /** Side length (px) of the square link-icon tile. */
57
+ export const ITEM_LINK_ICON_TILE_SIZE_PX = 14;
58
+ /** Distance (px) from the bar's right/bottom edges to the tile's edge. */
59
+ export const ITEM_LINK_ICON_INSET_PX = 6;
60
+ // ---- Narrow-bar decoration spill --------------------------------
61
+ /**
62
+ * Horizontal gap (px) between consecutive decorations (status dot,
63
+ * link icon, footnote, title) when they spill into the column to
64
+ * the right of a bar that's too narrow to host them inside.
65
+ */
66
+ export const ITEM_DECORATION_SPILL_GAP_PX = 4;
67
+ /**
68
+ * Minimum bar width (px) needed to host the status dot inside the
69
+ * bar with its full inset. Below this, the dot would have to
70
+ * extend past the bar's left edge, so the dot spills into the
71
+ * caption column to the right of the bar instead.
72
+ */
73
+ export const MIN_BAR_WIDTH_FOR_DOT_PX = ITEM_STATUS_DOT_INSET_RIGHT_PX + ITEM_STATUS_DOT_RADIUS_PX;
74
+ /**
75
+ * Minimum bar width (px) needed to host the link-icon tile AND the
76
+ * status dot inside the bar with at least
77
+ * `ITEM_DECORATION_SPILL_GAP_PX` of breathing room between them.
78
+ * Below this, the link icon would visually collide with (or push
79
+ * into) the dot's column, so the icon spills out and renders ahead
80
+ * of the (also-spilled) title.
81
+ */
82
+ export const MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX = ITEM_LINK_ICON_INSET_PX +
83
+ ITEM_LINK_ICON_TILE_SIZE_PX +
84
+ ITEM_DECORATION_SPILL_GAP_PX +
85
+ ITEM_STATUS_DOT_INSET_RIGHT_PX +
86
+ ITEM_STATUS_DOT_RADIUS_PX;
87
+ /**
88
+ * Minimum bar width (px) needed to host the footnote indicator at
89
+ * its inset-right position without overshooting the bar's left
90
+ * edge or colliding with a leading link icon. Approximate width
91
+ * for one digit is 8px (font-size 10, bold).
92
+ */
93
+ export const MIN_BAR_WIDTH_FOR_FOOTNOTE_PX = ITEM_FOOTNOTE_INDICATOR_INSET_RIGHT_PX + 1;
94
+ // ---- Inline-date glyph (after:DATE / before:DATE corner badge) ---
95
+ //
96
+ // Renders the renderer's built-in `calendar` SVG at top-LEFT (`after`)
97
+ // or top-RIGHT (`before`) of the entity bar. Sized as a sibling of
98
+ // the status dot and footnote indicators (smaller than the link tile)
99
+ // so the top-decoration row stays visually unified. See
100
+ // specs/rendering.md "Inline-date glyph".
101
+ /** Side length (px) of the inline-date calendar glyph tile. */
102
+ export const INLINE_DATE_GLYPH_TILE_SIZE_PX = 12;
103
+ /** Distance (px) from the bar's left edge to the glyph's left edge
104
+ * when the glyph sits at the leftmost top-decoration slot (no link
105
+ * icon present). */
106
+ export const INLINE_DATE_GLYPH_INSET_LEFT_PX = 6;
107
+ /** Distance (px) from the bar's right edge to the glyph's right edge
108
+ * when the glyph sits at the rightmost top-decoration slot (no
109
+ * status dot or footnote present). */
110
+ export const INLINE_DATE_GLYPH_INSET_RIGHT_PX = 6;
111
+ /** Distance (px) from the bar's top edge to the glyph's top edge. */
112
+ export const INLINE_DATE_GLYPH_INSET_TOP_PX = 5;
113
+ /** Horizontal gap (px) between the inline-date glyph and an adjacent
114
+ * decoration (link icon on the left, status dot / footnote indicator
115
+ * on the right). Matches the existing decoration spill gap so the
116
+ * top-decoration row reads as one rhythm. */
117
+ export const INLINE_DATE_GLYPH_GAP_PX = ITEM_DECORATION_SPILL_GAP_PX;
118
+ /**
119
+ * Minimum bar width (px) needed to host the inline-date glyph inside
120
+ * the bar. Below this, the glyph spills:
121
+ * - `before:` glyph spills RIGHT into the column the status dot
122
+ * uses (same family of decorations, same step pitch).
123
+ * - `after:` glyph spills LEFT of the bar's leading edge so the
124
+ * side semantics (after vs before) stay readable in the spill.
125
+ *
126
+ * The threshold reserves room for the glyph itself plus a small
127
+ * clearance from the link-icon column (when present) so the two
128
+ * glyphs don't overlap inside narrow bars.
129
+ */
130
+ export const MIN_BAR_WIDTH_FOR_INLINE_DATE_PX = INLINE_DATE_GLYPH_INSET_LEFT_PX +
131
+ INLINE_DATE_GLYPH_TILE_SIZE_PX +
132
+ INLINE_DATE_GLYPH_GAP_PX +
133
+ INLINE_DATE_GLYPH_TILE_SIZE_PX +
134
+ INLINE_DATE_GLYPH_INSET_RIGHT_PX;
135
+ // ---- Label chips (along the bar's bottom) ------------------------
136
+ /** Height (px) of a label chip rectangle. */
137
+ export const LABEL_CHIP_HEIGHT_PX = 13;
138
+ /**
139
+ * Vertical gap (px) between the top of the bottom progress strip and
140
+ * the BOTTOM of a label chip. Keeps chips from touching the strip and
141
+ * gives a clear visual rail.
142
+ */
143
+ export const LABEL_CHIP_GAP_ABOVE_PROGRESS_STRIP_PX = 3;
144
+ /** Horizontal gap (px) between consecutive chips in a row. */
145
+ export const LABEL_CHIP_GAP_BETWEEN_PX = 4;
146
+ /**
147
+ * Vertical gap (px) between two stacked chip rows when chips spill
148
+ * outside the bar and form a multi-row column to the right.
149
+ */
150
+ export const LABEL_CHIP_ROW_GAP_PX = 4;
151
+ /**
152
+ * Vertical row pitch (px) for a stacked chip column — chip height +
153
+ * inter-row gap.
154
+ */
155
+ export const LABEL_CHIP_ROW_STEP_PX = LABEL_CHIP_HEIGHT_PX + LABEL_CHIP_ROW_GAP_PX;
156
+ /**
157
+ * Slack budget (fraction) applied ONCE per item when chips spill
158
+ * outside the bar. If a chip would overflow its row by less than
159
+ * `SPILL_ROW_SLACK_FRACTION × chip.width`, the row is allowed to
160
+ * stretch by the overflow amount and keep the chip on it instead of
161
+ * wrapping. This rescues "lonely chip" cases where one chip just
162
+ * barely overshoots; once the slack is consumed, no further row
163
+ * expansions happen for that item.
164
+ */
165
+ export const SPILL_ROW_SLACK_FRACTION = 0.25;
166
+ /**
167
+ * Pack `chips` into rows for the SPILL column to the right of an
168
+ * item bar. Each row is capped at `barVisualWidth` (with gaps
169
+ * between chips). When a chip would overflow:
170
+ *
171
+ * - If the row is empty, place the chip anyway (a chip wider than
172
+ * the cap occupies its own row).
173
+ * - Else if the slack rule applies (`overflow ≤ 0.25 × chip.width`)
174
+ * AND the slack hasn't already been used for this item, expand
175
+ * the current row by `overflow` and keep the chip on it.
176
+ * - Else wrap the chip to a fresh row.
177
+ */
178
+ export function packSpillChips(chips, barVisualWidth) {
179
+ if (chips.length === 0)
180
+ return { rows: [[]], expanded: false };
181
+ const rows = [[]];
182
+ let used = 0;
183
+ let rowCap = barVisualWidth;
184
+ let expanded = false;
185
+ for (const chip of chips) {
186
+ const row = rows[rows.length - 1];
187
+ const needed = (row.length === 0 ? 0 : LABEL_CHIP_GAP_BETWEEN_PX) + chip.width;
188
+ const wouldBe = used + needed;
189
+ if (wouldBe <= rowCap) {
190
+ row.push(chip);
191
+ used = wouldBe;
192
+ continue;
193
+ }
194
+ if (row.length === 0) {
195
+ row.push(chip);
196
+ used = chip.width;
197
+ continue;
198
+ }
199
+ const overflow = wouldBe - rowCap;
200
+ const slack = chip.width * SPILL_ROW_SLACK_FRACTION;
201
+ if (!expanded && overflow <= slack) {
202
+ row.push(chip);
203
+ used = wouldBe;
204
+ rowCap = wouldBe;
205
+ expanded = true;
206
+ continue;
207
+ }
208
+ rows.push([chip]);
209
+ used = chip.width;
210
+ rowCap = barVisualWidth;
211
+ }
212
+ return { rows, expanded };
213
+ }
214
+ //# sourceMappingURL=item-bar-geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item-bar-geometry.js","sourceRoot":"","sources":["../src/item-bar-geometry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import type { DefaultDeclaration, SwimlaneDeclaration } from '@nowline/core';
2
+ import type { PositionedLaneUtilization, PositionedTrackChild, UtilizationClassification } from './types.js';
3
+ /**
4
+ * Built-in defaults used when neither the lane nor its `default swimlane`
5
+ * declares a threshold. Mirrors specs/dsl.md rule 17d.
6
+ */
7
+ export declare const DEFAULT_UTILIZATION_WARN_FRACTION = 0.8;
8
+ export declare const DEFAULT_UTILIZATION_OVER_FRACTION = 1;
9
+ /**
10
+ * One contributor to a lane's load function: a single positioned item with
11
+ * its visual horizontal span and the load it contributes during that span.
12
+ * Children inside `parallel` / `group` blocks contribute individually so
13
+ * concurrent load reads correctly (the parallel block itself spans the union
14
+ * of its children's bars but does not itself add load — only the items do).
15
+ */
16
+ interface LoadContributor {
17
+ startX: number;
18
+ endX: number;
19
+ load: number;
20
+ }
21
+ /**
22
+ * Walk the lane's positioned children and return one `LoadContributor` per
23
+ * item that contributes load. Items inside `parallel` and `group` blocks are
24
+ * descended into recursively; the block envelopes themselves contribute
25
+ * nothing (they are pure containers).
26
+ *
27
+ * `inferItemLoad` mirrors the spec's "Default capacity" table:
28
+ * - Explicit `capacity:N` → that value.
29
+ * - Sized item without explicit `capacity:` → 1 (the default for sized
30
+ * items, matching the duration-derivation default).
31
+ * - Duration-literal item without `capacity:` → 0 (the bar paints normally
32
+ * but does not contribute load — this is the legacy "uncounted" item
33
+ * family from before the size system existed).
34
+ */
35
+ export declare function collectLoadContributors(children: PositionedTrackChild[]): LoadContributor[];
36
+ /**
37
+ * Compute a lane's utilization model. Returns `null` when there is no
38
+ * meaningful underline to paint:
39
+ * - Lane has no `capacity:` (no denominator → undefined utilization).
40
+ * - Lane has no items contributing load (collectLoadContributors returns
41
+ * an empty list).
42
+ * - Both thresholds resolve to `none` (caller has opted out of every
43
+ * color band; nothing to paint).
44
+ *
45
+ * The returned `PositionedLaneUtilization` carries the resolved thresholds
46
+ * (in fraction form, or `null` for opt-out) alongside the segments so the
47
+ * renderer can render legends / tooltips without re-resolving anything.
48
+ */
49
+ export declare function computeLaneUtilization(opts: {
50
+ children: PositionedTrackChild[];
51
+ capacityValue: number;
52
+ warnFraction: number | null;
53
+ overFraction: number | null;
54
+ }): PositionedLaneUtilization | null;
55
+ /**
56
+ * Classify a single load value against the lane's resolved capacity and
57
+ * thresholds. Spec § "Load function and segmentation":
58
+ * - `u < warn-at` → green (healthy; includes `u = 0` so the underline
59
+ * stays continuous).
60
+ * - `warn-at ≤ u < over-at` → yellow.
61
+ * - `u ≥ over-at` → red.
62
+ *
63
+ * `null` thresholds disable that color band: a `null` warnFraction collapses
64
+ * to a binary green / red indicator; a `null` overFraction collapses to
65
+ * green / yellow; both `null` is unreachable here (caller short-circuits
66
+ * upstream).
67
+ */
68
+ export declare function classifyLoad(load: number, capacity: number, warnFraction: number | null, overFraction: number | null): UtilizationClassification;
69
+ /**
70
+ * Resolve the lane's effective utilization thresholds, applying the spec's
71
+ * resolution order:
72
+ *
73
+ * 1. Lane's own `utilization-warn-at:` / `utilization-over-at:` properties.
74
+ * 2. The applicable `default swimlane` declaration's properties.
75
+ * 3. Built-in defaults (`warn-at:80%`, `over-at:100%`).
76
+ *
77
+ * Each side resolves independently — a lane can pin only one threshold and
78
+ * inherit the other from defaults / built-ins. The returned `null` means
79
+ * "opted out via `none`" and disables that color band downstream.
80
+ *
81
+ * Malformed values (already reported by the validator) are treated as
82
+ * "unset" — the resolver falls through to the next layer rather than
83
+ * double-reporting.
84
+ */
85
+ export declare function resolveLaneUtilizationThresholds(lane: SwimlaneDeclaration, defaults: Map<string, DefaultDeclaration>): {
86
+ warn: number | null;
87
+ over: number | null;
88
+ };
89
+ export {};
90
+ //# sourceMappingURL=lane-utilization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lane-utilization.d.ts","sourceRoot":"","sources":["../src/lane-utilization.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE7E,OAAO,KAAK,EAER,yBAAyB,EACzB,oBAAoB,EAEpB,yBAAyB,EAC5B,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,iCAAiC,IAAM,CAAC;AAErD;;;;;;GAMG;AACH,UAAU,eAAe;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,oBAAoB,EAAE,GAAG,eAAe,EAAE,CAI3F;AAyBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IACzC,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,GAAG,yBAAyB,GAAG,IAAI,CA+DnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CACxB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,GAC5B,yBAAyB,CAK3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gCAAgC,CAC5C,IAAI,EAAE,mBAAmB,EACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAC1C;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAa9C"}