gutterpress 0.11.20-alpha.4 → 0.11.20-alpha.5
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/dist/api/index.js +2 -2
- package/dist/{build-epdcrm2c.js → build-8ffhvydf.js} +4 -4
- package/dist/{cli-fmksazej.js → cli-553ce1d4.js} +1 -1
- package/dist/{cli-h4tfmpg3.js → cli-6xym15vm.js} +1 -1
- package/dist/{cli-61vd4ews.js → cli-cyej3p9v.js} +4 -4
- package/dist/{cli-0ey902sx.js → cli-sc5t7c74.js} +36 -25
- package/dist/cli.js +12 -12
- package/dist/{doctor-78es5scm.js → doctor-gyes0hxt.js} +2 -2
- package/dist/engine/shared/pin-edges.d.ts +5 -5
- package/dist/engine/shared/print-area.d.ts +12 -7
- package/dist/{engine-67f68bxs.js → engine-4rkhv5ga.js} +1 -1
- package/dist/{engine-7ha27n2b.js → engine-5fw55dfb.js} +2 -2
- package/dist/{ext-9mc7sr0d.js → ext-x61yptd1.js} +4 -4
- package/dist/{gutterpress-agent-mrdnwktc.js → gutterpress-agent-yvd8w8my.js} +1 -0
- package/dist/{gutterpress-viewer-jr30yw8t.js → gutterpress-viewer-w7njnkj7.js} +115 -20
- package/dist/{index-t13p8y08.js → index-bta8hc2q.js} +35 -24
- package/dist/{index-gm5xbx5w.js → index-crys9951.js} +3 -3
- package/dist/{index-ad95fws8.js → index-yw16cfm8.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/lib/markdown/assemble.d.ts +1 -1
- package/dist/lib/markdown/gutterpress-css.d.ts +8 -8
- package/dist/{manifest.schema-13dfax80.json → manifest.schema-c4wfpg6g.json} +8 -2
- package/dist/{new-s4f0tnya.js → new-87dqcxtn.js} +4 -4
- package/dist/{preflight-7ck4rbsq.js → preflight-nb4wndqf.js} +4 -4
- package/dist/preview/file-watcher.d.ts +1 -0
- package/dist/{preview-j91sxadd.js → preview-7bcr5j2p.js} +4 -4
- package/dist/{publish-f12xaenm.js → publish-4qfm5ch9.js} +4 -4
- package/dist/render.js +22 -13
- package/dist/schema/manifest.types.d.ts +20 -6
- package/dist/{validate-pb5gysvr.js → validate-dzht9qxk.js} +4 -4
- package/package.json +1 -1
|
@@ -119,7 +119,38 @@
|
|
|
119
119
|
);
|
|
120
120
|
position: relative;
|
|
121
121
|
width: var(--gp-content-w);
|
|
122
|
-
|
|
122
|
+
/* Exactly one column tall, but DEFINITE only where print's flow root is.
|
|
123
|
+
A page root's percentage height (\`.page { height: 100% }\`) resolves
|
|
124
|
+
against its containing block. In print that is the flow root (\`body\` or
|
|
125
|
+
\`main\`), whose height is auto in an ordinary book, so the percentage
|
|
126
|
+
computes to \`auto\` and MARKER_CSS's min-height cushion sizes the root.
|
|
127
|
+
Here the strip stands in as that containing block, and a plain
|
|
128
|
+
\`height: var(--gp-content-h)\` resolved the percentage to the full column:
|
|
129
|
+
the root ended exactly on the column's edge, and the same percentage
|
|
130
|
+
reached everything else the author's rule matched (\`[data-name="x"]\`
|
|
131
|
+
selects a block AND its heading, so the heading filled the root and the
|
|
132
|
+
paragraph after it spilled into the next column). Mid-run the next root's
|
|
133
|
+
forced break absorbed the spill; a run's LAST root left a blank column —
|
|
134
|
+
a preview page the PDF does not have. Measured on
|
|
135
|
+
examples/with-print-effects with \`height: 100%\` on its page blocks: print
|
|
136
|
+
5pp, viewer 8pp (a pin root is a run of its own, so three runs ended on
|
|
137
|
+
such a root).
|
|
138
|
+
|
|
139
|
+
\`height: 100%\` asks the strip's own containing block the question the
|
|
140
|
+
root asks print's: is the flow root's height definite? Auto there (the
|
|
141
|
+
ordinary book) is auto here, and the root's percentage is auto exactly
|
|
142
|
+
as in print. A definite flow root — \`html, body { height: 100% }\`, where
|
|
143
|
+
print's percentage DOES resolve to the page area — is definite here too
|
|
144
|
+
(measured, the same 5-root book: 6pp in both renderers). Either way the
|
|
145
|
+
min/max pair pins the used height, so the column height, to the content
|
|
146
|
+
height: \`clientHeight\` is still one column.
|
|
147
|
+
|
|
148
|
+
\`decorate.ts\`'s \`.gp-run\` wrapper sizes itself with \`min-height\` for the
|
|
149
|
+
same reason — a definite wrapper would resolve the percentage after the
|
|
150
|
+
run was measured and re-fragment it under its sheets. */
|
|
151
|
+
height: 100%;
|
|
152
|
+
min-height: var(--gp-content-h);
|
|
153
|
+
max-height: var(--gp-content-h);
|
|
123
154
|
column-width: var(--gp-content-w);
|
|
124
155
|
column-gap: var(--gp-col-gap);
|
|
125
156
|
column-fill: auto;
|
|
@@ -1055,7 +1086,7 @@
|
|
|
1055
1086
|
}
|
|
1056
1087
|
function pinInsets(area, side, edges) {
|
|
1057
1088
|
const binding = side === "recto" ? "left" : "right";
|
|
1058
|
-
const inset = (edge) => !area || !edges.includes(edge) ? 0 : edge === binding ? area.
|
|
1089
|
+
const inset = (edge) => !area || !edges.includes(edge) ? 0 : edge === binding ? area.gutter : area.bleed + area.artSafe;
|
|
1059
1090
|
return { top: inset("top"), right: inset("right"), bottom: inset("bottom"), left: inset("left") };
|
|
1060
1091
|
}
|
|
1061
1092
|
var NO_PADDING = { top: 0, right: 0, bottom: 0, left: 0 };
|
|
@@ -1097,6 +1128,7 @@
|
|
|
1097
1128
|
bleed: n(v.bleed),
|
|
1098
1129
|
safe,
|
|
1099
1130
|
artSafe: typeof v.artSafe === "number" ? n(v.artSafe) : safe,
|
|
1131
|
+
gutter: typeof v.gutter === "number" ? n(v.gutter) : safe,
|
|
1100
1132
|
preset: typeof v.preset === "string" ? v.preset : undefined,
|
|
1101
1133
|
spec
|
|
1102
1134
|
};
|
|
@@ -1213,7 +1245,7 @@
|
|
|
1213
1245
|
continue;
|
|
1214
1246
|
const stripTop = strip.getBoundingClientRect().top;
|
|
1215
1247
|
const edge = prop === "break-after" ? rect.bottom : rect.top;
|
|
1216
|
-
if (columnReserve((edge - stripTop) / cssZoomOf(strip), strip.
|
|
1248
|
+
if (columnReserve((edge - stripTop) / cssZoomOf(strip), stripMetrics(strip).height) === null)
|
|
1217
1249
|
continue;
|
|
1218
1250
|
const spacer = document.createElement("div");
|
|
1219
1251
|
spacer.className = "gp-column-break-spacer";
|
|
@@ -1597,12 +1629,11 @@
|
|
|
1597
1629
|
let previous = "";
|
|
1598
1630
|
for (let pass = 0;pass < maxPasses; pass++) {
|
|
1599
1631
|
passes = Math.max(passes, pass + 1);
|
|
1600
|
-
const stride =
|
|
1632
|
+
const { stride, height: colBottom } = stripMetrics(strip.el);
|
|
1601
1633
|
const stripLeft = strip.el.getBoundingClientRect().left - strip.el.scrollLeft;
|
|
1602
1634
|
const zoom = cssZoomOf(strip.el);
|
|
1603
1635
|
const colOf = (r) => Math.floor(((r.left - stripLeft) / zoom + 1) / stride);
|
|
1604
1636
|
const stripTop = strip.el.getBoundingClientRect().top;
|
|
1605
|
-
const colBottom = strip.el.clientHeight;
|
|
1606
1637
|
const plans = [];
|
|
1607
1638
|
for (const table of tables) {
|
|
1608
1639
|
const head = repeatingHead(table);
|
|
@@ -1773,7 +1804,7 @@
|
|
|
1773
1804
|
const colGap = parseFloat(cs.columnGap) || 0;
|
|
1774
1805
|
const h = parseFloat(cs.getPropertyValue("--gp-content-h"));
|
|
1775
1806
|
const rowGap = parseFloat(cs.rowGap) || 0;
|
|
1776
|
-
return { stride: w + colGap, rowStride: h + rowGap };
|
|
1807
|
+
return { stride: w + colGap, rowStride: h + rowGap, height: h };
|
|
1777
1808
|
}
|
|
1778
1809
|
function rowStrideOf(strip) {
|
|
1779
1810
|
return stripMetrics(strip).rowStride;
|
|
@@ -2225,16 +2256,23 @@
|
|
|
2225
2256
|
const b = Math.max(0, area.bleed) * scale;
|
|
2226
2257
|
const s = Math.max(0, area.safe) * scale;
|
|
2227
2258
|
const a = Math.max(0, area.artSafe) * scale;
|
|
2228
|
-
const
|
|
2259
|
+
const g = Math.max(0, area.gutter) * scale;
|
|
2260
|
+
const [textLeft, textRight] = side === "verso" ? [s, g] : [g, s];
|
|
2261
|
+
const [artLeft, artRight] = side === "verso" ? [a, g] : [g, a];
|
|
2229
2262
|
const trim = { x: side === "verso" ? b : 0, y: b, w: Math.max(0, w - b), h: Math.max(0, h - 2 * b) };
|
|
2230
2263
|
return {
|
|
2231
2264
|
sheet: { x: 0, y: 0, w, h },
|
|
2232
2265
|
trim,
|
|
2233
|
-
safe: {
|
|
2266
|
+
safe: {
|
|
2267
|
+
x: trim.x + textLeft,
|
|
2268
|
+
y: trim.y + s,
|
|
2269
|
+
w: Math.max(0, trim.w - textLeft - textRight),
|
|
2270
|
+
h: Math.max(0, trim.h - 2 * s)
|
|
2271
|
+
},
|
|
2234
2272
|
safeArt: {
|
|
2235
|
-
x: trim.x +
|
|
2273
|
+
x: trim.x + artLeft,
|
|
2236
2274
|
y: trim.y + a,
|
|
2237
|
-
w: Math.max(0, trim.w -
|
|
2275
|
+
w: Math.max(0, trim.w - artLeft - artRight),
|
|
2238
2276
|
h: Math.max(0, trim.h - 2 * a)
|
|
2239
2277
|
},
|
|
2240
2278
|
bleed: b
|
|
@@ -2299,7 +2337,7 @@
|
|
|
2299
2337
|
};
|
|
2300
2338
|
function sheetArea(sheet) {
|
|
2301
2339
|
const px = (name) => parseFloat(sheet.style.getPropertyValue(name)) || 0;
|
|
2302
|
-
return { width: px("--gp-page-w") / PX_PER_PT, height: px("--gp-page-h") / PX_PER_PT, bleed: 0, safe: 0, artSafe: 0 };
|
|
2340
|
+
return { width: px("--gp-page-w") / PX_PER_PT, height: px("--gp-page-h") / PX_PER_PT, bleed: 0, safe: 0, artSafe: 0, gutter: 0 };
|
|
2303
2341
|
}
|
|
2304
2342
|
var OVERLAY_CSS = `
|
|
2305
2343
|
.root {
|
|
@@ -2311,12 +2349,12 @@
|
|
|
2311
2349
|
--gp-ov-bleed-color: #e5484d; /* bleed layer: the band, and (darker) the trim line */
|
|
2312
2350
|
--gp-ov-bleed-alpha: 0.45; /* opacity of the bleed band */
|
|
2313
2351
|
--gp-ov-trim-color: color-mix(in srgb, var(--gp-ov-bleed-color) 60%, #000); /* darker than the band so it reads as an edge */
|
|
2314
|
-
--gp-ov-art-color: #
|
|
2352
|
+
--gp-ov-art-color: #a855f7; /* art layer: the art line (dotted, a second cue) */
|
|
2315
2353
|
--gp-ov-margins-color: #1a9c6b; /* margins layer: the page's own content box */
|
|
2316
2354
|
--gp-ov-flag-width: 2px; /* overflow flag outline */
|
|
2317
2355
|
--gp-ov-warn: #f59e0b; /* leaves the safe area */
|
|
2318
2356
|
--gp-ov-error: #e5484d; /* crosses the trim / leaves the sheet */
|
|
2319
|
-
--gp-ov-intent: #
|
|
2357
|
+
--gp-ov-intent: #ec4899; /* placed on purpose (pink: apart from the purple art line) */
|
|
2320
2358
|
/* The overlay is scaled with the stage, so a 2px line would shrink with the
|
|
2321
2359
|
zoom; dividing by it keeps lines the same width on screen at every zoom. */
|
|
2322
2360
|
--gp-ov-w: calc(var(--gp-ov-width) / var(--gp-ov-z, 1));
|
|
@@ -2386,7 +2424,7 @@
|
|
|
2386
2424
|
.gp-ov-flag[data-kind="intended"] {
|
|
2387
2425
|
outline: var(--gp-ov-flag-w) dotted var(--gp-ov-intent);
|
|
2388
2426
|
}
|
|
2389
|
-
/* the legend: small, fixed in a corner of the viewport, and the one part that takes the pointer (its link) */
|
|
2427
|
+
/* the legend: small, fixed in a corner of the viewport, and the one part that takes the pointer (its link and its fold button) */
|
|
2390
2428
|
.gp-ov-legend {
|
|
2391
2429
|
position: fixed;
|
|
2392
2430
|
right: 12px;
|
|
@@ -2401,8 +2439,33 @@
|
|
|
2401
2439
|
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
|
|
2402
2440
|
pointer-events: auto;
|
|
2403
2441
|
}
|
|
2404
|
-
.gp-ov-
|
|
2442
|
+
.gp-ov-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2443
|
+
.gp-ov-body { margin-top: 6px; }
|
|
2444
|
+
.gp-ov-body > div + div { margin-top: 3px; }
|
|
2405
2445
|
.gp-ov-legend a { color: #9cc2ff; }
|
|
2446
|
+
/* the fold button: a chevron made of two borders, down while the legend is open, up once it is folded to its header */
|
|
2447
|
+
.gp-ov-toggle {
|
|
2448
|
+
flex: none;
|
|
2449
|
+
display: grid;
|
|
2450
|
+
place-items: center;
|
|
2451
|
+
width: 18px;
|
|
2452
|
+
height: 18px;
|
|
2453
|
+
padding: 0;
|
|
2454
|
+
border: 0;
|
|
2455
|
+
background: none;
|
|
2456
|
+
color: inherit;
|
|
2457
|
+
cursor: pointer;
|
|
2458
|
+
}
|
|
2459
|
+
.gp-ov-toggle:focus-visible { outline: 2px solid #9cc2ff; }
|
|
2460
|
+
.gp-ov-toggle::before {
|
|
2461
|
+
content: "";
|
|
2462
|
+
width: 6px;
|
|
2463
|
+
height: 6px;
|
|
2464
|
+
border: solid currentColor;
|
|
2465
|
+
border-width: 0 2px 2px 0;
|
|
2466
|
+
transform: translateY(-2px) rotate(45deg);
|
|
2467
|
+
}
|
|
2468
|
+
.gp-ov-toggle[aria-expanded="false"]::before { transform: translateY(2px) rotate(-135deg); }
|
|
2406
2469
|
.gp-ov-row { display: flex; align-items: flex-start; gap: 6px; }
|
|
2407
2470
|
.gp-ov-swatch {
|
|
2408
2471
|
flex: none;
|
|
@@ -2550,12 +2613,19 @@
|
|
|
2550
2613
|
var ART_TAGS = new Set(["img", "svg", "video", "canvas"]);
|
|
2551
2614
|
var inches = (px) => +(Math.abs(px) / 96).toFixed(2);
|
|
2552
2615
|
var ptIn = (pt2) => `${+(pt2 / 72).toFixed(3)}in`;
|
|
2616
|
+
var LEGEND_FOLD_KEY = "gutterpress.overlay-legend-collapsed";
|
|
2617
|
+
var legendCollapsed = false;
|
|
2618
|
+
try {
|
|
2619
|
+
legendCollapsed = localStorage.getItem(LEGEND_FOLD_KEY) === "true";
|
|
2620
|
+
} catch {}
|
|
2553
2621
|
function legend(layers, area, summary) {
|
|
2554
2622
|
const el = div("gp-ov-legend");
|
|
2623
|
+
const body = div("gp-ov-body");
|
|
2624
|
+
body.id = "gp-ov-body";
|
|
2555
2625
|
const add = (cls, ...content) => {
|
|
2556
2626
|
const line = div(cls);
|
|
2557
2627
|
line.append(...content);
|
|
2558
|
-
|
|
2628
|
+
body.appendChild(line);
|
|
2559
2629
|
};
|
|
2560
2630
|
const row = (layer, name, text) => {
|
|
2561
2631
|
const swatch = div("gp-ov-swatch");
|
|
@@ -2567,14 +2637,15 @@
|
|
|
2567
2637
|
add("gp-ov-row", swatch, label);
|
|
2568
2638
|
};
|
|
2569
2639
|
const { bleedPt, safePt, artSafePt, counts } = summary;
|
|
2640
|
+
const gutterPt = area?.gutter ?? safePt;
|
|
2570
2641
|
if (layers.bleed) {
|
|
2571
2642
|
row("bleed", "Bleed", bleedPt > 0 ? ` ${ptIn(bleedPt)} — top, bottom, outside; none at the binding` : " — none set (page.bleed in manifest.yaml)");
|
|
2572
2643
|
}
|
|
2573
2644
|
if (layers.safe) {
|
|
2574
|
-
row("safe", "Safe area: text", safePt
|
|
2645
|
+
row("safe", "Safe area: text", safePt <= 0 ? " — none set (page.safe in manifest.yaml)" : gutterPt === safePt ? ` — ${ptIn(safePt)}, every edge` : ` — ${ptIn(safePt)}; ${ptIn(gutterPt)} at the binding`);
|
|
2575
2646
|
}
|
|
2576
2647
|
if (layers.art) {
|
|
2577
|
-
row("art", "Safe area: art", safePt <= 0 && artSafePt <= 0 ? " — none set (page.artSafe in manifest.yaml)" : artSafePt ===
|
|
2648
|
+
row("art", "Safe area: art", safePt <= 0 && artSafePt <= 0 ? " — none set (page.artSafe in manifest.yaml)" : artSafePt === gutterPt ? ` — ${ptIn(artSafePt)}, every edge` : ` — ${ptIn(artSafePt)} top, bottom, outside; ${ptIn(gutterPt)} at the binding`);
|
|
2578
2649
|
}
|
|
2579
2650
|
if (layers.margins)
|
|
2580
2651
|
row("margins", "Page margins", " — your stylesheet's @page margins");
|
|
@@ -2610,6 +2681,30 @@
|
|
|
2610
2681
|
const key = document.documentElement.dataset.overlayKey;
|
|
2611
2682
|
if (key)
|
|
2612
2683
|
add("gp-ov-note", `${key} hides`);
|
|
2684
|
+
const title = document.createElement("b");
|
|
2685
|
+
title.id = "gp-ov-title";
|
|
2686
|
+
title.textContent = "Print overlays";
|
|
2687
|
+
const toggle = document.createElement("button");
|
|
2688
|
+
toggle.type = "button";
|
|
2689
|
+
toggle.className = "gp-ov-toggle";
|
|
2690
|
+
toggle.setAttribute("aria-labelledby", title.id);
|
|
2691
|
+
toggle.setAttribute("aria-controls", body.id);
|
|
2692
|
+
const fold = () => {
|
|
2693
|
+
body.hidden = legendCollapsed;
|
|
2694
|
+
toggle.setAttribute("aria-expanded", String(!legendCollapsed));
|
|
2695
|
+
toggle.title = legendCollapsed ? "Show the legend" : "Hide the legend";
|
|
2696
|
+
};
|
|
2697
|
+
toggle.addEventListener("click", () => {
|
|
2698
|
+
legendCollapsed = !legendCollapsed;
|
|
2699
|
+
try {
|
|
2700
|
+
localStorage.setItem(LEGEND_FOLD_KEY, String(legendCollapsed));
|
|
2701
|
+
} catch {}
|
|
2702
|
+
fold();
|
|
2703
|
+
});
|
|
2704
|
+
fold();
|
|
2705
|
+
const head = div("gp-ov-head");
|
|
2706
|
+
head.append(title, toggle);
|
|
2707
|
+
el.append(head, body);
|
|
2613
2708
|
return el;
|
|
2614
2709
|
}
|
|
2615
2710
|
function paintOverlay(pages, strips, area, layers) {
|
|
@@ -3029,7 +3124,7 @@
|
|
|
3029
3124
|
overlayPages.push({ index: bookIndex, sheet, strip, padded: ctx.padded });
|
|
3030
3125
|
}
|
|
3031
3126
|
const rows = Math.max(1, Math.ceil((strip.pages + shift) / perRow));
|
|
3032
|
-
run.style.
|
|
3127
|
+
run.style.minHeight = `${rowStride * (rows - 1) + PX_PER_PT * g.height}px`;
|
|
3033
3128
|
run.style.width = `${stride * perRow}px`;
|
|
3034
3129
|
const composed = !!strip.wrapCols && shift === 1 && !first;
|
|
3035
3130
|
run.style.marginTop = composed ? `${-(prevPageH + sheetGap)}px` : "";
|
|
@@ -3117,7 +3212,7 @@
|
|
|
3117
3212
|
}
|
|
3118
3213
|
}
|
|
3119
3214
|
function checkOverflow(strip, out) {
|
|
3120
|
-
const h = strip.el
|
|
3215
|
+
const { height: h } = stripMetrics(strip.el);
|
|
3121
3216
|
for (const el of Array.from(strip.el.querySelectorAll("figure,table,img,pre"))) {
|
|
3122
3217
|
const r = el.getBoundingClientRect();
|
|
3123
3218
|
if (r.height > h + 1) {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getAssetsDir,
|
|
10
10
|
lastPageMode,
|
|
11
11
|
parseSize
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-crys9951.js";
|
|
13
13
|
import {
|
|
14
14
|
countLinkAnnotations,
|
|
15
15
|
getImageResolutions,
|
|
@@ -180,7 +180,8 @@ var DTRPG_PRESET = {
|
|
|
180
180
|
tolerance: 0.5,
|
|
181
181
|
bleed: 9,
|
|
182
182
|
safe: 36,
|
|
183
|
-
artSafe: 18
|
|
183
|
+
artSafe: 18,
|
|
184
|
+
gutter: undefined
|
|
184
185
|
},
|
|
185
186
|
ink: {
|
|
186
187
|
maxTac: 240,
|
|
@@ -267,7 +268,8 @@ var BOOK_PRESET = {
|
|
|
267
268
|
tolerance: 0.5,
|
|
268
269
|
bleed: undefined,
|
|
269
270
|
safe: 18,
|
|
270
|
-
artSafe: undefined
|
|
271
|
+
artSafe: undefined,
|
|
272
|
+
gutter: undefined
|
|
271
273
|
},
|
|
272
274
|
ink: {
|
|
273
275
|
maxTac: 400,
|
|
@@ -631,7 +633,8 @@ function resolveCustomPage(c, m) {
|
|
|
631
633
|
tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
|
|
632
634
|
bleed: c.page?.bleed ?? m.page?.bleed,
|
|
633
635
|
safe: c.page?.safe ?? m.page?.safe,
|
|
634
|
-
artSafe: c.page?.artSafe ?? m.page?.artSafe
|
|
636
|
+
artSafe: c.page?.artSafe ?? m.page?.artSafe,
|
|
637
|
+
gutter: c.page?.gutter ?? m.page?.gutter
|
|
635
638
|
};
|
|
636
639
|
}
|
|
637
640
|
function resolveWithPreset(cliOverrides, manifest, preset) {
|
|
@@ -1987,11 +1990,15 @@ var GUTTERPRESS_CSS = `
|
|
|
1987
1990
|
|
|
1988
1991
|
/* sizes — AFTER the flow positions so max-width:100% lifts the floats' 50%
|
|
1989
1992
|
cap at equal specificity */
|
|
1990
|
-
.gp-
|
|
1991
|
-
.gp-
|
|
1992
|
-
.gp-
|
|
1993
|
-
.gp-
|
|
1994
|
-
.gp-
|
|
1993
|
+
.gp-size-10 { width: 10%; max-width: 100%; }
|
|
1994
|
+
.gp-size-20 { width: 20%; max-width: 100%; }
|
|
1995
|
+
.gp-size-30 { width: 30%; max-width: 100%; }
|
|
1996
|
+
.gp-size-40 { width: 40%; max-width: 100%; }
|
|
1997
|
+
.gp-size-50 { width: 50%; max-width: 100%; }
|
|
1998
|
+
.gp-size-60 { width: 60%; max-width: 100%; }
|
|
1999
|
+
.gp-size-70 { width: 70%; max-width: 100%; }
|
|
2000
|
+
.gp-size-80 { width: 80%; max-width: 100%; }
|
|
2001
|
+
.gp-size-90 { width: 90%; max-width: 100%; }
|
|
1995
2002
|
|
|
1996
2003
|
/* float clearance presets — consumed by var(--gp-gap) in the float rules
|
|
1997
2004
|
above and by .gp-shape's shape-margin below; --gp-gap itself is
|
|
@@ -2189,11 +2196,15 @@ var GP_CLASSES = new Set([
|
|
|
2189
2196
|
"gp-center",
|
|
2190
2197
|
"gp-full",
|
|
2191
2198
|
"gp-bleed",
|
|
2192
|
-
"gp-
|
|
2193
|
-
"gp-
|
|
2194
|
-
"gp-
|
|
2195
|
-
"gp-
|
|
2196
|
-
"gp-
|
|
2199
|
+
"gp-size-10",
|
|
2200
|
+
"gp-size-20",
|
|
2201
|
+
"gp-size-30",
|
|
2202
|
+
"gp-size-40",
|
|
2203
|
+
"gp-size-50",
|
|
2204
|
+
"gp-size-60",
|
|
2205
|
+
"gp-size-70",
|
|
2206
|
+
"gp-size-80",
|
|
2207
|
+
"gp-size-90",
|
|
2197
2208
|
"gp-tight",
|
|
2198
2209
|
"gp-loose",
|
|
2199
2210
|
"gp-columns",
|
|
@@ -2318,10 +2329,10 @@ import markdownItAttrs from "markdown-it-attrs";
|
|
|
2318
2329
|
import markdownItFootnote from "markdown-it-footnote";
|
|
2319
2330
|
|
|
2320
2331
|
// src/lib/markdown/gp-pin-scope.js
|
|
2321
|
-
function warn3(env, line, type, message,
|
|
2332
|
+
function warn3(env, line, type, message, severity) {
|
|
2322
2333
|
if (!env.layoutWarnings)
|
|
2323
2334
|
env.layoutWarnings = [];
|
|
2324
|
-
env.layoutWarnings.push({ line, type, message,
|
|
2335
|
+
env.layoutWarnings.push(severity ? { line, type, message, severity } : { line, type, message });
|
|
2325
2336
|
}
|
|
2326
2337
|
function editDistance2(a, b) {
|
|
2327
2338
|
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
@@ -2364,7 +2375,7 @@ function checkFlushArt(token, env, line) {
|
|
|
2364
2375
|
return;
|
|
2365
2376
|
if (!classes.some((c) => PIN_EDGES.includes(c)))
|
|
2366
2377
|
return;
|
|
2367
|
-
warn3(env, line, "flush_art", FLUSH_ART_MSG);
|
|
2378
|
+
warn3(env, line, "flush_art", FLUSH_ART_MSG, "info");
|
|
2368
2379
|
}
|
|
2369
2380
|
function elementNameFor(token) {
|
|
2370
2381
|
switch (token.type) {
|
|
@@ -3185,6 +3196,7 @@ function printAreaMeta(page) {
|
|
|
3185
3196
|
bleed: page.bleed ?? 0,
|
|
3186
3197
|
safe: page.safe ?? 0,
|
|
3187
3198
|
artSafe: page.artSafe,
|
|
3199
|
+
gutter: page.gutter,
|
|
3188
3200
|
preset: page.preset,
|
|
3189
3201
|
spec: page.spec
|
|
3190
3202
|
});
|
|
@@ -3813,7 +3825,7 @@ import { satisfies, validRange } from "semver";
|
|
|
3813
3825
|
// package.json
|
|
3814
3826
|
var package_default = {
|
|
3815
3827
|
name: "gutterpress",
|
|
3816
|
-
version: "0.11.20-alpha.
|
|
3828
|
+
version: "0.11.20-alpha.5",
|
|
3817
3829
|
description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
|
|
3818
3830
|
author: "itlackey",
|
|
3819
3831
|
license: "MPL-2.0",
|
|
@@ -6875,11 +6887,11 @@ var inches = (pt) => `${+(pt / 72).toFixed(2)}in`;
|
|
|
6875
6887
|
var check4 = {
|
|
6876
6888
|
id: "pdf.print.safe-margins",
|
|
6877
6889
|
name: "Safe margins",
|
|
6878
|
-
description: "Fails when text sits closer to the trim than `page.safe` (DriveThruRPG: 0.5in on every edge). Text only: art is judged in the preview's safe-area overlay, not here. Skipped when `page.safe` is not set",
|
|
6890
|
+
description: "Fails when text sits closer to the trim than `page.safe` (DriveThruRPG: 0.5in on every edge), or than `page.gutter` at the binding edge where one is set. Text only: art is judged in the preview's safe-area overlay, not here. Skipped when `page.safe` is not set",
|
|
6879
6891
|
category: "pdf",
|
|
6880
6892
|
phase: "post-build",
|
|
6881
6893
|
async run(ctx) {
|
|
6882
|
-
const { width, height, bleed = 0, safe = 0 } = ctx.config.page;
|
|
6894
|
+
const { width, height, bleed = 0, safe = 0, gutter = safe } = ctx.config.page;
|
|
6883
6895
|
if (!ctx.pdfPath || !safe)
|
|
6884
6896
|
return [];
|
|
6885
6897
|
const doc = await loadPdf(ctx.pdfPath);
|
|
@@ -6917,8 +6929,7 @@ var check4 = {
|
|
|
6917
6929
|
["bottom", Math.min(...ys) - trim.bottom],
|
|
6918
6930
|
["top", trim.top - Math.max(...ys)]
|
|
6919
6931
|
];
|
|
6920
|
-
const [edge,
|
|
6921
|
-
const shortfall = safe - gap;
|
|
6932
|
+
const [edge, shortfall] = gaps.map(([name, gap]) => [name, (name === "binding" ? gutter : safe) - gap]).reduce((worst, g) => g[1] > worst[1] ? g : worst);
|
|
6922
6933
|
if (shortfall <= TOLERANCE_PT)
|
|
6923
6934
|
continue;
|
|
6924
6935
|
const text = item.str.trim();
|
|
@@ -6926,7 +6937,7 @@ var check4 = {
|
|
|
6926
6937
|
severity: "error",
|
|
6927
6938
|
message: `Page ${pageNum}: "${text.slice(0, 40)}${text.length > 40 ? "…" : ""}" is ${inches(shortfall)} too close to the ${edge} edge.`,
|
|
6928
6939
|
file: ctx.pdfPath,
|
|
6929
|
-
detail: `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.`,
|
|
6940
|
+
detail: gutter === safe ? `${rule}: text must stay at least ${inches(safe)} inside the trim on every edge.` : `${rule}: text must stay at least ${inches(safe)} inside the trim, ${inches(gutter)} at the binding.`,
|
|
6930
6941
|
code: "text-outside-safe-margin",
|
|
6931
6942
|
data: { page: pageNum, edge, shortfallPt: +shortfall.toFixed(2) }
|
|
6932
6943
|
}));
|
|
@@ -10867,7 +10878,7 @@ class PdfOutput {
|
|
|
10867
10878
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
10868
10879
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
10869
10880
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
10870
|
-
const { buildNativePdf } = await import("./engine-
|
|
10881
|
+
const { buildNativePdf } = await import("./engine-5fw55dfb.js");
|
|
10871
10882
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
10872
10883
|
title: config.title,
|
|
10873
10884
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -148,7 +148,7 @@ import { fileURLToPath } from "node:url";
|
|
|
148
148
|
var favicon_default = "./favicon-272ga6y0.ico";
|
|
149
149
|
|
|
150
150
|
// src/assets/manifest.schema.json
|
|
151
|
-
var manifest_schema_default = "./manifest.schema-
|
|
151
|
+
var manifest_schema_default = "./manifest.schema-c4wfpg6g.json";
|
|
152
152
|
|
|
153
153
|
// src/assets/preview/scripts/preview-interface.js
|
|
154
154
|
var preview_interface_default = "./preview-interface-nnmrxyqm.js";
|
|
@@ -163,10 +163,10 @@ var preview_shell_default = "./preview-shell-wk9k3gcd.js";
|
|
|
163
163
|
var CGATS21_CRPC1_default = "./CGATS21_CRPC1-g0e3k7kr.icc";
|
|
164
164
|
|
|
165
165
|
// src/assets/engine/gutterpress-viewer.js
|
|
166
|
-
var gutterpress_viewer_default = "./gutterpress-viewer-
|
|
166
|
+
var gutterpress_viewer_default = "./gutterpress-viewer-w7njnkj7.js";
|
|
167
167
|
|
|
168
168
|
// src/assets/engine/gutterpress-agent.js
|
|
169
|
-
var gutterpress_agent_default = "./gutterpress-agent-
|
|
169
|
+
var gutterpress_agent_default = "./gutterpress-agent-yvd8w8my.js";
|
|
170
170
|
|
|
171
171
|
// src/assets/templates/book/manifest.yaml
|
|
172
172
|
var manifest_default = "./manifest-n1gh3qw5.yaml";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
stringValueAt,
|
|
20
20
|
toFolioPage,
|
|
21
21
|
wantsRecto
|
|
22
|
-
} from "./index-
|
|
22
|
+
} from "./index-crys9951.js";
|
|
23
23
|
import {
|
|
24
24
|
launchChromium
|
|
25
25
|
} from "./index-bt0qws5c.js";
|
|
@@ -49,7 +49,7 @@ function flushMargins(margin, edges) {
|
|
|
49
49
|
}
|
|
50
50
|
function pinInsets(area, side, edges) {
|
|
51
51
|
const binding = side === "recto" ? "left" : "right";
|
|
52
|
-
const inset = (edge) => !area || !edges.includes(edge) ? 0 : edge === binding ? area.
|
|
52
|
+
const inset = (edge) => !area || !edges.includes(edge) ? 0 : edge === binding ? area.gutter : area.bleed + area.artSafe;
|
|
53
53
|
return { top: inset("top"), right: inset("right"), bottom: inset("bottom"), left: inset("left") };
|
|
54
54
|
}
|
|
55
55
|
var NO_PADDING = { top: 0, right: 0, bottom: 0, left: 0 };
|
package/dist/index.js
CHANGED
|
@@ -154,14 +154,14 @@ import {
|
|
|
154
154
|
testRemoteAccess,
|
|
155
155
|
updateExtensions,
|
|
156
156
|
validateProjectExtensions
|
|
157
|
-
} from "./index-
|
|
157
|
+
} from "./index-bta8hc2q.js";
|
|
158
158
|
import"./index-ht1bcqdd.js";
|
|
159
159
|
import {
|
|
160
160
|
BUILD_DIAGNOSTIC_CODES
|
|
161
|
-
} from "./index-
|
|
161
|
+
} from "./index-yw16cfm8.js";
|
|
162
162
|
import {
|
|
163
163
|
BuildError
|
|
164
|
-
} from "./index-
|
|
164
|
+
} from "./index-crys9951.js";
|
|
165
165
|
import"./index-sth219v1.js";
|
|
166
166
|
import {
|
|
167
167
|
DEFAULT_PRINT_OPTS,
|
|
@@ -44,7 +44,7 @@ export interface LayoutWarning {
|
|
|
44
44
|
type: string;
|
|
45
45
|
message: string;
|
|
46
46
|
marker?: unknown;
|
|
47
|
-
/** Set
|
|
47
|
+
/** Set by a component's `validate` and by `flush_art`; absent means `"warning"`. */
|
|
48
48
|
severity?: "error" | "warning" | "info";
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Also ships the author-facing `gp-*` image/block vocabulary (CLAUDE.md §0 —
|
|
19
19
|
* a behavior broadly useful to non-technical authors belongs in core, not a
|
|
20
20
|
* project layer). markdown-it-attrs is bundled by
|
|
21
|
-
* default (renderer.ts), so `{.gp-right .gp-
|
|
21
|
+
* default (renderer.ts), so `{.gp-right .gp-size-30}` already
|
|
22
22
|
* attaches the classes to the rendered `<img>` — these rules are what make
|
|
23
23
|
* them actually do something print-safe. One vocabulary, no aliases: there
|
|
24
24
|
* is no duplicate way to spell each layout (the desktop editor rewrites the
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
* .gp-right — floats right, text wraps.
|
|
30
30
|
* .gp-center — centers a block element.
|
|
31
31
|
* .gp-full — fills the page's content width.
|
|
32
|
-
* .gp-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* .gp-size-10 … .gp-size-90
|
|
33
|
+
* — width 10% … 90% of the column, in 10% steps. Sizes
|
|
34
|
+
* compose with any position (including .gp-pin, where
|
|
35
|
+
* the % resolves against the page root: the page box,
|
|
36
|
+
* reaching the paper on an edge a pin uses).
|
|
37
37
|
* There is no 100% step: that is .gp-full, a position.
|
|
38
38
|
* .gp-tight — float/shape clearance 0.5em (sets --gp-gap; default 1em).
|
|
39
39
|
* .gp-loose — float/shape clearance 2em (sets --gp-gap).
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
* is pinned. The pin's containing
|
|
133
133
|
* block is the root's padding box, and the engine sets `--gp-pin-<edge>` on
|
|
134
134
|
* the root to the art-safe line's distance from the sheet edge (the bleed
|
|
135
|
-
* plus page.artSafe on the top, bottom and outside edge; page.
|
|
135
|
+
* plus page.artSafe on the top, bottom and outside edge; page.gutter at the
|
|
136
136
|
* binding, from the manifest); `.gp-pin` reads them as its four insets.
|
|
137
137
|
* `.gp-flush` zeroes them on the pin: the paper's edge. Under plain
|
|
138
138
|
* markdown-it nothing sets them and every inset is 0.
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
* carrying gp-left could start shifting; that is standards-tracking per
|
|
152
152
|
* CLAUDE.md ("Chrome wins once it ships"), not a bug in the author's book.
|
|
153
153
|
*/
|
|
154
|
-
export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-small { width: 25%; max-width: 100%; }\n.gp-medium { width: 50%; max-width: 100%; }\n.gp-large { width: 60%; max-width: 100%; }\n.gp-xl { width: 80%; max-width: 100%; }\n.gp-xxl { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n";
|
|
154
|
+
export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-size-10 { width: 10%; max-width: 100%; }\n.gp-size-20 { width: 20%; max-width: 100%; }\n.gp-size-30 { width: 30%; max-width: 100%; }\n.gp-size-40 { width: 40%; max-width: 100%; }\n.gp-size-50 { width: 50%; max-width: 100%; }\n.gp-size-60 { width: 60%; max-width: 100%; }\n.gp-size-70 { width: 70%; max-width: 100%; }\n.gp-size-80 { width: 80%; max-width: 100%; }\n.gp-size-90 { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n";
|
|
155
155
|
/**
|
|
156
156
|
* GP_CLASSES — every class an author may legitimately write with a `gp-`
|
|
157
157
|
* prefix: the `.gp-*` selectors in `GUTTERPRESS_CSS` above, the two
|
|
@@ -228,15 +228,21 @@
|
|
|
228
228
|
},
|
|
229
229
|
"safe": {
|
|
230
230
|
"type": "number",
|
|
231
|
-
"description": "The TEXT rule, in points: how far inside the trim glyphs must stay, on every edge. For DriveThruRPG it is the \"text at least 0.5in inside the trim\" rule. The preview's overlay outlines it (dashed) and flags text outside it, and the `pdf.print.safe-margins` check fails a PDF whose text is closer. An image pinned to
|
|
231
|
+
"description": "The TEXT rule, in points: how far inside the trim glyphs must stay, on every edge (the binding edge too, unless `gutter` is set). For DriveThruRPG it is the \"text at least 0.5in inside the trim\" rule. The preview's overlay outlines it (dashed) and flags text outside it, and the `pdf.print.safe-margins` check fails a PDF whose text is closer. An image pinned to an edge (`.gp-pin`) where `artSafe` is not set sits on it; no book CSS moves it. Preset default: dtrpg 36 (0.5in), book 18 (0.25in); custom none.",
|
|
232
232
|
"minimum": 0,
|
|
233
233
|
"examples": [18, 36]
|
|
234
234
|
},
|
|
235
235
|
"artSafe": {
|
|
236
236
|
"type": "number",
|
|
237
|
-
"description": "The ART rule, in points: how far inside the trim art that does not bleed (images, backgrounds, borders, rules) must stay on the top, bottom and OUTER edge; at the binding edge art uses `
|
|
237
|
+
"description": "The ART rule, in points: how far inside the trim art that does not bleed (images, backgrounds, borders, rules) must stay on the top, bottom and OUTER edge; at the binding edge art uses `gutter`. For DriveThruRPG it is the \"non-bleeding art at least 0.25in from the outside edges (0.5in at the binding)\" rule. Art that runs out to the page's edge is a bleed and is not judged on that edge. The preview's overlay outlines it (dotted) and flags art outside it, and an image pinned to the top, bottom or outer edge (`.gp-pin`) sits on it unless it carries `.gp-flush`. Absent = the same as `safe`. Preset default: dtrpg 18 (0.25in); book and custom none.",
|
|
238
238
|
"minimum": 0,
|
|
239
239
|
"examples": [18]
|
|
240
|
+
},
|
|
241
|
+
"gutter": {
|
|
242
|
+
"type": "number",
|
|
243
|
+
"description": "The binding allowance, in points: how far content stays from the spine, measured from the trim at the binding edge (the left edge of a right-hand page, the right edge of a left-hand one). It stands in for `safe` there, for text and for art alike: the preview's overlay draws and judges that edge against it, the `pdf.print.safe-margins` check fails text closer to the spine than it, and an image pinned to the binding edge (`.gp-pin`) sits on it. The other edges keep `safe` and `artSafe`. Use it when your book keeps its text further from the spine than the printer's minimum: the Field Guide keeps 0.75in (54) where DriveThruRPG asks for 0.5in. Absent = the same as `safe`. Presets leave it unset.",
|
|
244
|
+
"minimum": 0,
|
|
245
|
+
"examples": [54]
|
|
240
246
|
}
|
|
241
247
|
}
|
|
242
248
|
},
|
|
@@ -7,17 +7,17 @@ import {
|
|
|
7
7
|
TARGET_IDS,
|
|
8
8
|
scaffoldExtension,
|
|
9
9
|
scaffoldProject
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-sc5t7c74.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveGhostscript
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-6xym15vm.js";
|
|
14
14
|
import {
|
|
15
15
|
UsageError,
|
|
16
16
|
rejectExtraPositionals,
|
|
17
17
|
rejectUnknownFlags
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-553ce1d4.js";
|
|
19
19
|
import"./cli-ht1bcqdd.js";
|
|
20
|
-
import"./cli-
|
|
20
|
+
import"./cli-cyej3p9v.js";
|
|
21
21
|
import {
|
|
22
22
|
EXIT_CODES
|
|
23
23
|
} from "./cli-46ycxe6r.js";
|