@nowline/layout 0.2.5 → 0.4.1

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.
@@ -0,0 +1,46 @@
1
+ import type { BoundingBox, InlineDatePin } from './types.js';
2
+ export interface ItemInlineDatePinInputs {
3
+ box: BoundingBox;
4
+ /** ISO date string from `after:DATE`, or undefined when no inline `after`. */
5
+ afterDate: string | undefined;
6
+ /** ISO date string from `before:DATE`, or undefined when no inline `before`. */
7
+ beforeDate: string | undefined;
8
+ hasLinkIcon: boolean;
9
+ /** Number of footnote indicators rendered in the bar's top-RIGHT cluster. */
10
+ footnoteCount: number;
11
+ }
12
+ /**
13
+ * Compute inline-date pin glyph placements for an item bar. Returns an
14
+ * empty array when neither `afterDate` nor `beforeDate` is set.
15
+ *
16
+ * Item bars participate in the narrow-bar spill family — when the bar is
17
+ * narrower than `MIN_BAR_WIDTH_FOR_INLINE_DATE_PX`, the `before:` glyph
18
+ * spills RIGHT of the bar (joining the status-dot / footnote spill
19
+ * column) and the `after:` glyph spills LEFT of the bar's leading edge
20
+ * so the side semantics stay readable.
21
+ */
22
+ export declare function computeItemInlineDatePins(opts: ItemInlineDatePinInputs): InlineDatePin[];
23
+ export interface ContainerInlineDatePinInputs {
24
+ /** Bounding box that anchors the glyph corners. For styled groups and
25
+ * bracketed parallels this is the visible box; for unstyled groups
26
+ * and bare parallels it is the logical bounding box (leftmost child
27
+ * start, rightmost child end, top of the highest child row). */
28
+ box: BoundingBox;
29
+ afterDate: string | undefined;
30
+ beforeDate: string | undefined;
31
+ }
32
+ /**
33
+ * Compute inline-date pin glyph placements for a container (group or
34
+ * parallel). The glyphs sit flush to the box's top-LEFT (`after`) and
35
+ * top-RIGHT (`before`) corners with the standard inset; containers
36
+ * never spill (they always have room for a 12 px tile in their own
37
+ * top-decoration row).
38
+ */
39
+ export declare function computeContainerInlineDatePins(opts: ContainerInlineDatePinInputs): InlineDatePin[];
40
+ /**
41
+ * Returns the first ISO date literal in `values`, or undefined when none
42
+ * is present. The validator enforces "at most one inline date per
43
+ * direction" so this lookup is unambiguous.
44
+ */
45
+ export declare function pickInlineDate(values: readonly string[]): string | undefined;
46
+ //# sourceMappingURL=inline-date-pin-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-date-pin-geometry.d.ts","sourceRoot":"","sources":["../src/inline-date-pin-geometry.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAS,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,uBAAuB;IACpC,GAAG,EAAE,WAAW,CAAC;IACjB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,uBAAuB,GAAG,aAAa,EAAE,CAmExF;AAED,MAAM,WAAW,4BAA4B;IACzC;;;qEAGiE;IACjE,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAC1C,IAAI,EAAE,4BAA4B,GACnC,aAAa,EAAE,CAgCjB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAK5E"}
@@ -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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"}
@@ -67,6 +67,36 @@ export declare const MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX: number;
67
67
  * for one digit is 8px (font-size 10, bold).
68
68
  */
69
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;
70
100
  /** Height (px) of a label chip rectangle. */
71
101
  export declare const LABEL_CHIP_HEIGHT_PX = 13;
72
102
  /**
@@ -1 +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;AAIxF,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"}
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"}
@@ -91,6 +91,47 @@ export const MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX = ITEM_LINK_ICON_INSET_PX +
91
91
  * for one digit is 8px (font-size 10, bold).
92
92
  */
93
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;
94
135
  // ---- Label chips (along the bar's bottom) ------------------------
95
136
  /** Height (px) of a label chip rectangle. */
96
137
  export const LABEL_CHIP_HEIGHT_PX = 13;
