gutterpress 0.10.0-beta.1 → 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.
Files changed (27) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{audit-85zn35v8.js → audit-cvarpa72.js} +4 -4
  3. package/dist/{build-1faqmp0y.js → build-6r502chw.js} +4 -4
  4. package/dist/{cli-7e79d7se.js → cli-149edp6b.js} +1 -1
  5. package/dist/{cli-v935c2ws.js → cli-cqtggsng.js} +1 -1
  6. package/dist/{cli-5tffrg48.js → cli-eq5naw4m.js} +136 -122
  7. package/dist/{cli-afm8pa0h.js → cli-zfcryxg8.js} +2 -2
  8. package/dist/cli.js +15 -15
  9. package/dist/{doctor-69v5c9wy.js → doctor-akvxbtjb.js} +2 -2
  10. package/dist/{engine-nbfdy0pp.js → engine-b159tbns.js} +2 -2
  11. package/dist/{engine-hrtn0b28.js → engine-ft4cr3ep.js} +1 -1
  12. package/dist/{gutterpress-viewer-fb6fhwsy.js → gutterpress-viewer-te8g5grx.js} +55 -21
  13. package/dist/{index-052x5fef.js → index-ge7q9xj3.js} +244 -230
  14. package/dist/{index-wsydvp08.js → index-wq3r5pj7.js} +1 -1
  15. package/dist/{index-c2b6gyeg.js → index-ycpvr0am.js} +2 -2
  16. package/dist/index.js +3 -3
  17. package/dist/lib/build-staging.d.ts +51 -0
  18. package/dist/{lint-17404bsx.js → lint-p2sw53d9.js} +4 -4
  19. package/dist/{new-vsgpxzek.js → new-hvq0x91q.js} +4 -4
  20. package/dist/{plugin-s4yfpx6p.js → plugin-pssmk0dx.js} +4 -4
  21. package/dist/{preflight-1h3apvqh.js → preflight-4j00yd0g.js} +4 -4
  22. package/dist/{preview-cqf383g4.js → preview-d4s6gk6p.js} +4 -4
  23. package/dist/{preview-interface-hg5eqv88.js → preview-interface-0ssk8bmm.js} +4 -2
  24. package/dist/{publish-arvycera.js → publish-tkc26en7.js} +4 -4
  25. package/dist/{repair-vmwh2bhe.js → repair-2va4w12t.js} +4 -4
  26. package/dist/{validate-ztzq7p35.js → validate-w9vfefrw.js} +4 -4
  27. package/package.json +1 -1
@@ -24,6 +24,7 @@
24
24
  strideOf: () => strideOf,
25
25
  stabilizeFullHeightPageRoots: () => stabilizeFullHeightPageRoots,
26
26
  spreadModeSupported: () => spreadModeSupported,
27
+ runPageBox: () => runPageBox,
27
28
  rowStrideOf: () => rowStrideOf,
28
29
  pageRangeOf: () => pageRangeOf,
29
30
  pageOf: () => pageOf,
@@ -96,11 +97,23 @@
96
97
  transform: translate(var(--gp-margin-left), var(--gp-margin-top));
97
98
  }
98
99
 
99
- /* Sheets/margin boxes are painted behind/around the strip by the decoration
100
- layer; the strip itself must stay transparent so they show through. */
101
- .gp-strip > * {
102
- break-inside: auto;
103
- }
100
+ /* NOTE: there is deliberately no \`.gp-strip > * { break-inside: auto }\` here.
101
+ The original spike shipped one, under a comment about sheet transparency that
102
+ described no rule of its own, and it was never justified anywhere. Because
103
+ viewer chrome is injected after the author's stylesheet, it beat every
104
+ single-class author rule at equal specificity: a top-level block the author
105
+ declared atomic (\`.card\`, \`.section\`, a stat block) split on screen while the
106
+ PDF moved it whole and left a short page. Measured on
107
+ \`docs/fixtures/atomic-blocks\`: print 6pp, viewer 5pp with the rule, 6pp and
108
+ 6pp without it; a longer probe of the same shape ran 25pp against 21pp.
109
+ Chromium honours \`break-inside: avoid\` in multicol as it does in paged media,
110
+ including relaxing it for a block too tall to fit an empty fragmentainer, so
111
+ nothing here needs to override the author.
112
+
113
+ Scope: \`>\` means this only ever reached the flow root's OWN children, so a
114
+ book whose atomic blocks sit inside \`@page\`/\`@section\` wrappers was never
115
+ affected — which is why removing it does not move the dc-op-manual field
116
+ guide's remaining 4-page divergence. That one is still open. */
104
117
 
