gutterpress 0.10.0-beta.1 → 0.10.1-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 (56) hide show
  1. package/README.md +3 -16
  2. package/dist/api/index.d.ts +10 -19
  3. package/dist/api/index.js +8 -64
  4. package/dist/{audit-85zn35v8.js → audit-7a37g51p.js} +5 -5
  5. package/dist/{build-1faqmp0y.js → build-kdaet3pd.js} +5 -5
  6. package/dist/checks/source/index.d.ts +1 -0
  7. package/dist/checks/source/merge-markers.d.ts +3 -0
  8. package/dist/{cli-afm8pa0h.js → cli-ak1tagkr.js} +95 -58
  9. package/dist/{cli-5tffrg48.js → cli-df7gcyns.js} +510 -1375
  10. package/dist/{cli-7e79d7se.js → cli-dx6sxj2t.js} +1 -1
  11. package/dist/{cli-v935c2ws.js → cli-mcb484g3.js} +2 -47
  12. package/dist/{cli-k1065rkg.js → cli-w692g6dx.js} +75 -29
  13. package/dist/cli.js +14 -15
  14. package/dist/{doctor-69v5c9wy.js → doctor-vajd5h45.js} +2 -2
  15. package/dist/{engine-nbfdy0pp.js → engine-0nt35svz.js} +2 -2
  16. package/dist/{engine-hrtn0b28.js → engine-gkcqdye5.js} +1 -1
  17. package/dist/{gutterpress-viewer-fb6fhwsy.js → gutterpress-viewer-tma1qwz2.js} +111 -35
  18. package/dist/{index-052x5fef.js → index-6g72h2n1.js} +980 -1677
  19. package/dist/{index-c2b6gyeg.js → index-b2rqw41a.js} +2 -2
  20. package/dist/{index-mdefp0y5.js → index-dtrftayr.js} +75 -27
  21. package/dist/{index-wsydvp08.js → index-kvgq4q3q.js} +94 -57
  22. package/dist/index.js +9 -65
  23. package/dist/lib/build-staging.d.ts +51 -0
  24. package/dist/lib/git-fs.d.ts +43 -0
  25. package/dist/lib/host-policy.d.ts +14 -2
  26. package/dist/lib/remote-auth/clone.d.ts +0 -21
  27. package/dist/lib/remote-auth/converge-merge.d.ts +24 -7
  28. package/dist/lib/remote-auth/generic-auth.d.ts +0 -22
  29. package/dist/lib/remote-auth/git-http.d.ts +1 -1
  30. package/dist/lib/remote-auth/github-auth.d.ts +2 -22
  31. package/dist/lib/remote-auth/operation-log.d.ts +4 -9
  32. package/dist/lib/remote-auth/sync-messages.d.ts +35 -12
  33. package/dist/lib/remote-auth/sync-types.d.ts +30 -101
  34. package/dist/lib/remote-auth/sync.d.ts +19 -21
  35. package/dist/lib/remote-auth/token-store.d.ts +0 -10
  36. package/dist/lib/remote-auth/transport.d.ts +42 -40
  37. package/dist/lib/source-provider.d.ts +12 -0
  38. package/dist/{lint-17404bsx.js → lint-p6a4bkvh.js} +5 -5
  39. package/dist/{new-vsgpxzek.js → new-4cdzfbsj.js} +5 -5
  40. package/dist/{plugin-s4yfpx6p.js → plugin-a89tqspy.js} +5 -5
  41. package/dist/{preflight-1h3apvqh.js → preflight-r4y3f8f9.js} +5 -5
  42. package/dist/{preview-interface-hg5eqv88.js → preview-interface-0ssk8bmm.js} +4 -2
  43. package/dist/{preview-cqf383g4.js → preview-k8n2kew7.js} +5 -5
  44. package/dist/{publish-arvycera.js → publish-jnhjp2vd.js} +5 -5
  45. package/dist/{source-provider-3tcj6qg2.js → source-provider-2j5x2hyw.js} +1 -1
  46. package/dist/{source-provider-vanafrt9.js → source-provider-kn41jmbs.js} +1 -1
  47. package/dist/{validate-ztzq7p35.js → validate-prnybpkx.js} +5 -5
  48. package/package.json +1 -1
  49. package/dist/lib/app-heartbeat.d.ts +0 -102
  50. package/dist/lib/remote-auth/image-clash.d.ts +0 -17
  51. package/dist/lib/remote-auth/recovery/classify.d.ts +0 -97
  52. package/dist/lib/remote-auth/recovery/inspect.d.ts +0 -75
  53. package/dist/lib/remote-auth/recovery/locks.d.ts +0 -20
  54. package/dist/lib/remote-auth/recovery/repair.d.ts +0 -29
  55. package/dist/lib/remote-auth/recovery/types.d.ts +0 -39
  56. package/dist/repair-vmwh2bhe.js +0 -137