@@ -1 +1 @@
1
- {"version":3,"file":"item-bar-geometry.js","sourceRoot":"","sources":["../src/item-bar-geometry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"item-bar-geometry.js","sourceRoot":"","sources":["../src/item-bar-geometry.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKR,WAAW,EAEX,aAAa,EAEhB,MAAM,eAAe,CAAC;AA0DvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAanD,OAAO,KAAK,EAYR,iBAAiB,EAIpB,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,aAAa;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAkyC7C,wBAAgB,aAAa,CACzB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,aAAkB,GAC5B,YAAY,CAcd"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKR,WAAW,EAEX,aAAa,EAEhB,MAAM,eAAe,CAAC;AA2DvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAanD,OAAO,KAAK,EAYR,iBAAiB,EAIpB,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,aAAa;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAu2C7C,wBAAgB,aAAa,CACzB,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,aAAkB,GAC5B,YAAY,CAcd"}
package/dist/layout.js CHANGED
@@ -5,6 +5,7 @@ import { parseDate, propValue, propValues } from './dsl-utils.js';
5
5
  import { ChannelGrid, collectRoutingObstacles, routeChannelEdges, } from './edge-routing.js';
6
6
  import { HEADER_AUTHOR_FONT_SIZE_PX, HEADER_AUTHOR_LINE_HEIGHT_PX, HEADER_CARD_PADDING_BOTTOM, HEADER_CARD_PADDING_TOP, HEADER_CARD_PADDING_X, HEADER_TITLE_FONT_SIZE_PX, HEADER_TITLE_LINE_HEIGHT_PX, HEADER_TITLE_TO_AUTHOR_GAP_PX, } from './header-card-geometry.js';
7
7
  import { localeStrings } from './i18n.js';
8
+ import { computeItemInlineDatePins, pickInlineDate } from './inline-date-pin-geometry.js';
8
9
  import { ITEM_CAPTION_INSET_X_PX, ITEM_CAPTION_META_BASELINE_OFFSET_PX, ITEM_CAPTION_SPILL_GAP_PX, ITEM_CAPTION_TITLE_FONT_SIZE_PX, ITEM_DECORATION_SPILL_GAP_PX, ITEM_FOOTNOTE_INDICATOR_STEP_PX, ITEM_LINK_ICON_TILE_SIZE_PX, ITEM_STATUS_DOT_RADIUS_PX, LABEL_CHIP_GAP_ABOVE_PROGRESS_STRIP_PX, LABEL_CHIP_GAP_BETWEEN_PX, LABEL_CHIP_HEIGHT_PX, LABEL_CHIP_ROW_STEP_PX, MIN_BAR_WIDTH_FOR_DOT_PX, MIN_BAR_WIDTH_FOR_FOOTNOTE_PX, MIN_BAR_WIDTH_FOR_LINK_AND_DOT_PX, packSpillChips, } from './item-bar-geometry.js';
9
10
  import { newCursor } from './layout-context.js';
10
11
  import { GroupNode } from './nodes/group-node.js';
@@ -111,10 +112,14 @@ function sequenceItem(node, cursor, ctx, ownerOverride) {
111
112
  // "one engineer-week of work left".
112
113
  const totalEffortDays = deriveTotalEffortDays(props, ctx.sizes, ctx.cal);
113
114
  const afterRaw = propValues(props, 'after');
114
- const beforeRaw = propValue(props, 'before');
115
+ const beforeRaw = propValues(props, 'before');
115
116
  const dateRaw = propValue(props, 'date');
116
117
  const remainingDays = resolveDuration(propValue(props, 'remaining'), ctx.sizes, ctx.cal);
117
- // Resolve start x: explicit date > after-chain > cursor position
118
+ // Resolve start x: explicit date > after-chain > cursor position.
119
+ // `after:` accepts both entity ids (looked up in entityRightEdges) and
120
+ // inline ISO date literals (resolved through the time scale). The
121
+ // validator enforces "at most one inline date per direction" so at most
122
+ // one element will hit the date path per item.
118
123
  let startX = cursor.x;
119
124
  const explicitDate = parseDate(dateRaw);
120
125
  if (explicitDate) {
@@ -125,6 +130,13 @@ function sequenceItem(node, cursor, ctx, ownerOverride) {
125
130
  else if (afterRaw.length > 0) {
126
131
  let maxEnd = cursor.x;
127
132
  for (const ref of afterRaw) {
133
+ const inlineDate = parseDate(ref);
134
+ if (inlineDate) {
135
+ const xd = ctx.scale.forwardWithinDomain(inlineDate);
136
+ if (xd !== null)
137
+ maxEnd = Math.max(maxEnd, xd);
138
+ continue;
139
+ }
128
140
  const endX = ctx.entityRightEdges.get(ref);
129
141
  if (endX !== undefined)
130
142
  maxEnd = Math.max(maxEnd, endX);
@@ -171,24 +183,39 @@ function sequenceItem(node, cursor, ctx, ownerOverride) {
171
183
  }
172
184
  const chipInsideAvailWidth = Math.max(0, visualWidthPredict - 2 * ITEM_CAPTION_INSET_X_PX);
173
185
  const chipsOutside = chipSamples.length > 0 && chipRowWidth > chipInsideAvailWidth;
174
- // Handle `before:` — item must end by the named anchor/milestone x.
186
+ // Handle `before:` — item must end by the earliest cap. Each cap is
187
+ // either an entity id (looked up in entityLeftEdges) or an inline ISO
188
+ // date literal (resolved through the time scale). The validator enforces
189
+ // "at most one inline date per direction" so at most one element per
190
+ // before-list will hit the date path.
175
191
  let hasOverflow = false;
176
192
  let overflowBox;
177
193
  let overflowAnchorId;
178
- if (beforeRaw) {
179
- const beforeX = ctx.entityLeftEdges.get(beforeRaw);
180
- if (beforeX !== undefined) {
181
- if (logicalRight > beforeX) {
182
- hasOverflow = true;
183
- overflowBox = {
184
- x: beforeX,
185
- y: cursor.y,
186
- width: logicalRight - beforeX,
187
- height: ctx.bandScale.bandwidth(),
188
- };
189
- overflowAnchorId = beforeRaw;
194
+ if (beforeRaw.length > 0) {
195
+ let earliestCapX;
196
+ let earliestCapRef;
197
+ for (const ref of beforeRaw) {
198
+ const inlineDate = parseDate(ref);
199
+ const capX = inlineDate
200
+ ? (ctx.scale.forwardWithinDomain(inlineDate) ?? undefined)
201
+ : ctx.entityLeftEdges.get(ref);
202
+ if (capX === undefined)
203
+ continue;
204
+ if (earliestCapX === undefined || capX < earliestCapX) {
205
+ earliestCapX = capX;
206
+ earliestCapRef = ref;
190
207
  }
191
208
  }
209
+ if (earliestCapX !== undefined && logicalRight > earliestCapX) {
210
+ hasOverflow = true;
211
+ overflowBox = {
212
+ x: earliestCapX,
213
+ y: cursor.y,
214
+ width: logicalRight - earliestCapX,
215
+ height: ctx.bandScale.bandwidth(),
216
+ };
217
+ overflowAnchorId = earliestCapRef;
218
+ }
192
219
  }
193
220
  // Progress fraction
194
221
  const statusRaw = propValue(props, 'status');
@@ -564,6 +591,13 @@ function sequenceItem(node, cursor, ctx, ownerOverride) {
564
591
  // inter-row gap stays constant — the next row's bar starts
565
592
  // `step − bandwidth` px below the (now-taller) bar bottom.
566
593
  cursor.height = Math.max(cursor.height, ctx.bandScale.step() + chipBarExtra);
594
+ const inlineDatePins = computeItemInlineDatePins({
595
+ box: itemBox,
596
+ afterDate: pickInlineDate(afterRaw),
597
+ beforeDate: pickInlineDate(beforeRaw),
598
+ hasLinkIcon,
599
+ footnoteCount: footnoteIndicators.length,
600
+ });
567
601
  const result = {
568
602
  kind: 'item',
569
603
  id,
@@ -594,6 +628,7 @@ function sequenceItem(node, cursor, ctx, ownerOverride) {
594
628
  capacity,
595
629
  size: sizeResolved,
596
630
  style,
631
+ inlineDatePins: inlineDatePins.length > 0 ? inlineDatePins : undefined,
597
632
  };
598
633
  return result;
599
634
  }
@@ -738,6 +773,11 @@ function predictItemChipExtraHeight(item, ctx) {
738
773
  // pin) > `start:` (fixed pin) > `after:` (chain after refs) > sequential
739
774
  // default (continue from `seqDefault`, which is the lane's rightmost time
740
775
  // cursor across all rows).
776
+ //
777
+ // `after:` accepts both entity ids (looked up in `ctx.entityRightEdges`) and
778
+ // inline ISO date literals (looked up via `ctx.scale.forwardWithinDomain`).
779
+ // The validator already enforces "at most one inline date per direction" so
780
+ // at most one element in the list will hit the date path.
741
781
  function resolveChildStart(props, seqDefault, laneLeftX, ctx) {
742
782
  const explicitDate = parseDate(propValue(props, 'date')) ?? parseDate(propValue(props, 'start'));
743
783
  if (explicitDate) {
@@ -749,6 +789,13 @@ function resolveChildStart(props, seqDefault, laneLeftX, ctx) {
749
789
  if (afterRefs.length > 0) {
750
790
  let maxEnd = laneLeftX;
751
791
  for (const ref of afterRefs) {
792
+ const inlineDate = parseDate(ref);
793
+ if (inlineDate) {
794
+ const xd = ctx.scale.forwardWithinDomain(inlineDate);
795
+ if (xd !== null)
796
+ maxEnd = Math.max(maxEnd, xd);
797
+ continue;
798
+ }
752
799
  const endX = ctx.entityRightEdges.get(ref);
753
800
  if (endX !== undefined)
754
801
  maxEnd = Math.max(maxEnd, endX);
@@ -912,7 +959,15 @@ function computeContentEndDay(resolved, ctx, startDate, today) {
912
959
  const anchorEnd = new Map();
913
960
  const milestoneEnd = new Map();
914
961
  let maxDay = 0;
915
- const refEndDay = (ref) => {
962
+ // Resolve a single `after:` element to a day-offset from `startDate`.
963
+ // The element is either an entity id (looked up in itemEnd / anchorEnd /
964
+ // milestoneEnd) or an inline ISO date literal (converted directly via
965
+ // `daysBetween`). The validator already enforces "at most one inline date
966
+ // per direction", so at most one element per list will hit the date path.
967
+ const resolveAfterDay = (ref) => {
968
+ const inlineDate = parseDate(ref);
969
+ if (inlineDate)
970
+ return daysBetween(startDate, inlineDate);
916
971
  if (itemEnd.has(ref))
917
972
  return itemEnd.get(ref);
918
973
  if (anchorEnd.has(ref))
@@ -955,7 +1010,7 @@ function computeContentEndDay(resolved, ctx, startDate, today) {
955
1010
  start = daysBetween(startDate, startProp);
956
1011
  }
957
1012
  else if (afterRefs.length > 0) {
958
- start = Math.max(prevEnd, ...afterRefs.map(refEndDay));
1013
+ start = Math.max(prevEnd, ...afterRefs.map(resolveAfterDay));
959
1014
  }
960
1015
  const end = start + dur;
961
1016
  if (node.name)
@@ -964,18 +1019,29 @@ function computeContentEndDay(resolved, ctx, startDate, today) {
964
1019
  }
965
1020
  if (isParallelBlock(node)) {
966
1021
  // All children share the parallel's start; the block's effective
967
- // end is the maximum child end.
968
- let parallelEnd = prevEnd;
1022
+ // end is the maximum child end. The parallel's own `after:`
1023
+ // (including inline-date pins) widens that shared start.
1024
+ const afterRefs = propValues(node.properties, 'after');
1025
+ const containerStart = afterRefs.length > 0
1026
+ ? Math.max(prevEnd, ...afterRefs.map(resolveAfterDay))
1027
+ : prevEnd;
1028
+ let parallelEnd = containerStart;
969
1029
  for (const child of node.content) {
970
1030
  if (child.$type === 'DescriptionDirective')
971
1031
  continue;
972
- const childEnd = walkNode(child, prevEnd);
1032
+ const childEnd = walkNode(child, containerStart);
973
1033
  parallelEnd = Math.max(parallelEnd, childEnd);
974
1034
  }
975
1035
  return parallelEnd;
976
1036
  }
977
1037
  if (isGroupBlock(node)) {
978
- return walkLane(node.content, prevEnd);
1038
+ // The group's own `after:` (including inline-date pins) widens
1039
+ // the baseline before walking the inner sequential lane.
1040
+ const afterRefs = propValues(node.properties, 'after');
1041
+ const containerStart = afterRefs.length > 0
1042
+ ? Math.max(prevEnd, ...afterRefs.map(resolveAfterDay))
1043
+ : prevEnd;
1044
+ return walkLane(node.content, containerStart);
979
1045
  }
980
1046
  return prevEnd;
981
1047
  };
@@ -994,7 +1060,12 @@ function computeContentEndDay(resolved, ctx, startDate, today) {
994
1060
  }
995
1061
  const after = propValues(ms.properties, 'after');
996
1062
  if (after.length > 0) {
997
- const day = Math.max(0, ...after.map(refEndDay));
1063
+ // Milestones disallow inline date literals at the validator
1064
+ // level (rule 24a — milestones already have `date:`). The
1065
+ // shared `resolveAfterDay` helper still handles such input
1066
+ // gracefully if it slips past, treating the date as the
1067
+ // milestone's effective day.
1068
+ const day = Math.max(0, ...after.map(resolveAfterDay));
998
1069
  if (ms.name)
999
1070
  milestoneEnd.set(ms.name, day);
1000
1071
  maxDay = Math.max(maxDay, day);