@tangle-network/charts 0.7.0 → 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.
@@ -1,491 +0,0 @@
1
- //#region src/text.ts
2
- /**
3
- * Text: escaping, measurement and wrapping.
4
- *
5
- * Layout runs with no DOM (a Worker, a build step), so text width comes from
6
- * Inter's advance widths, the face tangle.tools and the reports load. Numbers
7
- * render in a monospace face at 0.6 em per glyph. Widths carry a 4% margin so a
8
- * fallback face does not collide with its neighbour.
9
- */
10
- /** Every dynamic string in markup passes through here. */
11
- function esc(value) {
12
- return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
13
- }
14
- const REGULAR = [
15
- 281,
16
- 288,
17
- 466,
18
- 633,
19
- 642,
20
- 982,
21
- 644,
22
- 300,
23
- 365,
24
- 365,
25
- 501,
26
- 662,
27
- 288,
28
- 460,
29
- 288,
30
- 360,
31
- 631,
32
- 407,
33
- 610,
34
- 618,
35
- 646,
36
- 608,
37
- 620,
38
- 566,
39
- 619,
40
- 620,
41
- 288,
42
- 302,
43
- 662,
44
- 662,
45
- 662,
46
- 511,
47
- 966,
48
- 690,
49
- 654,
50
- 730,
51
- 722,
52
- 601,
53
- 590,
54
- 746,
55
- 743,
56
- 269,
57
- 571,
58
- 672,
59
- 565,
60
- 903,
61
- 753,
62
- 765,
63
- 639,
64
- 765,
65
- 644,
66
- 642,
67
- 646,
68
- 744,
69
- 690,
70
- 985,
71
- 682,
72
- 679,
73
- 629,
74
- 365,
75
- 360,
76
- 365,
77
- 471,
78
- 456,
79
- 323,
80
- 562,
81
- 612,
82
- 571,
83
- 612,
84
- 583,
85
- 370,
86
- 613,
87
- 591,
88
- 242,
89
- 242,
90
- 549,
91
- 242,
92
- 876,
93
- 591,
94
- 600,
95
- 612,
96
- 612,
97
- 376,
98
- 528,
99
- 327,
100
- 591,
101
- 562,
102
- 818,
103
- 546,
104
- 562,
105
- 552,
106
- 426,
107
- 333,
108
- 426,
109
- 662
110
- ];
111
- const SEMIBOLD = [
112
- 252,
113
- 321,
114
- 523,
115
- 644,
116
- 650,
117
- 1004,
118
- 663,
119
- 326,
120
- 373,
121
- 373,
122
- 540,
123
- 673,
124
- 319,
125
- 465,
126
- 319,
127
- 379,
128
- 660,
129
- 423,
130
- 623,
131
- 636,
132
- 666,
133
- 629,
134
- 640,
135
- 576,
136
- 640,
137
- 640,
138
- 319,
139
- 329,
140
- 673,
141
- 673,
142
- 673,
143
- 543,
144
- 999,
145
- 728,
146
- 659,
147
- 737,
148
- 722,
149
- 605,
150
- 588,
151
- 749,
152
- 746,
153
- 277,
154
- 580,
155
- 703,
156
- 565,
157
- 922,
158
- 759,
159
- 769,
160
- 645,
161
- 773,
162
- 652,
163
- 650,
164
- 660,
165
- 736,
166
- 728,
167
- 1020,
168
- 720,
169
- 713,
170
- 652,
171
- 373,
172
- 379,
173
- 373,
174
- 481,
175
- 469,
176
- 351,
177
- 574,
178
- 624,
179
- 583,
180
- 624,
181
- 591,
182
- 389,
183
- 625,
184
- 612,
185
- 262,
186
- 262,
187
- 569,
188
- 262,
189
- 900,
190
- 612,
191
- 609,
192
- 624,
193
- 624,
194
- 397,
195
- 549,
196
- 353,
197
- 612,
198
- 587,
199
- 839,
200
- 569,
201
- 588,
202
- 566,
203
- 455,
204
- 359,
205
- 455,
206
- 673
207
- ];
208
- const EXTRA = {
209
- "·": 300,
210
- "×": 670,
211
- "—": 1e3,
212
- "–": 500,
213
- "…": 900,
214
- "≥": 670,
215
- "≤": 670,
216
- "±": 670,
217
- "→": 954,
218
- "−": 662
219
- };
220
- const MARGIN = 1.04;
221
- function textWidth(text, size, face = "sans") {
222
- if (face === "mono") return text.length * .6 * size * MARGIN;
223
- const table = face === "sans-semibold" ? SEMIBOLD : REGULAR;
224
- let units = 0;
225
- for (const ch of text) {
226
- const code = ch.codePointAt(0) ?? 0;
227
- units += code >= 32 && code <= 126 ? table[code - 32] : EXTRA[ch] ?? 620;
228
- }
229
- return units / 1e3 * size * MARGIN;
230
- }
231
- /**
232
- * Breaks `text` into at most `maxLines` lines no wider than `width`. Breaks at
233
- * spaces, and after `-`, `/`, `_` or `·` when a word alone is too wide. A line
234
- * that still overflows the last allowed line ends with an ellipsis; callers put
235
- * the full text in a `<title>` and in the table.
236
- */
237
- function wrap(text, width, size, face = "sans", maxLines = 2) {
238
- const fits = (s) => textWidth(s, size, face) <= width;
239
- const lines = [];
240
- const glue = [];
241
- let line = "";
242
- const breakLine = (next, joiner) => {
243
- lines.push(line);
244
- glue.push(joiner);
245
- line = next;
246
- };
247
- for (const word of text.split(/\s+/).filter(Boolean)) {
248
- const joined = line ? `${line} ${word}` : word;
249
- if (fits(joined)) line = joined;
250
- else if (fits(word)) {
251
- if (line) breakLine(word, " ");
252
- else line = word;
253
- } else {
254
- const pieces = word.match(/[^\-/_·]+[\-/_·]?|[\-/_·]/g) ?? [word];
255
- for (const [index, piece] of pieces.entries()) {
256
- const joiner = index === 0 ? " " : "";
257
- const candidate = line ? `${line}${joiner}${piece}` : piece;
258
- if (fits(candidate) || !line) line = candidate;
259
- else breakLine(piece, joiner);
260
- }
261
- }
262
- }
263
- if (line) lines.push(line);
264
- if (lines.length <= maxLines && lines.every(fits)) return {
265
- lines,
266
- truncated: false
267
- };
268
- const kept = lines.slice(0, maxLines);
269
- let last = lines.slice(maxLines - 1).reduce((acc, l, i) => i === 0 ? l : `${acc}${glue[maxLines - 2 + i]}${l}`, "");
270
- while (last.length > 1 && !fits(`${last}…`)) last = last.slice(0, -1);
271
- kept[maxLines - 1] = `${last.trimEnd()}…`;
272
- return {
273
- lines: kept,
274
- truncated: true
275
- };
276
- }
277
- /** One `<text>` element with a line per `<tspan>`. */
278
- function textBlock(lines, x, y, lineHeight, attrs) {
279
- if (lines.length === 1) return `<text x="${x}" y="${y}" ${attrs}>${esc(lines[0])}</text>`;
280
- return `<text x="${x}" y="${y}" ${attrs}>${lines.map((line, i) => `<tspan x="${x}" dy="${i === 0 ? 0 : lineHeight}">${esc(line)}</tspan>`).join("")}</text>`;
281
- }
282
- /** Joins `parts` with " · ", starting a new line wherever the next part would pass `width`. */
283
- function fitLines(parts, width, size, face = "sans") {
284
- const lines = [];
285
- for (const part of parts) {
286
- const last = lines[lines.length - 1];
287
- if (last !== void 0 && textWidth(`${last} · ${part}`, size, face) <= width) lines[lines.length - 1] = `${last} · ${part}`;
288
- else lines.push(part);
289
- }
290
- return lines;
291
- }
292
- function plural(count, one, many = `${one}s`) {
293
- return count === 1 ? one : many;
294
- }
295
- /** "a, b and c" */
296
- function listJoin(items) {
297
- if (items.length <= 1) return items.join("");
298
- return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
299
- }
300
- function capitalize(text) {
301
- return text.charAt(0).toUpperCase() + text.slice(1);
302
- }
303
- //#endregion
304
- //#region src/svg.ts
305
- /**
306
- * SVG marks. Every mark carries a class and `figure.css` maps classes to the
307
- * `--chart-*` tokens, so a render holds no colour and follows the page theme.
308
- * Geometry stays in attributes, so an SVG shown without the stylesheet keeps
309
- * its layout.
310
- */
311
- const r2 = (n) => Math.round(n * 100) / 100;
312
- function svgRoot(width, height, inner) {
313
- const h = Math.ceil(height);
314
- return `<svg class="tgc-svg" xmlns="http://www.w3.org/2000/svg" width="${width}" height="${h}" viewBox="0 0 ${width} ${h}" aria-hidden="true" focusable="false">${inner}</svg>`;
315
- }
316
- /** A horizontal bar anchored at `x`, square at the baseline, 4 px round at the data end. */
317
- function hbar(x, y, w, h, cls) {
318
- if (w <= 0) return "";
319
- const r = Math.min(4, h / 2, w);
320
- return `<path class="${cls}" d="M${r2(x)} ${r2(y)}h${r2(w - r)}a${r} ${r} 0 0 1 ${r} ${r}v${r2(h - 2 * r)}a${r} ${r} 0 0 1 -${r} ${r}h-${r2(w - r)}z"/>`;
321
- }
322
- function rect(x, y, w, h, cls, extra = "") {
323
- if (w <= 0 || h <= 0) return "";
324
- return `<rect class="${cls}" x="${r2(x)}" y="${r2(y)}" width="${r2(w)}" height="${r2(h)}"${extra ? ` ${extra}` : ""}/>`;
325
- }
326
- /**
327
- * 45° hatch lines clipped to a rectangle, as one path `d`. Pure geometry with
328
- * no pattern id, so a hatch survives duplicate SVGs on one page and hidden
329
- * wide/narrow renders. Shared with the React renderers.
330
- */
331
- function hatchPath(x, y, w, h, step = 6) {
332
- if (!(w > 0) || !(h > 0)) return "";
333
- let d = "";
334
- for (let k = step / 2; k < w + h; k += step) {
335
- const x0 = Math.max(0, k - h), x1 = Math.min(w, k);
336
- d += `M${r2(x + x0)} ${r2(y + k - x0)}L${r2(x + x1)} ${r2(y + k - x1)}`;
337
- }
338
- return d;
339
- }
340
- /** A hatched rectangle: a neutral wash plus hatch lines. Missing data is hatched, never drawn as zero. */
341
- function hatch(x, y, w, h, cls) {
342
- if (w <= 0 || h <= 0) return "";
343
- return `<g class="${cls}" data-hatched="true">${rect(x, y, w, h, "tgc-hatch-bg")}<path class="tgc-hatch" d="${hatchPath(x, y, w, h)}"/></g>`;
344
- }
345
- function line(x1, y1, x2, y2, cls) {
346
- return `<line class="${cls}" x1="${r2(x1)}" y1="${r2(y1)}" x2="${r2(x2)}" y2="${r2(y2)}"/>`;
347
- }
348
- function circle(cx, cy, r, cls) {
349
- return `<circle class="${cls}" cx="${r2(cx)}" cy="${r2(cy)}" r="${r}"/>`;
350
- }
351
- const MARKER_SHAPES = [
352
- "circle",
353
- "square",
354
- "triangle",
355
- "diamond"
356
- ];
357
- /** A marker outline as a path `d`, centred on (cx, cy) with radius-like size `r`. Shared with the React renderers. */
358
- function markerPath(shape, cx, cy, r) {
359
- const [x, y] = [r2(cx), r2(cy)];
360
- if (shape === "square") {
361
- const s = r2(r * .9);
362
- return `M${r2(x - s)} ${r2(y - s)}h${r2(2 * s)}v${r2(2 * s)}h${r2(-2 * s)}z`;
363
- }
364
- if (shape === "triangle") {
365
- const s = r2(r * 1.2);
366
- return `M${x} ${r2(y - s)}L${r2(x + s)} ${r2(y + s * .8)}L${r2(x - s)} ${r2(y + s * .8)}z`;
367
- }
368
- if (shape === "diamond") {
369
- const s = r2(r * 1.3);
370
- return `M${x} ${r2(y - s)}L${r2(x + s)} ${y}L${x} ${r2(y + s)}L${r2(x - s)} ${y}z`;
371
- }
372
- return `M${r2(x - r)} ${y}a${r} ${r} 0 1 0 ${r2(2 * r)} 0a${r} ${r} 0 1 0 ${r2(-2 * r)} 0`;
373
- }
374
- function polyline(points, cls) {
375
- return `<polyline class="${cls}" points="${points.map(([x, y]) => `${r2(x)},${r2(y)}`).join(" ")}"/>`;
376
- }
377
- function text(x, y, value, o = {}) {
378
- const anchor = o.anchor && o.anchor !== "start" ? ` text-anchor="${o.anchor}"` : "";
379
- const weight = o.weight === 600 ? ` font-weight="600"` : "";
380
- return `<text class="${o.cls ?? "tgc-ink"}" x="${r2(x)}" y="${r2(y)}" font-size="${o.size ?? 12}"${anchor}${weight}>${esc(value)}</text>`;
381
- }
382
- /** A group whose `<title>` is the hover text for everything inside it. */
383
- function titled(title, inner) {
384
- return `<g><title>${esc(title)}</title>${inner}</g>`;
385
- }
386
- //#endregion
387
- //#region src/table.ts
388
- /** A plain HTML table: the accessible form of a figure and its phone view. */
389
- function table(caption, columns, rows) {
390
- const head = columns.map((c) => `<th scope="col"${c.numeric ? " class=\"tgc-n\"" : ""}>${esc(c.label)}</th>`).join("");
391
- const body = rows.map((row) => `<tr>${row.map((cell, i) => i === 0 ? `<th scope="row">${esc(cell)}</th>` : `<td${columns[i]?.numeric ? " class=\"tgc-n\"" : ""}>${esc(cell)}</td>`).join("")}</tr>`).join("");
392
- return `<table class="tgc-table"><caption>${esc(caption)}</caption><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>`;
393
- }
394
- //#endregion
395
- //#region src/types.ts
396
- function isRefusal(value) {
397
- return "refused" in value;
398
- }
399
- //#endregion
400
- //#region src/events.ts
401
- /**
402
- * Recorded events and spans drawn over a continuous x axis: vertical marks
403
- * (first pass, deadline, death) and shaded bands (time after the first pass,
404
- * idle time, no data). The producer records every position; the library never
405
- * places an event.
406
- */
407
- const MARK_KINDS = [
408
- "pass",
409
- "deadline",
410
- "died",
411
- "event"
412
- ];
413
- const BAND_KINDS = [
414
- "after-pass",
415
- "idle",
416
- "missing"
417
- ];
418
- /** Why the marks or bands cannot be drawn on `domain` (null when they can). Omit `domain` to check shape only. */
419
- function eventsGap(marks = [], bands = [], domain) {
420
- const inside = (value) => !domain || value >= domain[0] && value <= domain[1];
421
- for (const mark of marks) {
422
- if (!mark || typeof mark.label !== "string" || !mark.label.trim()) return "Every mark needs a label.";
423
- if (!MARK_KINDS.includes(mark.kind)) return `${mark.label}: unknown mark kind.`;
424
- if (!Number.isFinite(mark.at) || !inside(mark.at)) return `${mark.label}: mark position must be finite and inside the axis domain.`;
425
- }
426
- for (const band of bands) {
427
- if (!band || typeof band.label !== "string" || !band.label.trim()) return "Every band needs a label.";
428
- if (!BAND_KINDS.includes(band.kind)) return `${band.label}: unknown band kind.`;
429
- if (!Number.isFinite(band.from) || !Number.isFinite(band.to) || band.from >= band.to || !inside(band.from) || !inside(band.to)) return `${band.label}: a band needs finite, increasing bounds inside the axis domain.`;
430
- }
431
- return null;
432
- }
433
- /** Bands sit under the data; a missing band is hatched over a neutral wash. */
434
- function drawBands(bands, px, top, bottom, format) {
435
- return bands.map((band) => {
436
- const x = px(band.from), w = px(band.to) - x, cls = `tgc-band tgc-band-${band.kind}`;
437
- const mark = band.kind === "missing" ? hatch(x, top, w, bottom - top, cls) : rect(x, top, w, bottom - top, cls);
438
- return titled(`${band.label}: ${format(band.from)} to ${format(band.to)}`, `<g data-band="${band.kind}">${mark}</g>`);
439
- }).join("");
440
- }
441
- /** Marks sit over the data; labels stagger in three rows so adjacent events stay readable. */
442
- function drawMarks(marks, px, top, bottom, left, right, format) {
443
- return [...marks].sort((a, b) => a.at - b.at).map((mark, i) => {
444
- const x = px(mark.at), y = top + 12 + i % 3 * 14;
445
- const flip = x > left + (right - left) * .62;
446
- let glyph = line(x, top, x, bottom, `tgc-mark tgc-mark-${mark.kind}`);
447
- if (mark.kind === "died") glyph += line(x - 4, bottom - 8, x + 4, bottom, "tgc-mark tgc-mark-died") + line(x - 4, bottom, x + 4, bottom - 8, "tgc-mark tgc-mark-died");
448
- glyph += text(flip ? x - 4 : x + 4, y, mark.label, {
449
- size: 11,
450
- anchor: flip ? "end" : "start",
451
- cls: "tgc-mark-label"
452
- });
453
- return titled(`${mark.label}: ${format(mark.at)}`, `<g data-mark="${mark.kind}">${glyph}</g>`);
454
- }).join("");
455
- }
456
- /** The table form of marks and bands, in axis order. Empty when there are none. */
457
- function eventsTable(marks, bands, axisLabel, format) {
458
- if (!marks.length && !bands.length) return "";
459
- const rows = [...marks.map((mark) => ({
460
- at: mark.at,
461
- cells: [
462
- mark.label,
463
- mark.kind,
464
- format(mark.at),
465
- ""
466
- ]
467
- })), ...bands.map((band) => ({
468
- at: band.from,
469
- cells: [
470
- band.label,
471
- band.kind === "missing" ? "no data" : band.kind,
472
- format(band.from),
473
- format(band.to)
474
- ]
475
- }))].sort((a, b) => a.at - b.at);
476
- return table(`Recorded events by ${axisLabel}`, [
477
- { label: "Event" },
478
- { label: "Kind" },
479
- {
480
- label: `${axisLabel} from`,
481
- numeric: true
482
- },
483
- {
484
- label: `${axisLabel} to`,
485
- numeric: true
486
- }
487
- ], rows.map((row) => row.cells));
488
- }
489
- const EVENTS_READ = "Vertical rules mark recorded events (solid: pass, dashed: deadline, cross: died, dotted: other); tinted spans are recorded periods and hatched spans have no data.";
490
- //#endregion
491
- export { listJoin as C, wrap as D, textWidth as E, fitLines as S, textBlock as T, svgRoot as _, eventsTable as a, capitalize as b, MARKER_SHAPES as c, hatchPath as d, hbar as f, rect as g, polyline as h, eventsGap as i, circle as l, markerPath as m, drawBands as n, isRefusal as o, line as p, drawMarks as r, table as s, EVENTS_READ as t, hatch as u, text as v, plural as w, esc as x, titled as y };
@@ -1,183 +0,0 @@
1
- //#region src/types.d.ts
2
- /**
3
- * Inputs and outputs of the figure functions.
4
- *
5
- * The library draws and never decides. Ranks, intervals, frontier membership,
6
- * estimate labels and pass counts arrive from the producer (a board record, a
7
- * VerticalBench report, an agent-eval lens). A figure function returns a
8
- * `Refusal` instead of a figure it cannot draw honestly.
9
- */
10
- /** agent-eval `SearchEstimateMethod`, the same strings: what a unit count supports. */
11
- type Estimate = "none" | "insufficient" | "descriptive" | "bootstrap";
12
- type IntervalMethod = "task-cluster-bootstrap" | "mcnemar-exact" | "wilson";
13
- /**
14
- * A confidence interval as the producer recorded it. `level` and `method` are
15
- * nullable because real records omit them; a figure refuses to draw an
16
- * interval that does not name both.
17
- */
18
- interface Interval {
19
- lower: number;
20
- upper: number;
21
- level: number | null;
22
- method: IntervalMethod | null;
23
- }
24
- /** The App Grade ladder, lowest first. U (unable to measure) is not on it. */
25
- type Tier = "F" | "C" | "B" | "A" | "S";
26
- type TierCounts = Record<Tier, number>;
27
- /** Where a cost comes from. Only `receipts` is billed spend. */
28
- type CostBasis = "receipts" | "estimated" | "unknown";
29
- /** A column or row identity: an AgentProfile, a model, a judge, a search node. */
30
- interface Setup {
31
- id: string;
32
- label: string;
33
- /** Null when the order is a sort, not a ranking backed by comparisons. */
34
- rank: number | null;
35
- }
36
- /** One setup's rate. Field names follow the board record's `BoardProfile`. */
37
- interface RateRow extends Setup {
38
- solved: number;
39
- attempts: number;
40
- rate: number;
41
- /** The producer's label for what `attempts` supports; the library holds no threshold. */
42
- estimate: Estimate;
43
- interval: Interval | null;
44
- tiers: TierCounts | null;
45
- cost: {
46
- perSolvedUsd: number | null;
47
- basis: CostBasis;
48
- receipts: number | null;
49
- };
50
- medianWallMs: number | null;
51
- /** Null when the producer computed no frontier. */
52
- onFront: boolean | null;
53
- }
54
- /** One task × setup cell. */
55
- interface MatrixCell {
56
- task: string;
57
- setup: string;
58
- /** Attempts that count; null when the cell never ran. */
59
- attempts: number | null;
60
- /** Attempts that passed; null when no pass rule applies. */
61
- solved: number | null;
62
- /** Attempts graded on the App Grade ladder. */
63
- tiers: TierCounts | null;
64
- /** Attempts graded U: no floor check failed, but one could not be observed. */
65
- unmeasured?: number;
66
- /** Attempts whose grade failed an integrity check and awaits human review. */
67
- flagged?: number;
68
- /** A lens cell's mean score; null when the setup never scored the task. */
69
- mean: number | null;
70
- costUsd: number | null;
71
- }
72
- interface ComparisonRow {
73
- /** Setup ids. */
74
- favored: string;
75
- other: string;
76
- pairs: number | null;
77
- minimumEffect: number | null;
78
- /** The clustered interval on the difference, in the measure's units. */
79
- interval: Interval | null;
80
- /** The exact interval, drawn under the clustered one when present. */
81
- exactInterval: Interval | null;
82
- }
83
- interface BreakdownInput {
84
- reasons: string[];
85
- setups: Array<{
86
- id: string;
87
- label: string;
88
- n: number | null;
89
- }>;
90
- counts: Array<{
91
- reason: string;
92
- setup: string;
93
- count: number | null;
94
- }>;
95
- excluded: {
96
- count: number;
97
- why: string;
98
- } | null;
99
- /** True when one attempt can count under several reasons. */
100
- overlapping?: boolean;
101
- }
102
- /** Attempts left out of a figure, stated in its note. */
103
- interface Exclusion {
104
- count: number;
105
- why: string;
106
- }
107
- interface Figure {
108
- id: string;
109
- /** Generated from the input; no function accepts heading text. */
110
- finding: string;
111
- /** One quantified sentence. */
112
- lede: string;
113
- /** How to read the marks. */
114
- read: string;
115
- /** Wide and phone renders; vertical columns retain a readable minimum width. Null means the phone view uses the table. */
116
- svg: {
117
- wide: string;
118
- narrow: string | null;
119
- interactive?: boolean;
120
- };
121
- /** An HTML table with every number the plot shows. */
122
- table: string;
123
- note: {
124
- method: string;
125
- n: string;
126
- exclusions: string[];
127
- };
128
- }
129
- interface Refusal {
130
- id: string;
131
- refused: string;
132
- }
133
- declare function isRefusal(value: Figure | Refusal): value is Refusal;
134
- //#endregion
135
- //#region src/events.d.ts
136
- /** `pass` is solid, `deadline` dashed, `died` ends in a cross and `event` is dotted, so kinds read without colour. */
137
- type MarkKind = "pass" | "deadline" | "died" | "event";
138
- interface ChartMark {
139
- at: number;
140
- label: string;
141
- kind: MarkKind;
142
- }
143
- /** `missing` is hatched: a span with no data is never drawn as zero. */
144
- type BandKind = "after-pass" | "idle" | "missing";
145
- interface ChartBand {
146
- from: number;
147
- to: number;
148
- label: string;
149
- kind: BandKind;
150
- }
151
- //#endregion
152
- //#region src/timeline.d.ts
153
- /** Epoch milliseconds. `end: null` is an open span; it needs the figure's `now`. */
154
- interface TimelineSpan {
155
- start: number;
156
- end: number | null;
157
- state: string;
158
- note?: string;
159
- }
160
- interface TimelineLane {
161
- id: string;
162
- label: string;
163
- spans: TimelineSpan[];
164
- }
165
- interface TimelineOptions {
166
- measure: string;
167
- /** The recorded window; defaults to the extent of spans, marks, bands and `now`. Everything must fall inside it. */
168
- start?: number;
169
- end?: number;
170
- /** The time an open span is drawn to. Required when any span is open. */
171
- now?: number;
172
- marks?: ChartMark[];
173
- bands?: ChartBand[];
174
- /** States drawn hatched because their time has no data. Default: missing, untraced. */
175
- hatch?: string[];
176
- /** States drawn in the muted tone, such as idle. Default: idle. */
177
- muted?: string[];
178
- formatTime?: (ms: number) => string;
179
- id?: string;
180
- }
181
- declare function timeline(lanes: TimelineLane[], o: TimelineOptions): Figure | Refusal;
182
- //#endregion
183
- export { isRefusal as C, TierCounts as S, MatrixCell as _, BandKind as a, Setup as b, MarkKind as c, CostBasis as d, Estimate as f, IntervalMethod as g, Interval as h, timeline as i, BreakdownInput as l, Figure as m, TimelineOptions as n, ChartBand as o, Exclusion as p, TimelineSpan as r, ChartMark as s, TimelineLane as t, ComparisonRow as u, RateRow as v, Tier as x, Refusal as y };