@@ -24,15 +24,16 @@
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,
30
31
  measure: () => measure,
32
+ makeOverflowFragmentable: () => makeOverflowFragmentable,
31
33
  loadStyleSources: () => loadStyleSources,
32
34
  injectViewerCss: () => injectViewerCss,
33
35
  injectBreakMapping: () => injectBreakMapping,
34
36
  fragmentDocument: () => fragmentDocument,
35
- forcedColumnBreaksSupported: () => forcedColumnBreaksSupported,
36
37
  contentEdgeRect: () => contentEdgeRect,
37
38
  compensateTrailingMarginsBeforeAvoids: () => compensateTrailingMarginsBeforeAvoids,
38
39
  compensateRepeatedHeaders: () => compensateRepeatedHeaders,
@@ -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
@@ -1035,15 +1048,10 @@
1035
1048
  doc.getElementById("gp-break-mapping")?.remove();
1036
1049
  return "";
1037
1050
  }
1038
- function forcedColumnBreaksSupported() {
1039
- return typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("break-before", "column") && CSS.supports("break-after", "column");
1040
- }
1041
1051
  var FRAGMENT_EPSILON_PX = 1;
1042
1052
  function contentEdgeRect(site, atEnd) {
1043
1053
  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];
1054
+ return atEnd ? rects.at(-1) : rects[0];
1047
1055
  }
1048
1056
  function columnReserve(offset, columnHeight) {
1049
1057
  const remaining = columnHeight - offset;
@@ -1103,13 +1111,12 @@
1103
1111
  continue;
1104
1112
  const stripTop = strip.getBoundingClientRect().top;
1105
1113
  const edge = prop === "break-after" ? rect.bottom : rect.top;
1106
- const reserve = columnReserve(edge - stripTop, strip.clientHeight);
1107
- if (reserve === null)
1114
+ if (columnReserve((edge - stripTop) / cssZoomOf(strip), strip.clientHeight) === null)
1108
1115
  continue;
1109
1116
  const spacer = document.createElement("div");
1110
1117
  spacer.className = "gp-column-break-spacer";
1111
1118
  spacer.setAttribute("aria-hidden", "true");
1112
- spacer.style.cssText = `height:${reserve}px;margin:0;padding:0;border:0;`;
1119
+ spacer.style.cssText = `${prop}: column; height:0; margin:0; padding:0; border:0;`;
1113
1120
  if (prop === "break-after")
1114
1121
  el.after(spacer);
1115
1122
  else
@@ -1267,6 +1274,47 @@
1267
1274
  delete el.dataset.gpLeadingPageRootDisplayPriority;
1268
1275
  }
1269
1276
  }