105
118
  /* View mode (\`applySpreadMode\` in fragment.ts): wraps this run's multicol
106
119
  columns into \`--gp-wrap-cols\` ROWS instead of one long row — 2 for
@@ -1103,7 +1116,7 @@
1103
1116
  continue;
1104
1117
  const stripTop = strip.getBoundingClientRect().top;
1105
1118
  const edge = prop === "break-after" ? rect.bottom : rect.top;
1106
- const reserve = columnReserve(edge - stripTop, strip.clientHeight);
1119
+ const reserve = columnReserve((edge - stripTop) / cssZoomOf(strip), strip.clientHeight);
1107
1120
  if (reserve === null)
1108
1121
  continue;
1109
1122
  const spacer = document.createElement("div");
@@ -1301,19 +1314,21 @@
1301
1314
  const prevRect = prevRects.at(-1);
1302
1315
  const stripRect = stripEl.getBoundingClientRect();
1303
1316
  const { stride } = stripMetrics(stripEl);
1304
- const colOf = (r) => Math.floor((r.left - stripRect.left + 1) / stride);
1317
+ const zoom = cssZoomOf(stripEl);
1318
+ const colOf = (r) => Math.floor(((r.left - stripRect.left) / zoom + 1) / stride);
1305
1319
  const currentCol = colOf(rect);
1306
1320
  if (currentCol !== colOf(prevRect) + 1)
1307
1321
  continue;
1308
- if (Math.abs(rect.top - stripRect.top) > 0.5)
1322
+ if (Math.abs(rect.top - stripRect.top) / zoom > 0.5)
1309
1323
  continue;
1310
1324
  const marginEnd = parseFloat(getComputedStyle(prev).marginBlockEnd) || 0;
1311
1325
  if (marginEnd <= 0.5)
1312
1326
  continue;
1313
- const remaining = stripEl.clientHeight - (prevRect.bottom - stripRect.top);
1314
- if (rect.height > remaining + 0.5)
1327
+ const remaining = stripEl.clientHeight - (prevRect.bottom - stripRect.top) / zoom;
1328
+ const height = rect.height / zoom;
1329
+ if (height > remaining + 0.5)
1315
1330
  continue;
1316
- if (rect.height + marginEnd <= remaining + 0.5)
1331
+ if (height + marginEnd <= remaining + 0.5)
1317
1332
  continue;
1318
1333
  prev.dataset.gpTrailingMargin = "compensated";
1319
1334
  prev.dataset.gpTrailingMarginValue = prev.style.getPropertyValue("margin-block-end");
@@ -1346,6 +1361,20 @@
1346
1361
  }
1347
1362
  }
1348
1363
  }
1364
+ function runPageBox(model, name, warnings = []) {
1365
+ const right = resolvePage(model, { name, pseudos: ["right"] }).geometry;
1366
+ const left = resolvePage(model, { name, pseudos: ["left"] }).geometry;
1367
+ const box = (g) => ({
1368
+ w: g.width - g.margin.left - g.margin.right,
1369
+ h: g.height - g.margin.top - g.margin.bottom
1370
+ });
1371
+ const r = box(right);
1372
+ const l = box(left);
1373
+ if (Math.abs(r.w - l.w) < 0.01 && Math.abs(r.h - l.h) < 0.01)
1374
+ return right;
1375
+ warnings.push(`@page :left and @page :right give ${name ? `the "${name}" page` : "this book"} ` + `different content areas (${l.w.toFixed(1)}×${l.h.toFixed(1)}pt vs ` + `${r.w.toFixed(1)}×${r.h.toFixed(1)}pt). Print alternates between them; the preview can ` + `only show one, so page breaks here may not match the PDF. Mirror the margins instead: ` + `swap which edge is wide and keep the total the same.`);
1376
+ return resolvePage(model, { name }).geometry;
1377
+ }
1349
1378
  function buildStrips(model, opts = {}, warnings = []) {
1350
1379
  const doc = document;
1351
1380
  const root = opts.root ?? doc.querySelector("main") ?? doc.body;
@@ -1353,7 +1382,7 @@
1353
1382
  const runs = explodeChildren(root, model);
1354
1383
  const strips = [];
1355
1384
  for (const run of runs) {
1356
- const { geometry } = resolvePage(model, { name: run.page });
1385
+ const geometry = runPageBox(model, run.page, warnings);
1357
1386
  const strip = doc.createElement("div");
1358
1387
  strip.className = "gp-strip";
1359
1388
  if (run.page)
@@ -1406,14 +1435,15 @@
1406
1435
  passes = Math.max(passes, pass + 1);
1407
1436
  const stride = strideOf(strip.el);
1408
1437
  const stripLeft = strip.el.getBoundingClientRect().left - strip.el.scrollLeft;
1409
- const colOf = (r) => Math.floor((r.left - stripLeft + 1) / stride);
1438
+ const zoom = cssZoomOf(strip.el);
1439
+ const colOf = (r) => Math.floor(((r.left - stripLeft) / zoom + 1) / stride);
1410
1440
  const stripTop = strip.el.getBoundingClientRect().top;
1411
1441
  const colBottom = strip.el.clientHeight;
1412
1442
  const plans = [];
1413
1443
  for (const table of tables) {
1414
1444
  const head = table.tHead;
1415
1445
  const headRect = head?.getClientRects()[0];
1416
- const footHeight = table.tFoot?.getBoundingClientRect().height ?? 0;
1446
+ const footHeight = (table.tFoot?.getBoundingClientRect().height ?? 0) / zoom;
1417
1447
  const rows = Array.from(table.querySelectorAll("tbody > tr")).filter((r) => !r.classList.contains("gp-thead-shim") && !r.classList.contains("gp-tfoot-shim"));
1418
1448
  if (!rows.length)
1419
1449
  continue;
@@ -1428,10 +1458,10 @@
1428
1458
  const row = rows[i];
1429
1459
  if (cols[i] === lastCol || claims.has(row))
1430
1460
  continue;
1431
- const bottom = rects[i].bottom - stripTop;
1461
+ const bottom = (rects[i].bottom - stripTop) / zoom;
1432
1462
  if (bottom > colBottom - footHeight + 0.5) {
1433
1463
  newClaim = row;
1434
- claims.set(row, colBottom - (rects[i].top - stripTop));
1464
+ claims.set(row, colBottom - (rects[i].top - stripTop) / zoom);
1435
1465
  break;
1436
1466
  }
1437
1467
  }
@@ -1439,7 +1469,7 @@
1439
1469
  plans.push({
1440
1470
  table,
1441
1471
  push: headRect ? colOf(headRect) < cols[0] : false,
1442
- headHeight: headRect?.height ?? 0,
1472
+ headHeight: (headRect?.height ?? 0) / zoom,
1443
1473
  footHeight,
1444
1474
  breakRows: headRect ? rows.filter((_, i) => i > 0 && cols[i] > cols[i - 1]) : [],
1445
1475
  footRows: [...claims.entries()],
@@ -1563,6 +1593,9 @@
1563
1593
  function strideOf(strip) {
1564
1594
  return stripMetrics(strip).stride;
1565
1595
  }
1596
+ function cssZoomOf(el) {
1597
+ return el.currentCSSZoom ?? 1;
1598
+ }
1566
1599
  function stripMetrics(strip) {
1567
1600
  const cs = getComputedStyle(strip);
1568
1601
  const w = parseFloat(cs.getPropertyValue("--gp-content-w"));
@@ -1585,12 +1618,13 @@
1585
1618
  function indexInStrip(left, top, strip) {
1586
1619
  const { stride, rowStride } = stripMetrics(strip.el);
1587
1620
  const stripBox = strip.el.getBoundingClientRect();
1588
- const stripLeft = stripBox.left - strip.el.scrollLeft;
1589
- const stripTop = stripBox.top;
1621
+ const zoom = cssZoomOf(strip.el);
1622
+ const x = (left - stripBox.left) / zoom + strip.el.scrollLeft;
1623
+ const y = (top - stripBox.top) / zoom;
1590
1624
  const { perRow, shift } = wrapGeometry(strip);
1591
- const colVisual = Math.floor((left - stripLeft + 1) / stride);
1625
+ const colVisual = Math.floor((x + 1) / stride);
1592
1626
  const colClamped = Math.max(0, Math.min(perRow - 1, colVisual));
1593
- const row = Math.max(0, Math.floor((top - stripTop + 1) / rowStride));
1627
+ const row = Math.max(0, Math.floor((y + 1) / rowStride));
1594
1628
  const idx = row * perRow + colClamped - shift;
1595
1629
  return Math.max(0, Math.min(strip.pages - 1, idx));
1596
1630
  }