flowfig 0.6.0 → 0.8.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/README.md +28 -8
- package/dist/browser.d.ts +1 -3
- package/dist/browser.js +67 -59
- package/dist/check.d.ts +5 -3
- package/dist/check.js +24 -17
- package/dist/cli.js +43 -49
- package/dist/code.d.ts +26 -0
- package/dist/code.js +568 -0
- package/dist/diff.js +0 -4
- package/dist/draw.d.ts +1 -3
- package/dist/draw.js +3 -7
- package/dist/edges.d.ts +7 -0
- package/dist/edges.js +510 -0
- package/dist/geometry.d.ts +6 -8
- package/dist/geometry.js +75 -82
- package/dist/gif.d.ts +1 -2
- package/dist/gif.js +115 -42
- package/dist/guide.d.ts +1 -4
- package/dist/guide.js +10 -11
- package/dist/imports.d.ts +11 -0
- package/dist/imports.js +235 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +20 -78
- package/dist/init.d.ts +3 -13
- package/dist/init.js +13 -18
- package/dist/load.d.ts +1 -3
- package/dist/load.js +2 -5
- package/dist/mcp.d.ts +1 -2
- package/dist/mcp.js +15 -15
- package/dist/model.d.ts +57 -109
- package/dist/model.js +20 -89
- package/dist/open.d.ts +2 -5
- package/dist/open.js +6 -11
- package/dist/page.d.ts +0 -1
- package/dist/page.js +1 -3
- package/dist/picker.d.ts +2 -4
- package/dist/picker.js +11 -14
- package/dist/png.js +9 -10
- package/dist/rail.d.ts +1 -3
- package/dist/rail.js +2 -16
- package/dist/scene.d.ts +8 -8
- package/dist/source.d.ts +1 -2
- package/dist/source.js +5 -5
- package/dist/svg.d.ts +5 -8
- package/dist/svg.js +50 -112
- package/dist/text.d.ts +0 -2
- package/dist/text.js +1 -4
- package/dist/type-index.d.ts +55 -0
- package/dist/type-index.js +383 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +733 -0
- package/dist/verify.d.ts +26 -4
- package/dist/verify.js +68 -26
- package/dist/version.d.ts +1 -2
- package/dist/version.js +2 -2
- package/package.json +4 -2
package/dist/mcp.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
// The MCP server: the check, render, verify, diff and docs tools over stdio JSON-RPC, for an agent with no shell.
|
|
2
|
-
// No SDK: the subset the clients need is small, and the package keeps its zero dependencies.
|
|
3
1
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
4
2
|
import { dirname } from 'node:path';
|
|
5
3
|
import { createInterface } from 'node:readline';
|
|
6
4
|
import { diff, formatDiff } from './diff.js';
|
|
7
5
|
import { GUIDE } from './guide.js';
|
|
8
6
|
import { loadSpec, reportLines, sortFindings, svgWithSpec } from './load.js';
|
|
9
|
-
import { check } from './svg.js';
|
|
10
|
-
import {
|
|
7
|
+
import { check, render } from './svg.js';
|
|
8
|
+
import { checkRendered } from './check.js';
|
|
9
|
+
import { coverageLine, unsureLines, verifyReport } from './verify.js';
|
|
11
10
|
import { VERSION } from './version.js';
|
|
12
11
|
const specSchema = {
|
|
13
12
|
description: 'The figure: the props object, { props }, or a path to a .json file or an SVG that flowfig wrote.',
|
|
@@ -55,9 +54,9 @@ export const TOOLS = [
|
|
|
55
54
|
},
|
|
56
55
|
];
|
|
57
56
|
class Bad extends Error {
|
|
58
|
-
}
|
|
57
|
+
}
|
|
59
58
|
class Fault extends Error {
|
|
60
|
-
}
|
|
59
|
+
}
|
|
61
60
|
const ok = (text, isError = false) => isError ? { content: [{ type: 'text', text }], isError: true } : { content: [{ type: 'text', text }] };
|
|
62
61
|
const spec = (v, name) => {
|
|
63
62
|
if (typeof v !== 'string' && (typeof v !== 'object' || v === null))
|
|
@@ -66,7 +65,6 @@ const spec = (v, name) => {
|
|
|
66
65
|
return loadSpec(v);
|
|
67
66
|
}
|
|
68
67
|
catch (e) {
|
|
69
|
-
// The message of a path failure already starts with the path.
|
|
70
68
|
throw typeof v === 'string' ? new Fault(e.message) : new Bad(`${name}: ${e.message}`);
|
|
71
69
|
}
|
|
72
70
|
};
|
|
@@ -75,7 +73,6 @@ const need = (args, tool, keys) => {
|
|
|
75
73
|
if (args[k] == null)
|
|
76
74
|
throw new Bad(`${tool}: ${k} is required`);
|
|
77
75
|
};
|
|
78
|
-
/** A present argument must have its type; a number must be positive. */
|
|
79
76
|
const types = (args, tool, want) => {
|
|
80
77
|
for (const [k, t] of Object.entries(want)) {
|
|
81
78
|
const v = args[k];
|
|
@@ -94,13 +91,14 @@ function run(name, args) {
|
|
|
94
91
|
types(args, name, { width: 'number', minText: 'number', out: 'string', strict: 'boolean' });
|
|
95
92
|
const props = spec(args.spec, name);
|
|
96
93
|
const opts = { width: args.width, minText: args.minText };
|
|
97
|
-
const
|
|
94
|
+
const rendered = render(props, opts);
|
|
95
|
+
const findings = sortFindings(checkRendered(props, opts, rendered.scene), args.strict === true);
|
|
98
96
|
const errors = findings.some((f) => f.severity === 'error');
|
|
99
97
|
const lines = reportLines(props, findings);
|
|
100
98
|
if (name === 'check' || errors)
|
|
101
99
|
return ok(lines.join('\n'), errors);
|
|
102
100
|
const out = String(args.out);
|
|
103
|
-
const svg = svgWithSpec(props, opts);
|
|
101
|
+
const svg = svgWithSpec(props, opts, rendered.svg);
|
|
104
102
|
try {
|
|
105
103
|
mkdirSync(dirname(out), { recursive: true });
|
|
106
104
|
writeFileSync(out, svg);
|
|
@@ -117,6 +115,7 @@ function run(name, args) {
|
|
|
117
115
|
types(args, name, { root: 'string', strict: 'boolean' });
|
|
118
116
|
const root = args.root == null ? process.cwd() : String(args.root);
|
|
119
117
|
const lines = [];
|
|
118
|
+
const counts = [];
|
|
120
119
|
let errors = 0;
|
|
121
120
|
for (const p of args.paths) {
|
|
122
121
|
let props;
|
|
@@ -130,13 +129,15 @@ function run(name, args) {
|
|
|
130
129
|
lines.push(`${'error'.padEnd(8)} ${'missing-file'.padEnd(18)} ${e.message}`);
|
|
131
130
|
continue;
|
|
132
131
|
}
|
|
133
|
-
const
|
|
132
|
+
const report = verifyReport(props, { root });
|
|
133
|
+
counts.push(coverageLine(p, report.coverage), ...unsureLines(report.coverage));
|
|
134
|
+
const findings = sortFindings([...check(props), ...report.findings], args.strict === true);
|
|
134
135
|
errors += findings.filter((f) => f.severity === 'error').length;
|
|
135
136
|
lines.push(...findings.map((f) => `${f.severity.padEnd(8)} ${f.rule.padEnd(18)} ${p}: ${f.message}`));
|
|
136
137
|
}
|
|
137
138
|
const n = (k, w) => `${k} ${w}${k === 1 ? '' : 's'}`;
|
|
138
139
|
const total = lines.length;
|
|
139
|
-
lines.push(`${n(errors, 'error')}, ${n(total - errors, 'warning')}
|
|
140
|
+
lines.push(`${n(errors, 'error')}, ${n(total - errors, 'warning')}`, ...counts);
|
|
140
141
|
return ok(lines.join('\n'), errors > 0);
|
|
141
142
|
}
|
|
142
143
|
if (name === 'diff') {
|
|
@@ -147,7 +148,7 @@ function run(name, args) {
|
|
|
147
148
|
}
|
|
148
149
|
const reply = (id, result) => ({ jsonrpc: '2.0', id, result });
|
|
149
150
|
const fail = (id, code, message) => ({ jsonrpc: '2.0', id, error: { code, message } });
|
|
150
|
-
/**
|
|
151
|
+
/** Returns `undefined` for a notification. */
|
|
151
152
|
export function handle(message) {
|
|
152
153
|
if (typeof message !== 'object' || message === null)
|
|
153
154
|
return fail(null, -32600, 'invalid request');
|
|
@@ -156,7 +157,7 @@ export function handle(message) {
|
|
|
156
157
|
if (typeof method !== 'string')
|
|
157
158
|
return notification ? undefined : fail(id, -32600, 'invalid request');
|
|
158
159
|
if (notification)
|
|
159
|
-
return undefined;
|
|
160
|
+
return undefined;
|
|
160
161
|
const info = { name: 'flowfig', version: VERSION };
|
|
161
162
|
try {
|
|
162
163
|
switch (method) {
|
|
@@ -195,7 +196,6 @@ export function handle(message) {
|
|
|
195
196
|
return fail(id, -32603, e.message);
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
|
-
/** Read one message per line from `input`, write one response per line to `output`. Returns when `input` ends. */
|
|
199
199
|
export async function serve(input, output) {
|
|
200
200
|
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
201
201
|
for await (const line of rl) {
|
package/dist/model.d.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
import type { Rect } from './geometry.ts';
|
|
3
|
-
/** A box
|
|
3
|
+
/** A box. A box with `show` in a step becomes a content card. */
|
|
4
4
|
export type FigNode = {
|
|
5
|
-
/** Name that edges, steps and beats use
|
|
5
|
+
/** Name that edges, steps and beats use for this box. Unique in the figure. */
|
|
6
6
|
id: string;
|
|
7
7
|
/** The title in the box. */
|
|
8
8
|
label: ReactNode;
|
|
9
9
|
/** A smaller line under the label. */
|
|
10
10
|
sub?: ReactNode;
|
|
11
|
-
/** `
|
|
11
|
+
/** `decision` is a diamond, `store` a cylinder for data at rest. Default: `box`. */
|
|
12
12
|
shape?: 'box' | 'decision' | 'store';
|
|
13
|
-
/** The least
|
|
13
|
+
/** The least text lines a content card keeps. It still grows to fit. */
|
|
14
14
|
lines?: number;
|
|
15
15
|
/** Width in px. Overrides the width the layout picks. */
|
|
16
16
|
width?: number;
|
|
17
|
-
/** In
|
|
17
|
+
/** In `lanes`: the 0-based time column. Default: the first appearance in the steps. */
|
|
18
18
|
at?: number;
|
|
19
|
-
/** In
|
|
19
|
+
/** In `timeline`: the start date, or the milestone date, as YYYY-MM-DD. */
|
|
20
20
|
from?: string;
|
|
21
|
-
/** In
|
|
21
|
+
/** In `timeline`: the last day, as YYYY-MM-DD. No `to` makes a milestone. */
|
|
22
22
|
to?: string;
|
|
23
|
-
/** A permanent state color
|
|
23
|
+
/** A permanent state color. An arriving hop with no tone uses it too. */
|
|
24
24
|
tone?: FigTone;
|
|
25
|
-
/**
|
|
25
|
+
/** `start` draws a filled dot before the box, `end` a ringed dot after it. */
|
|
26
26
|
mark?: 'start' | 'end';
|
|
27
|
-
/**
|
|
27
|
+
/** Code this draws: `path` or `path#symbol` from the repo root. Checked by `flowfig verify`. */
|
|
28
28
|
source?: string;
|
|
29
29
|
};
|
|
30
|
-
/** A frame that lays out its children in a row or a column.
|
|
30
|
+
/** A frame that lays out its children in a row or a column. */
|
|
31
31
|
export type FigGroup = {
|
|
32
32
|
/** Name that edges can point at, to reach the group as a whole. */
|
|
33
33
|
id?: string;
|
|
34
|
-
/** The title on the frame. A group
|
|
34
|
+
/** The title on the frame. A labeled group is drawn as a frame. */
|
|
35
35
|
label?: ReactNode;
|
|
36
36
|
/** `row` puts the children side by side, `column` stacks them. Default: `row`. */
|
|
37
37
|
direction?: 'row' | 'column';
|
|
38
|
-
/** Space between children in px. Default: 28 for a column
|
|
38
|
+
/** Space between children in px. Default: 28 for a column, fitted for a row. */
|
|
39
39
|
gap?: number;
|
|
40
40
|
/** Where children line up across the group's direction. Default: center (columns stretch). */
|
|
41
41
|
align?: 'start' | 'center' | 'end';
|
|
@@ -54,10 +54,12 @@ export type FigEdge = {
|
|
|
54
54
|
label?: ReactNode;
|
|
55
55
|
/** Routes the edge over or under the boxes in between (loops, skip-ahead edges). */
|
|
56
56
|
around?: 'above' | 'below';
|
|
57
|
-
/** Draws the edge only while a step uses it.
|
|
57
|
+
/** Draws the edge only while a step uses it. */
|
|
58
58
|
quiet?: boolean;
|
|
59
|
-
/**
|
|
59
|
+
/** Code this draws: `path` or `path#symbol` from the repo root. Checked by `flowfig verify`. */
|
|
60
60
|
source?: string;
|
|
61
|
+
/** The route, queue, topic or table that carries this edge. Checked by `flowfig verify`. */
|
|
62
|
+
via?: string;
|
|
61
63
|
};
|
|
62
64
|
/** One packet on one edge. A string is the edge `id`. */
|
|
63
65
|
export type FigHop = string | {
|
|
@@ -67,12 +69,14 @@ export type FigHop = string | {
|
|
|
67
69
|
back?: boolean;
|
|
68
70
|
/** A small card that rides along with the packet. */
|
|
69
71
|
data?: ReactNode;
|
|
70
|
-
/** Colors the packet,
|
|
72
|
+
/** Colors the packet, data card, edge and arrival box for this beat. Default: the accent. */
|
|
71
73
|
tone?: FigTone;
|
|
72
|
-
/** Marks a message that
|
|
74
|
+
/** Marks a message that needs no reply. The rail draws it dashed. Default: `false`. */
|
|
73
75
|
async?: boolean;
|
|
74
|
-
/**
|
|
76
|
+
/** Code this draws: `path` or `path#symbol` from the repo root. Checked by `flowfig verify`. */
|
|
75
77
|
source?: string;
|
|
78
|
+
/** The route, queue, topic or table that carries this edge. Checked by `flowfig verify`. */
|
|
79
|
+
via?: string;
|
|
76
80
|
};
|
|
77
81
|
/** One moment of a step. No edges means a pause. */
|
|
78
82
|
export type FigBeat = {
|
|
@@ -80,13 +84,13 @@ export type FigBeat = {
|
|
|
80
84
|
edges?: FigHop | FigHop[];
|
|
81
85
|
/** The line of narration for this beat. */
|
|
82
86
|
say?: ReactNode;
|
|
83
|
-
/** Fills the content card of each named box
|
|
87
|
+
/** Fills the content card of each named box until the step ends. */
|
|
84
88
|
show?: Record<string, FigContent>;
|
|
85
89
|
/** Ids of boxes to highlight for this beat, without a packet or a content card. */
|
|
86
90
|
light?: string[];
|
|
87
|
-
/** Ids of boxes that take the active look
|
|
91
|
+
/** Ids of boxes that take the active look in this beat. */
|
|
88
92
|
focus?: string[];
|
|
89
|
-
/**
|
|
93
|
+
/** Beat length in ms. Default: `speed` plus the time to read `say`. */
|
|
90
94
|
ms?: number;
|
|
91
95
|
};
|
|
92
96
|
/** One line of a content card. */
|
|
@@ -104,7 +108,7 @@ export type FigRow = {
|
|
|
104
108
|
/** Draws the text in a monospace font. Default: `false`. */
|
|
105
109
|
mono?: boolean;
|
|
106
110
|
};
|
|
107
|
-
/** The color of a row tag, a hop or a box.
|
|
111
|
+
/** The color of a row tag, a hop or a box. */
|
|
108
112
|
export type FigTone = 'blue' | 'purple' | 'green' | 'orange' | 'red' | 'gray';
|
|
109
113
|
/** What a content card shows: rows, or anything React can render. */
|
|
110
114
|
export type FigContent = FigRow[] | ReactNode;
|
|
@@ -114,12 +118,12 @@ export type FigStep = {
|
|
|
114
118
|
label: ReactNode;
|
|
115
119
|
/** The line shown under the figure while no beat of the step has a `say`. */
|
|
116
120
|
caption?: ReactNode;
|
|
117
|
-
/** The beats in play order. A
|
|
121
|
+
/** The beats in play order. A bare hop or hop array is one beat. */
|
|
118
122
|
flow: (FigHop | FigHop[] | FigBeat)[];
|
|
119
123
|
/** Ids of boxes to highlight for the whole step. */
|
|
120
124
|
nodes?: string[];
|
|
121
125
|
};
|
|
122
|
-
/** Colors for the figure.
|
|
126
|
+
/** Colors for the figure. A key you leave out keeps the built-in color. */
|
|
123
127
|
export type FigTheme = Partial<Record<'accent' | 'fg' | 'muted' | 'bg' | 'surface' | 'border' | 'font', string>>;
|
|
124
128
|
/** The spec of one figure. Give it to `Flow` (React) or to `toSvg` (SVG string). */
|
|
125
129
|
export type FlowProps = {
|
|
@@ -129,21 +133,21 @@ export type FlowProps = {
|
|
|
129
133
|
edges: FigEdge[];
|
|
130
134
|
/** The stories the figure tells. With no steps, the figure is a still map. */
|
|
131
135
|
steps?: FigStep[];
|
|
132
|
-
/** Colors.
|
|
136
|
+
/** Colors. CSS `--fig-accent`, `--fig-bg` on `.flowfig` also set them. `font` defaults to `inherit`. */
|
|
133
137
|
theme?: FigTheme;
|
|
134
138
|
/** Milliseconds a packet takes to cross one edge. Default: 900. */
|
|
135
139
|
speed?: number;
|
|
136
140
|
/** Starts playing when the figure mounts. Default: `true`. Only the React player reads it. */
|
|
137
141
|
autoplay?: boolean;
|
|
138
|
-
/**
|
|
142
|
+
/** Dev aid: run the `flowfig check` rules on the drawn figure, then console.warn faults. */
|
|
139
143
|
check?: boolean;
|
|
140
|
-
/**
|
|
144
|
+
/** Draws the steps as a lifeline rail under the map. `'only'` draws no map. */
|
|
141
145
|
rail?: boolean | 'only';
|
|
142
|
-
/**
|
|
146
|
+
/** Draws the layout as swimlanes: a `column` group of labeled groups, one per role. */
|
|
143
147
|
lanes?: true;
|
|
144
|
-
/**
|
|
148
|
+
/** Draws a timeline: one labeled group per track, boxes placed by `from` and `to`. */
|
|
145
149
|
timeline?: true;
|
|
146
|
-
/** In
|
|
150
|
+
/** In `timeline`: the date of the today marker, as YYYY-MM-DD. */
|
|
147
151
|
today?: string;
|
|
148
152
|
};
|
|
149
153
|
/** A figure file's default export: a title, a source note and the props. */
|
|
@@ -152,7 +156,7 @@ export type Figure = {
|
|
|
152
156
|
source?: string;
|
|
153
157
|
props: FlowProps;
|
|
154
158
|
};
|
|
155
|
-
/** A beat with
|
|
159
|
+
/** A beat with every hop spelled out: an edge id and a direction. */
|
|
156
160
|
export type Beat = Omit<FigBeat, 'edges'> & {
|
|
157
161
|
hops: {
|
|
158
162
|
edge: string;
|
|
@@ -161,33 +165,23 @@ export type Beat = Omit<FigBeat, 'edges'> & {
|
|
|
161
165
|
tone?: FigTone;
|
|
162
166
|
async?: boolean;
|
|
163
167
|
source?: string;
|
|
168
|
+
via?: string;
|
|
164
169
|
}[];
|
|
165
170
|
};
|
|
166
171
|
export declare const toBeat: (b: FigHop | FigHop[] | FigBeat) => Beat;
|
|
167
|
-
/** A React node can be an object too, so a row is told apart by its `text` key. */
|
|
168
172
|
export declare const isRows: (c: FigContent) => c is FigRow[];
|
|
169
173
|
export declare const edgeId: (e: FigEdge) => string;
|
|
170
174
|
export declare const isGroup: (x: FigNode | FigGroup) => x is FigGroup;
|
|
171
175
|
export declare const decisions: (g: FigGroup) => string[];
|
|
172
|
-
/** The width of a content card in px. Both renderers use it, so a figure lays out the same way in each. */
|
|
173
176
|
export declare const CARD_WIDTH = 166;
|
|
174
|
-
/** The player runs 1.25 times faster than the timings in a figure: figure timings are written to be read slowly. The SVG matches it. */
|
|
175
177
|
export declare const BASE_RATE = 1.25;
|
|
176
|
-
/** The height of one text line in a content card, and the padding above and below the lines. */
|
|
177
178
|
export declare const CARD_LINE = 15, CARD_PAD = 6;
|
|
178
|
-
/** The fixed color of each row tone. */
|
|
179
179
|
export declare const TONES: Record<FigTone, string>;
|
|
180
|
-
/** A tone as a fill under white text: darker, so the text keeps its contrast. */
|
|
181
180
|
export declare const toneFill: (c: string) => string;
|
|
182
|
-
/** The light tint of a tone over a background: the fill of a toned box. */
|
|
183
181
|
export declare const toneTint: (c: string, bg: string, pct?: number) => string;
|
|
184
|
-
/** The width of an edge-label pill in px: the label text plus its padding. The rail uses it for a payload, so both look the same. */
|
|
185
182
|
export declare const labelPillW: (label: string) => number;
|
|
186
|
-
/** The width of the `async` tag in px. */
|
|
187
183
|
export declare const ASYNC_TAG_W: number;
|
|
188
|
-
/** The stroke width in px of an edge that a packet crosses now, and of an idle edge. */
|
|
189
184
|
export declare const EDGE_ON = 2, EDGE_OFF = 1.25;
|
|
190
|
-
/** The built-in colors. Both renderers and `flowfig check` read these, so they cannot drift apart. */
|
|
191
185
|
export declare const LIGHT: {
|
|
192
186
|
accent: string;
|
|
193
187
|
fg: string;
|
|
@@ -204,27 +198,20 @@ export declare const DARK: {
|
|
|
204
198
|
surface: string;
|
|
205
199
|
border: string;
|
|
206
200
|
};
|
|
207
|
-
/** Text on an accent fill: the data chips and a lit edge label. */
|
|
208
201
|
export declare const ON_ACCENT = "#ffffff";
|
|
209
|
-
/** Only text survives outside React: a React element has no string form. */
|
|
210
202
|
export declare const str: (n: unknown) => string;
|
|
211
|
-
/** The time a reader needs for a caption line: 400 ms plus 240 ms per word. The result is 0 for no text. */
|
|
212
203
|
export declare const readMs: (say: unknown) => number;
|
|
213
|
-
/** How long a beat lasts. An explicit `ms` wins as written. Else the beat lasts `speed` plus the time to read `say`. */
|
|
214
204
|
export declare const beatMs: (b: Beat, speed: number) => number;
|
|
215
|
-
/** The item the playhead rests on at beat `i`: the first dated id in the latest focus up to `i`, or undefined for home. */
|
|
216
205
|
export declare const playheadItem: <T extends {
|
|
217
206
|
id: string;
|
|
218
207
|
}>(items: T[], beats: {
|
|
219
208
|
focus?: string[];
|
|
220
209
|
}[], i: number) => T | undefined;
|
|
221
|
-
/** The item the playhead rests on when the loop starts: the playhead item of the first beat of the first step. */
|
|
222
210
|
export declare const loopStartItem: <T extends {
|
|
223
211
|
id: string;
|
|
224
212
|
}>(items: T[], steps: {
|
|
225
213
|
flow: FigStep['flow'];
|
|
226
214
|
}[]) => T | undefined;
|
|
227
|
-
/** The box of a timeline label that sits beside its bar, in the bar's coordinates. */
|
|
228
215
|
export declare const outsideLabelRect: (bar: {
|
|
229
216
|
x: number;
|
|
230
217
|
y: number;
|
|
@@ -236,38 +223,22 @@ export declare const outsideLabelRect: (bar: {
|
|
|
236
223
|
w: number;
|
|
237
224
|
h: number;
|
|
238
225
|
};
|
|
239
|
-
/**
|
|
240
|
-
* The time span in which the playhead label shows its date. The label waits for the line to arrive: it stays hidden for
|
|
241
|
-
* the ramp (the move), so it never shows a date at a place that is not that date, and it never meets "today" on the way.
|
|
242
|
-
*/
|
|
243
226
|
export declare const labelSpan: (t0: number, end: number, ramp: number) => [number, number];
|
|
244
|
-
/** True if a playhead date label at x would overlap the "today" label, so it moves to the top row. Widths are in px. */
|
|
245
227
|
export declare const dateLabelRaised: (x: number, today: number | null | undefined, w: number, todayW: number) => boolean;
|
|
246
|
-
/** The hold at the end of each step, before the next step starts. */
|
|
247
228
|
export declare const STEP_HOLD_MS = 2000;
|
|
248
229
|
export declare const nodes: (g: FigGroup) => FigNode[];
|
|
249
|
-
/** Lane layout constants, shared by both renderers: the gap between time columns, the padding inside a lane, and the gap between lanes. */
|
|
250
230
|
export declare const LANE_GAP = 56, LANE_PAD = 24, LANE_ROW_GAP = 20;
|
|
251
|
-
/** True if the layout can draw as lanes: a `column` group whose every child is a labeled group with boxes only. */
|
|
252
231
|
export declare const isLanesLayout: (g: FigGroup) => boolean;
|
|
253
|
-
/** True if `at` is a usable time column: an integer of 0 or more. */
|
|
254
232
|
export declare const validAt: (at: unknown) => at is number;
|
|
255
|
-
/** The time column of each box
|
|
256
|
-
* then the boxes no step touches in layout order. `at` overrides. Two boxes may share a column. */
|
|
233
|
+
/** The time column of each box: first use in the steps, then layout order. `at` wins. */
|
|
257
234
|
export declare function laneColumns(fig: FlowProps): Map<string, number>;
|
|
258
|
-
/** The side of an SVG frame, and the width limits of a box with no card. The lane split measures with them. */
|
|
259
235
|
export declare const FRAME_SIDE = 18, NODE_MIN_W = 100, NODE_MAX_W = 190;
|
|
260
|
-
/** The gap between two blocks of wrapped lanes. */
|
|
261
236
|
export declare const LANE_BLOCK_GAP = 40;
|
|
262
|
-
/** The length of a stub line between a box and its pill, the clear space past a pill, and the room a lane grows by for a pill. */
|
|
263
237
|
export declare const STUB = 12, STUB_CLEAR = 8, STUB_ROOM = 30;
|
|
264
238
|
/** The width of a box as the SVG draws it, before a diamond adds its 70 px. */
|
|
265
239
|
export declare function nodeWidth(item: FigNode, carded: boolean): number;
|
|
266
|
-
/** The room a diamond of size w by h gives a text line whose far edge sits `far` px from the center: the width of the outline there, less 8 px. */
|
|
267
240
|
export declare const diamondRoom: (w: number, h: number, far: number) => number;
|
|
268
|
-
/** The lines of a diamond with no card. The
|
|
269
|
-
* for each line until the lines fit. `dy` is the baseline of each line below the center, as the SVG draws it: the label sits 2 px
|
|
270
|
-
* above the center over a one-line sub, and 5 px below it with no sub. `far` is the distance from the center to the far edge of the text. */
|
|
241
|
+
/** The lines of a diamond with no card. The diamond grows 15 px for each sub line that wraps. */
|
|
271
242
|
export declare function diamondLines(item: FigNode, w: number): {
|
|
272
243
|
h: number;
|
|
273
244
|
label: {
|
|
@@ -280,58 +251,46 @@ export declare function diamondLines(item: FigNode, w: number): {
|
|
|
280
251
|
far: number;
|
|
281
252
|
}[];
|
|
282
253
|
};
|
|
283
|
-
/** The lane copies
|
|
284
|
-
* STUB_ROOM and routes again: the pill then fits under or over its box, inside the band. Both renderers call it with their routes. */
|
|
254
|
+
/** The lane copies whose stub end has no clear place. The renderer grows each by STUB_ROOM. */
|
|
285
255
|
export declare function tightCopies(lanes: FigGroup[], ends: Map<string, [number, number]>, edges: FlowProps['edges'], routed: {
|
|
286
256
|
id: string;
|
|
287
257
|
stub?: {
|
|
288
258
|
tight: [boolean, boolean];
|
|
289
259
|
};
|
|
290
260
|
}[]): Set<string>;
|
|
291
|
-
/** The width of the label gutter of lanes and of a timeline: the widest track label plus the frame sides. */
|
|
292
261
|
export declare const laneGutter: (lanes: FigGroup[]) => number;
|
|
293
|
-
/** How a lanes figure wraps. Both renderers lay out with it
|
|
262
|
+
/** How a lanes figure wraps. Both renderers lay out with it. */
|
|
294
263
|
export type LanePlan = {
|
|
295
264
|
/** The time column of each box. */
|
|
296
265
|
cols: Map<string, number>;
|
|
297
|
-
/** The first column of each block, then the column count
|
|
266
|
+
/** The first column of each block, then the column count. */
|
|
298
267
|
starts: number[];
|
|
299
268
|
/** The most columns in one block. */
|
|
300
269
|
per: number;
|
|
301
|
-
/** The blocks that hold a box, in order.
|
|
270
|
+
/** The blocks that hold a box, in order. An empty figure keeps block 0. */
|
|
302
271
|
blocks: number[];
|
|
303
272
|
/** The width of the label gutter, the same in every block. */
|
|
304
273
|
gutter: number;
|
|
305
|
-
/** The space after each column: the gap to the next
|
|
274
|
+
/** The space after each column: the gap to the next, or the band margin. */
|
|
306
275
|
gaps: number[];
|
|
307
276
|
/** The room before the first column of each block, after the gutter. */
|
|
308
277
|
lead: number[];
|
|
309
|
-
/** The block of
|
|
278
|
+
/** The block of each edge source and target, by edge id. */
|
|
310
279
|
ends: Map<string, [number, number]>;
|
|
311
|
-
/** The
|
|
312
|
-
* text without the edge label, if the edge has one. */
|
|
280
|
+
/** The pill texts of an edge across two blocks: source, target, then a short source. */
|
|
313
281
|
stubs: Map<string, string[]>;
|
|
314
|
-
/**
|
|
315
|
-
* A straight route would cross that box, so the renderers route them around below. */
|
|
282
|
+
/** Same-lane, same-block edges with a box between them and no `around`. They route below. */
|
|
316
283
|
around: Set<string>;
|
|
317
|
-
/** The lane copies (`laneIndex@block`) that grow by STUB_ROOM
|
|
284
|
+
/** The lane copies (`laneIndex@block`) that grow by STUB_ROOM: a stub pill has no room. */
|
|
318
285
|
tall: Set<string>;
|
|
319
|
-
/** Edges with a lane end
|
|
286
|
+
/** Edges with a lane end in no block on the other side. `check` reports them. */
|
|
320
287
|
lost: string[];
|
|
321
288
|
};
|
|
322
|
-
/** The block of a time column, from the block starts of a plan. */
|
|
323
289
|
export declare const laneBlock: (starts: number[], c: number) => number;
|
|
324
|
-
/** The pill texts of a cross-block edge: "label → Target" at the source, "from Source" at the target, and "→ Target", a
|
|
325
|
-
* shorter source text for a pill that has no place for the long one. */
|
|
326
290
|
export declare const stubTexts: (label: unknown, from: unknown, to: unknown) => string[];
|
|
327
291
|
/**
|
|
328
|
-
* The wrap of a lanes figure
|
|
329
|
-
*
|
|
330
|
-
* The text test is the one `check` runs: the smallest text (11 px, or 10.5 px with a mono card row) times width / figure width.
|
|
331
|
-
* If the wrap cannot make the text readable, the lanes do not wrap: the smallest font is under `minText`, or `floor` (the least
|
|
332
|
-
* width of the rest of the figure, such as the rail) is already too wide.
|
|
333
|
-
* The blocks share the columns as evenly as they can, and a block holds 2 columns at least: if 2 do not fit, the lanes keep
|
|
334
|
-
* one block. An edge between two blocks becomes two stubs with pills. A pill takes no room of its own, so n depends on the boxes.
|
|
292
|
+
* The wrap of a lanes figure: it wraps like text when the time columns do not fit `width`.
|
|
293
|
+
* The block count depends on the boxes only, not on pills.
|
|
335
294
|
*/
|
|
336
295
|
export declare function lanePlan(fig: FlowProps, { width, minText, padding, floor }?: {
|
|
337
296
|
floor?: number | undefined;
|
|
@@ -339,16 +298,9 @@ export declare function lanePlan(fig: FlowProps, { width, minText, padding, floo
|
|
|
339
298
|
padding?: number | undefined;
|
|
340
299
|
width?: number | undefined;
|
|
341
300
|
}): LanePlan;
|
|
342
|
-
/**
|
|
343
|
-
* The rect id that an edge end routes to in wrapped lanes. A box keeps its id. A lane has one band per block, so a lane end names
|
|
344
|
-
* the band in the block from the plan (`band` finds it). A stub end at a lane is a point inside that band: the source at the band
|
|
345
|
-
* end, past the room for its pill, and the target at the first column of the block. The function adds the rect it names to `rects`.
|
|
346
|
-
*/
|
|
301
|
+
/** The rect id that an edge end routes to in wrapped lanes. A stub end is a point in the band. */
|
|
347
302
|
export declare function laneEnd(plan: LanePlan, edge: string, id: string, start: boolean, band: (lane: string, block: number) => Rect | undefined, rects: Record<string, Rect>): string;
|
|
348
|
-
/** Timeline constants, shared by both renderers: the bar height, the gap between rows, the least bar width, the milestone width,
|
|
349
|
-
* the axis strip height and the axis width of the SVG. */
|
|
350
303
|
export declare const TL_BAR_H = 28, TL_ROW_GAP = 8, TL_MIN_BAR = 24, TL_DIAMOND = 14, TL_AXIS_H = 28, TL_AXIS_W = 640;
|
|
351
|
-
/** The days since 1970-01-01 (UTC) of a YYYY-MM-DD date. It is null for any other text and for a date that does not exist. */
|
|
352
304
|
export declare const dayOf: (iso: string) => number | null;
|
|
353
305
|
export type TimelineItem = {
|
|
354
306
|
id: string;
|
|
@@ -362,7 +314,7 @@ export type TimelineItem = {
|
|
|
362
314
|
date: string;
|
|
363
315
|
};
|
|
364
316
|
export type TimelineLayout = {
|
|
365
|
-
/** The range in days, rounded out to whole
|
|
317
|
+
/** The range in days, rounded out to whole Monday-to-Sunday weeks. */
|
|
366
318
|
start: number;
|
|
367
319
|
end: number;
|
|
368
320
|
/** The week ticks (`W41`) or the month ticks (`Oct`). */
|
|
@@ -370,21 +322,20 @@ export type TimelineLayout = {
|
|
|
370
322
|
x: number;
|
|
371
323
|
label: string;
|
|
372
324
|
}[];
|
|
373
|
-
/** Bars and milestones. `x` and `w` are px
|
|
325
|
+
/** Bars and milestones. `x` and `w` are px in the axis. */
|
|
374
326
|
items: TimelineItem[];
|
|
375
327
|
/** The row count of each track. */
|
|
376
328
|
rows: number[];
|
|
377
|
-
/** The x of the today marker
|
|
329
|
+
/** The x of the today marker, or null with no valid `today`. */
|
|
378
330
|
today: number | null;
|
|
379
|
-
/** The x and
|
|
331
|
+
/** The x and date of the last day. Null x: no dated item. */
|
|
380
332
|
last: number | null;
|
|
381
333
|
lastDate: string;
|
|
382
334
|
};
|
|
383
335
|
/** Where each dated item, tick and the today line sit on the axis, `axisWidth` px wide. Both renderers call it. */
|
|
384
336
|
export declare function timelineLayout(fig: FlowProps, axisWidth: number): TimelineLayout;
|
|
385
|
-
/** One step
|
|
337
|
+
/** One step with one beat for each dated item, in date order. */
|
|
386
338
|
export declare function timelineBeats(fig: FlowProps): FigStep[];
|
|
387
|
-
/** The part counts of a spec, for the line that `flowfig check` prints. An agent copies the line into its reply. */
|
|
388
339
|
export declare const counts: (p: FlowProps) => {
|
|
389
340
|
boxes: number;
|
|
390
341
|
groups: number;
|
|
@@ -392,8 +343,5 @@ export declare const counts: (p: FlowProps) => {
|
|
|
392
343
|
steps: number;
|
|
393
344
|
messages: number;
|
|
394
345
|
};
|
|
395
|
-
/**
|
|
396
|
-
* A group's gap. A row's default gap grows to hold the widest label of an edge between two of its children, with 8px each side:
|
|
397
|
-
* the label sits at the middle of that gap. A gap the author sets stays as set; `flowfig check` reports it if it is too narrow.
|
|
398
|
-
*/
|
|
346
|
+
/** A row's default gap grows to hold its widest edge label, plus 8 px each side. */
|
|
399
347
|
export declare function groupGap(g: FigGroup, edges: FigEdge[]): number;
|