flowfig 0.10.0 → 0.12.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 +152 -23
- package/dist/atlas.js +6 -2
- package/dist/auto.js +104 -9
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +9 -4
- package/dist/check.d.ts +4 -3
- package/dist/check.js +143 -8
- package/dist/cli.js +180 -70
- package/dist/diff.d.ts +2 -0
- package/dist/diff.js +79 -24
- package/dist/element-looks.js +4 -0
- package/dist/element-world.js +6 -0
- package/dist/element.d.ts +27 -0
- package/dist/element.js +67 -0
- package/dist/explain.d.ts +45 -0
- package/dist/explain.js +161 -0
- package/dist/frames.d.ts +34 -0
- package/dist/frames.js +117 -0
- package/dist/index.js +328 -152
- package/dist/load.js +9 -4
- package/dist/looks.d.ts +15 -0
- package/dist/looks.js +32 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/mcp.js +14 -6
- package/dist/model.d.ts +105 -3
- package/dist/model.js +221 -21
- package/dist/scene.d.ts +2 -0
- package/dist/source.d.ts +4 -2
- package/dist/source.js +15 -9
- package/dist/svg.d.ts +12 -0
- package/dist/svg.js +245 -81
- package/dist/verify.d.ts +6 -0
- package/dist/verify.js +104 -56
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world-art.d.ts +59 -0
- package/dist/world-art.js +854 -0
- package/dist/world.d.ts +233 -0
- package/dist/world.js +1577 -0
- package/package.json +9 -3
package/dist/frames.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { beatTimes, DARK, edgeId, LIGHT, n2, nodes, scenes, str, toBeat, tourPlan } from './model.js';
|
|
2
|
+
import { esc } from './page.js';
|
|
3
|
+
import { textWidth } from './text.js';
|
|
4
|
+
export function keyFrames(fig, o) {
|
|
5
|
+
const steps = fig.steps ?? [];
|
|
6
|
+
const beats = steps.map((s) => s.flow.map(toBeat));
|
|
7
|
+
const tour = scenes(fig).length > 1 ? tourPlan(fig) : undefined;
|
|
8
|
+
const model = tour?.times ?? beatTimes(beats, fig.speed ?? 900);
|
|
9
|
+
const loop = tour ? tour.ms / 1000 : (model.ends.at(-1) ?? 0);
|
|
10
|
+
const dur = n2(loop || 1);
|
|
11
|
+
const scale = loop ? dur / loop : 1;
|
|
12
|
+
const times = {
|
|
13
|
+
beats: model.beats.map((step) => step.map((b) => ({ t0: b.t0 * scale, t1: b.t1 * scale, tEnd: b.tEnd * scale }))),
|
|
14
|
+
ends: model.ends.map((e) => e * scale),
|
|
15
|
+
};
|
|
16
|
+
const frames = Math.max(1, Math.round(dur * o.fps));
|
|
17
|
+
const first = o.step ?? 1;
|
|
18
|
+
const name = new Map(nodes(fig.layout).map((n) => [n.id, str(n.label) || n.id]));
|
|
19
|
+
const edges = new Map(fig.edges.map((e) => [edgeId(e), e]));
|
|
20
|
+
const hopText = (si, bi) => beats[si][bi].hops
|
|
21
|
+
.map((h) => {
|
|
22
|
+
const e = edges.get(h.edge);
|
|
23
|
+
if (!e)
|
|
24
|
+
return h.edge;
|
|
25
|
+
const [a, b] = h.back ? [e.to, e.from] : [e.from, e.to];
|
|
26
|
+
const label = str(e.label);
|
|
27
|
+
return `${name.get(a) ?? a} → ${name.get(b) ?? b}${label ? ` (${label})` : ''}`;
|
|
28
|
+
})
|
|
29
|
+
.join('; ') || 'no hop';
|
|
30
|
+
const said = (si, bi) => {
|
|
31
|
+
const at = beats[si].slice(0, bi + 1).findLastIndex((b) => b.say != null);
|
|
32
|
+
return str(at === -1 ? steps[si].caption : beats[si][at].say);
|
|
33
|
+
};
|
|
34
|
+
const tiles = [];
|
|
35
|
+
const add = (at, kind, si, bi, text, say) => {
|
|
36
|
+
const last = tiles.at(-1);
|
|
37
|
+
const frame = Math.min(frames - 1, Math.max(at, last?.frame ?? 0));
|
|
38
|
+
if (last && frame === last.frame) {
|
|
39
|
+
if (!last.kinds.includes(kind))
|
|
40
|
+
last.kinds.push(kind);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
tiles.push({ frame, kinds: [kind], step: si + first, beat: bi + 1, text, say });
|
|
44
|
+
};
|
|
45
|
+
const f = o.fps;
|
|
46
|
+
if (o.every) {
|
|
47
|
+
const flat = beats.flatMap((step, si) => step.map((b, bi) => ({ si, bi, hop: b.hops.length > 0, ...times.beats[si][bi] })));
|
|
48
|
+
for (let k = 0; k * o.every < dur * 1000; k++) {
|
|
49
|
+
if (Math.round((k * o.every * f) / 1000) >= frames)
|
|
50
|
+
break;
|
|
51
|
+
const t = (k * o.every) / 1000;
|
|
52
|
+
const at = flat.findLast((x) => x.t0 <= t) ?? flat[0];
|
|
53
|
+
const kind = at.hop && t < Math.min(at.t1, at.tEnd) ? 'move' : t < at.tEnd ? 'read' : 'hold';
|
|
54
|
+
add(Math.round((k * o.every * f) / 1000), kind, at.si, at.bi, hopText(at.si, at.bi), said(at.si, at.bi));
|
|
55
|
+
}
|
|
56
|
+
return { tiles, frames };
|
|
57
|
+
}
|
|
58
|
+
beats.forEach((step, si) => {
|
|
59
|
+
step.forEach((b, bi) => {
|
|
60
|
+
const { t0, t1, tEnd } = times.beats[si][bi];
|
|
61
|
+
const text = hopText(si, bi), say = said(si, bi);
|
|
62
|
+
if (b.hops.length) {
|
|
63
|
+
add(Math.round(((t0 + Math.min(t1, tEnd)) / 2) * f), 'move', si, bi, text, say);
|
|
64
|
+
if (t1 <= tEnd)
|
|
65
|
+
add(Math.ceil(t1 * f + 0.5), 'arrive', si, bi, text, say);
|
|
66
|
+
}
|
|
67
|
+
add(Math.floor(tEnd * f - 0.5), 'read', si, bi, text, say);
|
|
68
|
+
});
|
|
69
|
+
add(Math.floor(times.ends[si] * f - 0.5), 'end', si, -1, `end of step ${si + first}: ${str(steps[si].label)}`, '');
|
|
70
|
+
});
|
|
71
|
+
return { tiles, frames };
|
|
72
|
+
}
|
|
73
|
+
export function sheetGrid(count, width, height, scale = 1) {
|
|
74
|
+
const [limit, gap, head, band] = [1568, 8, 24, 34].map((n) => Math.round(n * scale));
|
|
75
|
+
const size = (cols, tile) => {
|
|
76
|
+
const rows = Math.ceil(count / cols), tileH = Math.round((tile * height) / width);
|
|
77
|
+
return { cols, rows, tile, tileH, width: cols * tile + (cols + 1) * gap, height: head + rows * (tileH + band) + (rows + 1) * gap };
|
|
78
|
+
};
|
|
79
|
+
let best = size(1, 0);
|
|
80
|
+
for (let c = 1; c <= count; c++) {
|
|
81
|
+
const rows = Math.ceil(count / c);
|
|
82
|
+
let tile = Math.floor(Math.min((limit - (c + 1) * gap) / c, (((limit - head - (rows + 1) * gap) / rows - band) * width) / height));
|
|
83
|
+
while (tile > 1 && Math.max(size(c, tile).width, size(c, tile).height) > limit)
|
|
84
|
+
tile--;
|
|
85
|
+
if (tile > best.tile)
|
|
86
|
+
best = size(c, tile);
|
|
87
|
+
}
|
|
88
|
+
const small = 160 * scale;
|
|
89
|
+
return best.tile < small
|
|
90
|
+
? { ...best, warning: `warning: the tiles are ${best.tile} px wide, under ${small} px. Use --step <n> to show fewer tiles.` }
|
|
91
|
+
: best;
|
|
92
|
+
}
|
|
93
|
+
export function sheetSvg(tiles, pngs, grid, o) {
|
|
94
|
+
const c = o.dark ? DARK : LIGHT;
|
|
95
|
+
const [gap, head, band, font, pad] = [8, 24, 34, 12, 4].map((n) => Math.round(n * o.scale));
|
|
96
|
+
const fit = (line) => {
|
|
97
|
+
let cut = line;
|
|
98
|
+
while (cut && textWidth(cut === line ? cut : cut + '…', font, true) > grid.tile - 2 * pad)
|
|
99
|
+
cut = cut.slice(0, -1);
|
|
100
|
+
return cut === line ? line : cut + '…';
|
|
101
|
+
};
|
|
102
|
+
const text = (x, y, s) => `<text x="${x}" y="${y}">${esc(s)}</text>`;
|
|
103
|
+
const cells = tiles.map((t, i) => {
|
|
104
|
+
const x = gap + (i % grid.cols) * (grid.tile + gap), y = head + gap + Math.floor(i / grid.cols) * (grid.tileH + band + gap);
|
|
105
|
+
const line1 = `#${t.frame} · ${Math.round((t.frame * 1000) / o.fps)} ms · step ${t.step}${t.beat ? ` beat ${t.beat}` : ''} · ${t.kinds.join('+')}`;
|
|
106
|
+
return (`<image x="${x}" y="${y}" width="${grid.tile}" height="${grid.tileH}" href="data:image/png;base64,${pngs[i].toString('base64')}"/>` +
|
|
107
|
+
`<rect x="${x - 0.5}" y="${y - 0.5}" width="${grid.tile + 1}" height="${grid.tileH + 1}" fill="none" stroke="${c.border}"/>` +
|
|
108
|
+
text(x + pad, y + grid.tileH + Math.round(14 * o.scale), fit(line1)) +
|
|
109
|
+
text(x + pad, y + grid.tileH + Math.round(29 * o.scale), fit(t.text)));
|
|
110
|
+
});
|
|
111
|
+
return (`<svg xmlns="http://www.w3.org/2000/svg" width="${grid.width}" height="${grid.height}" viewBox="0 0 ${grid.width} ${grid.height}" ` +
|
|
112
|
+
`font-family="ui-monospace, Menlo, Consolas, monospace" font-size="${font}" fill="${c.fg}">` +
|
|
113
|
+
`<rect width="${grid.width}" height="${grid.height}" fill="${c.surface}"/>` +
|
|
114
|
+
text(gap, head - Math.round(7 * o.scale), o.header) +
|
|
115
|
+
cells.join('') +
|
|
116
|
+
`</svg>`);
|
|
117
|
+
}
|