tsquare 0.12.4 → 0.13.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/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/dist/anchors.d.ts +12 -3
- package/dist/browser/index.js +303 -85
- package/dist/chunks/{chunk-XWGHLFRX.js → chunk-PGX7ANZH.js} +1 -1
- package/dist/chunks/{chunk-BSE346UI.js → chunk-YOZFCNSK.js} +154 -69
- package/dist/cli.js +1 -1
- package/dist/compile.d.ts +2 -0
- package/dist/flows.d.ts +31 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +166 -3
- package/dist/measure.d.ts +97 -0
- package/dist/playground/index.js +2 -2
- package/dist/render.d.ts +24 -0
- package/dist/tsquare.min.js +68 -68
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
languageData
|
|
3
|
-
} from "./chunks/chunk-
|
|
3
|
+
} from "./chunks/chunk-PGX7ANZH.js";
|
|
4
4
|
import {
|
|
5
5
|
DEVICES,
|
|
6
|
+
LABEL_H,
|
|
7
|
+
PRIMARY_PROP,
|
|
6
8
|
RENAMED_PROPS,
|
|
9
|
+
SpecError,
|
|
7
10
|
WireframeError,
|
|
8
11
|
boardLayout,
|
|
9
12
|
boardSize,
|
|
@@ -11,20 +14,32 @@ import {
|
|
|
11
14
|
checkSpec,
|
|
12
15
|
compileWireframe,
|
|
13
16
|
componentDefinitions,
|
|
17
|
+
findAnchors,
|
|
18
|
+
flowEnds,
|
|
19
|
+
flowPolyline,
|
|
14
20
|
formatIssues,
|
|
15
21
|
languageReference,
|
|
22
|
+
layoutFlows,
|
|
23
|
+
loadFonts,
|
|
24
|
+
overlayBoxes,
|
|
25
|
+
paletteFor,
|
|
16
26
|
parseWireframeText,
|
|
17
27
|
printWireframeText,
|
|
28
|
+
readMarkers,
|
|
18
29
|
registry,
|
|
19
30
|
renderWireframe,
|
|
20
31
|
renderWireframePng,
|
|
21
32
|
renderWireframeSvg,
|
|
22
33
|
repairPrompt,
|
|
34
|
+
tagForMeasuring,
|
|
23
35
|
theme,
|
|
24
36
|
upgradeSpec,
|
|
25
37
|
upgradeWireframe,
|
|
26
|
-
wireframePrompt
|
|
27
|
-
|
|
38
|
+
wireframePrompt,
|
|
39
|
+
withBoardGap,
|
|
40
|
+
withMarkers,
|
|
41
|
+
withPalette
|
|
42
|
+
} from "./chunks/chunk-YOZFCNSK.js";
|
|
28
43
|
|
|
29
44
|
// src/deflate.ts
|
|
30
45
|
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
@@ -240,6 +255,151 @@ function addStyles(doc) {
|
|
|
240
255
|
style.textContent = STYLES;
|
|
241
256
|
doc.head.append(style);
|
|
242
257
|
}
|
|
258
|
+
|
|
259
|
+
// src/measure.ts
|
|
260
|
+
import { renderToSvg } from "@json-render/image/render";
|
|
261
|
+
var isFlow = (e) => e.type === "Flow";
|
|
262
|
+
var COVERS_SCREEN = /* @__PURE__ */ new Set(["Modal", "Drawer"]);
|
|
263
|
+
var OVERLAYS = /* @__PURE__ */ new Set(["Modal", "Drawer", "Toast"]);
|
|
264
|
+
var CONTAINERS = new Set(Object.entries(componentDefinitions).filter(([, d]) => d.slots.length).map(([name]) => name));
|
|
265
|
+
var BARS = /* @__PURE__ */ new Set(["NavBar", "TabBar"]);
|
|
266
|
+
var toBox = (r) => ({ x: round(r.x), y: round(r.y), width: round(r.w), height: round(r.h) });
|
|
267
|
+
var round = (n) => Math.round(n * 100) / 100;
|
|
268
|
+
function clip(r, to) {
|
|
269
|
+
if (!to) return r;
|
|
270
|
+
const x = Math.max(r.x, to.x), y = Math.max(r.y, to.y);
|
|
271
|
+
const w = Math.min(r.x + r.w, to.x + to.w) - x, h = Math.min(r.y + r.h, to.y + to.h) - y;
|
|
272
|
+
return w >= 0 && h >= 0 && (w > 0 || r.w === 0) && (h > 0 || r.h === 0) ? { x, y, w, h } : null;
|
|
273
|
+
}
|
|
274
|
+
async function measureElements(spec, lines, opts = {}) {
|
|
275
|
+
upgradeSpec(spec);
|
|
276
|
+
if (!opts.skipValidation) {
|
|
277
|
+
const issues = checkSpec(spec);
|
|
278
|
+
if (issues.length) throw new SpecError(issues);
|
|
279
|
+
}
|
|
280
|
+
const { width, height } = boardLayout(spec, { flows: opts.flows });
|
|
281
|
+
spec = withBoardGap(spec, opts.flows);
|
|
282
|
+
const els = spec.elements;
|
|
283
|
+
const anchors = findAnchors(spec);
|
|
284
|
+
const every = Object.keys(els).filter((k) => !["Board", "Screen", "Flow"].includes(els[k].type)).map((id) => ({ id, screen: id, kind: "tooltip", field: false }));
|
|
285
|
+
const screens = Object.keys(els).filter((k) => els[k].type === "Screen");
|
|
286
|
+
const { tagged, colors } = tagForMeasuring(spec, [...anchors, ...every], screens);
|
|
287
|
+
const svg = await renderToSvg(tagged, { registry: withMarkers(withPalette(paletteFor())), includeStandard: false, fonts: await loadFonts(), width, height });
|
|
288
|
+
const boxes = readMarkers(svg, colors);
|
|
289
|
+
const { items } = boardLayout(spec, { flows: opts.flows });
|
|
290
|
+
const laidOut = (els[spec.root].children ?? []).filter((k) => els[k]?.type === "Screen" || els[k]?.type === "Note");
|
|
291
|
+
const frames = /* @__PURE__ */ new Map(), bands = /* @__PURE__ */ new Map();
|
|
292
|
+
laidOut.forEach((k, i) => {
|
|
293
|
+
const it = items[i];
|
|
294
|
+
if (!it || els[k].type !== "Screen") return;
|
|
295
|
+
frames.set(k, { x: it.x, y: it.y + LABEL_H, w: it.width, h: it.height - LABEL_H });
|
|
296
|
+
bands.set(k, { x: it.x, y: it.y, w: it.width, h: LABEL_H });
|
|
297
|
+
});
|
|
298
|
+
const overlays = /* @__PURE__ */ new Map();
|
|
299
|
+
for (const o of overlayBoxes(spec, anchors, boxes)) overlays.set(o.id, [...overlays.get(o.id) ?? [], o.box]);
|
|
300
|
+
const lineOf = (k) => k == null ? null : lines[k] ?? 0;
|
|
301
|
+
const endLine = (k) => Math.max(lines[k] ?? 0, ...(els[k].children ?? []).filter((c) => els[c]).map(endLine));
|
|
302
|
+
const labelOf = (k) => {
|
|
303
|
+
const p = els[k].props ?? {};
|
|
304
|
+
const v = p[PRIMARY_PROP[els[k].type] ?? ""];
|
|
305
|
+
return typeof v === "string" ? v : void 0;
|
|
306
|
+
};
|
|
307
|
+
const out = [];
|
|
308
|
+
const visit = (k, parent, screen, cut) => {
|
|
309
|
+
const el = els[k];
|
|
310
|
+
if (!el || el.type === "Flow") return;
|
|
311
|
+
const p = el.props ?? {};
|
|
312
|
+
const base = { line: lineOf(k), endLine: endLine(k), ...typeof p.id === "string" ? { id: p.id } : {}, type: el.type, ...labelOf(k) != null ? { label: labelOf(k) } : {}, screenLine: lineOf(screen), parentLine: lineOf(parent) };
|
|
313
|
+
let entry = null;
|
|
314
|
+
if (el.type === "Board") entry = { ...base, box: { x: 0, y: 0, width, height } };
|
|
315
|
+
else if (el.type === "Screen") {
|
|
316
|
+
const frame = frames.get(k);
|
|
317
|
+
if (frame) entry = { ...base, box: toBox(frame), labelBox: toBox(bands.get(k)) };
|
|
318
|
+
} else {
|
|
319
|
+
const r = boxes[k];
|
|
320
|
+
const seen = r && clip(r, cut);
|
|
321
|
+
if (seen) {
|
|
322
|
+
const view = screen ? boxes[screen] ?? frames.get(screen) : void 0;
|
|
323
|
+
const os = (overlays.get(k) ?? []).map((o) => clip(o, view)).filter((o) => !!o && o.w > 0 && o.h > 0);
|
|
324
|
+
entry = { ...base, box: toBox(seen), ...os.length ? { overlays: os.map(toBox) } : {}, ...COVERS_SCREEN.has(el.type) ? { scrim: p.scrim ?? true } : {} };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (entry) out.push(entry);
|
|
328
|
+
const inScreen = el.type === "Screen" ? k : screen;
|
|
329
|
+
const kids = el.children ?? [];
|
|
330
|
+
if (el.type === "Screen") {
|
|
331
|
+
const frame = frames.get(k) ?? null;
|
|
332
|
+
const body = clip(boxes[`${k}#body`] ?? frame, frame);
|
|
333
|
+
const over = (c) => OVERLAYS.has(els[c]?.type);
|
|
334
|
+
for (const c of [...kids.filter((c2) => !over(c2)), ...kids.filter(over)]) visit(c, k, k, BARS.has(els[c]?.type) || over(c) ? frame : body);
|
|
335
|
+
} else {
|
|
336
|
+
const inner = el.type === "Drawer" && boxes[k] ? clip(boxes[k], cut) : cut;
|
|
337
|
+
for (const c of kids) visit(c, k, inScreen, inner);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
visit(spec.root, null, null, null);
|
|
341
|
+
if (opts.flows !== false) {
|
|
342
|
+
const flows = flowEnds(spec);
|
|
343
|
+
const resolved = flows.resolve(boxes);
|
|
344
|
+
const ends = new Map(flows.list.map((f) => [f.key, f]));
|
|
345
|
+
layoutFlows(spec, resolved, flows.screens, flows.gap, paletteFor()).forEach((f) => {
|
|
346
|
+
const p = els[f.key].props ?? {};
|
|
347
|
+
const e = ends.get(f.key);
|
|
348
|
+
out.push({
|
|
349
|
+
line: lineOf(f.key),
|
|
350
|
+
endLine: lineOf(f.key),
|
|
351
|
+
type: "Flow",
|
|
352
|
+
...f.label ? { label: f.label.text } : {},
|
|
353
|
+
from: String(p.from),
|
|
354
|
+
to: String(p.to),
|
|
355
|
+
fromLine: lineOf(e.from),
|
|
356
|
+
toLine: lineOf(e.to),
|
|
357
|
+
path: flowPolyline(f).map((q) => [round(q.x), round(q.y)]),
|
|
358
|
+
...f.label ? { labelBox: toBox(f.label) } : {}
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
var inside = (b, x, y) => !!b && b.width > 0 && b.height > 0 && x >= b.x && x <= b.x + b.width && y >= b.y && y <= b.y + b.height;
|
|
365
|
+
var center = (b) => [b.x + b.width / 2, b.y + b.height / 2];
|
|
366
|
+
function distanceToPath(path, x, y) {
|
|
367
|
+
let best = Infinity;
|
|
368
|
+
for (let i = 1; i < path.length; i++) {
|
|
369
|
+
const [ax, ay] = path[i - 1], [bx, by] = path[i];
|
|
370
|
+
const dx = bx - ax, dy = by - ay, len = dx * dx + dy * dy;
|
|
371
|
+
const t = len ? Math.max(0, Math.min(1, ((x - ax) * dx + (y - ay) * dy) / len)) : 0;
|
|
372
|
+
best = Math.min(best, Math.hypot(x - ax - t * dx, y - ay - t * dy));
|
|
373
|
+
}
|
|
374
|
+
return best;
|
|
375
|
+
}
|
|
376
|
+
var isContainer = (e) => CONTAINERS.has(e.type);
|
|
377
|
+
function elementsAt(elements, x, y, opts = {}) {
|
|
378
|
+
const tolerance = opts.tolerance ?? 6;
|
|
379
|
+
const order = new Map(elements.map((e, i) => [e, i]));
|
|
380
|
+
const topFirst = (a, b) => order.get(b) - order.get(a);
|
|
381
|
+
const covered = /* @__PURE__ */ new Set();
|
|
382
|
+
const screens = elements.filter((e) => e.type === "Screen");
|
|
383
|
+
const screenAround = (b) => screens.find((s) => inside(s.box, ...center(b)));
|
|
384
|
+
for (const [i, m] of elements.entries()) {
|
|
385
|
+
if (isFlow(m) || !OVERLAYS.has(m.type)) continue;
|
|
386
|
+
const screen = screenAround(m.box);
|
|
387
|
+
if (!screen || !inside(m.box, x, y) && !(m.scrim && inside(screen.box, x, y))) continue;
|
|
388
|
+
for (const e of elements.slice(0, i)) if (!isFlow(e) && e.type !== "Screen" && e.type !== "Board" && screenAround(e.box) === screen) covered.add(e);
|
|
389
|
+
}
|
|
390
|
+
const owners = [], labels = [], leaves = [], lines = [], containers = [];
|
|
391
|
+
for (const e of elements) {
|
|
392
|
+
if (isFlow(e)) {
|
|
393
|
+
if (inside(e.labelBox, x, y)) labels.push(e);
|
|
394
|
+
else if (distanceToPath(e.path, x, y) <= tolerance) lines.push(e);
|
|
395
|
+
} else if (e.overlays?.some((o) => inside(o, x, y))) owners.push(e);
|
|
396
|
+
else if (!covered.has(e) && (inside(e.box, x, y) || inside(e.labelBox, x, y))) (isContainer(e) ? containers : leaves).push(e);
|
|
397
|
+
}
|
|
398
|
+
return [owners, labels, leaves, lines, containers].flatMap((g) => g.sort(topFirst));
|
|
399
|
+
}
|
|
400
|
+
function elementsAtLine(elements, line) {
|
|
401
|
+
return elements.filter((e) => e.line > 0 && e.line <= line && line <= e.endLine).sort((a, b) => b.line - a.line);
|
|
402
|
+
}
|
|
243
403
|
export {
|
|
244
404
|
DEFAULT_LINKS,
|
|
245
405
|
DEFAULT_SELECTOR,
|
|
@@ -257,11 +417,14 @@ export {
|
|
|
257
417
|
compileWireframe,
|
|
258
418
|
componentDefinitions,
|
|
259
419
|
decodeWireframe,
|
|
420
|
+
elementsAt,
|
|
421
|
+
elementsAtLine,
|
|
260
422
|
encodeWireframe,
|
|
261
423
|
formatIssues,
|
|
262
424
|
printWireframeText as formatWireframe,
|
|
263
425
|
languageData,
|
|
264
426
|
mcpTools,
|
|
427
|
+
measureElements,
|
|
265
428
|
parseWireframeText,
|
|
266
429
|
registry,
|
|
267
430
|
renderAll,
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element positions: where every element and flow of a board is drawn, for laying hit boxes over
|
|
3
|
+
* the image (click an element to comment on it or ask for a change), and what is under a point.
|
|
4
|
+
*
|
|
5
|
+
* measureElements renders the board once more with a marker on every element (the measuring pass
|
|
6
|
+
* of anchored overlays, see anchors.tsx) and reads the boxes back. Flow lines come from the same
|
|
7
|
+
* layout the renderer draws them with. elementsAt and elementsAtLine are plain lookups on its result,
|
|
8
|
+
* so measure once per text and look up as often as needed (they also run on the JSON in a browser).
|
|
9
|
+
*/
|
|
10
|
+
import type { Spec } from "@json-render/core";
|
|
11
|
+
import { type ComponentName } from "./catalog.js";
|
|
12
|
+
/** A box on the board, in board pixels: the same coordinates as the rendered SVG. */
|
|
13
|
+
export interface MeasuredBox {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MeasuredElement {
|
|
20
|
+
/** The element's line in the wireframe text; 0 when `lines` doesn't have it. */
|
|
21
|
+
line: number;
|
|
22
|
+
/** The last line of its block (its children included); `line` when it has no children. */
|
|
23
|
+
endLine: number;
|
|
24
|
+
/** Its #id, when the text gives one. */
|
|
25
|
+
id?: string;
|
|
26
|
+
/** "Button", "Screen", … */
|
|
27
|
+
type: Exclude<ComponentName, "Flow">;
|
|
28
|
+
/** Its main text: a button's label, a screen's name, a heading's text. */
|
|
29
|
+
label?: string;
|
|
30
|
+
/** The line of the screen it's on; null for the board, screens and notes beside them. */
|
|
31
|
+
screenLine: number | null;
|
|
32
|
+
/** The line of the element containing it; null for the board. */
|
|
33
|
+
parentLine: number | null;
|
|
34
|
+
/**
|
|
35
|
+
* Where it's drawn: the part that shows, for content cut off by its screen (past the bottom, or
|
|
36
|
+
* under a tab bar) or by a drawer's panel. A screen's box is its frame. Can be 0 wide or 0 tall
|
|
37
|
+
* (a spacer in a row): such an element is listed, but never under a point.
|
|
38
|
+
*/
|
|
39
|
+
box: MeasuredBox;
|
|
40
|
+
/** Screens: the band above the frame with the screen's name. */
|
|
41
|
+
labelBox?: MeasuredBox;
|
|
42
|
+
/** Its open menu, option list or date picker, and its tooltip, in drawing order (sizes partly estimated from the text). */
|
|
43
|
+
overlays?: MeasuredBox[];
|
|
44
|
+
/** Modals and drawers: whether the screen behind is dimmed (on unless the text says no-scrim). */
|
|
45
|
+
scrim?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface MeasuredFlow {
|
|
48
|
+
/** The flow's line in the wireframe text; 0 when `lines` doesn't have it. */
|
|
49
|
+
line: number;
|
|
50
|
+
/** Always `line`: a flow has no children. */
|
|
51
|
+
endLine: number;
|
|
52
|
+
type: "Flow";
|
|
53
|
+
label?: string;
|
|
54
|
+
/** The ids it connects, as written in the flow line. */
|
|
55
|
+
from: string;
|
|
56
|
+
to: string;
|
|
57
|
+
/** The lines of the elements (or screens) it connects. */
|
|
58
|
+
fromLine: number;
|
|
59
|
+
toLine: number;
|
|
60
|
+
/** The drawn line as points, from the tip of one end's marker to the other's; curves and corners are sampled. */
|
|
61
|
+
path: [number, number][];
|
|
62
|
+
/** The label pill. */
|
|
63
|
+
labelBox?: MeasuredBox;
|
|
64
|
+
}
|
|
65
|
+
export type Measured = MeasuredElement | MeasuredFlow;
|
|
66
|
+
export interface MeasureOptions {
|
|
67
|
+
/** As passed to renderWireframeSvg (default true): flow labels can widen the gaps between screens, so the boxes only match a render with the same setting. */
|
|
68
|
+
flows?: boolean;
|
|
69
|
+
/** Skip validation (as renderWireframeSvg). */
|
|
70
|
+
skipValidation?: boolean;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Every element and flow on the board and where it's drawn, in drawing order (what's drawn later
|
|
74
|
+
* sits on top), starting with the board. `lines` maps the spec's element keys to source lines:
|
|
75
|
+
* compileWireframe returns it.
|
|
76
|
+
* Elements that aren't drawn (content entirely cut off, children of a closed accordion) are left out.
|
|
77
|
+
*/
|
|
78
|
+
export declare function measureElements(spec: Spec, lines: Record<string, number>, opts?: MeasureOptions): Promise<[MeasuredElement, ...Measured[]]>;
|
|
79
|
+
/**
|
|
80
|
+
* Everything under a point (board pixels), most likely meant first:
|
|
81
|
+
* 1. an open menu, option list, date picker or tooltip: the element it belongs to
|
|
82
|
+
* 2. a flow's label
|
|
83
|
+
* 3. elements that hold no others (buttons, inputs, text, …), topmost first
|
|
84
|
+
* 4. a flow's line, within `tolerance` px of it (default 6; pass more when the board is shown zoomed out)
|
|
85
|
+
* 5. stacks, cards, modals and drawers, then the screen, then the board
|
|
86
|
+
* A modal, drawer or toast hides the screen's content under it. One that dims its screen (modals and
|
|
87
|
+
* drawers, unless no-scrim) hides all of it: on the dimmed part, only the screen and the board.
|
|
88
|
+
*/
|
|
89
|
+
export declare function elementsAt(elements: Measured[], x: number, y: number, opts?: {
|
|
90
|
+
tolerance?: number;
|
|
91
|
+
}): Measured[];
|
|
92
|
+
/**
|
|
93
|
+
* The element on a line and every block around it, innermost first: for a code view that follows
|
|
94
|
+
* the caret. A comment or blank line between an element's children gives that element. A flow's
|
|
95
|
+
* line gives the flow, then the board.
|
|
96
|
+
*/
|
|
97
|
+
export declare function elementsAtLine(elements: Measured[], line: number): Measured[];
|
package/dist/playground/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
languageData
|
|
3
|
-
} from "../chunks/chunk-
|
|
3
|
+
} from "../chunks/chunk-PGX7ANZH.js";
|
|
4
4
|
import {
|
|
5
5
|
boardLayout,
|
|
6
6
|
compileWireframe,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
renderWireframePng,
|
|
10
10
|
renderWireframeSvg,
|
|
11
11
|
wireframePrompt
|
|
12
|
-
} from "../chunks/chunk-
|
|
12
|
+
} from "../chunks/chunk-YOZFCNSK.js";
|
|
13
13
|
|
|
14
14
|
// src/playground/index.ts
|
|
15
15
|
import { createServer } from "node:http";
|
package/dist/render.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Spec } from "@json-render/core";
|
|
2
2
|
import { type Anchor, type Rect } from "./anchors.js";
|
|
3
|
+
import { type FlowBoxes } from "./flows.js";
|
|
3
4
|
export declare class SpecError extends Error {
|
|
4
5
|
issues: string[];
|
|
5
6
|
constructor(issues: string[]);
|
|
@@ -43,6 +44,29 @@ export interface RenderWireframeOptions {
|
|
|
43
44
|
flows?: boolean;
|
|
44
45
|
}
|
|
45
46
|
export declare function renderWireframeSvg(spec: Spec, opts?: RenderWireframeOptions): Promise<string>;
|
|
47
|
+
/**
|
|
48
|
+
* The spec as it's drawn: a labeled flow between neighboring screens can widen the board's gap
|
|
49
|
+
* (boardLayout), and the Board component reads the gap from its props.
|
|
50
|
+
*/
|
|
51
|
+
export declare function withBoardGap(spec: Spec, flows?: boolean): Spec;
|
|
52
|
+
/**
|
|
53
|
+
* The flows on a board and how to find their ends: element ends are measured (`measure`), screen
|
|
54
|
+
* ends come from the board layout. `resolve(boxes)` gives each drawable flow's end boxes once measured.
|
|
55
|
+
*/
|
|
56
|
+
export declare function flowEnds(spec: Spec): {
|
|
57
|
+
list: {
|
|
58
|
+
key: string;
|
|
59
|
+
from: string;
|
|
60
|
+
to: string;
|
|
61
|
+
}[];
|
|
62
|
+
measure: string[];
|
|
63
|
+
screens: Rect[];
|
|
64
|
+
gap: any;
|
|
65
|
+
resolve(boxes: Record<string, Rect>): {
|
|
66
|
+
key: string;
|
|
67
|
+
ends: FlowBoxes;
|
|
68
|
+
}[];
|
|
69
|
+
};
|
|
46
70
|
/**
|
|
47
71
|
* The measuring pass: the board's anchored elements (and their screens) and their boxes on the
|
|
48
72
|
* canvas, keyed by element id (`<id>#field` for a field). No render at all when there are none.
|