1277
+ var SCROLLABLE = /^(auto|scroll|hidden)$/;
1278
+ function makeOverflowFragmentable(strips) {
1279
+ const todo = [];
1280
+ for (const strip of strips) {
1281
+ for (const el of Array.from(strip.el.querySelectorAll("*"))) {
1282
+ const cs = getComputedStyle(el);
1283
+ const x = SCROLLABLE.test(cs.overflowX);
1284
+ const y = SCROLLABLE.test(cs.overflowY);
1285
+ if (x || y)
1286
+ todo.push({ el, x, y });
1287
+ }
1288
+ }
1289
+ for (const { el, x, y } of todo) {
1290
+ el.dataset.gpOverflow = "clipped";
1291
+ el.dataset.gpOverflowX = el.style.getPropertyValue("overflow-x");
1292
+ el.dataset.gpOverflowY = el.style.getPropertyValue("overflow-y");
1293
+ el.dataset.gpOverflowXPriority = el.style.getPropertyPriority("overflow-x");
1294
+ el.dataset.gpOverflowYPriority = el.style.getPropertyPriority("overflow-y");
1295
+ if (x)
1296
+ el.style.setProperty("overflow-x", "clip");
1297
+ if (y)
1298
+ el.style.setProperty("overflow-y", "clip");
1299
+ }
1300
+ }
1301
+ function restoreScrollContainers(doc = document) {
1302
+ for (const el of Array.from(doc.querySelectorAll('[data-gp-overflow="clipped"]'))) {
1303
+ for (const axis of ["x", "y"]) {
1304
+ const value = axis === "x" ? el.dataset.gpOverflowX : el.dataset.gpOverflowY;
1305
+ const priority = axis === "x" ? el.dataset.gpOverflowXPriority : el.dataset.gpOverflowYPriority;
1306
+ if (value)
1307
+ el.style.setProperty(`overflow-${axis}`, value, priority || "");
1308
+ else
1309
+ el.style.removeProperty(`overflow-${axis}`);
1310
+ }
1311
+ delete el.dataset.gpOverflow;
1312
+ delete el.dataset.gpOverflowX;
1313
+ delete el.dataset.gpOverflowY;
1314
+ delete el.dataset.gpOverflowXPriority;
1315
+ delete el.dataset.gpOverflowYPriority;
1316
+ }
1317
+ }
1270
1318
  function compensateTrailingMarginsBeforeAvoids(model, strips) {
1271
1319
  const candidates = new Set;
1272
1320
  for (const decl of model.breaks) {
@@ -1301,19 +1349,21 @@
1301
1349
  const prevRect = prevRects.at(-1);
1302
1350
  const stripRect = stripEl.getBoundingClientRect();
1303
1351
  const { stride } = stripMetrics(stripEl);
1304
- const colOf = (r) => Math.floor((r.left - stripRect.left + 1) / stride);
1352
+ const zoom = cssZoomOf(stripEl);
1353
+ const colOf = (r) => Math.floor(((r.left - stripRect.left) / zoom + 1) / stride);
1305
1354
  const currentCol = colOf(rect);
1306
1355
  if (currentCol !== colOf(prevRect) + 1)
1307
1356
  continue;
1308
- if (Math.abs(rect.top - stripRect.top) > 0.5)
1357
+ if (Math.abs(rect.top - stripRect.top) / zoom > 0.5)
1309
1358
  continue;
1310
1359
  const marginEnd = parseFloat(getComputedStyle(prev).marginBlockEnd) || 0;
1311
1360
  if (marginEnd <= 0.5)
1312
1361
  continue;
1313
- const remaining = stripEl.clientHeight - (prevRect.bottom - stripRect.top);
1314
- if (rect.height > remaining + 0.5)
1362
+ const remaining = stripEl.clientHeight - (prevRect.bottom - stripRect.top) / zoom;
1363
+ const height = rect.height / zoom;
1364
+ if (height > remaining + 0.5)
1315
1365
  continue;
1316
- if (rect.height + marginEnd <= remaining + 0.5)
1366
+ if (height + marginEnd <= remaining + 0.5)
1317
1367
  continue;
1318
1368
  prev.dataset.gpTrailingMargin = "compensated";
1319
1369
  prev.dataset.gpTrailingMarginValue = prev.style.getPropertyValue("margin-block-end");
@@ -1346,6 +1396,20 @@
1346
1396
  }
1347
1397
  }
1348
1398
  }
