gutterpress 0.10.0-alpha.3 → 0.10.0-beta.1

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.
Files changed (37) hide show
  1. package/README.md +2 -0
  2. package/dist/api/index.js +2 -2
  3. package/dist/{audit-xxegv0t0.js → audit-85zn35v8.js} +4 -4
  4. package/dist/{build-w0vb6mgq.js → build-1faqmp0y.js} +6 -4
  5. package/dist/{cli-5czby0dd.js → cli-5tffrg48.js} +90 -23
  6. package/dist/{cli-f4rcbt2t.js → cli-7e79d7se.js} +1 -1
  7. package/dist/{cli-yp3p0sf2.js → cli-afm8pa0h.js} +327 -24
  8. package/dist/{cli-gk3wsdpb.js → cli-v935c2ws.js} +1 -1
  9. package/dist/{cli-revgt4pr.js → cli-yzf38679.js} +1 -0
  10. package/dist/cli.js +16 -16
  11. package/dist/{doctor-ctgn1dnt.js → doctor-69v5c9wy.js} +2 -2
  12. package/dist/engine/compiler/build.d.ts +1 -1
  13. package/dist/engine/shared/flush.d.ts +59 -0
  14. package/dist/engine/shared/margin-box-support.d.ts +24 -0
  15. package/dist/{engine-eng4xh2q.js → engine-hrtn0b28.js} +1 -1
  16. package/dist/{engine-q5skhgms.js → engine-nbfdy0pp.js} +2 -2
  17. package/dist/{gutterpress-agent-1ctgfz92.js → gutterpress-agent-cazqstr1.js} +49 -1
  18. package/dist/{gutterpress-viewer-cem7dmr5.js → gutterpress-viewer-fb6fhwsy.js} +137 -51
  19. package/dist/{index-s416qpdv.js → index-052x5fef.js} +89 -22
  20. package/dist/{index-mvehxh0e.js → index-c2b6gyeg.js} +47 -4
  21. package/dist/{index-a4kr77td.js → index-wsydvp08.js} +287 -23
  22. package/dist/index.js +3 -3
  23. package/dist/lib/build-runner.d.ts +7 -0
  24. package/dist/lib/engine.d.ts +9 -0
  25. package/dist/lib/markdown/gutterpress-css.d.ts +9 -1
  26. package/dist/lib/markdown/markers.d.ts +51 -1
  27. package/dist/{lint-gsh6qwrq.js → lint-17404bsx.js} +4 -4
  28. package/dist/{new-nkhycqy4.js → new-vsgpxzek.js} +4 -4
  29. package/dist/{plugin-129wcs93.js → plugin-s4yfpx6p.js} +4 -4
  30. package/dist/{preflight-41hcejg9.js → preflight-1h3apvqh.js} +4 -4
  31. package/dist/{preview-pftsvr9z.js → preview-cqf383g4.js} +6 -5
  32. package/dist/{preview-interface-fnqb2y4v.js → preview-interface-hg5eqv88.js} +193 -4
  33. package/dist/{publish-dnjvb4jg.js → publish-arvycera.js} +4 -4
  34. package/dist/render.js +83 -7
  35. package/dist/{repair-a3enjttn.js → repair-vmwh2bhe.js} +4 -4
  36. package/dist/{validate-ph1xffqy.js → validate-ztzq7p35.js} +4 -4
  37. package/package.json +1 -1
@@ -10,3 +10,27 @@
10
10
  */
11
11
  export declare const MARGIN_BOX_IGNORED_PROPERTIES: ReadonlySet<string>;
12
12
  export declare function isIgnoredMarginBoxProperty(property: string): boolean;
13
+ /**
14
+ * Geometry of each of the 16 margin boxes, per CSS Paged Media §5.3, in pt
15
+ * SHEET coordinates (origin at the sheet's top-left). One implementation for
16
+ * both furniture painters: the viewer's `drawMarginBoxes` overlays and the
17
+ * compiler's `.gp-flush` furniture relocation must place a box at the same
18
+ * spot or preview and print disagree about where the folio sits.
19
+ */
20
+ export declare function marginBoxRectPt(name: string, g: {
21
+ width: number;
22
+ height: number;
23
+ margin: {
24
+ top: number;
25
+ right: number;
26
+ bottom: number;
27
+ left: number;
28
+ };
29
+ }): {
30
+ x: number;
31
+ y: number;
32
+ w: number;
33
+ h: number;
34
+ };
35
+ /** Horizontal alignment of a margin box's content within its slot. */
36
+ export declare function marginBoxAlign(name: string): "start" | "center" | "end";
@@ -4,7 +4,7 @@ import {
4
4
  build,
5
5
  connectChromium,
6
6
  getBrowser
7
- } from "./cli-yp3p0sf2.js";
7
+ } from "./cli-afm8pa0h.js";
8
8
  import"./cli-c41yr7he.js";
