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/rail.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { edgeId, isGroup, labelPillW, str, toBeat } from './model.js';
|
|
2
2
|
import { textWidth } from './text.js';
|
|
3
|
-
// band: the band-label line; cols: the column-label row; bandInset: a band's inset from its outer column edges, so two adjacent
|
|
4
|
-
// bands keep 16 px between them; clear: the space between a phase label or counter and a line; inset: the space a parallel band
|
|
5
|
-
// leaves around its arrows and pills.
|
|
6
3
|
export const RAIL = {
|
|
7
4
|
gap: 24,
|
|
8
5
|
band: 20,
|
|
@@ -16,9 +13,7 @@ export const RAIL = {
|
|
|
16
13
|
inset: 6,
|
|
17
14
|
maxOpen: 10,
|
|
18
15
|
};
|
|
19
|
-
/** The folded phase text. Both renderers and the layout use it, so the measured width matches the drawn width. */
|
|
20
16
|
export const foldedLabel = (row) => `▸ ${row.label} · ${row.messages} messages`;
|
|
21
|
-
/** Rail columns follow this order, so a reader meets the boxes in the same order on the rail as on the map. */
|
|
22
17
|
const order = (item) => isGroup(item)
|
|
23
18
|
? [...(item.id ? [{ id: item.id, label: str(item.label) }] : []), ...item.children.flatMap(order)]
|
|
24
19
|
: [{ id: item.id, label: str(item.label) }];
|
|
@@ -51,15 +46,12 @@ export function layoutRail(fig, mapWidth) {
|
|
|
51
46
|
const used = new Set(msgs.flatMap((m) => [edges.get(m.edge).from, edges.get(m.edge).to]));
|
|
52
47
|
const cols = order(fig.layout).filter((c) => used.has(c.id));
|
|
53
48
|
const index = new Map(cols.map((c, i) => [c.id, i]));
|
|
54
|
-
// Every column gets the same width: at least RAIL.minCol, wide enough for each label and pill, and together as wide as the map.
|
|
55
|
-
// A column label also needs room for the band inset on each side.
|
|
56
49
|
let need = RAIL.minCol;
|
|
57
50
|
for (const c of cols)
|
|
58
51
|
need = Math.max(need, textWidth(c.label, 12) + 16 + RAIL.bandInset * 2);
|
|
59
52
|
for (const m of msgs)
|
|
60
53
|
if (m.text)
|
|
61
54
|
need = Math.max(need, labelPillW(m.text) + 16);
|
|
62
|
-
// The phase labels start at RAIL.pad and the counters end at width - RAIL.pad. No band border comes within RAIL.clear of them.
|
|
63
55
|
const folds = msgs.length > RAIL.maxOpen;
|
|
64
56
|
const labelW = (text) => textWidth(text, 13, true);
|
|
65
57
|
const counterW = (() => {
|
|
@@ -70,8 +62,6 @@ export function layoutRail(fig, mapWidth) {
|
|
|
70
62
|
const label = str(steps[si].label);
|
|
71
63
|
return labelW(folds ? foldedLabel({ label, messages: b.flat().length }) : label);
|
|
72
64
|
}));
|
|
73
|
-
// Bands: each labeled group around the columns it holds, RAIL.bandInset inside their outer edges; a nested group sits 4 px inside
|
|
74
|
-
// its parent and one label line (RAIL.band) below it. The height comes later.
|
|
75
65
|
const place = (left, right, colW) => {
|
|
76
66
|
const width = left + colW * cols.length + right;
|
|
77
67
|
const columns = cols.map((c, i) => ({ id: c.id, label: c.label, x: left + colW * (i + 0.5), w: colW }));
|
|
@@ -90,7 +80,6 @@ export function layoutRail(fig, mapWidth) {
|
|
|
90
80
|
};
|
|
91
81
|
walk(fig.layout, 0);
|
|
92
82
|
const borders = bands.flatMap((b) => [b.rect.x, b.rect.x + b.rect.w]);
|
|
93
|
-
// How far the columns must move in from each side, so every band border clears the label and the counter.
|
|
94
83
|
const dl = Math.max(0, RAIL.pad + widest + RAIL.clear - Math.min(Infinity, ...borders));
|
|
95
84
|
const dr = Math.max(0, Math.max(-Infinity, ...borders) - (width - RAIL.pad - counterW - RAIL.clear));
|
|
96
85
|
return { width, columns, bands, dl, dr };
|
|
@@ -102,14 +91,12 @@ export function layoutRail(fig, mapWidth) {
|
|
|
102
91
|
for (let k = 0; placed.dl > 0.01 || placed.dr > 0.01; k++) {
|
|
103
92
|
left += placed.dl;
|
|
104
93
|
right += placed.dr;
|
|
105
|
-
// Narrower columns keep the rail as wide as the map. After a few tries the columns keep their width, and the rail grows.
|
|
106
94
|
if (k < 8)
|
|
107
95
|
colW = spread(left, right);
|
|
108
96
|
placed = place(left, right, colW);
|
|
109
97
|
}
|
|
110
98
|
const { width, columns, bands } = placed;
|
|
111
99
|
const head = (bands.length ? Math.max(...bands.map((b) => b.rect.y)) + RAIL.band : 0) + RAIL.cols;
|
|
112
|
-
// Rows: a phase header per step, then one row per message.
|
|
113
100
|
const rows = [];
|
|
114
101
|
const groups = [];
|
|
115
102
|
let n = 0;
|
|
@@ -145,7 +132,6 @@ export function layoutRail(fig, mapWidth) {
|
|
|
145
132
|
}
|
|
146
133
|
}
|
|
147
134
|
});
|
|
148
|
-
// Above maxOpen messages, only the playing step shows its rows; the others shrink to their phase header.
|
|
149
135
|
const states = steps.map((_, open) => {
|
|
150
136
|
let y = head;
|
|
151
137
|
return rows.map((row) => {
|
|
@@ -159,7 +145,7 @@ export function layoutRail(fig, mapWidth) {
|
|
|
159
145
|
const bottom = (s) => s.reduce((m, st, i) => (st.shown ? Math.max(m, st.y + (rows[i].kind === 'phase' ? RAIL.phase : RAIL.row)) : m), 0);
|
|
160
146
|
const height = Math.max(...states.map(bottom)) + 8;
|
|
161
147
|
for (const b of bands)
|
|
162
|
-
b.rect.h = height - b.rect.y - (b.rect.y / RAIL.band) * 4;
|
|
148
|
+
b.rect.h = height - b.rect.y - (b.rect.y / RAIL.band) * 4;
|
|
163
149
|
return { width, head, height, columns, bands, rows, groups, total: n, folds, states };
|
|
164
150
|
}
|
|
165
151
|
/** A parallel band around its rows, relative to the y of its first row: RAIL.inset around every arrow and pill it holds. */
|
|
@@ -171,6 +157,6 @@ export function groupBox(rail, g) {
|
|
|
171
157
|
return [rail.columns[r.from].x, rail.columns[r.to].x, ...(r.pill ? [r.pill.x, r.pill.x + r.pill.w] : [])];
|
|
172
158
|
});
|
|
173
159
|
const x = Math.min(...xs) - RAIL.inset;
|
|
174
|
-
const top = (RAIL.row - 18) / 2 - RAIL.inset;
|
|
160
|
+
const top = (RAIL.row - 18) / 2 - RAIL.inset;
|
|
175
161
|
return { x, y: top, w: Math.max(...xs) + RAIL.inset - x, h: g.rows.length * RAIL.row - top * 2 };
|
|
176
162
|
}
|
package/dist/scene.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Pt, Rect } from './geometry.ts';
|
|
2
|
-
/**
|
|
2
|
+
/** `need` is the measured width; without it, `check` estimates it. */
|
|
3
3
|
export type TextRun = {
|
|
4
4
|
text: string;
|
|
5
5
|
fontSize: number;
|
|
@@ -12,34 +12,34 @@ export type SceneBox = {
|
|
|
12
12
|
rect: Rect;
|
|
13
13
|
texts: TextRun[];
|
|
14
14
|
};
|
|
15
|
-
/** `step` marks a rail row: rows of two steps never show at the same time, so their labels cannot overlap. */
|
|
16
|
-
/** `behind` marks an edge drawn under the boxes (a timeline dependency): `check` skips its crossing test. */
|
|
17
|
-
/** `pts` are the corners of an edge drawn as straight runs (a cross-block lanes edge): `check` tests them in place of `curve`. */
|
|
18
15
|
export type SceneEdge = {
|
|
19
16
|
id: string;
|
|
20
17
|
from: string;
|
|
21
18
|
to: string;
|
|
22
19
|
curve: [Pt, Pt, Pt, Pt];
|
|
20
|
+
/** Corners of a straight-run edge (cross-block lanes). `check` tests them, not `curve`. */
|
|
23
21
|
pts?: Pt[];
|
|
24
22
|
label?: Rect;
|
|
23
|
+
/** A rail row: rows of two steps never show together, so labels cannot overlap. */
|
|
25
24
|
step?: number;
|
|
25
|
+
/** Drawn under the boxes (a timeline dependency): `check` skips the crossing test. */
|
|
26
26
|
behind?: true;
|
|
27
|
-
/**
|
|
27
|
+
/** Timeline elbow corners. `check` tests a `behind` edge only when it has them. */
|
|
28
28
|
elbow?: Pt[];
|
|
29
29
|
};
|
|
30
|
-
/** `minFont` is the smallest reading text in px, before any scale. Group frames are not boxes. */
|
|
31
|
-
/** `lanes` are the lane bands of a swimlanes figure, one per lane and block, named by the lane label. A stub pill stays inside one. */
|
|
32
30
|
export type Scene = {
|
|
33
31
|
width: number;
|
|
34
32
|
boxes: SceneBox[];
|
|
35
33
|
edges: SceneEdge[];
|
|
34
|
+
/** The smallest reading text in px, before any scale. */
|
|
36
35
|
minFont: number;
|
|
36
|
+
/** The lane bands of a swimlanes figure. A stub pill stays inside one. */
|
|
37
37
|
lanes?: {
|
|
38
38
|
id: string;
|
|
39
39
|
rect: Rect;
|
|
40
40
|
}[];
|
|
41
41
|
};
|
|
42
|
-
/** One fault that `flowfig check` found. `
|
|
42
|
+
/** One fault that `flowfig check` found. `ids` name the boxes or edges. */
|
|
43
43
|
export type Finding = {
|
|
44
44
|
rule: string;
|
|
45
45
|
severity: 'error' | 'warning';
|
package/dist/source.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export declare function parseSource(s: string): {
|
|
|
4
4
|
path: string;
|
|
5
5
|
symbol?: string;
|
|
6
6
|
} | null;
|
|
7
|
-
/** The GitHub anchor of a heading: lower case, only letters, digits, spaces and hyphens, spaces to hyphens. */
|
|
8
7
|
export declare const headingSlug: (text: string) => string;
|
|
9
8
|
export type Link = {
|
|
10
9
|
owner: string;
|
|
@@ -13,6 +12,6 @@ export type Link = {
|
|
|
13
12
|
symbol?: string;
|
|
14
13
|
};
|
|
15
14
|
/** Every box, edge and hop with its raw `source`, well-formed or not. The one owner list that `links` and `checkSpec` share. */
|
|
16
|
-
export declare function owners(fig: FlowProps): [string, string | undefined][];
|
|
15
|
+
export declare function owners(fig: FlowProps): [string, string | undefined, string | undefined][];
|
|
17
16
|
/** Every well-formed link in the figure, with the box, edge or hop that carries it. `bad-source` covers the rest. */
|
|
18
17
|
export declare function links(fig: FlowProps): Link[];
|
package/dist/source.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// No Node import: the React and SVG entries must stay browser-safe.
|
|
2
2
|
import { edgeId, nodes, toBeat } from './model.js';
|
|
3
3
|
/** `path` or `path#symbol`. No spaces, one `#` at most, both parts non-empty. `null` for a bad form. */
|
|
4
4
|
export function parseSource(s) {
|
|
@@ -7,7 +7,7 @@ export function parseSource(s) {
|
|
|
7
7
|
return null;
|
|
8
8
|
return m[2] ? { path: m[1], symbol: m[2] } : { path: m[1] };
|
|
9
9
|
}
|
|
10
|
-
|
|
10
|
+
// GitHub anchor rules: drop other characters, then spaces become hyphens.
|
|
11
11
|
export const headingSlug = (text) => text
|
|
12
12
|
.toLowerCase()
|
|
13
13
|
.replace(/[^\p{L}\p{N} -]/gu, '')
|
|
@@ -15,9 +15,9 @@ export const headingSlug = (text) => text
|
|
|
15
15
|
/** Every box, edge and hop with its raw `source`, well-formed or not. The one owner list that `links` and `checkSpec` share. */
|
|
16
16
|
export function owners(fig) {
|
|
17
17
|
return [
|
|
18
|
-
...nodes(fig.layout).map((n) => [`box "${n.id}"`, n.source]),
|
|
19
|
-
...fig.edges.map((e) => [`edge "${edgeId(e)}"`, e.source]),
|
|
20
|
-
...(fig.steps ?? []).flatMap((s) => s.flow.flatMap((b) => toBeat(b).hops.map((h) => [`hop on "${h.edge}"`, h.source]))),
|
|
18
|
+
...nodes(fig.layout).map((n) => [`box "${n.id}"`, n.source, undefined]),
|
|
19
|
+
...fig.edges.map((e) => [`edge "${edgeId(e)}"`, e.source, e.via]),
|
|
20
|
+
...(fig.steps ?? []).flatMap((s) => s.flow.flatMap((b) => toBeat(b).hops.map((h) => [`hop on "${h.edge}"`, h.source, h.via]))),
|
|
21
21
|
];
|
|
22
22
|
}
|
|
23
23
|
/** Every well-formed link in the figure, with the box, edge or hop that carries it. `bad-source` covers the rest. */
|
package/dist/svg.d.ts
CHANGED
|
@@ -9,25 +9,22 @@ import { type FigTheme, type FlowProps } from './model.ts';
|
|
|
9
9
|
export { LIGHT, DARK } from './model.ts';
|
|
10
10
|
/** Options for `toSvg`, `render` and `check`. */
|
|
11
11
|
export type SvgOptions = {
|
|
12
|
-
/** Milliseconds a packet takes to cross one edge
|
|
12
|
+
/** Milliseconds a packet takes to cross one edge. Default: `FlowProps.speed`, else 900. */
|
|
13
13
|
speed?: number;
|
|
14
14
|
/** Space around the figure in px. Default: 24. */
|
|
15
15
|
padding?: number;
|
|
16
|
-
/** Colors
|
|
16
|
+
/** Colors. Default: `FlowProps.theme`. */
|
|
17
17
|
theme?: FigTheme;
|
|
18
|
-
/** Lanes:
|
|
18
|
+
/** Lanes: page width and smallest text size that set the wrap. Default: 830 and 10. */
|
|
19
19
|
width?: number;
|
|
20
20
|
minText?: number;
|
|
21
21
|
};
|
|
22
|
-
/**
|
|
23
|
-
* The SVG string, and the scene it drew. The scene lets `flowfig check` test the same layout the SVG uses.
|
|
24
|
-
* The scene is for tools, and its shape may change. Use `toSvg` if you only need the SVG.
|
|
25
|
-
*/
|
|
22
|
+
/** The SVG string and the scene it drew. The scene shape may change; use `toSvg` for the SVG only. */
|
|
26
23
|
export declare function render(fig: FlowProps, opts?: SvgOptions): {
|
|
27
24
|
svg: string;
|
|
28
25
|
scene: Scene;
|
|
29
26
|
};
|
|
30
27
|
/** One self-contained animated SVG string for the figure. Needs no React and no browser. */
|
|
31
28
|
export declare function toSvg(fig: FlowProps, opts?: SvgOptions): string;
|
|
32
|
-
/** Every fault `flowfig check` knows about, for this figure as the SVG lays it out.
|
|
29
|
+
/** Every fault `flowfig check` knows about, for this figure as the SVG lays it out. */
|
|
33
30
|
export declare function check(fig: FlowProps, opts?: SvgOptions & CheckOptions): Finding[];
|