@nowline/renderer 0.8.6 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-size.d.ts +9 -0
- package/dist/image-size.d.ts.map +1 -0
- package/dist/image-size.js +104 -0
- package/dist/image-size.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/logo.d.ts +24 -0
- package/dist/logo.d.ts.map +1 -0
- package/dist/logo.js +60 -0
- package/dist/logo.js.map +1 -0
- package/dist/stage.d.ts +78 -0
- package/dist/stage.d.ts.map +1 -0
- package/dist/stage.js +88 -0
- package/dist/stage.js.map +1 -0
- package/dist/svg/edges.d.ts +3 -0
- package/dist/svg/edges.d.ts.map +1 -0
- package/dist/svg/edges.js +144 -0
- package/dist/svg/edges.js.map +1 -0
- package/dist/svg/footnotes.d.ts +4 -0
- package/dist/svg/footnotes.d.ts.map +1 -0
- package/dist/svg/footnotes.js +81 -0
- package/dist/svg/footnotes.js.map +1 -0
- package/dist/svg/header.d.ts +6 -0
- package/dist/svg/header.d.ts.map +1 -0
- package/dist/svg/header.js +215 -0
- package/dist/svg/header.js.map +1 -0
- package/dist/svg/include-region.d.ts +4 -0
- package/dist/svg/include-region.d.ts.map +1 -0
- package/dist/svg/include-region.js +157 -0
- package/dist/svg/include-region.js.map +1 -0
- package/dist/svg/item.d.ts +36 -0
- package/dist/svg/item.d.ts.map +1 -0
- package/dist/svg/item.js +549 -0
- package/dist/svg/item.js.map +1 -0
- package/dist/svg/markers.d.ts +7 -0
- package/dist/svg/markers.d.ts.map +1 -0
- package/dist/svg/markers.js +151 -0
- package/dist/svg/markers.js.map +1 -0
- package/dist/svg/nowline.d.ts +4 -0
- package/dist/svg/nowline.d.ts.map +1 -0
- package/dist/svg/nowline.js +133 -0
- package/dist/svg/nowline.js.map +1 -0
- package/dist/svg/primitives.d.ts +7 -0
- package/dist/svg/primitives.d.ts.map +1 -0
- package/dist/svg/primitives.js +70 -0
- package/dist/svg/primitives.js.map +1 -0
- package/dist/svg/render.d.ts +13 -7
- package/dist/svg/render.d.ts.map +1 -1
- package/dist/svg/render.js +64 -1701
- package/dist/svg/render.js.map +1 -1
- package/dist/svg/sanitize.d.ts +10 -0
- package/dist/svg/sanitize.d.ts.map +1 -1
- package/dist/svg/sanitize.js +264 -16
- package/dist/svg/sanitize.js.map +1 -1
- package/dist/svg/swimlanes.d.ts +5 -0
- package/dist/svg/swimlanes.d.ts.map +1 -0
- package/dist/svg/swimlanes.js +228 -0
- package/dist/svg/swimlanes.js.map +1 -0
- package/dist/svg/timeline.d.ts +24 -0
- package/dist/svg/timeline.d.ts.map +1 -0
- package/dist/svg/timeline.js +284 -0
- package/dist/svg/timeline.js.map +1 -0
- package/dist/svg/waves.d.ts +62 -0
- package/dist/svg/waves.d.ts.map +1 -0
- package/dist/svg/waves.js +397 -0
- package/dist/svg/waves.js.map +1 -0
- package/package.json +6 -6
- package/src/image-size.ts +113 -0
- package/src/index.ts +10 -1
- package/src/logo.ts +82 -0
- package/src/stage.ts +191 -0
- package/src/svg/edges.ts +154 -0
- package/src/svg/footnotes.ts +115 -0
- package/src/svg/header.ts +285 -0
- package/src/svg/include-region.ts +201 -0
- package/src/svg/item.ts +672 -0
- package/src/svg/markers.ts +178 -0
- package/src/svg/nowline.ts +152 -0
- package/src/svg/primitives.ts +86 -0
- package/src/svg/render.ts +94 -2110
- package/src/svg/sanitize.ts +286 -16
- package/src/svg/swimlanes.ts +317 -0
- package/src/svg/timeline.ts +343 -0
- package/src/svg/waves.ts +502 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// The swimlane layers: the lane background tint and tri-state utilization
|
|
2
|
+
// underline, the lane frame tab and badges, and the group and parallel
|
|
3
|
+
// containers that wrap a lane's items.
|
|
4
|
+
import { CONTAINER_BODY_FILL_OPACITY, CONTAINER_BRACKET_LINE_OFFSET_PX, CONTAINER_HEADER_TITLE_BASELINE_PX, CONTAINER_HEADER_TITLE_FONT_SIZE_PX, CONTAINER_MAX_FILLED_DEPTH, CONTAINER_NESTED_BODY_FILL_OPACITY, containerHeaderTitleX, FRAME_TAB_HEIGHT_PX, FRAME_TAB_LABEL_BASELINE_OFFSET_PX, FRAME_TAB_OWNER_GLYPH_PX, } from '@nowline/layout';
|
|
5
|
+
import { CAPACITY_ICON_SVG } from './icons.js';
|
|
6
|
+
import { hasAfterPin, renderCapacitySuffix, renderInlineDatePins, renderItem } from './item.js';
|
|
7
|
+
import { fontAttrs } from './primitives.js';
|
|
8
|
+
import { escAttr, num, tag, textTag } from './xml.js';
|
|
9
|
+
/**
|
|
10
|
+
* A group or parallel, drawn above and around its content
|
|
11
|
+
* (specs/true-width-layout.md §3.3): a body shaded with the container's `bg`
|
|
12
|
+
* spanning exactly its dates, a header bracket over those dates (solid for
|
|
13
|
+
* a group, dashed for a parallel by default; `bracket:` styles it), the
|
|
14
|
+
* title at the container's start with any inline-date glyphs beside it,
|
|
15
|
+
* then the children. A wave-only group draws only its children.
|
|
16
|
+
*
|
|
17
|
+
* `depth` is the container's nesting depth inside its lane, 1 for a
|
|
18
|
+
* top-level container. A nested body takes a lighter tint, and bodies deeper
|
|
19
|
+
* than `CONTAINER_MAX_FILLED_DEPTH` are outlined instead of filled, so tints
|
|
20
|
+
* never compound three deep (§8 O4).
|
|
21
|
+
*/
|
|
22
|
+
function renderContainer(c, depth, options, idPrefix, palette, fonts) {
|
|
23
|
+
const parts = [];
|
|
24
|
+
const isDrawn = !(c.kind === 'group' && c.waveOnly);
|
|
25
|
+
if (isDrawn) {
|
|
26
|
+
const bodyY = c.box.y + c.headerHeight;
|
|
27
|
+
const bodyHeight = c.box.height - c.headerHeight;
|
|
28
|
+
if (c.style.bg !== 'none') {
|
|
29
|
+
const isFilled = depth <= CONTAINER_MAX_FILLED_DEPTH;
|
|
30
|
+
parts.push(tag('rect', {
|
|
31
|
+
x: num(c.box.x),
|
|
32
|
+
y: num(bodyY),
|
|
33
|
+
width: num(c.box.width),
|
|
34
|
+
height: num(bodyHeight),
|
|
35
|
+
fill: isFilled ? c.style.bg : 'none',
|
|
36
|
+
'fill-opacity': isFilled
|
|
37
|
+
? depth === 1
|
|
38
|
+
? CONTAINER_BODY_FILL_OPACITY
|
|
39
|
+
: CONTAINER_NESTED_BODY_FILL_OPACITY
|
|
40
|
+
: null,
|
|
41
|
+
stroke: isFilled ? null : c.style.bg,
|
|
42
|
+
'stroke-opacity': isFilled ? null : 0.6,
|
|
43
|
+
'stroke-width': isFilled ? null : 1,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
// The title and the bracket share one ink, the header's: a group's
|
|
47
|
+
// `fg`, a parallel's bracket stroke. The title sits on the lane band,
|
|
48
|
+
// never on the body, so a `text:` colour chosen to read on `bg`
|
|
49
|
+
// (white on a filled chiclet, before the header) would vanish.
|
|
50
|
+
const ink = c.kind === 'group' ? c.style.fg : palette.parallel.bracketStroke;
|
|
51
|
+
if (c.headerHeight > 0 && c.style.bracket !== 'none') {
|
|
52
|
+
const lineY = c.box.y + CONTAINER_BRACKET_LINE_OFFSET_PX;
|
|
53
|
+
const x0 = c.box.x;
|
|
54
|
+
const x1 = c.box.x + c.box.width;
|
|
55
|
+
parts.push(tag('path', {
|
|
56
|
+
d: `M${num(x0)} ${num(bodyY)} V${num(lineY)} H${num(x1)} V${num(bodyY)}`,
|
|
57
|
+
fill: 'none',
|
|
58
|
+
stroke: ink,
|
|
59
|
+
'stroke-width': 1.2,
|
|
60
|
+
'stroke-dasharray': c.style.bracket === 'dashed' ? '3 2' : null,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (c.title && c.headerHeight > 0) {
|
|
64
|
+
parts.push(textTag({
|
|
65
|
+
// Past the `after:` glyph when there is one; layout
|
|
66
|
+
// clamps the `before:` glyph past this text.
|
|
67
|
+
x: num(containerHeaderTitleX(c.box.x, hasAfterPin(c.inlineDatePins))),
|
|
68
|
+
y: num(c.box.y + CONTAINER_HEADER_TITLE_BASELINE_PX),
|
|
69
|
+
...fontAttrs(c.style, fonts, CONTAINER_HEADER_TITLE_FONT_SIZE_PX),
|
|
70
|
+
'font-weight': 600,
|
|
71
|
+
fill: ink,
|
|
72
|
+
}, c.title));
|
|
73
|
+
}
|
|
74
|
+
// The glyphs sit in the header's title row, never on a child, so
|
|
75
|
+
// painting them before the children is safe.
|
|
76
|
+
parts.push(renderInlineDatePins(c.inlineDatePins, c.style.fg));
|
|
77
|
+
}
|
|
78
|
+
for (const child of c.children) {
|
|
79
|
+
parts.push(renderTrackChild(child, options, idPrefix, palette, fonts, depth));
|
|
80
|
+
}
|
|
81
|
+
return tag('g', { 'data-layer': c.kind, 'data-id': c.id ?? null }, parts.join(''));
|
|
82
|
+
}
|
|
83
|
+
function renderTrackChild(c, options, idPrefix, palette, fonts, parentDepth = 0) {
|
|
84
|
+
if (c.kind === 'item')
|
|
85
|
+
return renderItem(c, options, idPrefix, palette, fonts);
|
|
86
|
+
return renderContainer(c, parentDepth + 1, options, idPrefix, palette, fonts);
|
|
87
|
+
}
|
|
88
|
+
// Renders only the swimlane's background tint rect. Emitted before the
|
|
89
|
+
// chart-body grid lines so those lines visibly span the full chart width.
|
|
90
|
+
// The frame tab (chiclet at top-left) and item content are emitted later,
|
|
91
|
+
// in renderSwimlaneContent, so they appear on top of the grid.
|
|
92
|
+
// Tri-state lane utilization underline. Painted along the bottom edge of
|
|
93
|
+
// the band when the lane has `capacity:` AND at least one item contributing
|
|
94
|
+
// load AND has not opted out of every color band via `utilization-*-at:none`.
|
|
95
|
+
// Geometry per specs/rendering.md § Lane utilization underline:
|
|
96
|
+
// - height: 2px (matches the milestone-line stroke weight)
|
|
97
|
+
// - y: flush with the bottom edge of the band, fully inside it
|
|
98
|
+
// - x: aligned to the segment boundaries the layout already pre-coalesced
|
|
99
|
+
// One <rect> per coalesced segment; classification → palette token mapping
|
|
100
|
+
// is the only renderer-side decision.
|
|
101
|
+
const LANE_UTILIZATION_HEIGHT_PX = 2;
|
|
102
|
+
function utilizationColor(classification, palette) {
|
|
103
|
+
switch (classification) {
|
|
104
|
+
case 'green':
|
|
105
|
+
return palette.swimlane.utilizationOk;
|
|
106
|
+
case 'yellow':
|
|
107
|
+
return palette.swimlane.utilizationWarn;
|
|
108
|
+
case 'red':
|
|
109
|
+
return palette.swimlane.utilizationOver;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function renderLaneUtilization(s, palette) {
|
|
113
|
+
if (!s.utilization || s.utilization.segments.length === 0)
|
|
114
|
+
return '';
|
|
115
|
+
const y = s.box.y + s.box.height - LANE_UTILIZATION_HEIGHT_PX;
|
|
116
|
+
const rects = s.utilization.segments.map((seg) => {
|
|
117
|
+
const width = seg.endX - seg.startX;
|
|
118
|
+
if (width <= 0)
|
|
119
|
+
return '';
|
|
120
|
+
return tag('rect', {
|
|
121
|
+
x: num(seg.startX),
|
|
122
|
+
y: num(y),
|
|
123
|
+
width: num(width),
|
|
124
|
+
height: LANE_UTILIZATION_HEIGHT_PX,
|
|
125
|
+
fill: utilizationColor(seg.classification, palette),
|
|
126
|
+
'data-utilization': seg.classification,
|
|
127
|
+
'data-load': num(seg.load),
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
return tag('g', {
|
|
131
|
+
'data-layer': 'lane-utilization',
|
|
132
|
+
'data-id': s.id ?? null,
|
|
133
|
+
}, rects.join(''));
|
|
134
|
+
}
|
|
135
|
+
export function renderSwimlaneBg(s, palette) {
|
|
136
|
+
const tint = s.bandIndex % 2 === 0 ? palette.swimlane.rowTintEven : palette.swimlane.rowTintOdd;
|
|
137
|
+
const borderColor = palette.swimlane.border;
|
|
138
|
+
return tag('g', { 'data-layer': 'swimlane-bg', 'data-id': s.id ?? null }, tag('rect', {
|
|
139
|
+
x: num(s.box.x),
|
|
140
|
+
y: num(s.box.y),
|
|
141
|
+
width: num(s.box.width),
|
|
142
|
+
height: num(s.box.height),
|
|
143
|
+
fill: tint,
|
|
144
|
+
stroke: borderColor,
|
|
145
|
+
'stroke-width': 1,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
export function renderSwimlane(s, options, idPrefix, palette, fonts) {
|
|
149
|
+
const tabFill = palette.swimlane.tabFill;
|
|
150
|
+
const tabStroke = palette.swimlane.tabStroke;
|
|
151
|
+
const tabText = palette.swimlane.tabText;
|
|
152
|
+
const ownerText = palette.swimlane.ownerText;
|
|
153
|
+
const footnoteColor = palette.swimlane.footnoteIndicator;
|
|
154
|
+
const parts = [];
|
|
155
|
+
// Frame-tab chiclet at the top-left of the band, auto-sized to fit
|
|
156
|
+
// title, owner, capacity badge and footnote indicators. Layout sized it
|
|
157
|
+
// (`PositionedSwimlane.tab`, the same rectangle it reserved for the
|
|
158
|
+
// row packer) and placed each element inside it; this only draws.
|
|
159
|
+
const tab = s.tab;
|
|
160
|
+
if (tab) {
|
|
161
|
+
const LANE_BADGE_FONT_SIZE_PX = 10;
|
|
162
|
+
const footnoteIndicatorText = s.footnoteIndicators.length > 0 ? s.footnoteIndicators.join(',') : '';
|
|
163
|
+
const tabH = FRAME_TAB_HEIGHT_PX;
|
|
164
|
+
const tabY = s.box.y + 10;
|
|
165
|
+
const labelY = tabY + FRAME_TAB_LABEL_BASELINE_OFFSET_PX;
|
|
166
|
+
parts.push(tag('rect', {
|
|
167
|
+
x: num(tab.tabX),
|
|
168
|
+
y: num(tabY),
|
|
169
|
+
width: num(tab.tabW),
|
|
170
|
+
height: num(tabH),
|
|
171
|
+
rx: 4,
|
|
172
|
+
ry: 4,
|
|
173
|
+
fill: tabFill,
|
|
174
|
+
stroke: tabStroke,
|
|
175
|
+
'stroke-width': 1,
|
|
176
|
+
}));
|
|
177
|
+
parts.push(textTag({
|
|
178
|
+
x: num(tab.titleX),
|
|
179
|
+
y: num(labelY),
|
|
180
|
+
'font-family': fonts[s.style.font],
|
|
181
|
+
'font-size': 12,
|
|
182
|
+
'font-weight': 600,
|
|
183
|
+
fill: tabText,
|
|
184
|
+
}, s.title));
|
|
185
|
+
if (s.owner) {
|
|
186
|
+
// A person or people glyph leads the owner name, lifted onto
|
|
187
|
+
// the text's x-height like the capacity badge's glyph.
|
|
188
|
+
const glyph = CAPACITY_ICON_SVG[s.ownerKind === 'person' ? 'person' : 'people'];
|
|
189
|
+
const glyphSize = FRAME_TAB_OWNER_GLYPH_PX;
|
|
190
|
+
parts.push(`<svg x="${num(tab.ownerX)}" y="${num(labelY - glyphSize * 0.85)}" width="${num(glyphSize)}" height="${num(glyphSize)}" viewBox="${glyph.viewBox}" style="color:${escAttr(ownerText)}" aria-hidden="true">${glyph.body}</svg>`);
|
|
191
|
+
parts.push(textTag({
|
|
192
|
+
x: num(tab.ownerTextX),
|
|
193
|
+
y: num(labelY),
|
|
194
|
+
'font-family': fonts[s.style.font],
|
|
195
|
+
'font-size': 10,
|
|
196
|
+
fill: ownerText,
|
|
197
|
+
}, s.owner));
|
|
198
|
+
}
|
|
199
|
+
if (s.capacity) {
|
|
200
|
+
// Re-uses the same `renderCapacitySuffix` helper that paints
|
|
201
|
+
// item-level suffixes (m6) so multiplier / built-in SVG /
|
|
202
|
+
// inline literal / dereferenced-custom-glyph paths stay
|
|
203
|
+
// consistent across both contexts.
|
|
204
|
+
parts.push(renderCapacitySuffix(s.capacity, tab.badgeX, labelY, LANE_BADGE_FONT_SIZE_PX, fonts[s.style.font], ownerText));
|
|
205
|
+
}
|
|
206
|
+
if (footnoteIndicatorText) {
|
|
207
|
+
parts.push(textTag({
|
|
208
|
+
x: num(tab.footnoteRightX),
|
|
209
|
+
y: num(tabY + 14),
|
|
210
|
+
'font-family': fonts.sans,
|
|
211
|
+
'font-size': LANE_BADGE_FONT_SIZE_PX,
|
|
212
|
+
'font-weight': 700,
|
|
213
|
+
fill: footnoteColor,
|
|
214
|
+
'text-anchor': 'end',
|
|
215
|
+
}, footnoteIndicatorText));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const c of s.children) {
|
|
219
|
+
parts.push(renderTrackChild(c, options, idPrefix, palette, fonts));
|
|
220
|
+
}
|
|
221
|
+
// m13: tri-state utilization underline along the band's bottom edge.
|
|
222
|
+
// Painted after items so it overlays any item that happens to extend
|
|
223
|
+
// to the band's bottom; under cut-lines / now-line which run as
|
|
224
|
+
// separate top-level passes.
|
|
225
|
+
parts.push(renderLaneUtilization(s, palette));
|
|
226
|
+
return tag('g', { 'data-layer': 'swimlane', 'data-id': s.id ?? null }, parts.join(''));
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=swimlanes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swimlanes.js","sourceRoot":"","sources":["../../src/svg/swimlanes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BoundingBox, PositionedTimelineScale, Theme } from '@nowline/layout';
|
|
2
|
+
import type { FontFamilies } from './render.js';
|
|
3
|
+
export declare function renderGridLines(t: PositionedTimelineScale, swimlaneTopY: number, palette: Theme): string;
|
|
4
|
+
/**
|
|
5
|
+
* Non-working days under `show` (specs/working-calendar.md §7.3): a faint
|
|
6
|
+
* band over each run layout flagged `band`, on the seam / minor-grid span
|
|
7
|
+
* (from the top of the chart body to the timeline bottom), clipped to
|
|
8
|
+
* `clip` when given (an include region's painted rect). Empty under `hide`,
|
|
9
|
+
* where no run has `band`, so that view emits no bytes.
|
|
10
|
+
*/
|
|
11
|
+
export declare function renderNonWorkingBands(t: PositionedTimelineScale, chartTopY: number, palette: Theme, clip?: BoundingBox): string;
|
|
12
|
+
/**
|
|
13
|
+
* Labels of the titled non-working runs and the working exceptions
|
|
14
|
+
* (specs/working-calendar.md §7.4): each one's text in the marker row,
|
|
15
|
+
* placed by the layout like an anchor label. Under `hide` a titled run
|
|
16
|
+
* also draws a dotted cut line at the seam from the label row's centre to
|
|
17
|
+
* the timeline bottom, since the zero-width seam has no band to name. A
|
|
18
|
+
* working exception is text only in either view: a working day removes no
|
|
19
|
+
* time, so it has no cut. Empty when nothing has a label, so a roadmap
|
|
20
|
+
* without one emits no bytes.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderNonWorkingLabels(t: PositionedTimelineScale, palette: Theme, fonts: FontFamilies): string;
|
|
23
|
+
export declare function renderTimeline(t: PositionedTimelineScale, palette: Theme, fonts: FontFamilies): string;
|
|
24
|
+
//# sourceMappingURL=timeline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../src/svg/timeline.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAOnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD,wBAAgB,eAAe,CAC3B,CAAC,EAAE,uBAAuB,EAC1B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,KAAK,GACf,MAAM,CAkFR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACjC,CAAC,EAAE,uBAAuB,EAC1B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,KAAK,EACd,IAAI,CAAC,EAAE,WAAW,GACnB,MAAM,CA0BR;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAClC,CAAC,EAAE,uBAAuB,EAC1B,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,YAAY,GACpB,MAAM,CAkCR;AAyBD,wBAAgB,cAAc,CAC1B,CAAC,EAAE,uBAAuB,EAC1B,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,YAAY,GACpB,MAAM,CAoHR"}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
// The timeline layer: the tick scale across the top, the vertical grid lines
|
|
2
|
+
// behind the lanes, and the shaded non-working bands with their labels.
|
|
3
|
+
import { NON_WORKING_FILL_OPACITY, TIMELINE_TICK_LABEL_FONT_SIZE_PX, timelineTickLabelBaselineOffsetPx, } from '@nowline/layout';
|
|
4
|
+
import { intersectBox } from './primitives.js';
|
|
5
|
+
import { num, tag, textTag } from './xml.js';
|
|
6
|
+
// Renders the chart-body vertical grid lines (major dotted at every
|
|
7
|
+
// labeled tick, plus optional faint minor lines at every tick when
|
|
8
|
+
// `minorGrid` is set). Emitted as its own layer drawn AFTER the
|
|
9
|
+
// swimlane backgrounds so the lines actually span the chart body
|
|
10
|
+
// instead of being occluded — without this layer the lines would only
|
|
11
|
+
// be visible inside the timeline header strip. Grid lines stay BEHIND
|
|
12
|
+
// items, edges, anchor/milestone cuts, and the now-line so item bars
|
|
13
|
+
// sit cleanly on top of the ruled-paper backdrop.
|
|
14
|
+
export function renderGridLines(t, swimlaneTopY, palette) {
|
|
15
|
+
const gridColor = palette.timeline.gridLine;
|
|
16
|
+
const minorGridColor = palette.timeline.minorGridLine;
|
|
17
|
+
// Major lines thread through the FULL timeline strip — they start
|
|
18
|
+
// at the top of the top date-label panel (when present) and run
|
|
19
|
+
// all the way through the bottom date-label panel (when one is
|
|
20
|
+
// mirrored at the chart bottom via `timeline-position:both` or
|
|
21
|
+
// `timeline-position:bottom`). This ties date labels at both ends
|
|
22
|
+
// to their column boundaries.
|
|
23
|
+
//
|
|
24
|
+
// Minor lines stay quieter: they start at the TOP OF THE TOPMOST
|
|
25
|
+
// SWIMLANE (i.e. below the top date panel AND below the marker
|
|
26
|
+
// row, so they don't streak through anchor/milestone diamonds in
|
|
27
|
+
// the header) and stop ABOVE the bottom date panel.
|
|
28
|
+
//
|
|
29
|
+
// Anchor diamonds, milestone markers, and date label text are
|
|
30
|
+
// rendered later in the orchestrator so they sit on top of any
|
|
31
|
+
// crossing line.
|
|
32
|
+
const bottomTickPanelHeight = t.bottomTickPanelHeight ?? 0;
|
|
33
|
+
const hasBottomTickPanel = t.bottomTickPanelY !== undefined && bottomTickPanelHeight > 0;
|
|
34
|
+
const majorTopY = t.tickPanelY;
|
|
35
|
+
const majorBottomY = hasBottomTickPanel
|
|
36
|
+
? t.bottomTickPanelY + bottomTickPanelHeight
|
|
37
|
+
: t.box.y + t.box.height;
|
|
38
|
+
// Use the topmost swimlane's top edge directly — `markerRow.height`
|
|
39
|
+
// alone misses the small gap (`timelineHeightBudget` slack) between
|
|
40
|
+
// the marker row and the swimlane area, which would leave the minor
|
|
41
|
+
// lines short and floating in dead space above the swimlane.
|
|
42
|
+
const minorTopY = swimlaneTopY;
|
|
43
|
+
const minorBottomY = t.box.y + t.box.height;
|
|
44
|
+
const parts = [];
|
|
45
|
+
for (const tick of t.ticks) {
|
|
46
|
+
if (tick.major) {
|
|
47
|
+
// Solid major line at every labeled tick — the dominant
|
|
48
|
+
// column boundary, drawn in the stronger gridLine color.
|
|
49
|
+
parts.push(tag('line', {
|
|
50
|
+
x1: num(tick.x),
|
|
51
|
+
y1: num(majorTopY),
|
|
52
|
+
x2: num(tick.x),
|
|
53
|
+
y2: num(majorBottomY),
|
|
54
|
+
stroke: gridColor,
|
|
55
|
+
'stroke-width': 1,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
else if (t.minorGrid) {
|
|
59
|
+
// Solid faint minor line at every non-major tick boundary.
|
|
60
|
+
// Hierarchy is established by color (lighter than the major)
|
|
61
|
+
// rather than by texture. Skip the very last tick since it
|
|
62
|
+
// has no following column — a line at the chart's right edge
|
|
63
|
+
// just doubles up the chart border.
|
|
64
|
+
if (tick.labelX === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
parts.push(tag('line', {
|
|
67
|
+
x1: num(tick.x),
|
|
68
|
+
y1: num(minorTopY),
|
|
69
|
+
x2: num(tick.x),
|
|
70
|
+
y2: num(minorBottomY),
|
|
71
|
+
stroke: minorGridColor,
|
|
72
|
+
'stroke-width': 1,
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Seams where hidden non-working days collapse and no grid line
|
|
77
|
+
// already marks the join (specs/working-calendar.md §7.2): faint and
|
|
78
|
+
// dotted, on the minor line's span. Layout marks which runs get one.
|
|
79
|
+
for (const run of t.nonWorking ?? []) {
|
|
80
|
+
if (!run.seam)
|
|
81
|
+
continue;
|
|
82
|
+
parts.push(tag('line', {
|
|
83
|
+
x1: num(run.x),
|
|
84
|
+
y1: num(minorTopY),
|
|
85
|
+
x2: num(run.x),
|
|
86
|
+
y2: num(minorBottomY),
|
|
87
|
+
stroke: palette.timeline.nonWorkingSeam,
|
|
88
|
+
'stroke-width': 1,
|
|
89
|
+
'stroke-dasharray': '1 3',
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
return tag('g', { 'data-layer': 'grid' }, parts.join(''));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Non-working days under `show` (specs/working-calendar.md §7.3): a faint
|
|
96
|
+
* band over each run layout flagged `band`, on the seam / minor-grid span
|
|
97
|
+
* (from the top of the chart body to the timeline bottom), clipped to
|
|
98
|
+
* `clip` when given (an include region's painted rect). Empty under `hide`,
|
|
99
|
+
* where no run has `band`, so that view emits no bytes.
|
|
100
|
+
*/
|
|
101
|
+
export function renderNonWorkingBands(t, chartTopY, palette, clip) {
|
|
102
|
+
const bottomY = t.box.y + t.box.height;
|
|
103
|
+
const parts = [];
|
|
104
|
+
for (const run of t.nonWorking ?? []) {
|
|
105
|
+
if (!run.band)
|
|
106
|
+
continue;
|
|
107
|
+
const span = {
|
|
108
|
+
x: run.x,
|
|
109
|
+
y: chartTopY,
|
|
110
|
+
width: run.width,
|
|
111
|
+
height: bottomY - chartTopY,
|
|
112
|
+
};
|
|
113
|
+
const box = clip ? intersectBox(span, clip) : span;
|
|
114
|
+
if (!box || box.width <= 0 || box.height <= 0)
|
|
115
|
+
continue;
|
|
116
|
+
parts.push(tag('rect', {
|
|
117
|
+
x: num(box.x),
|
|
118
|
+
y: num(box.y),
|
|
119
|
+
width: num(box.width),
|
|
120
|
+
height: num(box.height),
|
|
121
|
+
fill: palette.timeline.nonWorkingFill,
|
|
122
|
+
'fill-opacity': NON_WORKING_FILL_OPACITY,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
if (parts.length === 0)
|
|
126
|
+
return '';
|
|
127
|
+
return tag('g', { 'data-layer': 'non-working' }, parts.join(''));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Labels of the titled non-working runs and the working exceptions
|
|
131
|
+
* (specs/working-calendar.md §7.4): each one's text in the marker row,
|
|
132
|
+
* placed by the layout like an anchor label. Under `hide` a titled run
|
|
133
|
+
* also draws a dotted cut line at the seam from the label row's centre to
|
|
134
|
+
* the timeline bottom, since the zero-width seam has no band to name. A
|
|
135
|
+
* working exception is text only in either view: a working day removes no
|
|
136
|
+
* time, so it has no cut. Empty when nothing has a label, so a roadmap
|
|
137
|
+
* without one emits no bytes.
|
|
138
|
+
*/
|
|
139
|
+
export function renderNonWorkingLabels(t, palette, fonts) {
|
|
140
|
+
const bottomY = t.box.y + t.box.height;
|
|
141
|
+
const parts = [];
|
|
142
|
+
for (const run of t.nonWorking ?? []) {
|
|
143
|
+
if (run.label === undefined || !run.labelBox)
|
|
144
|
+
continue;
|
|
145
|
+
if (run.cut) {
|
|
146
|
+
parts.push(tag('line', {
|
|
147
|
+
x1: num(run.x),
|
|
148
|
+
y1: num(run.labelBox.y + 4),
|
|
149
|
+
x2: num(run.x),
|
|
150
|
+
y2: num(bottomY),
|
|
151
|
+
stroke: palette.timeline.nonWorkingSeam,
|
|
152
|
+
'stroke-width': 1,
|
|
153
|
+
'stroke-dasharray': '1 3',
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
parts.push(calendarLabelText(run.label, run.labelBox, run.labelSide, palette, fonts));
|
|
157
|
+
}
|
|
158
|
+
for (const exception of t.workingExceptions ?? []) {
|
|
159
|
+
if (!exception.labelBox)
|
|
160
|
+
continue;
|
|
161
|
+
parts.push(calendarLabelText(exception.label, exception.labelBox, exception.labelSide, palette, fonts));
|
|
162
|
+
}
|
|
163
|
+
if (parts.length === 0)
|
|
164
|
+
return '';
|
|
165
|
+
return tag('g', { 'data-layer': 'non-working-label' }, parts.join(''));
|
|
166
|
+
}
|
|
167
|
+
/** One calendar label's text in the marker row. */
|
|
168
|
+
function calendarLabelText(label, box, side, palette, fonts) {
|
|
169
|
+
// Left-side labels end-anchor at the box's right edge, as anchor
|
|
170
|
+
// labels do: the box width is a pessimistic estimate.
|
|
171
|
+
const isLeft = side === 'left';
|
|
172
|
+
const centerY = box.y + 4;
|
|
173
|
+
const labelAttrs = {
|
|
174
|
+
x: num(isLeft ? box.x + box.width : box.x),
|
|
175
|
+
y: num(centerY + 4),
|
|
176
|
+
'font-family': fonts.sans,
|
|
177
|
+
'font-size': 10,
|
|
178
|
+
fill: palette.timeline.labelText,
|
|
179
|
+
};
|
|
180
|
+
if (isLeft)
|
|
181
|
+
labelAttrs['text-anchor'] = 'end';
|
|
182
|
+
return textTag(labelAttrs, label);
|
|
183
|
+
}
|
|
184
|
+
export function renderTimeline(t, palette, fonts) {
|
|
185
|
+
const panelFill = palette.timeline.panelFill;
|
|
186
|
+
const borderColor = palette.timeline.border;
|
|
187
|
+
const labelColor = palette.timeline.labelText;
|
|
188
|
+
const parts = [];
|
|
189
|
+
// Header layout from top: now-pill row → tick-label panel → (wave
|
|
190
|
+
// strip) → marker row.
|
|
191
|
+
// The pill row owns its space (no panel rect); the now-line crosses it
|
|
192
|
+
// visually. Marker row is omitted entirely when empty. The top tick
|
|
193
|
+
// panel is also omitted when the roadmap requested
|
|
194
|
+
// `timeline-position:bottom` (height 0). The optional bottom tick
|
|
195
|
+
// panel mirrors the top panel directly above the footnote area.
|
|
196
|
+
const tickPanelY = t.tickPanelY;
|
|
197
|
+
const tickPanelHeight = t.tickPanelHeight;
|
|
198
|
+
const hasTopTickPanel = tickPanelHeight > 0;
|
|
199
|
+
const hasMarkerRow = t.markerRow.height > 0;
|
|
200
|
+
// The wave strip, when there is one, sits between the tick panel and
|
|
201
|
+
// the marker rows (specs/waves.md §8.8); its panel is the wave-strip
|
|
202
|
+
// layer's.
|
|
203
|
+
const markerRowY = t.waveStrip
|
|
204
|
+
? t.waveStrip.y + t.waveStrip.height
|
|
205
|
+
: tickPanelY + tickPanelHeight;
|
|
206
|
+
const markerRowHeight = t.markerRow.height;
|
|
207
|
+
const bottomTickPanelY = t.bottomTickPanelY;
|
|
208
|
+
const bottomTickPanelHeight = t.bottomTickPanelHeight ?? 0;
|
|
209
|
+
const hasBottomTickPanel = bottomTickPanelY !== undefined && bottomTickPanelHeight > 0;
|
|
210
|
+
if (hasTopTickPanel) {
|
|
211
|
+
parts.push(tag('rect', {
|
|
212
|
+
x: num(t.box.x),
|
|
213
|
+
y: num(tickPanelY),
|
|
214
|
+
width: num(t.box.width),
|
|
215
|
+
height: num(tickPanelHeight),
|
|
216
|
+
rx: 4,
|
|
217
|
+
ry: 4,
|
|
218
|
+
fill: panelFill,
|
|
219
|
+
stroke: borderColor,
|
|
220
|
+
'stroke-width': 1,
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
if (hasMarkerRow) {
|
|
224
|
+
parts.push(tag('rect', {
|
|
225
|
+
x: num(t.box.x),
|
|
226
|
+
y: num(markerRowY),
|
|
227
|
+
width: num(t.box.width),
|
|
228
|
+
height: num(markerRowHeight),
|
|
229
|
+
rx: 4,
|
|
230
|
+
ry: 4,
|
|
231
|
+
fill: panelFill,
|
|
232
|
+
stroke: borderColor,
|
|
233
|
+
'stroke-width': 1,
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
if (hasBottomTickPanel) {
|
|
237
|
+
parts.push(tag('rect', {
|
|
238
|
+
x: num(t.box.x),
|
|
239
|
+
y: num(bottomTickPanelY),
|
|
240
|
+
width: num(t.box.width),
|
|
241
|
+
height: num(bottomTickPanelHeight),
|
|
242
|
+
rx: 4,
|
|
243
|
+
ry: 4,
|
|
244
|
+
fill: panelFill,
|
|
245
|
+
stroke: borderColor,
|
|
246
|
+
'stroke-width': 1,
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
// Header-only labels — the chart-body grid lines themselves are
|
|
250
|
+
// emitted by `renderGridLines` after the swimlane backgrounds so
|
|
251
|
+
// they actually span the chart body rather than being occluded.
|
|
252
|
+
for (const tick of t.ticks) {
|
|
253
|
+
if (!tick.major)
|
|
254
|
+
continue;
|
|
255
|
+
if (!tick.label || tick.labelX === undefined)
|
|
256
|
+
continue;
|
|
257
|
+
// Label sits at the COLUMN CENTER (tick.labelX), not at the
|
|
258
|
+
// tick boundary. The last tick has no following column → no
|
|
259
|
+
// label.
|
|
260
|
+
if (hasTopTickPanel) {
|
|
261
|
+
parts.push(textTag({
|
|
262
|
+
x: num(tick.labelX),
|
|
263
|
+
y: num(tickPanelY + timelineTickLabelBaselineOffsetPx(tickPanelHeight)),
|
|
264
|
+
'font-family': fonts.sans,
|
|
265
|
+
'font-size': TIMELINE_TICK_LABEL_FONT_SIZE_PX,
|
|
266
|
+
fill: labelColor,
|
|
267
|
+
'text-anchor': 'middle',
|
|
268
|
+
}, tick.label));
|
|
269
|
+
}
|
|
270
|
+
if (hasBottomTickPanel) {
|
|
271
|
+
parts.push(textTag({
|
|
272
|
+
x: num(tick.labelX),
|
|
273
|
+
y: num(bottomTickPanelY +
|
|
274
|
+
timelineTickLabelBaselineOffsetPx(bottomTickPanelHeight)),
|
|
275
|
+
'font-family': fonts.sans,
|
|
276
|
+
'font-size': TIMELINE_TICK_LABEL_FONT_SIZE_PX,
|
|
277
|
+
fill: labelColor,
|
|
278
|
+
'text-anchor': 'middle',
|
|
279
|
+
}, tick.label));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return tag('g', { 'data-layer': 'timeline' }, parts.join(''));
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=timeline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../src/svg/timeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { BoundingBox, PositionedRoadmap, PositionedWave, PositionedWaveBoundary, PositionedWaveCrossing, PositionedWaveLegend, ResolvedStyle, Theme, WaveHatchKind } from '@nowline/layout';
|
|
2
|
+
import type { FontFamilies } from './render.js';
|
|
3
|
+
/**
|
|
4
|
+
* A hatch `<pattern>`: a `WAVE_HATCH_TILE_PX` tile rotated 45°, one
|
|
5
|
+
* `WAVE_HATCH_STROKE_PX` stroke down its middle (so the tile never clips
|
|
6
|
+
* it) at `WAVE_HATCH_OPACITY`. The id carries the SVG's prefix, like the
|
|
7
|
+
* shadow filters, so two SVGs on one page never share it.
|
|
8
|
+
*
|
|
9
|
+
* The line carries `opacity`, not `stroke-opacity`. Browsers and resvg draw
|
|
10
|
+
* the two the same (one stroke, nothing overlaps), but svg-to-pdfkit turns
|
|
11
|
+
* `stroke-opacity` into a stroke-only `CA` inside the pattern cell, which
|
|
12
|
+
* poppler ignores, so the PDF hatch drew opaque. With `opacity` it sets the
|
|
13
|
+
* fill alpha `ca` as well, which poppler and cairo both honour.
|
|
14
|
+
*/
|
|
15
|
+
export declare function waveHatchPatternDef(idPrefix: string, kind: WaveHatchKind, palette: Theme): string;
|
|
16
|
+
/**
|
|
17
|
+
* The hatch patterns the SVG uses, in a fixed order: one per kind that a
|
|
18
|
+
* background bar (main lanes or isolated regions) or the legend's hatch
|
|
19
|
+
* swatch needs, as layout resolved them (`waveHatch`, `hatch`). Empty
|
|
20
|
+
* without waves.
|
|
21
|
+
*/
|
|
22
|
+
export declare function usedWaveHatchKinds(model: PositionedRoadmap): WaveHatchKind[];
|
|
23
|
+
/**
|
|
24
|
+
* The hatch overlay over a background bar: the bar's rect inset by half its
|
|
25
|
+
* 1 px stroke, with the same corner radius, so it never covers the stroke.
|
|
26
|
+
* `kind` is the bar's hatch kind as layout resolved it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hatchOverlay(box: BoundingBox, style: ResolvedStyle, kind: WaveHatchKind, idPrefix: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* `wave-strip` (§9.1): the backing panel and one cell per non-empty wave,
|
|
31
|
+
* each with its tooltip. Drawn right after the timeline panels, so the
|
|
32
|
+
* major grid lines cross the cells as they cross the tick panel.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderWaveStrip(model: PositionedRoadmap, palette: Theme): string;
|
|
35
|
+
/**
|
|
36
|
+
* `wave-bg` (§9.4): a column tint over each styled wave's `columnBox`,
|
|
37
|
+
* clipped to `clip` when given (an include region's painted rect).
|
|
38
|
+
*/
|
|
39
|
+
export declare function renderWaveTints(waves: PositionedWave[], clip?: BoundingBox): string;
|
|
40
|
+
/**
|
|
41
|
+
* `wave-boundary` (§9.2): the 2 px boundary lines, from the strip top to the
|
|
42
|
+
* last lane. With `clip`, only the lines strictly inside it, cut to its
|
|
43
|
+
* vertical extent.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderWaveBoundaries(boundaries: PositionedWaveBoundary[] | undefined, clip?: BoundingBox): string;
|
|
46
|
+
/**
|
|
47
|
+
* `wave-labels` (§9.1): strip labels on halos in their cell's fill,
|
|
48
|
+
* right-aligned footnote superscripts, empty-wave diamonds, gap floor
|
|
49
|
+
* labels, and the all-empty placeholder. Drawn after the boundaries.
|
|
50
|
+
*/
|
|
51
|
+
export declare function renderWaveLabels(model: PositionedRoadmap, palette: Theme, fonts: FontFamilies): string;
|
|
52
|
+
/**
|
|
53
|
+
* `wave-cross` (§9.3): a 1 px dashed boundary over each background bar a
|
|
54
|
+
* boundary runs through, showing the barrier does not hold that work.
|
|
55
|
+
*/
|
|
56
|
+
export declare function renderWaveCrossings(crossings: PositionedWaveCrossing[] | undefined): string;
|
|
57
|
+
/**
|
|
58
|
+
* `wave-legend` (§9.5): the hatched bar swatch for background work and its
|
|
59
|
+
* text, as the layout placed them.
|
|
60
|
+
*/
|
|
61
|
+
export declare function renderWaveLegend(legend: PositionedWaveLegend | undefined, idPrefix: string, palette: Theme, fonts: FontFamilies): string;
|
|
62
|
+
//# sourceMappingURL=waves.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waves.d.ts","sourceRoot":"","sources":["../../src/svg/waves.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACR,WAAW,EACX,iBAAiB,EAIjB,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,aAAa,EACb,KAAK,EACL,aAAa,EAChB,MAAM,iBAAiB,CAAC;AAgBzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmBhD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,GAAG,MAAM,CAqBjG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,aAAa,EAAE,CAwB5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CACxB,GAAG,EAAE,WAAW,EAChB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,aAAa,EACnB,QAAQ,EAAE,MAAM,GACjB,MAAM,CAYR;AAyED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,GAAG,MAAM,CA+BhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAmBnF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAChC,UAAU,EAAE,sBAAsB,EAAE,GAAG,SAAS,EAChD,IAAI,CAAC,EAAE,WAAW,GACnB,MAAM,CAyBR;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,iBAAiB,EACxB,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,YAAY,GACpB,MAAM,CA6FR;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,sBAAsB,EAAE,GAAG,SAAS,GAAG,MAAM,CAc3F;AAQD;;;GAGG;AACH,wBAAgB,gBAAgB,CAC5B,MAAM,EAAE,oBAAoB,GAAG,SAAS,EACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,YAAY,GACpB,MAAM,CAiDR"}
|