9
9
  import {
10
10
  BuildError
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  build
3
- } from "./index-a4kr77td.js";
3
+ } from "./index-wsydvp08.js";
4
4
  import {
5
5
  BuildError,
6
6
  RENDER_TIMEOUT_MS,
7
7
  assertMilestone,
8
8
  connectChromium,
9
9
  getBrowser
10
- } from "./index-mvehxh0e.js";
10
+ } from "./index-c2b6gyeg.js";
11
11
  import"./index-37x76zdn.js";
12
12
 
13
13
  // src/lib/engine.ts
@@ -44,8 +44,10 @@
44
44
  targetTexts: () => targetTexts,
45
45
  stringSources: () => stringSources,
46
46
  setGenerated: () => setGenerated,
47
+ setFlushFurniture: () => setFlushFurniture,
47
48
  instrument: () => instrument,
48
49
  forcedBreakSites: () => forcedBreakSites,
50
+ flushRoots: () => flushRoots,
49
51
  fillLeaders: () => fillLeaders,
50
52
  counterResetSites: () => counterResetSites,
51
53
  collectCss: () => collectCss,
@@ -557,6 +559,50 @@
557
559
  addCss("gp-generated-content", css);
558
560
  return entries.length;
559
561
  }
562
+ function flushRoots() {
563
+ const out = [];
564
+ for (const root of Array.from(document.querySelectorAll(".page, .spread"))) {
565
+ const edges = ["top", "right", "bottom", "left"].filter((edge) => root.querySelector(`.gp-pin.gp-flush.gp-${edge}`));
566
+ if (!edges.length)
567
+ continue;
568
+ const page = getComputedStyle(root).page;
569
+ const key = `${page === "auto" ? "" : page}|${edges.map((e) => e[0]).join("")}`;
570
+ root.dataset.gpFlush = key;
571
+ out.push({ id: ensureAnchor(root), page, edges, key });
572
+ }
573
+ return out;
574
+ }
575
+ function setFlushFurniture(items) {
576
+ let painted = 0;
577
+ for (const item of items) {
578
+ const host = anchorHost(item.id);
579
+ if (!host)
580
+ continue;
581
+ let layer = host.querySelector(":scope > .gp-flush-furniture");
582
+ if (!layer) {
583
+ layer = document.createElement("div");
584
+ layer.className = "gp-flush-furniture";
585
+ layer.setAttribute("aria-hidden", "true");
586
+ layer.setAttribute("style", "position:absolute;inset:0;pointer-events:none;z-index:10;");
587
+ host.appendChild(layer);
588
+ }
589
+ layer.textContent = "";
590
+ for (const b of item.boxes) {
591
+ const slot = document.createElement("div");
592
+ slot.dataset.box = b.box;
593
+ const justify = b.align === "center" ? "center" : b.align === "end" ? "flex-end" : "flex-start";
594
+ slot.setAttribute("style", `position:absolute;left:${b.x}px;top:${b.y}px;width:${b.w}px;height:${b.h}px;` + `display:flex;align-items:center;justify-content:${justify};overflow:hidden;white-space:pre;`);
595
+ const content = document.createElement("span");
596
+ content.textContent = b.text;
597
+ for (const [prop, value] of Object.entries(b.decls))
598
+ content.style.setProperty(prop, value);
599
+ slot.appendChild(content);
600
+ layer.appendChild(slot);
601
+ painted++;
602
+ }
603
+ }
604
+ return painted;
605
+ }
560
606
  var api = {
561
607
  auditContent,
562
608
  collectCss,
@@ -569,7 +615,9 @@
569
615
  fillLeaders,
570
616
  instrument,
571
617
  addCss,
572
- setGenerated
618
+ setGenerated,
619
+ flushRoots,
620
+ setFlushFurniture
573
621
  };
