tsquare 0.13.2 → 0.14.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 +9 -0
- package/README.md +2 -2
- package/dist/browser/index.js +43 -41
- package/dist/chunks/chunk-NZWKURF4.js +236 -0
- package/dist/editor.js +10 -1
- package/dist/hit.d.ts +88 -0
- package/dist/index.js +6 -167
- package/dist/measure.d.ts +2 -74
- package/dist/playground/editor-options.d.ts +2 -0
- package/dist/playground/index.js +10 -5
- package/dist/playground/playground.js +19 -19
- package/dist/playground/viewer.d.ts +51 -6
- package/dist/playground/viewer.js +160 -5
- package/dist/tsquare.min.js +45 -45
- package/package.json +2 -2
- package/dist/chunks/chunk-W5RDSAXT.js +0 -65
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
elementsAt,
|
|
3
|
+
elementsAtLine,
|
|
4
|
+
languageData,
|
|
5
|
+
measureElements
|
|
6
|
+
} from "./chunks/chunk-NZWKURF4.js";
|
|
4
7
|
import {
|
|
5
8
|
DEVICES,
|
|
6
|
-
LABEL_H,
|
|
7
|
-
PRIMARY_PROP,
|
|
8
9
|
RENAMED_PROPS,
|
|
9
|
-
SpecError,
|
|
10
10
|
WireframeError,
|
|
11
11
|
boardLayout,
|
|
12
12
|
boardSize,
|
|
@@ -14,33 +14,20 @@ import {
|
|
|
14
14
|
checkSpec,
|
|
15
15
|
compileWireframe,
|
|
16
16
|
componentDefinitions,
|
|
17
|
-
findAnchors,
|
|
18
|
-
flowEnds,
|
|
19
|
-
flowPolyline,
|
|
20
17
|
formatIssues,
|
|
21
18
|
languageReference,
|
|
22
|
-
layoutFlows,
|
|
23
|
-
loadFonts,
|
|
24
19
|
measureBoardLayout,
|
|
25
|
-
overlayBoxes,
|
|
26
|
-
paletteFor,
|
|
27
20
|
parseWireframeText,
|
|
28
21
|
printWireframeText,
|
|
29
|
-
readMarkers,
|
|
30
22
|
registry,
|
|
31
23
|
renderWireframe,
|
|
32
24
|
renderWireframePng,
|
|
33
25
|
renderWireframeSvg,
|
|
34
26
|
repairPrompt,
|
|
35
|
-
tagForMeasuring,
|
|
36
27
|
theme,
|
|
37
28
|
upgradeSpec,
|
|
38
29
|
upgradeWireframe,
|
|
39
|
-
wireframePrompt
|
|
40
|
-
withBoardGap,
|
|
41
|
-
withMarkers,
|
|
42
|
-
withNoteHeights,
|
|
43
|
-
withPalette
|
|
30
|
+
wireframePrompt
|
|
44
31
|
} from "./chunks/chunk-QYGOXTAG.js";
|
|
45
32
|
|
|
46
33
|
// src/deflate.ts
|
|
@@ -257,154 +244,6 @@ function addStyles(doc) {
|
|
|
257
244
|
style.textContent = STYLES;
|
|
258
245
|
doc.head.append(style);
|
|
259
246
|
}
|
|
260
|
-
|
|
261
|
-
// src/measure.ts
|
|
262
|
-
import { renderToSvg } from "@json-render/image/render";
|
|
263
|
-
var isFlow = (e) => e.type === "Flow";
|
|
264
|
-
var COVERS_SCREEN = /* @__PURE__ */ new Set(["Modal", "Drawer"]);
|
|
265
|
-
var OVERLAYS = /* @__PURE__ */ new Set(["Modal", "Drawer", "Toast"]);
|
|
266
|
-
var CONTAINERS = new Set(Object.entries(componentDefinitions).filter(([, d]) => d.slots.length).map(([name]) => name));
|
|
267
|
-
var BARS = /* @__PURE__ */ new Set(["NavBar", "TabBar"]);
|
|
268
|
-
var toBox = (r) => ({ x: round(r.x), y: round(r.y), width: round(r.w), height: round(r.h) });
|
|
269
|
-
var round = (n) => Math.round(n * 100) / 100;
|
|
270
|
-
function clip(r, to) {
|
|
271
|
-
if (!to) return r;
|
|
272
|
-
const x = Math.max(r.x, to.x), y = Math.max(r.y, to.y);
|
|
273
|
-
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;
|
|
274
|
-
return w >= 0 && h >= 0 && (w > 0 || r.w === 0) && (h > 0 || r.h === 0) ? { x, y, w, h } : null;
|
|
275
|
-
}
|
|
276
|
-
async function measureElements(spec, lines, opts = {}) {
|
|
277
|
-
upgradeSpec(spec);
|
|
278
|
-
if (!opts.skipValidation) {
|
|
279
|
-
const issues = checkSpec(spec);
|
|
280
|
-
if (issues.length) throw new SpecError(issues);
|
|
281
|
-
}
|
|
282
|
-
spec = await withNoteHeights(spec);
|
|
283
|
-
const { width, height } = boardLayout(spec, { flows: opts.flows });
|
|
284
|
-
spec = withBoardGap(spec, opts.flows);
|
|
285
|
-
const els = spec.elements;
|
|
286
|
-
const anchors = findAnchors(spec);
|
|
287
|
-
const every = Object.keys(els).filter((k) => !["Board", "Screen", "Flow"].includes(els[k].type)).map((id) => ({ id, screen: id, kind: "tooltip", field: false }));
|
|
288
|
-
const screens = Object.keys(els).filter((k) => els[k].type === "Screen");
|
|
289
|
-
const inners = screens.map((id) => ({ id, screen: id, kind: "tooltip", field: false }));
|
|
290
|
-
const { tagged, colors } = tagForMeasuring(spec, [...anchors, ...every, ...inners], screens);
|
|
291
|
-
const svg = await renderToSvg(tagged, { registry: withMarkers(withPalette(paletteFor())), includeStandard: false, fonts: await loadFonts(), width, height });
|
|
292
|
-
const boxes = readMarkers(svg, colors);
|
|
293
|
-
const { items } = boardLayout(spec, { flows: opts.flows });
|
|
294
|
-
const laidOut = (els[spec.root].children ?? []).filter((k) => els[k]?.type === "Screen" || els[k]?.type === "Note");
|
|
295
|
-
const frames = /* @__PURE__ */ new Map(), bands = /* @__PURE__ */ new Map();
|
|
296
|
-
laidOut.forEach((k, i) => {
|
|
297
|
-
const it = items[i];
|
|
298
|
-
if (!it || els[k].type !== "Screen") return;
|
|
299
|
-
frames.set(k, { x: it.x, y: it.y + LABEL_H, w: it.width, h: it.height - LABEL_H });
|
|
300
|
-
bands.set(k, { x: it.x, y: it.y, w: it.width, h: LABEL_H });
|
|
301
|
-
});
|
|
302
|
-
const overlays = /* @__PURE__ */ new Map();
|
|
303
|
-
for (const o of overlayBoxes(spec, anchors, boxes)) overlays.set(o.id, [...overlays.get(o.id) ?? [], o.box]);
|
|
304
|
-
const lineOf = (k) => k == null ? null : lines[k] ?? 0;
|
|
305
|
-
const endLine = (k) => Math.max(lines[k] ?? 0, ...(els[k].children ?? []).filter((c) => els[c]).map(endLine));
|
|
306
|
-
const labelOf = (k) => {
|
|
307
|
-
const p = els[k].props ?? {};
|
|
308
|
-
const v = p[PRIMARY_PROP[els[k].type] ?? ""];
|
|
309
|
-
return typeof v === "string" ? v : void 0;
|
|
310
|
-
};
|
|
311
|
-
const out = [];
|
|
312
|
-
const visit = (k, parent, screen, cut) => {
|
|
313
|
-
const el = els[k];
|
|
314
|
-
if (!el || el.type === "Flow") return;
|
|
315
|
-
const p = el.props ?? {};
|
|
316
|
-
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) };
|
|
317
|
-
let entry = null;
|
|
318
|
-
if (el.type === "Board") entry = { ...base, box: { x: 0, y: 0, width, height } };
|
|
319
|
-
else if (el.type === "Screen") {
|
|
320
|
-
const frame = frames.get(k);
|
|
321
|
-
if (frame) entry = { ...base, box: toBox(frame), labelBox: toBox(bands.get(k)) };
|
|
322
|
-
} else {
|
|
323
|
-
const r = boxes[k];
|
|
324
|
-
const seen = r && clip(r, cut);
|
|
325
|
-
if (seen) {
|
|
326
|
-
const view = screen ? boxes[screen] ?? frames.get(screen) : void 0;
|
|
327
|
-
const os = (overlays.get(k) ?? []).map((o) => clip(o, view)).filter((o) => !!o && o.w > 0 && o.h > 0);
|
|
328
|
-
entry = { ...base, box: toBox(seen), ...os.length ? { overlays: os.map(toBox) } : {}, ...COVERS_SCREEN.has(el.type) ? { scrim: p.scrim ?? true } : {} };
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
if (entry) out.push(entry);
|
|
332
|
-
const inScreen = el.type === "Screen" ? k : screen;
|
|
333
|
-
const kids = el.children ?? [];
|
|
334
|
-
if (el.type === "Screen") {
|
|
335
|
-
const frame = frames.get(k) ?? null;
|
|
336
|
-
const inner = clip(boxes[k] ?? frame, frame);
|
|
337
|
-
const body = clip(boxes[`${k}#body`] ?? inner, inner);
|
|
338
|
-
const over = (c) => OVERLAYS.has(els[c]?.type);
|
|
339
|
-
for (const c of [...kids.filter((c2) => !over(c2)), ...kids.filter(over)]) visit(c, k, k, BARS.has(els[c]?.type) || over(c) ? inner : body);
|
|
340
|
-
} else {
|
|
341
|
-
const inner = el.type === "Drawer" && boxes[k] ? clip(boxes[k], cut) : cut;
|
|
342
|
-
for (const c of kids) visit(c, k, inScreen, inner);
|
|
343
|
-
}
|
|
344
|
-
};
|
|
345
|
-
visit(spec.root, null, null, null);
|
|
346
|
-
if (opts.flows !== false) {
|
|
347
|
-
const flows = flowEnds(spec);
|
|
348
|
-
const resolved = flows.resolve(boxes);
|
|
349
|
-
const ends = new Map(flows.list.map((f) => [f.key, f]));
|
|
350
|
-
layoutFlows(spec, resolved, flows.screens, flows.gap, paletteFor()).forEach((f) => {
|
|
351
|
-
const p = els[f.key].props ?? {};
|
|
352
|
-
const e = ends.get(f.key);
|
|
353
|
-
out.push({
|
|
354
|
-
line: lineOf(f.key),
|
|
355
|
-
endLine: lineOf(f.key),
|
|
356
|
-
type: "Flow",
|
|
357
|
-
...f.label ? { label: f.label.text } : {},
|
|
358
|
-
from: String(p.from),
|
|
359
|
-
to: String(p.to),
|
|
360
|
-
fromLine: lineOf(e.from),
|
|
361
|
-
toLine: lineOf(e.to),
|
|
362
|
-
path: flowPolyline(f).map((q) => [round(q.x), round(q.y)]),
|
|
363
|
-
...f.label ? { labelBox: toBox(f.label) } : {}
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
return out;
|
|
368
|
-
}
|
|
369
|
-
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;
|
|
370
|
-
var center = (b) => [b.x + b.width / 2, b.y + b.height / 2];
|
|
371
|
-
function distanceToPath(path, x, y) {
|
|
372
|
-
let best = Infinity;
|
|
373
|
-
for (let i = 1; i < path.length; i++) {
|
|
374
|
-
const [ax, ay] = path[i - 1], [bx, by] = path[i];
|
|
375
|
-
const dx = bx - ax, dy = by - ay, len = dx * dx + dy * dy;
|
|
376
|
-
const t = len ? Math.max(0, Math.min(1, ((x - ax) * dx + (y - ay) * dy) / len)) : 0;
|
|
377
|
-
best = Math.min(best, Math.hypot(x - ax - t * dx, y - ay - t * dy));
|
|
378
|
-
}
|
|
379
|
-
return best;
|
|
380
|
-
}
|
|
381
|
-
var isContainer = (e) => CONTAINERS.has(e.type);
|
|
382
|
-
function elementsAt(elements, x, y, opts = {}) {
|
|
383
|
-
const tolerance = opts.tolerance ?? 6;
|
|
384
|
-
const order = new Map(elements.map((e, i) => [e, i]));
|
|
385
|
-
const topFirst = (a, b) => order.get(b) - order.get(a);
|
|
386
|
-
const covered = /* @__PURE__ */ new Set();
|
|
387
|
-
const screens = elements.filter((e) => e.type === "Screen");
|
|
388
|
-
const screenAround = (b) => screens.find((s) => inside(s.box, ...center(b)));
|
|
389
|
-
for (const [i, m] of elements.entries()) {
|
|
390
|
-
if (isFlow(m) || !OVERLAYS.has(m.type)) continue;
|
|
391
|
-
const screen = screenAround(m.box);
|
|
392
|
-
if (!screen || !inside(m.box, x, y) && !(m.scrim && inside(screen.box, x, y))) continue;
|
|
393
|
-
for (const e of elements.slice(0, i)) if (!isFlow(e) && e.type !== "Screen" && e.type !== "Board" && screenAround(e.box) === screen) covered.add(e);
|
|
394
|
-
}
|
|
395
|
-
const owners = [], labels = [], leaves = [], lines = [], containers = [];
|
|
396
|
-
for (const e of elements) {
|
|
397
|
-
if (isFlow(e)) {
|
|
398
|
-
if (inside(e.labelBox, x, y)) labels.push(e);
|
|
399
|
-
else if (distanceToPath(e.path, x, y) <= tolerance) lines.push(e);
|
|
400
|
-
} else if (e.overlays?.some((o) => inside(o, x, y))) owners.push(e);
|
|
401
|
-
else if (!covered.has(e) && (inside(e.box, x, y) || inside(e.labelBox, x, y))) (isContainer(e) ? containers : leaves).push(e);
|
|
402
|
-
}
|
|
403
|
-
return [owners, labels, leaves, lines, containers].flatMap((g) => g.sort(topFirst));
|
|
404
|
-
}
|
|
405
|
-
function elementsAtLine(elements, line) {
|
|
406
|
-
return elements.filter((e) => e.line > 0 && e.line <= line && line <= e.endLine).sort((a, b) => b.line - a.line);
|
|
407
|
-
}
|
|
408
247
|
export {
|
|
409
248
|
DEFAULT_LINKS,
|
|
410
249
|
DEFAULT_SELECTOR,
|
package/dist/measure.d.ts
CHANGED
|
@@ -8,61 +8,8 @@
|
|
|
8
8
|
* so measure once per text and look up as often as needed (they also run on the JSON in a browser).
|
|
9
9
|
*/
|
|
10
10
|
import type { Spec } from "@json-render/core";
|
|
11
|
-
import { type
|
|
12
|
-
|
|
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, under
|
|
36
|
-
* a tab bar, or under the status or browser 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 (a tooltip's and a date picker's size within a few px). */
|
|
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;
|
|
11
|
+
import { type Measured, type MeasuredElement } from "./hit.js";
|
|
12
|
+
export { elementsAt, elementsAtLine, type Measured, type MeasuredBox, type MeasuredElement, type MeasuredFlow } from "./hit.js";
|
|
66
13
|
export interface MeasureOptions {
|
|
67
14
|
/** 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
15
|
flows?: boolean;
|
|
@@ -76,22 +23,3 @@ export interface MeasureOptions {
|
|
|
76
23
|
* Elements that aren't drawn (content entirely cut off, children of a closed accordion) are left out.
|
|
77
24
|
*/
|
|
78
25
|
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[];
|
|
@@ -6,6 +6,8 @@ export interface EditorOptions {
|
|
|
6
6
|
data?: LanguageData;
|
|
7
7
|
/** Called with the whole text after every change. */
|
|
8
8
|
onChange?: (text: string) => void;
|
|
9
|
+
/** Called with the caret's line (1 = the first) when it moves to another line. */
|
|
10
|
+
onCursorLine?: (line: number) => void;
|
|
9
11
|
/** Line numbers in a gutter. Default true. */
|
|
10
12
|
lineNumbers?: boolean;
|
|
11
13
|
/** Wrap long lines instead of scrolling sideways; continuation rows are indented under their line. Default false. */
|
package/dist/playground/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
languageData
|
|
3
|
-
|
|
2
|
+
languageData,
|
|
3
|
+
measureElements
|
|
4
|
+
} from "../chunks/chunk-NZWKURF4.js";
|
|
4
5
|
import {
|
|
5
6
|
compileWireframe,
|
|
6
7
|
componentDocs,
|
|
@@ -494,12 +495,16 @@ function createPlaygroundHandler() {
|
|
|
494
495
|
return true;
|
|
495
496
|
}
|
|
496
497
|
if (req.method === "POST" && (url.pathname === "/api/render" || url.pathname === "/api/png")) {
|
|
497
|
-
const { spec, issues } = compileWireframe(await readBody(req));
|
|
498
|
+
const { spec, issues, lines } = compileWireframe(await readBody(req));
|
|
498
499
|
const flows = url.searchParams.get("flows") !== "0";
|
|
499
500
|
if (url.pathname === "/api/render") {
|
|
500
501
|
if (!spec || issues.length) return json(res, 200, { issues });
|
|
501
|
-
const { width, height, items } = await
|
|
502
|
-
|
|
502
|
+
const [svg, { width, height, items }, elements] = await Promise.all([
|
|
503
|
+
renderWireframeSvg(spec, { skipValidation: true, flows }),
|
|
504
|
+
measureBoardLayout(spec, { flows }),
|
|
505
|
+
url.searchParams.get("elements") === "1" ? measureElements(spec, lines, { skipValidation: true, flows }) : void 0
|
|
506
|
+
]);
|
|
507
|
+
return json(res, 200, { issues, svg, width, height, items, ...elements ? { elements } : {} });
|
|
503
508
|
}
|
|
504
509
|
if (!spec || issues.length) return json(res, 400, { issues });
|
|
505
510
|
const scale = Math.min(4, Math.max(1, Number(url.searchParams.get("scale") ?? 2)));
|