1399
+ function runPageBox(model, name, warnings = []) {
1400
+ const right = resolvePage(model, { name, pseudos: ["right"] }).geometry;
1401
+ const left = resolvePage(model, { name, pseudos: ["left"] }).geometry;
1402
+ const box = (g) => ({
1403
+ w: g.width - g.margin.left - g.margin.right,
1404
+ h: g.height - g.margin.top - g.margin.bottom
1405
+ });
1406
+ const r = box(right);
1407
+ const l = box(left);
1408
+ if (Math.abs(r.w - l.w) < 0.01 && Math.abs(r.h - l.h) < 0.01)
1409
+ return right;
1410
+ 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.`);
1411
+ return resolvePage(model, { name }).geometry;
1412
+ }
1349
1413
  function buildStrips(model, opts = {}, warnings = []) {
1350
1414
  const doc = document;
1351
1415
  const root = opts.root ?? doc.querySelector("main") ?? doc.body;
@@ -1353,7 +1417,7 @@
1353
1417
  const runs = explodeChildren(root, model);
1354
1418
  const strips = [];
1355
1419
  for (const run of runs) {
1356
- const { geometry } = resolvePage(model, { name: run.page });
1420
+ const geometry = runPageBox(model, run.page, warnings);
1357
1421
  const strip = doc.createElement("div");
1358
1422
  strip.className = "gp-strip";
1359
1423
  if (run.page)
@@ -1406,14 +1470,15 @@
1406
1470
  passes = Math.max(passes, pass + 1);
1407
1471
  const stride = strideOf(strip.el);
1408
1472
  const stripLeft = strip.el.getBoundingClientRect().left - strip.el.scrollLeft;
1409
- const colOf = (r) => Math.floor((r.left - stripLeft + 1) / stride);
1473
+ const zoom = cssZoomOf(strip.el);
1474
+ const colOf = (r) => Math.floor(((r.left - stripLeft) / zoom + 1) / stride);
1410
1475
  const stripTop = strip.el.getBoundingClientRect().top;
1411
1476
  const colBottom = strip.el.clientHeight;
1412
1477
  const plans = [];
1413
1478
  for (const table of tables) {
1414
1479
  const head = table.tHead;
1415
1480
  const headRect = head?.getClientRects()[0];
1416
- const footHeight = table.tFoot?.getBoundingClientRect().height ?? 0;
1481
+ const footHeight = (table.tFoot?.getBoundingClientRect().height ?? 0) / zoom;
1417
1482
  const rows = Array.from(table.querySelectorAll("tbody > tr")).filter((r) => !r.classList.contains("gp-thead-shim") && !r.classList.contains("gp-tfoot-shim"));
1418
1483
  if (!rows.length)
1419
1484
  continue;
@@ -1428,10 +1493,10 @@
1428
1493
  const row = rows[i];
1429
1494
  if (cols[i] === lastCol || claims.has(row))
1430
1495
  continue;
1431
- const bottom = rects[i].bottom - stripTop;
1496
+ const bottom = (rects[i].bottom - stripTop) / zoom;
1432
1497
  if (bottom > colBottom - footHeight + 0.5) {
1433
1498
  newClaim = row;
1434
- claims.set(row, colBottom - (rects[i].top - stripTop));
1499
+ claims.set(row, colBottom - (rects[i].top - stripTop) / zoom);
1435
1500
  break;
1436
1501
  }
1437
1502
  }
@@ -1439,7 +1504,7 @@
1439
1504
  plans.push({
1440
1505
  table,
1441
1506
  push: headRect ? colOf(headRect) < cols[0] : false,
1442
- headHeight: headRect?.height ?? 0,
1507
+ headHeight: (headRect?.height ?? 0) / zoom,
1443
1508
  footHeight,
1444
1509
  breakRows: headRect ? rows.filter((_, i) => i > 0 && cols[i] > cols[i - 1]) : [],
1445
1510
  footRows: [...claims.entries()],
@@ -1525,11 +1590,15 @@
1525
1590
  for (const [i, site] of sites.entries()) {
1526
1591
  if (!plan[i])
1527
1592
  continue;
1528
- const spacer = document.createElement("div");
1529
- spacer.className = "gp-recto-spacer";
1530
- spacer.setAttribute("aria-hidden", "true");
1531
- spacer.style.cssText = "break-before: column; break-after: column; height: 0; margin: 0; padding: 0; border: 0;";
1532
- site.el.before(spacer);
1593
+ const leading = site.el.previousElementSibling;
1594
+ const need = leading?.classList.contains("gp-column-break-spacer") ? 1 : 2;
1595
+ for (let n = 0;n < need; n++) {
1596
+ const spacer = document.createElement("div");
1597
+ spacer.className = "gp-recto-spacer";
1598
+ spacer.setAttribute("aria-hidden", "true");
1599
+ spacer.style.cssText = "break-before: column; height: 0; margin: 0; padding: 0; border: 0;";
1600
+ site.el.before(spacer);
1601
+ }
1533
1602
  inserted++;
1534
1603
  }
1535
1604
  return inserted;
@@ -1563,6 +1632,9 @@
1563
1632
  function strideOf(strip) {
1564
1633
  return stripMetrics(strip).stride;
1565
1634
  }
1635
+ function cssZoomOf(el) {
1636
+ return el.currentCSSZoom ?? 1;
1637
+ }
1566
1638
  function stripMetrics(strip) {
1567
1639
  const cs = getComputedStyle(strip);
1568
1640
  const w = parseFloat(cs.getPropertyValue("--gp-content-w"));
@@ -1585,12 +1657,13 @@
1585
1657
  function indexInStrip(left, top, strip) {
1586
1658
  const { stride, rowStride } = stripMetrics(strip.el);
1587
1659
  const stripBox = strip.el.getBoundingClientRect();
1588
- const stripLeft = stripBox.left - strip.el.scrollLeft;
1589
- const stripTop = stripBox.top;
1660
+ const zoom = cssZoomOf(strip.el);
1661
+ const x = (left - stripBox.left) / zoom + strip.el.scrollLeft;
1662
+ const y = (top - stripBox.top) / zoom;
1590
1663
  const { perRow, shift } = wrapGeometry(strip);
1591
- const colVisual = Math.floor((left - stripLeft + 1) / stride);
1664
+ const colVisual = Math.floor((x + 1) / stride);
1592
1665
  const colClamped = Math.max(0, Math.min(perRow - 1, colVisual));
1593
- const row = Math.max(0, Math.floor((top - stripTop + 1) / rowStride));
1666
+ const row = Math.max(0, Math.floor((y + 1) / rowStride));
1594
1667
  const idx = row * perRow + colClamped - shift;
1595
1668
  return Math.max(0, Math.min(strip.pages - 1, idx));
1596
1669
  }
@@ -1679,6 +1752,7 @@
1679
1752
  const authoring = [];
1680
1753
  const strips = buildStrips(model, opts, authoring);
1681
1754
  await layoutReady;
1755
+ makeOverflowFragmentable(strips);
1682
1756
  stabilizeFullHeightPageRoots(model, strips);
1683
1757
  compensateTrailingMarginsBeforeAvoids(model, strips);
1684
1758
  synthesizeColumnBreaks(model);
@@ -1701,12 +1775,14 @@
1701
1775
  relayout: () => {
1702
1776
  restoreFullHeightPageRoots();
1703
1777
  restoreTrailingMargins();
1778
+ restoreScrollContainers();
1704
1779
  unwrapStrips(strips);
1705
1780
  for (const spacer of Array.from(document.querySelectorAll(".gp-recto-spacer")))
1706
1781
  spacer.remove();
1707
1782
  const rebuilt = buildStrips(model, opts, authoring);
1708
1783
  strips.length = 0;
1709
1784
  strips.push(...rebuilt);
1785
+ makeOverflowFragmentable(strips);
1710
1786
  stabilizeFullHeightPageRoots(model, strips);
1711
1787
  compensateTrailingMarginsBeforeAvoids(model, strips);
1712
1788
  synthesizeColumnBreaks(model);