574
622
  if (typeof window !== "undefined")
575
623
  window.__gp = api;
@@ -33,9 +33,11 @@
33
33
  injectBreakMapping: () => injectBreakMapping,
34
34
  fragmentDocument: () => fragmentDocument,
35
35
  forcedColumnBreaksSupported: () => forcedColumnBreaksSupported,
36
+ contentEdgeRect: () => contentEdgeRect,
36
37
  compensateTrailingMarginsBeforeAvoids: () => compensateTrailingMarginsBeforeAvoids,
37
38
  compensateRepeatedHeaders: () => compensateRepeatedHeaders,
38
39
  compensateRectoBreaks: () => compensateRectoBreaks,
40
+ columnReserve: () => columnReserve,
39
41
  collectCssText: () => collectCssText,
40
42
  buildStrips: () => buildStrips,
41
43
  blankPageIndices: () => blankPageIndices,
@@ -148,6 +150,23 @@
148
150
  .gp-run {
149
151
  position: relative;
150
152
  margin: 0 0 var(--gp-sheet-gap);
153
+ /* Hit-transparent, like \`.gp-layer\` below: in wrap mode a recto-starting
154
+ run is pulled up over the previous run's last row (decorate.ts's
155
+ cross-run negative margin-top — the spread-composition overlap), and
156
+ its leading \`.gp-wrap-spacer\` slot is visually empty. The covered page
157
+ paints through, but the run's full-width box used to win
158
+ elementFromPoint there, so clicks, right-clicks, link hits, and text
159
+ selection were all dead on every covered page (measured: 60/60 dead
160
+ probe points in two-column view). Runs are viewer chrome — only author
161
+ content may capture hits. */
162
+ pointer-events: none;
163
+ }
164
+
165
+ /* …and the author's content re-enables hit testing. Equal specificity with
166
+ \`.gp-run { pointer-events: none }\` above, so ORDER is load-bearing:
167
+ \`auto\` must come after \`none\`. */
168
+ .gp-strip > * {
169
+ pointer-events: auto;
151
170
  }
152
171
 
