gutterpress 0.10.0-alpha.4 → 0.10.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 +2 -0
- package/dist/api/index.js +2 -2
- package/dist/{audit-k1vnfwvc.js → audit-cvarpa72.js} +4 -4
- package/dist/{build-hqmwgvdw.js → build-6r502chw.js} +6 -4
- package/dist/{cli-0r0tq16s.js → cli-149edp6b.js} +1 -1
- package/dist/{cli-ra0ed2xt.js → cli-cqtggsng.js} +1 -1
- package/dist/{cli-n25qycwz.js → cli-eq5naw4m.js} +222 -141
- package/dist/{cli-revgt4pr.js → cli-yzf38679.js} +1 -0
- package/dist/{cli-hp9r2pzt.js → cli-zfcryxg8.js} +327 -24
- package/dist/cli.js +16 -16
- package/dist/{doctor-dxms7ehm.js → doctor-akvxbtjb.js} +2 -2
- package/dist/engine/compiler/build.d.ts +1 -1
- package/dist/engine/shared/flush.d.ts +59 -0
- package/dist/engine/shared/margin-box-support.d.ts +24 -0
- package/dist/{engine-z4p9sr4h.js → engine-b159tbns.js} +2 -2
- package/dist/{engine-wa7y9av9.js → engine-ft4cr3ep.js} +1 -1
- package/dist/{gutterpress-agent-1ctgfz92.js → gutterpress-agent-cazqstr1.js} +49 -1
- package/dist/{gutterpress-viewer-cem7dmr5.js → gutterpress-viewer-te8g5grx.js} +191 -71
- package/dist/{index-05y3dnxq.js → index-ge7q9xj3.js} +330 -249
- package/dist/{index-xxg4zfrg.js → index-wq3r5pj7.js} +287 -23
- package/dist/{index-9tyq9kks.js → index-ycpvr0am.js} +47 -4
- package/dist/index.js +3 -3
- package/dist/lib/build-runner.d.ts +7 -0
- package/dist/lib/build-staging.d.ts +51 -0
- package/dist/lib/engine.d.ts +9 -0
- package/dist/lib/markdown/gutterpress-css.d.ts +9 -1
- package/dist/lib/markdown/markers.d.ts +51 -1
- package/dist/{lint-xjwm5ep8.js → lint-p2sw53d9.js} +4 -4
- package/dist/{new-kwdwpf0j.js → new-hvq0x91q.js} +4 -4
- package/dist/{plugin-rg4tnn96.js → plugin-pssmk0dx.js} +4 -4
- package/dist/{preflight-3127y25z.js → preflight-4j00yd0g.js} +4 -4
- package/dist/{preview-ncgfhqmw.js → preview-d4s6gk6p.js} +6 -5
- package/dist/{preview-interface-435cczt5.js → preview-interface-0ssk8bmm.js} +153 -6
- package/dist/{publish-pr0rwh6p.js → publish-tkc26en7.js} +4 -4
- package/dist/render.js +83 -7
- package/dist/{repair-8270smfw.js → repair-2va4w12t.js} +4 -4
- package/dist/{validate-54e17rae.js → validate-w9vfefrw.js} +4 -4
- package/package.json +1 -1
|
@@ -891,6 +891,88 @@ function specificity(r) {
|
|
|
891
891
|
return (r.name ? 2 : 0) + r.pseudos.length;
|
|
892
892
|
}
|
|
893
893
|
|
|
894
|
+
// src/engine/shared/flush.ts
|
|
895
|
+
var FLUSH_EDGES = ["top", "right", "bottom", "left"];
|
|
896
|
+
function flushKey(edges) {
|
|
897
|
+
return FLUSH_EDGES.filter((e) => edges.includes(e)).map((e) => e[0]).join("");
|
|
898
|
+
}
|
|
899
|
+
function flushPageName(authorPage, edges) {
|
|
900
|
+
const safe = authorPage ? authorPage.replace(/[^A-Za-z0-9_-]/g, "_") : "";
|
|
901
|
+
return `gp--flush${safe ? `-${safe}` : ""}-${flushKey(edges)}`;
|
|
902
|
+
}
|
|
903
|
+
function flushMargins(margin, edges) {
|
|
904
|
+
return {
|
|
905
|
+
top: edges.includes("top") ? 0 : margin.top,
|
|
906
|
+
right: edges.includes("right") ? 0 : margin.right,
|
|
907
|
+
bottom: edges.includes("bottom") ? 0 : margin.bottom,
|
|
908
|
+
left: edges.includes("left") ? 0 : margin.left
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function marginBoxesOnEdges(edges) {
|
|
912
|
+
const owners = {
|
|
913
|
+
"top-left-corner": ["top", "left"],
|
|
914
|
+
"top-left": ["top"],
|
|
915
|
+
"top-center": ["top"],
|
|
916
|
+
"top-right": ["top"],
|
|
917
|
+
"top-right-corner": ["top", "right"],
|
|
918
|
+
"bottom-left-corner": ["bottom", "left"],
|
|
919
|
+
"bottom-left": ["bottom"],
|
|
920
|
+
"bottom-center": ["bottom"],
|
|
921
|
+
"bottom-right": ["bottom"],
|
|
922
|
+
"bottom-right-corner": ["bottom", "right"],
|
|
923
|
+
"left-top": ["left"],
|
|
924
|
+
"left-middle": ["left"],
|
|
925
|
+
"left-bottom": ["left"],
|
|
926
|
+
"right-top": ["right"],
|
|
927
|
+
"right-middle": ["right"],
|
|
928
|
+
"right-bottom": ["right"]
|
|
929
|
+
};
|
|
930
|
+
return Object.entries(owners).filter(([, own]) => own.some((e) => edges.includes(e))).map(([name]) => name);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// src/engine/shared/margin-box-support.ts
|
|
934
|
+
var MARGIN_BOX_IGNORED_PROPERTIES = new Set([
|
|
935
|
+
"transform",
|
|
936
|
+
"rotate",
|
|
937
|
+
"translate",
|
|
938
|
+
"scale",
|
|
939
|
+
"box-shadow"
|
|
940
|
+
]);
|
|
941
|
+
function isIgnoredMarginBoxProperty(property) {
|
|
942
|
+
return MARGIN_BOX_IGNORED_PROPERTIES.has(property.toLowerCase());
|
|
943
|
+
}
|
|
944
|
+
function marginBoxRectPt(name, g) {
|
|
945
|
+
const { top, right, bottom, left } = g.margin;
|
|
946
|
+
const cw = g.width - left - right;
|
|
947
|
+
const ch = g.height - top - bottom;
|
|
948
|
+
const third = (n) => n / 3;
|
|
949
|
+
const T = {
|
|
950
|
+
"top-left-corner": [0, 0, left, top],
|
|
951
|
+
"top-left": [left, 0, third(cw), top],
|
|
952
|
+
"top-center": [left + third(cw), 0, third(cw), top],
|
|
953
|
+
"top-right": [left + 2 * third(cw), 0, third(cw), top],
|
|
954
|
+
"top-right-corner": [g.width - right, 0, right, top],
|
|
955
|
+
"bottom-left-corner": [0, g.height - bottom, left, bottom],
|
|
956
|
+
"bottom-left": [left, g.height - bottom, third(cw), bottom],
|
|
957
|
+
"bottom-center": [left + third(cw), g.height - bottom, third(cw), bottom],
|
|
958
|
+
"bottom-right": [left + 2 * third(cw), g.height - bottom, third(cw), bottom],
|
|
959
|
+
"bottom-right-corner": [g.width - right, g.height - bottom, right, bottom],
|
|
960
|
+
"left-top": [0, top, left, third(ch)],
|
|
961
|
+
"left-middle": [0, top + third(ch), left, third(ch)],
|
|
962
|
+
"left-bottom": [0, top + 2 * third(ch), left, third(ch)],
|
|
963
|
+
"right-top": [g.width - right, top, right, third(ch)],
|
|
964
|
+
"right-middle": [g.width - right, top + third(ch), right, third(ch)],
|
|
965
|
+
"right-bottom": [g.width - right, top + 2 * third(ch), right, third(ch)]
|
|
966
|
+
};
|
|
967
|
+
const [x, y, w, h] = T[name] ?? [0, 0, 0, 0];
|
|
968
|
+
return { x, y, w, h };
|
|
969
|
+
}
|
|
970
|
+
function marginBoxAlign(name) {
|
|
971
|
+
if (name.includes("center") || name.includes("middle"))
|
|
972
|
+
return "center";
|
|
973
|
+
return /right/.test(name) ? "end" : "start";
|
|
974
|
+
}
|
|
975
|
+
|
|
894
976
|
// src/engine/shared/synthesis.ts
|
|
895
977
|
var RECTO_VERSO_VALUES = /^(right|recto|left|verso)$/;
|
|
896
978
|
function isRectoVersoBreak(decl) {
|
|
@@ -1340,7 +1422,7 @@ var favicon_default = "./favicon-wkbm9cjn.ico";
|
|
|
1340
1422
|
var manifest_schema_default = "./manifest.schema-zxgxnbg7.json";
|
|
1341
1423
|
|
|
1342
1424
|
// src/assets/preview/scripts/preview-interface.js
|
|
1343
|
-
var preview_interface_default = "./preview-interface-
|
|
1425
|
+
var preview_interface_default = "./preview-interface-0ssk8bmm.js";
|
|
1344
1426
|
|
|
1345
1427
|
// src/assets/preview/scripts/preview-bridge.js
|
|
1346
1428
|
var preview_bridge_default = "./preview-bridge-fz7vpk8m.js";
|
|
@@ -1352,10 +1434,10 @@ var preview_shell_default = "./preview-shell-c5mfa3q0.js";
|
|
|
1352
1434
|
var CGATS21_CRPC1_default = "./CGATS21_CRPC1-g0e3k7kr.icc";
|
|
1353
1435
|
|
|
1354
1436
|
// src/assets/engine/gutterpress-viewer.js
|
|
1355
|
-
var gutterpress_viewer_default = "./gutterpress-viewer-
|
|
1437
|
+
var gutterpress_viewer_default = "./gutterpress-viewer-te8g5grx.js";
|
|
1356
1438
|
|
|
1357
1439
|
// src/assets/engine/gutterpress-agent.js
|
|
1358
|
-
var gutterpress_agent_default = "./gutterpress-agent-
|
|
1440
|
+
var gutterpress_agent_default = "./gutterpress-agent-cazqstr1.js";
|
|
1359
1441
|
|
|
1360
1442
|
// src/assets/templates/book/manifest.yaml
|
|
1361
1443
|
var manifest_default = "./manifest-n1gh3qw5.yaml";
|
|
@@ -1739,8 +1821,7 @@ async function build(opts) {
|
|
|
1739
1821
|
await page.evaluate(AGENT);
|
|
1740
1822
|
await page.waitForReady();
|
|
1741
1823
|
const cssText = await page.evaluate(`window.__gp.collectCss()`);
|
|
1742
|
-
|
|
1743
|
-
const { tier3Reasons } = classify(model);
|
|
1824
|
+
let model = extract(cssText);
|
|
1744
1825
|
const baseGeom = resolvePage(model).geometry;
|
|
1745
1826
|
const sheetViewport = {
|
|
1746
1827
|
width: Math.max(1, Math.round(baseGeom.width * 96 / 72)),
|
|
@@ -1751,6 +1832,97 @@ async function build(opts) {
|
|
|
1751
1832
|
await page.send("Emulation.setDeviceMetricsOverride", sheetViewport);
|
|
1752
1833
|
await page.send("Emulation.setEmulatedMedia", { media: "print" });
|
|
1753
1834
|
log(`print media emulated for audits and measurement`);
|
|
1835
|
+
const flushRoots = await page.evaluate(`window.__gp.flushRoots()`);
|
|
1836
|
+
const flushGroups = new Map;
|
|
1837
|
+
for (const root of flushRoots) {
|
|
1838
|
+
if (flushGroups.has(root.key))
|
|
1839
|
+
continue;
|
|
1840
|
+
const authorPage = root.page === "auto" ? undefined : root.page;
|
|
1841
|
+
flushGroups.set(root.key, {
|
|
1842
|
+
authorPage,
|
|
1843
|
+
edges: root.edges,
|
|
1844
|
+
genName: flushPageName(authorPage, root.edges),
|
|
1845
|
+
key: root.key
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
if (flushGroups.size) {
|
|
1849
|
+
const lines = [];
|
|
1850
|
+
for (const group of flushGroups.values()) {
|
|
1851
|
+
const relocated = new Set(marginBoxesOnEdges(group.edges));
|
|
1852
|
+
const pseudoSets = [[]];
|
|
1853
|
+
if (group.authorPage) {
|
|
1854
|
+
for (const rule of model.pageRules) {
|
|
1855
|
+
if (rule.name !== group.authorPage)
|
|
1856
|
+
continue;
|
|
1857
|
+
const pseudo = rule.pseudos.length ? `:${rule.pseudos.join(":")}` : "";
|
|
1858
|
+
if (rule.pseudos.length && !pseudoSets.some((ps) => ps.length === rule.pseudos.length && rule.pseudos.every((x) => ps.includes(x))))
|
|
1859
|
+
pseudoSets.push(rule.pseudos);
|
|
1860
|
+
const body = [];
|
|
1861
|
+
for (const [prop, value] of Object.entries(rule.decls))
|
|
1862
|
+
body.push(` ${prop}: ${value};`);
|
|
1863
|
+
for (const [box, decls] of Object.entries(rule.marginBoxes)) {
|
|
1864
|
+
if (relocated.has(box.slice(1)))
|
|
1865
|
+
continue;
|
|
1866
|
+
body.push(` ${box} {`);
|
|
1867
|
+
for (const [prop, value] of Object.entries(decls))
|
|
1868
|
+
body.push(` ${prop}: ${value};`);
|
|
1869
|
+
body.push(` }`);
|
|
1870
|
+
}
|
|
1871
|
+
lines.push(`@page ${group.genName}${pseudo} {
|
|
1872
|
+
${body.join(`
|
|
1873
|
+
`)}
|
|
1874
|
+
}`);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
for (const pseudos of pseudoSets) {
|
|
1878
|
+
const pseudo = pseudos.length ? `:${pseudos.join(":")}` : "";
|
|
1879
|
+
const body = [];
|
|
1880
|
+
for (const edge of group.edges)
|
|
1881
|
+
body.push(` margin-${edge}: 0;`);
|
|
1882
|
+
for (const box of relocated)
|
|
1883
|
+
body.push(` @${box} { content: none; }`);
|
|
1884
|
+
lines.push(`@page ${group.genName}${pseudo} {
|
|
1885
|
+
${body.join(`
|
|
1886
|
+
`)}
|
|
1887
|
+
}`);
|
|
1888
|
+
}
|
|
1889
|
+
lines.push(`:where(.page, .spread)[data-gp-flush="${group.key}"][data-gp-flush][data-gp-flush][data-gp-flush] { page: ${group.genName}; }`);
|
|
1890
|
+
}
|
|
1891
|
+
const flushCss = lines.join(`
|
|
1892
|
+
`);
|
|
1893
|
+
await page.evaluate(`window.__gp.addCss("gp-flush-css", ${JSON.stringify(flushCss)})`);
|
|
1894
|
+
model = extract(`${cssText}
|
|
1895
|
+
${flushCss}`);
|
|
1896
|
+
log(`.gp-flush: ${flushRoots.length} pinned root(s) -> ${flushGroups.size} generated page context(s)`);
|
|
1897
|
+
}
|
|
1898
|
+
const { tier3Reasons } = classify(model);
|
|
1899
|
+
const pseudoContexts = pagePseudoContexts(model);
|
|
1900
|
+
const contentHeightPt = (name) => Math.min(...pseudoContexts.map((pseudos) => {
|
|
1901
|
+
const geometry = resolvePage(model, { name, pseudos }).geometry;
|
|
1902
|
+
return geometry.height - geometry.margin.top - geometry.margin.bottom;
|
|
1903
|
+
}));
|
|
1904
|
+
const contentHeightPx = contentHeightPt() * 96 / 72;
|
|
1905
|
+
const namedContentHeightsPx = Object.fromEntries(model.pageNames.map((name) => [name, contentHeightPt(name) * 96 / 72]));
|
|
1906
|
+
const minContentHeightPx = Math.min(contentHeightPt(), ...model.pageNames.map((name) => contentHeightPt(name))) * 96 / 72;
|
|
1907
|
+
const pageVarsCss = [
|
|
1908
|
+
`:root { --gp-content-h: ${contentHeightPx}px; }`,
|
|
1909
|
+
...model.pageAssignments.map((a) => `${a.selector} { --gp-content-h: ${namedContentHeightsPx[a.page] ?? contentHeightPx}px; }`)
|
|
1910
|
+
].join(`
|
|
1911
|
+
`);
|
|
1912
|
+
await page.evaluate(`window.__gp.addCss("gp-page-vars", ${JSON.stringify(pageVarsCss)})`);
|
|
1913
|
+
log(`page geometry published (--gp-content-h: ${Math.round(contentHeightPx)}px)`);
|
|
1914
|
+
const relocationBoxes = (group, pseudos) => {
|
|
1915
|
+
const ctx = resolvePage(model, { name: group.authorPage, pseudos });
|
|
1916
|
+
return marginBoxesOnEdges(group.edges).map((name) => [name, ctx.marginBoxes[`@${name}`]]).filter((pair) => {
|
|
1917
|
+
const c = pair[1]?.content?.trim();
|
|
1918
|
+
return !!c && c !== "none" && c !== "normal";
|
|
1919
|
+
});
|
|
1920
|
+
};
|
|
1921
|
+
const furnitureRoots = flushRoots.filter((root) => {
|
|
1922
|
+
const group = flushGroups.get(root.key);
|
|
1923
|
+
return pseudoContexts.some((pseudos) => relocationBoxes(group, pseudos).length > 0);
|
|
1924
|
+
});
|
|
1925
|
+
const flushDiagnosed = new Set;
|
|
1754
1926
|
const tier2 = synthesize({
|
|
1755
1927
|
model,
|
|
1756
1928
|
marks: opts.marks,
|
|
@@ -1770,11 +1942,15 @@ async function build(opts) {
|
|
|
1770
1942
|
}).join(`
|
|
1771
1943
|
`);
|
|
1772
1944
|
if (widthOffenders.boxes.length) {
|
|
1773
|
-
const
|
|
1945
|
+
const scale = shrinkScale(widthOffenders.boxes, widthOffenders.limitPx);
|
|
1946
|
+
const headline = scale === null ? `content outside the page content box risks Chromium print ` + `shrink-to-fit (the WHOLE book scales down, silently)` : `content wider than the page content box: Chromium print shrink-to-fit ` + `scales the WHOLE document — every page, every measurement — to about ` + `${scale.toFixed(2)}x its declared size (12pt type prints at ` + `${(12 * scale).toFixed(1)}pt). The page size and page count do not ` + `change, so the shrink is invisible in the PDF`;
|
|
1947
|
+
const msg = `${headline}:
|
|
1774
1948
|
${describe(widthOffenders.boxes)}`;
|
|
1775
1949
|
if (opts.allowShrink) {
|
|
1950
|
+
if (scale !== null)
|
|
1951
|
+
diagnose("engine.width.overflow", `${headline}.`);
|
|
1776
1952
|
for (const o of widthOffenders.boxes)
|
|
1777
|
-
diagnose("engine.width.overflow", `${o.desc} is
|
|
1953
|
+
diagnose("engine.width.overflow", `${o.desc} is ${Math.round(o.px)}px wide, past the ${Math.round(widthOffenders.limitPx)}px page content box. ${o.left < -1 ? "Keep it inside the page content box." : "Give it an explicit width that fits."}`);
|
|
1778
1954
|
log(`WARNING: ${msg}`);
|
|
1779
1955
|
} else {
|
|
1780
1956
|
throw new Error(`${msg}
|
|
@@ -1789,7 +1965,7 @@ ${describe(widthOffenders.intrinsics)}`;
|
|
|
1789
1965
|
log(`WARNING: ${msg}`);
|
|
1790
1966
|
}
|
|
1791
1967
|
const rectoDecls = model.breaks.filter(isRectoVersoBreak);
|
|
1792
|
-
const needsMeasure = tier3Reasons.length > 0 || consumedStrings(model).size > 0 || rectoDecls.length > 0 || model.counterResets.length > 0;
|
|
1968
|
+
const needsMeasure = tier3Reasons.length > 0 || consumedStrings(model).size > 0 || rectoDecls.length > 0 || model.counterResets.length > 0 || furnitureRoots.length > 0;
|
|
1793
1969
|
let tier = tier2.geometry.bleed > 0 || tier2.geometry.slug > 0 ? 2 : 1;
|
|
1794
1970
|
let passes = 1;
|
|
1795
1971
|
let pageMap = {};
|
|
@@ -1817,6 +1993,8 @@ ${describe(widthOffenders.intrinsics)}`;
|
|
|
1817
1993
|
targets.add(s.id);
|
|
1818
1994
|
for (const s of resetSites)
|
|
1819
1995
|
targets.add(s.id);
|
|
1996
|
+
for (const r of furnitureRoots)
|
|
1997
|
+
targets.add(r.id);
|
|
1820
1998
|
await page.evaluate(`window.__gp.instrument(${JSON.stringify([...targets])})`);
|
|
1821
1999
|
const targetText = await page.evaluate(`window.__gp.targetTexts(${JSON.stringify([...targets])})`);
|
|
1822
2000
|
const brokenXrefs = findBrokenXrefRefs(sites, targetText);
|
|
@@ -1901,6 +2079,73 @@ ${describe(widthOffenders.intrinsics)}`;
|
|
|
1901
2079
|
/* Tier 3 */
|
|
1902
2080
|
${mapCss}`;
|
|
1903
2081
|
}
|
|
2082
|
+
if (furnitureRoots.length) {
|
|
2083
|
+
const pageAt = (id) => map[id] ?? predictedForResult?.pageMap[id];
|
|
2084
|
+
const byName = new Map;
|
|
2085
|
+
for (const s of sources) {
|
|
2086
|
+
const p = pageAt(s.id);
|
|
2087
|
+
if (!p)
|
|
2088
|
+
continue;
|
|
2089
|
+
const list = byName.get(s.name) ?? [];
|
|
2090
|
+
list.push({ page: p, value: s.text });
|
|
2091
|
+
byName.set(s.name, list);
|
|
2092
|
+
}
|
|
2093
|
+
for (const entries of byName.values())
|
|
2094
|
+
entries.sort((a, b) => a.page - b.page);
|
|
2095
|
+
const PXPT = 96 / 72;
|
|
2096
|
+
const items = [];
|
|
2097
|
+
for (const root of furnitureRoots) {
|
|
2098
|
+
const physical = pageAt(root.id);
|
|
2099
|
+
if (!physical)
|
|
2100
|
+
continue;
|
|
2101
|
+
const group = flushGroups.get(root.key);
|
|
2102
|
+
const pseudos = [physical % 2 === 1 ? "right" : "left"];
|
|
2103
|
+
if (physical === 1)
|
|
2104
|
+
pseudos.push("first");
|
|
2105
|
+
const ctx = resolvePage(model, { name: group.authorPage, pseudos });
|
|
2106
|
+
const g = ctx.geometry;
|
|
2107
|
+
const eff = flushMargins(g.margin, group.edges);
|
|
2108
|
+
const boxes = [];
|
|
2109
|
+
for (const [name, decls] of relocationBoxes(group, pseudos)) {
|
|
2110
|
+
const text = evaluate(decls.content, {
|
|
2111
|
+
page: toFolioPage(physical, pageValues),
|
|
2112
|
+
pages: pageCount,
|
|
2113
|
+
strings: (n, w) => stringValueAt(byName.get(n) ?? [], physical, parseWhich(w)),
|
|
2114
|
+
targetPage: (u) => {
|
|
2115
|
+
const p = pageAt(u.replace(/^#/, ""));
|
|
2116
|
+
return p === undefined ? undefined : toFolioPage(p, pageValues);
|
|
2117
|
+
}
|
|
2118
|
+
});
|
|
2119
|
+
if (!text) {
|
|
2120
|
+
if (!flushDiagnosed.has(name)) {
|
|
2121
|
+
flushDiagnosed.add(name);
|
|
2122
|
+
diagnose("engine.flush.margin-box", `@${name} sits on an edge a .gp-flush pin frees, and its content could not be ` + `re-homed into the page (unsupported content value: ${decls.content}). It will ` + `not print on that page — simplify the box's content, or drop .gp-flush there.`);
|
|
2123
|
+
}
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
const r = marginBoxRectPt(name, g);
|
|
2127
|
+
const outDecls = {};
|
|
2128
|
+
for (const [prop, value] of Object.entries(decls)) {
|
|
2129
|
+
if (prop.toLowerCase() === "content" || isIgnoredMarginBoxProperty(prop))
|
|
2130
|
+
continue;
|
|
2131
|
+
outDecls[prop] = value;
|
|
2132
|
+
}
|
|
2133
|
+
boxes.push({
|
|
2134
|
+
box: name,
|
|
2135
|
+
x: (r.x - eff.left) * PXPT,
|
|
2136
|
+
y: (r.y - eff.top) * PXPT,
|
|
2137
|
+
w: r.w * PXPT,
|
|
2138
|
+
h: r.h * PXPT,
|
|
2139
|
+
align: marginBoxAlign(name),
|
|
2140
|
+
text,
|
|
2141
|
+
decls: outDecls
|
|
2142
|
+
});
|
|
2143
|
+
}
|
|
2144
|
+
items.push({ id: root.id, boxes });
|
|
2145
|
+
}
|
|
2146
|
+
if (items.length)
|
|
2147
|
+
await page.evaluate(`window.__gp.setFlushFurniture(${JSON.stringify(items)})`);
|
|
2148
|
+
}
|
|
1904
2149
|
};
|
|
1905
2150
|
const predicted = await predictPageMap(browser, url, AGENT, VIEWER, {
|
|
1906
2151
|
stringSets: model.stringSets.map((s) => ({ selector: s.selector, name: s.name, value: s.value })),
|
|
@@ -1939,14 +2184,6 @@ ${mapCss}`;
|
|
|
1939
2184
|
log(`tier 3: NOT converged after ${passes} passes`);
|
|
1940
2185
|
}
|
|
1941
2186
|
}
|
|
1942
|
-
const pseudoContexts = pagePseudoContexts(model);
|
|
1943
|
-
const contentHeightPt = (name) => Math.min(...pseudoContexts.map((pseudos) => {
|
|
1944
|
-
const geometry = resolvePage(model, { name, pseudos }).geometry;
|
|
1945
|
-
return geometry.height - geometry.margin.top - geometry.margin.bottom;
|
|
1946
|
-
}));
|
|
1947
|
-
const contentHeightPx = contentHeightPt() * 96 / 72;
|
|
1948
|
-
const namedContentHeightsPx = Object.fromEntries(model.pageNames.map((name) => [name, contentHeightPt(name) * 96 / 72]));
|
|
1949
|
-
const minContentHeightPx = Math.min(contentHeightPt(), ...model.pageNames.map((name) => contentHeightPt(name))) * 96 / 72;
|
|
1950
2187
|
{
|
|
1951
2188
|
const audit = await page.evaluate(`window.__gp.auditContent(${JSON.stringify({
|
|
1952
2189
|
default: contentHeightPx,
|
|
@@ -2000,6 +2237,26 @@ ${mapCss}`;
|
|
|
2000
2237
|
return reasons;
|
|
2001
2238
|
};
|
|
2002
2239
|
|
|
2240
|
+
// Does this box establish the containing block for an absolutely
|
|
2241
|
+
// positioned descendant? Overflow clipping binds an abspos
|
|
2242
|
+
// .gp-pin only from its containing block outward — MEASURED: a
|
|
2243
|
+
// pin whose box lay entirely outside a STATIC overflow:hidden
|
|
2244
|
+
// (and overflow:clip) wrapper printed complete and still behind
|
|
2245
|
+
// the page text; the wrapper's clip never touched it.
|
|
2246
|
+
const establishesAbsContainingBlock = (cs) => {
|
|
2247
|
+
if (cs.position !== "static") return true;
|
|
2248
|
+
for (const prop of [
|
|
2249
|
+
"transform", "translate", "rotate", "scale", "perspective",
|
|
2250
|
+
"filter", "backdropFilter",
|
|
2251
|
+
]) {
|
|
2252
|
+
const value = cs[prop];
|
|
2253
|
+
if (value && value !== "none") return true;
|
|
2254
|
+
}
|
|
2255
|
+
if (/\\b(layout|paint|strict|content)\\b/.test(cs.contain)) return true;
|
|
2256
|
+
if (cs.containerType && cs.containerType !== "normal") return true;
|
|
2257
|
+
return /\\b(transform|translate|rotate|scale|perspective|filter)\\b/.test(cs.willChange);
|
|
2258
|
+
};
|
|
2259
|
+
|
|
2003
2260
|
for (const el of document.querySelectorAll("*")) {
|
|
2004
2261
|
const cs = getComputedStyle(el);
|
|
2005
2262
|
if (leaks.length < 20) {
|
|
@@ -2040,20 +2297,58 @@ ${mapCss}`;
|
|
|
2040
2297
|
) {
|
|
2041
2298
|
const boundary = el.closest(".page, .spread");
|
|
2042
2299
|
if (boundary) {
|
|
2300
|
+
const elRect = el.getBoundingClientRect();
|
|
2301
|
+
// Clip binding: an in-flow .gp-behind is bound by every
|
|
2302
|
+
// ancestor's overflow, but an abspos one only from its
|
|
2303
|
+
// containing block outward — static wrappers in between
|
|
2304
|
+
// never clip it (measured; see the pass comment above).
|
|
2305
|
+
let clipBinds = cs.position !== "absolute" && cs.position !== "fixed";
|
|
2043
2306
|
for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
2044
2307
|
const ancestorStyle = getComputedStyle(ancestor);
|
|
2308
|
+
if (!clipBinds && establishesAbsContainingBlock(ancestorStyle)) clipBinds = true;
|
|
2045
2309
|
const reasons = stackingReasons(ancestor, ancestorStyle);
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2310
|
+
// Clipping never reorders layers — it can only CUT the
|
|
2311
|
+
// art (measured, pass comment above): warn only where the
|
|
2312
|
+
// border box crosses a binding ancestor's clip edge on an
|
|
2313
|
+
// axis whose overflow is not \`visible\`. The clip edge is
|
|
2314
|
+
// the padding box, grown by overflow-clip-margin where
|
|
2315
|
+
// that axis's value is \`clip\` (px values only; keyword
|
|
2316
|
+
// forms parse NaN -> 0, i.e. the ungrown padding box).
|
|
2317
|
+
// 1px epsilon: the measured cut lands exactly at the
|
|
2318
|
+
// edge, and sub-pixel layout rounding is not an overhang.
|
|
2319
|
+
const cuts = [];
|
|
2320
|
+
if (clipBinds && (ancestorStyle.overflowX !== "visible" || ancestorStyle.overflowY !== "visible")) {
|
|
2321
|
+
const r = ancestor.getBoundingClientRect();
|
|
2322
|
+
const clipMargin = parseFloat(ancestorStyle.overflowClipMargin) || 0;
|
|
2323
|
+
if (ancestorStyle.overflowX !== "visible") {
|
|
2324
|
+
const grow = ancestorStyle.overflowX === "clip" ? clipMargin : 0;
|
|
2325
|
+
const left = r.left + parseFloat(ancestorStyle.borderLeftWidth) - grow;
|
|
2326
|
+
const right = r.right - parseFloat(ancestorStyle.borderRightWidth) + grow;
|
|
2327
|
+
if (elRect.left < left - 1)
|
|
2328
|
+
cuts.push(Math.round(left - elRect.left) + "px past its left clip edge");
|
|
2329
|
+
if (elRect.right > right + 1)
|
|
2330
|
+
cuts.push(Math.round(elRect.right - right) + "px past its right clip edge");
|
|
2331
|
+
}
|
|
2332
|
+
if (ancestorStyle.overflowY !== "visible") {
|
|
2333
|
+
const grow = ancestorStyle.overflowY === "clip" ? clipMargin : 0;
|
|
2334
|
+
const top = r.top + parseFloat(ancestorStyle.borderTopWidth) - grow;
|
|
2335
|
+
const bottom = r.bottom - parseFloat(ancestorStyle.borderBottomWidth) + grow;
|
|
2336
|
+
if (elRect.top < top - 1)
|
|
2337
|
+
cuts.push(Math.round(top - elRect.top) + "px past its top clip edge");
|
|
2338
|
+
if (elRect.bottom > bottom + 1)
|
|
2339
|
+
cuts.push(Math.round(elRect.bottom - bottom) + "px past its bottom clip edge");
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
if ((reasons.length || cuts.length) && !seenLayerTraps.has(ancestor)) {
|
|
2049
2343
|
seenLayerTraps.add(ancestor);
|
|
2050
2344
|
const effects = [];
|
|
2051
2345
|
if (reasons.length)
|
|
2052
2346
|
effects.push("creates a stacking context (" + reasons.join(", ") + ")");
|
|
2053
|
-
if (
|
|
2347
|
+
if (cuts.length)
|
|
2054
2348
|
effects.push(
|
|
2055
|
-
"clips
|
|
2056
|
-
", overflow-y: " + ancestorStyle.overflowY +
|
|
2349
|
+
"clips it (overflow-x: " + ancestorStyle.overflowX +
|
|
2350
|
+
", overflow-y: " + ancestorStyle.overflowY +
|
|
2351
|
+
") — the art extends " + cuts.join(" and ") + " and is cut off there"
|
|
2057
2352
|
);
|
|
2058
2353
|
layerTraps.push({
|
|
2059
2354
|
behind: desc(el),
|
|
@@ -2121,10 +2416,17 @@ async function printPdf(page) {
|
|
|
2121
2416
|
await page.waitForReady();
|
|
2122
2417
|
return page.printToPDF();
|
|
2123
2418
|
}
|
|
2419
|
+
var MAX_SHRINK = 1.5;
|
|
2420
|
+
function shrinkScale(boxes, limitPx) {
|
|
2421
|
+
const maxRight = Math.max(...boxes.map((o) => o.px + Math.min(0, o.left)));
|
|
2422
|
+
if (!(maxRight > limitPx))
|
|
2423
|
+
return null;
|
|
2424
|
+
return Math.max(1 / MAX_SHRINK, limitPx / maxRight);
|
|
2425
|
+
}
|
|
2124
2426
|
async function findWidthOffenders(page, model, bleedSlugExtensionPt, restoreViewport) {
|
|
2125
2427
|
const contexts = [
|
|
2126
2428
|
resolvePage(model),
|
|
2127
|
-
...model.pageNames.filter((n) => !n.startsWith("gp-")).map((n) => resolvePage(model, { name: n }))
|
|
2429
|
+
...model.pageNames.filter((n) => !n.startsWith("gp-") || n.startsWith("gp--flush-")).map((n) => resolvePage(model, { name: n }))
|
|
2128
2430
|
];
|
|
2129
2431
|
const maxContentPt = Math.max(...contexts.map((c) => c.geometry.width - c.geometry.margin.left - c.geometry.margin.right)) + bleedSlugExtensionPt;
|
|
2130
2432
|
const limitPx = maxContentPt * 96 / 72;
|
|
@@ -2211,6 +2513,7 @@ async function predictPageMap(browser, url, agentScript, viewerScript, args, she
|
|
|
2211
2513
|
await page.waitForReady();
|
|
2212
2514
|
await page.evaluate(`window.__GP_MANUAL__ = true;`);
|
|
2213
2515
|
await page.evaluate(viewerScript);
|
|
2516
|
+
await page.evaluate(`window.__gp.flushRoots()`);
|
|
2214
2517
|
await page.evaluate(`window.__gp.stringSources(${JSON.stringify(args.stringSets)})`);
|
|
2215
2518
|
if (args.rectoDecls.length)
|
|
2216
2519
|
await page.evaluate(`window.__gp.forcedBreakSites(${JSON.stringify(args.rectoDecls)})`);
|
|
@@ -2340,4 +2643,4 @@ ${lines.length ? lines.join(`
|
|
|
2340
2643
|
`);
|
|
2341
2644
|
}
|
|
2342
2645
|
|
|
2343
|
-
export { RENDER_TIMEOUT_MS, prewarmBrowser, getBrowser, closeBrowser, getAssetPath, REQUIRED_MILESTONE, assertMilestone, connectChromium, build };
|
|
2646
|
+
export { RENDER_TIMEOUT_MS, prewarmBrowser, getBrowser, closeBrowser, getAssetPath, MARGIN_BOX_IGNORED_PROPERTIES, REQUIRED_MILESTONE, assertMilestone, connectChromium, build };
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
previewArgs
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-yzf38679.js";
|
|
5
5
|
import {
|
|
6
6
|
UsageError,
|
|
7
7
|
package_default,
|
|
8
8
|
rejectUnknownFlags
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-149edp6b.js";
|
|
10
10
|
import {
|
|
11
11
|
EXIT_CODES
|
|
12
12
|
} from "./cli-46ycxe6r.js";
|
|
@@ -18,17 +18,17 @@ import {
|
|
|
18
18
|
import { defineCommand, parseArgs, runMain } from "citty";
|
|
19
19
|
import { statSync } from "node:fs";
|
|
20
20
|
var SUBCOMMANDS = {
|
|
21
|
-
new: () => import("./new-
|
|
22
|
-
preview: () => import("./preview-
|
|
23
|
-
build: () => import("./build-
|
|
24
|
-
publish: () => import("./publish-
|
|
25
|
-
lint: () => import("./lint-
|
|
26
|
-
validate: () => import("./validate-
|
|
27
|
-
audit: () => import("./audit-
|
|
28
|
-
preflight: () => import("./preflight-
|
|
29
|
-
repair: () => import("./repair-
|
|
30
|
-
doctor: () => import("./doctor-
|
|
31
|
-
plugin: () => import("./plugin-
|
|
21
|
+
new: () => import("./new-hvq0x91q.js").then((m) => m.default),
|
|
22
|
+
preview: () => import("./preview-d4s6gk6p.js").then((m) => m.default),
|
|
23
|
+
build: () => import("./build-6r502chw.js").then((m) => m.default),
|
|
24
|
+
publish: () => import("./publish-tkc26en7.js").then((m) => m.default),
|
|
25
|
+
lint: () => import("./lint-p2sw53d9.js").then((m) => m.default),
|
|
26
|
+
validate: () => import("./validate-w9vfefrw.js").then((m) => m.default),
|
|
27
|
+
audit: () => import("./audit-cvarpa72.js").then((m) => m.default),
|
|
28
|
+
preflight: () => import("./preflight-4j00yd0g.js").then((m) => m.default),
|
|
29
|
+
repair: () => import("./repair-2va4w12t.js").then((m) => m.default),
|
|
30
|
+
doctor: () => import("./doctor-akvxbtjb.js").then((m) => m.default),
|
|
31
|
+
plugin: () => import("./plugin-pssmk0dx.js").then((m) => m.default)
|
|
32
32
|
};
|
|
33
33
|
var VERSION = package_default.version;
|
|
34
34
|
var main = defineCommand({
|
|
@@ -53,7 +53,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
53
53
|
const [command, ...commandArgs] = rawArgs;
|
|
54
54
|
try {
|
|
55
55
|
if (command === "new") {
|
|
56
|
-
const { newArgs } = await import("./new-
|
|
56
|
+
const { newArgs } = await import("./new-hvq0x91q.js");
|
|
57
57
|
rejectUnknownFlags(commandArgs, newArgs, "new");
|
|
58
58
|
const parsed2 = parseArgs(commandArgs, {
|
|
59
59
|
...newArgs,
|
|
@@ -65,7 +65,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
if (command === "preflight") {
|
|
68
|
-
const { preflightArgs } = await import("./preflight-
|
|
68
|
+
const { preflightArgs } = await import("./preflight-4j00yd0g.js");
|
|
69
69
|
rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
|
|
70
70
|
const parsed2 = parseArgs(commandArgs, {
|
|
71
71
|
...preflightArgs,
|
|
@@ -89,7 +89,7 @@ async function preflightRequiredInvocations(rawArgs) {
|
|
|
89
89
|
if (subcommand !== "add") {
|
|
90
90
|
throw new UsageError(`gutterpress plugin: unknown command "${subcommand}"`);
|
|
91
91
|
}
|
|
92
|
-
const { pluginAddArgs } = await import("./plugin-
|
|
92
|
+
const { pluginAddArgs } = await import("./plugin-pssmk0dx.js");
|
|
93
93
|
rejectUnknownFlags(subcommandArgs, pluginAddArgs, "plugin add");
|
|
94
94
|
const parsed = parseArgs(subcommandArgs, {
|
|
95
95
|
...pluginAddArgs,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSystemDiagnostics,
|
|
3
3
|
log
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-cqtggsng.js";
|
|
5
5
|
import {
|
|
6
6
|
UsageError,
|
|
7
7
|
rejectExtraPositionals,
|
|
8
8
|
rejectUnknownFlags
|
|
9
|
-
} from "./cli-
|
|
9
|
+
} from "./cli-149edp6b.js";
|
|
10
10
|
import"./cli-c41yr7he.js";
|
|
11
11
|
import"./cli-46ycxe6r.js";
|
|
12
12
|
import"./cli-37x76zdn.js";
|
|
@@ -7,7 +7,7 @@ import { type PostprocessResult } from "./postprocess.ts";
|
|
|
7
7
|
* set is closed here so a surface's label table can be asserted complete
|
|
8
8
|
* against it rather than drifting silently as checks are added.
|
|
9
9
|
*/
|
|
10
|
-
export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi";
|
|
10
|
+
export type BuildDiagnosticCode = "engine.width.overflow" | "engine.width.intrinsic" | "engine.xref.broken" | "engine.abspos.leak" | "engine.layer.trapped" | "engine.multicol.dead-column" | "engine.content.overheight" | "engine.image.low-dpi" | "engine.flush.margin-box";
|
|
11
11
|
export declare const BUILD_DIAGNOSTIC_CODES: readonly BuildDiagnosticCode[];
|
|
12
12
|
export interface BuildDiagnostic {
|
|
13
13
|
code: BuildDiagnosticCode;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.gp-flush` policy — shared by the compiler and the viewer, like every rule
|
|
3
|
+
* that DECIDES something (see synthesis.ts's header).
|
|
4
|
+
*
|
|
5
|
+
* A pinned image reaches the paper only if the page's own margin is removed
|
|
6
|
+
* on that edge: Chromium's printable area IS the page area — MEASURED, a
|
|
7
|
+
* pinned box pulled into the margin with negative insets fragments onto the
|
|
8
|
+
* NEXT sheet, one moved there with a transform is clipped away entirely, and
|
|
9
|
+
* a margin box in a ~zero margin (1px, with padding/height compensation)
|
|
10
|
+
* simply does not render. So flush is implemented as page GEOMETRY, per page:
|
|
11
|
+
*
|
|
12
|
+
* - The COMPILER aliases the flush root's own page context under a
|
|
13
|
+
* generated name (verbatim rule copies — never a resolved flatten, which
|
|
14
|
+
* is the "re-implement the @page cascade" trap tier2's history records),
|
|
15
|
+
* zeroes the flushed margins on the alias, and assigns the root to it.
|
|
16
|
+
* - The VIEWER keeps the author's page context and adjusts the strip
|
|
17
|
+
* geometry in JS from these same functions.
|
|
18
|
+
*
|
|
19
|
+
* Margin boxes live IN the margin, so the flushed edge's furniture (folio,
|
|
20
|
+
* running head) cannot render natively there — MEASURED, no compensation
|
|
21
|
+
* trick survives. Both renderers therefore keep painting that furniture
|
|
22
|
+
* themselves at its ORIGINAL coordinates: the viewer already synthesizes all
|
|
23
|
+
* furniture as DOM; the compiler injects the flushed edge's boxes into the
|
|
24
|
+
* page root as positioned elements with engine-resolved text. Nothing the
|
|
25
|
+
* author declared is lost — that is the contract this module's box map
|
|
26
|
+
* exists to keep.
|
|
27
|
+
*/
|
|
28
|
+
import type { PageGeometry } from "./gcpm-extract.ts";
|
|
29
|
+
export declare const FLUSH_EDGES: readonly ["top", "right", "bottom", "left"];
|
|
30
|
+
export type FlushEdge = (typeof FLUSH_EDGES)[number];
|
|
31
|
+
/** Minimal DOM shape needed by `flushEdgesIn` — keeps this module node-safe. */
|
|
32
|
+
interface QueryRoot {
|
|
33
|
+
querySelector(sel: string): unknown;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Which edges the flush pins inside `root` ask for, in canonical t/r/b/l
|
|
37
|
+
* order. A `.gp-flush` without `.gp-pin` or without an edge word is inert by
|
|
38
|
+
* construction — every selector here requires all three.
|
|
39
|
+
*/
|
|
40
|
+
export declare function flushEdgesIn(root: QueryRoot): FlushEdge[];
|
|
41
|
+
/** Canonical short key for an edge set: "b", "rb", "trbl"… (t/r/b/l order). */
|
|
42
|
+
export declare function flushKey(edges: readonly FlushEdge[]): string;
|
|
43
|
+
/**
|
|
44
|
+
* Generated page name for (author page context, edge set). The `gp--` double
|
|
45
|
+
* dash marks an engine-generated page (the `gp--blank` convention); these
|
|
46
|
+
* exist only in builds that actually contain a flush pin, which is why the
|
|
47
|
+
* width check may treat them as real author contexts.
|
|
48
|
+
*/
|
|
49
|
+
export declare function flushPageName(authorPage: string | undefined, edges: readonly FlushEdge[]): string;
|
|
50
|
+
/** The page's margins with the flushed edges freed. */
|
|
51
|
+
export declare function flushMargins(margin: PageGeometry["margin"], edges: readonly FlushEdge[]): PageGeometry["margin"];
|
|
52
|
+
/**
|
|
53
|
+
* Margin boxes that live (wholly or partly) in a flushed edge's margin area —
|
|
54
|
+
* the set both renderers must keep painting themselves. A corner box belongs
|
|
55
|
+
* to BOTH of its edges: `bottom-left-corner` dies when either the bottom or
|
|
56
|
+
* the left margin is freed.
|
|
57
|
+
*/
|
|
58
|
+
export declare function marginBoxesOnEdges(edges: readonly FlushEdge[]): string[];
|
|
59
|
+
export {};
|
|
@@ -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";
|