153
172
  .gp-layer {
@@ -957,6 +976,20 @@
957
976
  `);
958
977
  }
959
978
 
979
+ // src/engine/shared/flush.ts
980
+ var FLUSH_EDGES = ["top", "right", "bottom", "left"];
981
+ function flushEdgesIn(root) {
982
+ return FLUSH_EDGES.filter((edge) => root.querySelector(`.gp-pin.gp-flush.gp-${edge}`));
983
+ }
984
+ function flushMargins(margin, edges) {
985
+ return {
986
+ top: edges.includes("top") ? 0 : margin.top,
987
+ right: edges.includes("right") ? 0 : margin.right,
988
+ bottom: edges.includes("bottom") ? 0 : margin.bottom,
989
+ left: edges.includes("left") ? 0 : margin.left
990
+ };
991
+ }
992
+
960
993
  // src/engine/viewer/fragment.ts
961
994
  var PX_PER_PT = 96 / 72;
962
995
  function injectViewerCss(doc = document) {
@@ -1005,6 +1038,21 @@
1005
1038
  function forcedColumnBreaksSupported() {
1006
1039
  return typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("break-before", "column") && CSS.supports("break-after", "column");
1007
1040
  }
1041
+ var FRAGMENT_EPSILON_PX = 1;
1042
+ function contentEdgeRect(site, atEnd) {
1043
+ const rects = Array.from(site.getClientRects());
1044
+ const solid = rects.filter((r) => r.height >= FRAGMENT_EPSILON_PX);
1045
+ const pool = solid.length ? solid : rects;
1046
+ return atEnd ? pool.at(-1) : pool[0];
1047
+ }
1048
+ function columnReserve(offset, columnHeight) {
1049
+ const remaining = columnHeight - offset;
1050
+ if (remaining < FRAGMENT_EPSILON_PX)
1051
+ return null;
1052
+ if (remaining > columnHeight - FRAGMENT_EPSILON_PX)
1053
+ return null;
1054
+ return Math.ceil(remaining);
1055
+ }
1008
1056
  function synthesizeColumnBreaks(model) {
1009
1057
  const sites = [];
1010
1058
  const seen = new WeakMap;
@@ -1041,23 +1089,22 @@
1041
1089
  const strip = el.closest(".gp-strip");
1042
1090
  if (!strip)
1043
1091
  continue;
1044
- if (prop === "break-before" && !el.previousElementSibling)
1045
- continue;
1092
+ let site = el;
1093
+ if (prop === "break-before") {
1094
+ const target = propagatedBreakTarget(el, strip);
1095
+ if (!target)
1096
+ continue;
1097
+ site = target;
1098
+ }
1046
1099
  if (prop === "break-after" && !el.nextElementSibling)
1047
1100
  continue;
1048
- const rects = Array.from(el.getClientRects());
1049
- const rect = prop === "break-after" ? rects.at(-1) : rects[0];
1101
+ const rect = contentEdgeRect(site, prop === "break-after");
1050
1102
  if (!rect)
1051
1103
  continue;
1052
1104
  const stripTop = strip.getBoundingClientRect().top;
1053
1105
  const edge = prop === "break-after" ? rect.bottom : rect.top;
1054
- const offset = edge - stripTop;
1055
- if (prop === "break-before" && offset < 0.5)
1056
- continue;
1057
- if (prop === "break-after" && strip.clientHeight - offset < 0.5)
1058
- continue;
1059
- const reserve = Math.ceil(strip.clientHeight - offset);
1060
- if (reserve <= 0)
1106
+ const reserve = columnReserve(edge - stripTop, strip.clientHeight);
1107
+ if (reserve === null)
1061
1108
  continue;
1062
1109
  const spacer = document.createElement("div");
1063
1110
  spacer.className = "gp-column-break-spacer";
@@ -1066,9 +1113,29 @@
1066
1113
  if (prop === "break-after")
1067
1114
  el.after(spacer);
1068
1115
  else
1069
- el.before(spacer);
1116
+ site.before(spacer);
1070
1117
  }
1071
1118
  }
1119
+ function propagatedBreakTarget(el, strip) {
1120
+ let site = el;
1121
+ while (!site.previousElementSibling) {
1122
+ const parent = site.parentElement;
1123
+ if (!parent || parent === strip)
1124
+ return null;
1125
+ const cs = getComputedStyle(site);
1126
+ if (cs.position === "absolute" || cs.position === "fixed" || cs.float !== "none")
1127
+ return null;
1128
+ for (let node = site.previousSibling;node; node = node.previousSibling) {
1129
+ if (node.nodeType === 3 && (node.textContent ?? "").trim() !== "")
1130
+ return site;
1131
+ }
1132
+ const pcs = getComputedStyle(parent);
1133
+ if (!/^(block|flow-root)$/.test(pcs.display) || pcs.columnCount !== "auto")
1134
+ return null;
1135
+ site = parent;
1136
+ }
1137
+ return site;
1138
+ }
1072
1139
  function directPageName(el, model) {
1073
1140
  for (const a of model.pageAssignments) {
1074
1141
  try {
@@ -1087,12 +1154,13 @@
1087
1154
  }
1088
1155
  return false;
1089
1156
  }
1090
- function pushRun(runs, page, nodes) {
1157
+ function pushRun(runs, page, nodes, flushEdges) {
1158
+ const key = (r) => `${r.page ?? ""}\x00${(r.flushEdges ?? []).join(",")}`;
1091
1159
  const last = runs[runs.length - 1];
1092
- if (last && last.page === page)
1160
+ if (last && key(last) === key({ page, flushEdges }))
1093
1161
  last.nodes.push(...nodes);
1094
1162
  else
1095
- runs.push({ page, nodes });
1163
+ runs.push({ page, nodes, flushEdges: flushEdges?.length ? flushEdges : undefined });
1096
1164
  }
1097
1165
  function explodeChildren(container, model) {
1098
1166
  const runs = [];
@@ -1104,7 +1172,7 @@
1104
1172
  pending = [];
1105
1173
  const last = runs[runs.length - 1];
1106
1174
  if (last) {
1107
- pushRun(runs, last.page, held);
1175
+ pushRun(runs, last.page, held, last.flushEdges);
1108
1176
  return [];
1109
1177
  }
1110
1178
  if (held.some((n) => (n.textContent ?? "").trim() !== "")) {
@@ -1121,13 +1189,14 @@
1121
1189
  const kid = node;
1122
1190
  if (kid.classList.contains("gp-layer"))
1123
1191
  continue;
1192
+ const flush = flushEdgesIn(kid);
1124
1193
  const own = directPageName(kid, model);
1125
1194
  if (own !== undefined) {
1126
- pushRun(runs, own, [...carry(), kid]);
1195
+ pushRun(runs, own, [...carry(), kid], flush);
1127
1196
  continue;
1128
1197
  }
1129
1198
  if (!hasDescendantPageAssignment(kid, model)) {
1130
- pushRun(runs, undefined, [...carry(), kid]);
1199
+ pushRun(runs, undefined, [...carry(), kid], flush);
1131
1200
  continue;
1132
1201
  }
1133
1202
  const inner = explodeChildren(kid, model);
@@ -1141,7 +1210,7 @@
1141
1210
  for (const n of r.nodes)
1142
1211
  shell.appendChild(n);
1143
1212
  kid.before(shell);
1144
- pushRun(runs, r.page, [...lead, shell]);
1213
+ pushRun(runs, r.page, [...lead, shell], flushEdgesIn(shell));
1145
1214
  lead = [];
1146
1215
  }
1147
1216
  kid.remove();
@@ -1289,21 +1358,29 @@
1289
1358
  strip.className = "gp-strip";
1290
1359
  if (run.page)
1291
1360
  strip.dataset.page = run.page;
1292
- const w = pt(geometry.width - geometry.margin.left - geometry.margin.right);
1293
- const h = pt(geometry.height - geometry.margin.top - geometry.margin.bottom);
1361
+ const margin = run.flushEdges?.length ? flushMargins(geometry.margin, run.flushEdges) : geometry.margin;
1362
+ const w = pt(geometry.width - margin.left - margin.right);
1363
+ const h = pt(geometry.height - margin.top - margin.bottom);
1294
1364
  strip.style.setProperty("--gp-content-w", `${w}px`);
1295
1365
  strip.style.setProperty("--gp-content-h", `${h}px`);
1296
1366
  strip.style.setProperty("--gp-sheet-gap", `${gap}px`);
1297
1367
  strip.style.setProperty("--gp-page-w", `${pt(geometry.width)}px`);
1298
1368
  strip.style.setProperty("--gp-page-h", `${pt(geometry.height)}px`);
1299
- strip.style.setProperty("--gp-margin-top", `${pt(geometry.margin.top)}px`);
1300
- strip.style.setProperty("--gp-margin-right", `${pt(geometry.margin.right)}px`);
1301
- strip.style.setProperty("--gp-margin-bottom", `${pt(geometry.margin.bottom)}px`);
1302
- strip.style.setProperty("--gp-margin-left", `${pt(geometry.margin.left)}px`);
1369
+ strip.style.setProperty("--gp-margin-top", `${pt(margin.top)}px`);
1370
+ strip.style.setProperty("--gp-margin-right", `${pt(margin.right)}px`);
1371
+ strip.style.setProperty("--gp-margin-bottom", `${pt(margin.bottom)}px`);
1372
+ strip.style.setProperty("--gp-margin-left", `${pt(margin.left)}px`);
1303
1373
  run.nodes[0].before(strip);
1304
1374
  for (const n of run.nodes)
1305
1375
  strip.appendChild(n);
1306
- strips.push({ el: strip, page: run.page, geometry, pages: 0, offset: 0 });
1376
+ strips.push({
1377
+ el: strip,
1378
+ page: run.page,
1379
+ geometry,
1380
+ flushEdges: run.flushEdges,
1381
+ pages: 0,
1382
+ offset: 0
1383
+ });
1307
1384
  }
1308
1385
  for (const s of strips)
1309
1386
  clearLeadingForcedBreaks(s.el);
@@ -1864,6 +1941,37 @@
1864
1941
  function isIgnoredMarginBoxProperty(property) {
1865
1942
  return MARGIN_BOX_IGNORED_PROPERTIES.has(property.toLowerCase());
1866
1943
  }
1944
+ function marginBoxRectPt(name, g) {
1945
+ const { top, right, bottom, left } = g.margin;
1946
+ const cw = g.width - left - right;
1947
+ const ch = g.height - top - bottom;
1948
+ const third = (n) => n / 3;
1949
+ const T = {
1950
+ "top-left-corner": [0, 0, left, top],
1951
+ "top-left": [left, 0, third(cw), top],
1952
+ "top-center": [left + third(cw), 0, third(cw), top],
1953
+ "top-right": [left + 2 * third(cw), 0, third(cw), top],
1954
+ "top-right-corner": [g.width - right, 0, right, top],
1955
+ "bottom-left-corner": [0, g.height - bottom, left, bottom],
1956
+ "bottom-left": [left, g.height - bottom, third(cw), bottom],
1957
+ "bottom-center": [left + third(cw), g.height - bottom, third(cw), bottom],
1958
+ "bottom-right": [left + 2 * third(cw), g.height - bottom, third(cw), bottom],
1959
+ "bottom-right-corner": [g.width - right, g.height - bottom, right, bottom],
1960
+ "left-top": [0, top, left, third(ch)],
1961
+ "left-middle": [0, top + third(ch), left, third(ch)],
1962
+ "left-bottom": [0, top + 2 * third(ch), left, third(ch)],
1963
+ "right-top": [g.width - right, top, right, third(ch)],
1964
+ "right-middle": [g.width - right, top + third(ch), right, third(ch)],
1965
+ "right-bottom": [g.width - right, top + 2 * third(ch), right, third(ch)]
1966
+ };
1967
+ const [x, y, w, h] = T[name] ?? [0, 0, 0, 0];
1968
+ return { x, y, w, h };
1969
+ }
1970
+ function marginBoxAlign(name) {
1971
+ if (name.includes("center") || name.includes("middle"))
1972
+ return "center";
1973
+ return /right/.test(name) ? "end" : "start";
1974
+ }
1867
1975
 
1868
1976
  // src/engine/viewer/decorate.ts
1869
1977
  var px = (v) => `${v * PX_PER_PT}px`;
@@ -2120,7 +2228,7 @@
2120
2228
  box.className = "gp-marginbox";
2121
2229
  box.dataset.box = name;
2122
2230
  Object.assign(box.style, rectFor(name, g));
2123
- box.dataset.align = name.includes("center") || name.includes("middle") ? "center" : /right/.test(name) ? "end" : "start";
2231
+ box.dataset.align = marginBoxAlign(name);
2124
2232
  const content = document.createElement("span");
2125
2233
  content.className = "gp-marginbox-content";
2126
2234
  content.textContent = text;
@@ -2232,30 +2340,8 @@
2232
2340
  return run;
2233
2341
  }
2234
2342
  function rectFor(name, g) {
2235
- const { top, right, bottom, left } = g.margin;
2236
- const cw = g.width - left - right;
2237
- const ch = g.height - top - bottom;
2238
- const third = (n) => n / 3;
2239
- const T = {
2240
- "top-left-corner": [0, 0, left, top],
2241
- "top-left": [left, 0, third(cw), top],
2242
- "top-center": [left + third(cw), 0, third(cw), top],
2243
- "top-right": [left + 2 * third(cw), 0, third(cw), top],
2244
- "top-right-corner": [g.width - right, 0, right, top],
2245
- "bottom-left-corner": [0, g.height - bottom, left, bottom],
2246
- "bottom-left": [left, g.height - bottom, third(cw), bottom],
2247
- "bottom-center": [left + third(cw), g.height - bottom, third(cw), bottom],
2248
- "bottom-right": [left + 2 * third(cw), g.height - bottom, third(cw), bottom],
2249
- "bottom-right-corner": [g.width - right, g.height - bottom, right, bottom],
2250
- "left-top": [0, top, left, third(ch)],
2251
- "left-middle": [0, top + third(ch), left, third(ch)],
2252
- "left-bottom": [0, top + 2 * third(ch), left, third(ch)],
2253
- "right-top": [g.width - right, top, right, third(ch)],
2254
- "right-middle": [g.width - right, top + third(ch), right, third(ch)],
2255
- "right-bottom": [g.width - right, top + 2 * third(ch), right, third(ch)]
2256
- };
2257
- const [x, y, w, h] = T[name] ?? [0, 0, 0, 0];
2258
- return { left: px(x), top: px(y), width: px(w), height: px(h) };
2343
+ const r = marginBoxRectPt(name, g);
2344
+ return { left: px(r.x), top: px(r.y), width: px(r.w), height: px(r.h) };
2259
2345
  }
2260
2346
 
2261
2347
  // src/engine/viewer/index.ts