gutterpress 0.11.18-alpha.1 → 0.11.18-alpha.3

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 (29) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-fpd3qp17.js → build-5n7wpqg3.js} +4 -4
  3. package/dist/{cli-gv97c2a6.js → cli-12aq78aq.js} +1 -1
  4. package/dist/{cli-1msxnhzy.js → cli-72r4c2k4.js} +15 -10
  5. package/dist/{cli-bzz33hjz.js → cli-z835nehj.js} +1 -1
  6. package/dist/{cli-th1ve2cx.js → cli-zrs92eef.js} +5 -5
  7. package/dist/cli.js +12 -12
  8. package/dist/{doctor-cg0kb8h6.js → doctor-e3zkvkkh.js} +2 -2
  9. package/dist/{engine-0g9t43gx.js → engine-17gjf7t0.js} +1 -1
  10. package/dist/{engine-s2caze6b.js → engine-68avsbe2.js} +2 -2
  11. package/dist/{ext-keedd40p.js → ext-rcf6hfhq.js} +4 -4
  12. package/dist/{gutterpress-viewer-p50c4f08.js → gutterpress-viewer-k4mpqjh0.js} +175 -56
  13. package/dist/{index-jg1r9t4w.js → index-b81jc99n.js} +5 -5
  14. package/dist/{index-3pnmf25p.js → index-q88g67zg.js} +1 -1
  15. package/dist/{index-pdanxwn1.js → index-rpc74pqb.js} +14 -9
  16. package/dist/index.js +3 -3
  17. package/dist/lib/presets.d.ts +11 -0
  18. package/dist/{manifest.schema-gh0jjve9.json → manifest.schema-8m82f37q.json} +6 -0
  19. package/dist/{new-d010czw1.js → new-6pm52vh7.js} +4 -4
  20. package/dist/{preflight-jgcp1xsr.js → preflight-d84a2ab9.js} +4 -4
  21. package/dist/preview/file-watcher.d.ts +4 -1
  22. package/dist/{preview-m3afbq53.js → preview-9s4ktkbk.js} +4 -4
  23. package/dist/{preview-bridge-ga409pgz.js → preview-bridge-evecsh4w.js} +1 -1
  24. package/dist/{preview-interface-dm6wbdq6.js → preview-interface-2n1d9pbf.js} +10 -4
  25. package/dist/{preview-shell-c909krk2.js → preview-shell-hyp7g158.js} +7 -1
  26. package/dist/{publish-69x46m3f.js → publish-wcrtgxgk.js} +4 -4
  27. package/dist/schema/manifest.types.d.ts +11 -0
  28. package/dist/{validate-vc6cp1cs.js → validate-q9c00m8g.js} +4 -4
  29. package/package.json +1 -1
package/dist/api/index.js CHANGED
@@ -136,11 +136,11 @@ import {
136
136
  testRemoteAccess,
137
137
  updateExtensions,
138
138
  validateProjectExtensions
139
- } from "../index-pdanxwn1.js";
139
+ } from "../index-rpc74pqb.js";
140
140
  import"../index-ht1bcqdd.js";
141
141
  import {
142
142
  BuildError
143
- } from "../index-jg1r9t4w.js";
143
+ } from "../index-b81jc99n.js";
144
144
  import {
145
145
  DEFAULT_PRINT_OPTS,
146
146
  assertMilestone,
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  runBuild,
3
3
  splitOutPath
4
- } from "./cli-1msxnhzy.js";
4
+ } from "./cli-72r4c2k4.js";
5
5
  import {
6
6
  log
7
- } from "./cli-gv97c2a6.js";
7
+ } from "./cli-12aq78aq.js";
8
8
  import {
9
9
  UsageError,
10
10
  parseFormat,
11
11
  parsePdfxFlavor,
12
12
  rejectExtraPositionals,
13
13
  rejectUnknownFlags
14
- } from "./cli-bzz33hjz.js";
14
+ } from "./cli-z835nehj.js";
15
15
  import"./cli-ht1bcqdd.js";
16
- import"./cli-th1ve2cx.js";
16
+ import"./cli-zrs92eef.js";
17
17
  import {
18
18
  BuildError
19
19
  } from "./cli-46ycxe6r.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "./cli-bzz33hjz.js";
3
+ } from "./cli-z835nehj.js";
4
4
  import {
5
5
  execCapture,
6
6
  findTool,
@@ -13,12 +13,12 @@ import {
13
13
  stampCreator,
14
14
  stripAnnotations,
15
15
  warn
16
- } from "./cli-gv97c2a6.js";
16
+ } from "./cli-12aq78aq.js";
17
17
  import {
18
18
  DEBOUNCE,
19
19
  UsageError,
20
20
  resolvePort
21
- } from "./cli-bzz33hjz.js";
21
+ } from "./cli-z835nehj.js";
22
22
  import {
23
23
  blackTextToGray
24
24
  } from "./cli-ht1bcqdd.js";
@@ -27,7 +27,7 @@ import {
27
27
  getAssetPath,
28
28
  getAssetsDir,
29
29
  lastPageMode
30
- } from "./cli-th1ve2cx.js";
30
+ } from "./cli-zrs92eef.js";
31
31
  import {
32
32
  BuildError,
33
33
  EXIT_CODES
@@ -120,7 +120,8 @@ var DTRPG_PRESET = {
120
120
  page: {
121
121
  width: 621,
122
122
  height: 810,
123
- tolerance: 0.5
123
+ tolerance: 0.5,
124
+ bleed: 9
124
125
  },
125
126
  ink: {
126
127
  maxTac: 240,
@@ -196,7 +197,8 @@ var BOOK_PRESET = {
196
197
  page: {
197
198
  width: 432,
198
199
  height: 648,
199
- tolerance: 0.5
200
+ tolerance: 0.5,
201
+ bleed: undefined
200
202
  },
201
203
  ink: {
202
204
  maxTac: 400,
@@ -543,7 +545,8 @@ function resolveCustomPage(c, m) {
543
545
  return {
544
546
  width,
545
547
  height,
546
- tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5
548
+ tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
549
+ bleed: c.page?.bleed ?? m.page?.bleed
547
550
  };
548
551
  }
549
552
  function resolveWithPreset(cliOverrides, manifest, preset) {
@@ -9723,7 +9726,7 @@ class PdfOutput {
9723
9726
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
9724
9727
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
9725
9728
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
9726
- const { buildNativePdf } = await import("./engine-0g9t43gx.js");
9729
+ const { buildNativePdf } = await import("./engine-17gjf7t0.js");
9727
9730
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
9728
9731
  title: config.title,
9729
9732
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -9974,8 +9977,10 @@ async function renderPreviewBook(inputPath, config, opts) {
9974
9977
  }
9975
9978
  });
9976
9979
  }
9977
- function injectPreviewScripts(html) {
9978
- const scripts = ` <script src="/engine/gutterpress-viewer.js"></script>
9980
+ function injectPreviewScripts(html, insetBleedPt = 0) {
9981
+ const meta = insetBleedPt > 0 ? ` <meta name="gp-inset-bleed" content="${insetBleedPt}">
9982
+ ` : "";
9983
+ const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
9979
9984
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
9980
9985
  ` + `<script src="/preview/scripts/preview-bridge.js"></script>
9981
9986
  `;
@@ -10009,7 +10014,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
10009
10014
  for (const ref of nextImageRefs)
10010
10015
  imageRefs.add(ref);
10011
10016
  }
10012
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html), "utf-8");
10017
+ await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page?.bleed), "utf-8");
10013
10018
  }
10014
10019
  function isDotPathUnderRoot(candidatePath, root) {
10015
10020
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
@@ -4,7 +4,7 @@ import {
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "gutterpress",
7
- version: "0.11.18-alpha.1",
7
+ version: "0.11.18-alpha.3",
8
8
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
9
9
  author: "itlackey",
10
10
  license: "MPL-2.0",
@@ -1116,22 +1116,22 @@ import { fileURLToPath } from "node:url";
1116
1116
  var favicon_default = "./favicon-272ga6y0.ico";
1117
1117
 
1118
1118
  // src/assets/manifest.schema.json
1119
- var manifest_schema_default = "./manifest.schema-gh0jjve9.json";
1119
+ var manifest_schema_default = "./manifest.schema-8m82f37q.json";
1120
1120
 
1121
1121
  // src/assets/preview/scripts/preview-interface.js
1122
- var preview_interface_default = "./preview-interface-dm6wbdq6.js";
1122
+ var preview_interface_default = "./preview-interface-2n1d9pbf.js";
1123
1123
 
1124
1124
  // src/assets/preview/scripts/preview-bridge.js
1125
- var preview_bridge_default = "./preview-bridge-ga409pgz.js";
1125
+ var preview_bridge_default = "./preview-bridge-evecsh4w.js";
1126
1126
 
1127
1127
  // src/assets/preview/scripts/preview-shell.js
1128
- var preview_shell_default = "./preview-shell-c909krk2.js";
1128
+ var preview_shell_default = "./preview-shell-hyp7g158.js";
1129
1129
 
1130
1130
  // profiles/CGATS21_CRPC1.icc
1131
1131
  var CGATS21_CRPC1_default = "./CGATS21_CRPC1-g0e3k7kr.icc";
1132
1132
 
1133
1133
  // src/assets/engine/gutterpress-viewer.js
1134
- var gutterpress_viewer_default = "./gutterpress-viewer-p50c4f08.js";
1134
+ var gutterpress_viewer_default = "./gutterpress-viewer-k4mpqjh0.js";
1135
1135
 
1136
1136
  // src/assets/engine/gutterpress-agent.js
1137
1137
  var gutterpress_agent_default = "./gutterpress-agent-cazqstr1.js";
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  UsageError,
7
7
  package_default,
8
8
  rejectUnknownFlags
9
- } from "./cli-bzz33hjz.js";
9
+ } from "./cli-z835nehj.js";
10
10
  import {
11
11
  EXIT_CODES
12
12
  } from "./cli-46ycxe6r.js";
@@ -18,14 +18,14 @@ import {
18
18
  import { defineCommand, parseArgs, runMain } from "citty";
19
19
  import { statSync } from "node:fs";
20
20
  var SUBCOMMANDS = {
21
- new: () => import("./new-d010czw1.js").then((m) => m.default),
22
- preview: () => import("./preview-m3afbq53.js").then((m) => m.default),
23
- build: () => import("./build-fpd3qp17.js").then((m) => m.default),
24
- publish: () => import("./publish-69x46m3f.js").then((m) => m.default),
25
- validate: () => import("./validate-vc6cp1cs.js").then((m) => m.default),
26
- preflight: () => import("./preflight-jgcp1xsr.js").then((m) => m.default),
27
- doctor: () => import("./doctor-cg0kb8h6.js").then((m) => m.default),
28
- ext: () => import("./ext-keedd40p.js").then((m) => m.default)
21
+ new: () => import("./new-6pm52vh7.js").then((m) => m.default),
22
+ preview: () => import("./preview-9s4ktkbk.js").then((m) => m.default),
23
+ build: () => import("./build-5n7wpqg3.js").then((m) => m.default),
24
+ publish: () => import("./publish-wcrtgxgk.js").then((m) => m.default),
25
+ validate: () => import("./validate-q9c00m8g.js").then((m) => m.default),
26
+ preflight: () => import("./preflight-d84a2ab9.js").then((m) => m.default),
27
+ doctor: () => import("./doctor-e3zkvkkh.js").then((m) => m.default),
28
+ ext: () => import("./ext-rcf6hfhq.js").then((m) => m.default)
29
29
  };
30
30
  var VERSION = package_default.version;
31
31
  var main = defineCommand({
@@ -50,7 +50,7 @@ async function preflightRequiredInvocations(rawArgs) {
50
50
  const [command, ...commandArgs] = rawArgs;
51
51
  try {
52
52
  if (command === "new") {
53
- const { newArgs } = await import("./new-d010czw1.js");
53
+ const { newArgs } = await import("./new-6pm52vh7.js");
54
54
  rejectUnknownFlags(commandArgs, newArgs, "new");
55
55
  const parsed = parseArgs(commandArgs, {
56
56
  ...newArgs,
@@ -62,7 +62,7 @@ async function preflightRequiredInvocations(rawArgs) {
62
62
  return;
63
63
  }
64
64
  if (command === "preflight") {
65
- const { preflightArgs } = await import("./preflight-jgcp1xsr.js");
65
+ const { preflightArgs } = await import("./preflight-d84a2ab9.js");
66
66
  rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
67
67
  const parsed = parseArgs(commandArgs, {
68
68
  ...preflightArgs,
@@ -82,7 +82,7 @@ async function preflightRequiredInvocations(rawArgs) {
82
82
  }
83
83
  if (subcommand.startsWith("-"))
84
84
  return;
85
- const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-keedd40p.js");
85
+ const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-rcf6hfhq.js");
86
86
  if (!EXT_SUBCOMMANDS.includes(subcommand)) {
87
87
  throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
88
88
  }
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  getSystemDiagnostics,
3
3
  log
4
- } from "./cli-gv97c2a6.js";
4
+ } from "./cli-12aq78aq.js";
5
5
  import {
6
6
  UsageError,
7
7
  rejectExtraPositionals,
8
8
  rejectUnknownFlags
9
- } from "./cli-bzz33hjz.js";
9
+ } from "./cli-z835nehj.js";
10
10
  import"./cli-46ycxe6r.js";
11
11
  import"./cli-7fkhgtfn.js";
12
12
  import"./cli-37x76zdn.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  build
3
- } from "./cli-th1ve2cx.js";
3
+ } from "./cli-zrs92eef.js";
4
4
  import {
5
5
  BuildError
6
6
  } from "./cli-46ycxe6r.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  build
3
- } from "./index-3pnmf25p.js";
3
+ } from "./index-q88g67zg.js";
4
4
  import {
5
5
  BuildError
6
- } from "./index-jg1r9t4w.js";
6
+ } from "./index-b81jc99n.js";
7
7
  import"./index-rzm1phg0.js";
8
8
  import"./index-37x76zdn.js";
9
9
 
@@ -12,17 +12,17 @@ import {
12
12
  searchNpmExtensions,
13
13
  setExtensionEnabled,
14
14
  updateExtensions
15
- } from "./cli-1msxnhzy.js";
16
- import"./cli-gv97c2a6.js";
15
+ } from "./cli-72r4c2k4.js";
16
+ import"./cli-12aq78aq.js";
17
17
  import {
18
18
  UsageError,
19
19
  exitForUsage,
20
20
  rejectExtraPositionals,
21
21
  rejectUnknownFlags,
22
22
  resolveProjectDir
23
- } from "./cli-bzz33hjz.js";
23
+ } from "./cli-z835nehj.js";
24
24
  import"./cli-ht1bcqdd.js";
25
- import"./cli-th1ve2cx.js";
25
+ import"./cli-zrs92eef.js";
26
26
  import {
27
27
  EXIT_CODES
28
28
  } from "./cli-46ycxe6r.js";
@@ -19,6 +19,7 @@
19
19
  __export(exports_fragment, {
20
20
  wrapGeometry: () => wrapGeometry,
21
21
  waitForLayoutReady: () => waitForLayoutReady,
22
+ versoShiftPt: () => versoShiftPt,
22
23
  synthesizeColumnBreaks: () => synthesizeColumnBreaks,
23
24
  stripMetrics: () => stripMetrics,
24
25
  strideOf: () => strideOf,
@@ -108,13 +109,20 @@
108
109
  below is already sized to the run's full width and owns the actual
109
110
  clipping/visibility, so the strip does not need to. */
110
111
  .gp-strip {
112
+ /* Column pitch less the content width. \`--gp-verso-shift\` is set only on a
113
+ book with mirrored margins: decorate.ts slides each verso's sheet by it,
114
+ and growing the pitch by the same amount keeps the gap between a verso and
115
+ the recto it faces at the sheet gap (the gap across the page turn gets
116
+ twice the shift instead). Without it, it is 0px and nothing changes. */
117
+ --gp-col-gap: calc(
118
+ var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap) +
119
+ var(--gp-verso-shift, 0px)
120
+ );
111
121
  position: relative;
112
122
  width: var(--gp-content-w);
113
123
  height: var(--gp-content-h);
114
124
  column-width: var(--gp-content-w);
115
- column-gap: calc(
116
- var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap)
117
- );
125
+ column-gap: var(--gp-col-gap);
118
126
  column-fill: auto;
119
127
  overflow: visible;
120
128
  margin: 0;
@@ -176,9 +184,7 @@
176
184
  );
177
185
  width: calc(
178
186
  var(--gp-content-w) * var(--gp-wrap-cols, 2) +
179
- (
180
- var(--gp-margin-right) + var(--gp-margin-left) + var(--gp-sheet-gap)
181
- ) * (var(--gp-wrap-cols, 2) - 1)
187
+ var(--gp-col-gap) * (var(--gp-wrap-cols, 2) - 1)
182
188
  );
183
189
  }
184
190
 
@@ -270,6 +276,35 @@
270
276
  .gp-marginbox[data-align="end"] > .gp-marginbox-content { justify-content: flex-end; }
271
277
 
272
278
  /* margin & bleed overlay (overlay.ts) -------------------------------- */
279
+ /* THE LOOK LIVES HERE, in these tokens, and nowhere else: colours, line
280
+ width, line style, tint strength. The desktop app's settings change the
281
+ colour (an inline \`--gp-ov-color\` on <body>) and pick a strength preset
282
+ (\`data-overlay-strength\`); nothing in a book's CSS is involved or needed. */
283
+ .gp-stage {
284
+ --gp-ov-color: #2f80ed; /* margin box line + the tint over the margins */
285
+ --gp-ov-width: 2px; /* line width as seen on screen, at any zoom */
286
+ --gp-ov-style: dashed; /* margin box line style */
287
+ --gp-ov-tint: 0.1; /* opacity of the tint over the margin area */
288
+ --gp-ov-bleed-color: #e5484d; /* bleed band + trim line */
289
+ --gp-ov-bleed-alpha: 0.45; /* opacity of the bleed band */
290
+ --gp-ov-trim-color: #8a0b18; /* the trim line: darker than the band so it reads as an edge */
291
+ --gp-ov-flag-width: 2px; /* overflow flag outline */
292
+ --gp-ov-warn: #f59e0b; /* reaches into the margin */
293
+ --gp-ov-error: #e5484d; /* crosses the trim / leaves the page */
294
+ --gp-ov-intent: #8b5cf6; /* placed on purpose */
295
+ }
296
+ .gp-stage[data-overlay-strength="subtle"] {
297
+ --gp-ov-width: 1px;
298
+ --gp-ov-tint: 0.05;
299
+ --gp-ov-bleed-alpha: 0.3;
300
+ }
301
+ .gp-stage[data-overlay-strength="strong"] {
302
+ --gp-ov-width: 3px;
303
+ --gp-ov-tint: 0.2;
304
+ --gp-ov-bleed-alpha: 0.65;
305
+ --gp-ov-flag-width: 3px;
306
+ }
307
+
273
308
  /* One sibling layer per run, ABOVE the strip so the lines stay visible over
274
309
  author backgrounds and art. Absolutely positioned and pointer-transparent:
275
310
  it takes no flow space and no hits, so it cannot move a page break. Exists
@@ -279,31 +314,30 @@
279
314
  inset: 0;
280
315
  z-index: 5;
281
316
  pointer-events: none;
282
- --gp-ov-margin: #2f80ed;
283
- --gp-ov-bleed: 229 72 77;
284
- --gp-ov-warn: #f59e0b;
285
- --gp-ov-error: #e5484d;
286
- --gp-ov-intent: #8b5cf6;
317
+ /* The stage is a paint transform, so a 2px line would shrink with the zoom
318
+ and vanish at fit-to-width. Lines are sized in layout px divided by the
319
+ zoom, which makes them the same width on screen at every zoom. */
320
+ --gp-ov-w: calc(var(--gp-ov-width) / var(--gp-zoom, 1));
321
+ --gp-ov-flag-w: calc(var(--gp-ov-flag-width) / var(--gp-zoom, 1));
287
322
  }
288
323
 
289
324
  .gp-ov-page {
290
325
  position: absolute;
291
326
  }
292
327
 
293
- /* margin box: thin dashed outline of the content area */
294
- .gp-ov-margin {
328
+ /* the margins as a region: the trim minus the content box (clip-path, set in
329
+ overlay.ts), so a margin reads as an area, not a hairline */
330
+ .gp-ov-tint {
295
331
  position: absolute;
296
- box-sizing: border-box;
297
- border: 1px dashed var(--gp-ov-margin);
332
+ inset: 0;
333
+ background: color-mix(in srgb, var(--gp-ov-color) calc(var(--gp-ov-tint) * 100%), transparent);
298
334
  }
299
335
 
300
- /* the PDF's own margins for this page, where they differ from where the
301
- preview lays the text (mirrored :left/:right, .gp-flush): fainter, dotted */
302
- .gp-ov-print {
336
+ /* margin box: the content area's outline */
337
+ .gp-ov-margin {
303
338
  position: absolute;
304
339
  box-sizing: border-box;
305
- border: 1px dotted var(--gp-ov-margin);
306
- opacity: 0.55;
340
+ border: var(--gp-ov-w) var(--gp-ov-style) var(--gp-ov-color);
307
341
  }
308
342
 
309
343
  /* bleed: a translucent band OUTSIDE the trim (its width is the border) */
@@ -311,14 +345,16 @@
311
345
  position: absolute;
312
346
  box-sizing: border-box;
313
347
  border-style: solid;
314
- border-color: rgb(var(--gp-ov-bleed) / 0.3);
348
+ border-color: color-mix(in srgb, var(--gp-ov-bleed-color) calc(var(--gp-ov-bleed-alpha) * 100%), transparent);
315
349
  }
316
350
 
317
- /* trim: where the sheet will be cut, drawn only when there is bleed */
351
+ /* trim: where the sheet will be cut, drawn only when there is bleed — solid,
352
+ just inside the edge so it never paints over the band (overlay.ts sizes and
353
+ places it: the whole sheet, or the sheet less an in-sheet bleed) */
318
354
  .gp-ov-trim {
319
355
  position: absolute;
320
- inset: 0;
321
- outline: 1px solid rgb(var(--gp-ov-bleed) / 0.9);
356
+ outline: var(--gp-ov-w) solid var(--gp-ov-trim-color);
357
+ outline-offset: calc(-1 * var(--gp-ov-w));
322
358
  }
323
359
 
324
360
  /* overflow flags — outline + faint tint, never a box change */
@@ -327,17 +363,17 @@
327
363
  box-sizing: border-box;
328
364
  }
329
365
  .gp-ov-flag[data-kind="margin"] {
330
- outline: 2px solid var(--gp-ov-warn);
331
- background: rgb(245 158 11 / 0.14);
366
+ outline: var(--gp-ov-flag-w) solid var(--gp-ov-warn);
367
+ background: color-mix(in srgb, var(--gp-ov-warn) 16%, transparent);
332
368
  }
333
369
  .gp-ov-flag[data-kind="bleed"],
334
370
  .gp-ov-flag[data-kind="off-page"] {
335
- outline: 2px solid var(--gp-ov-error);
336
- background: rgb(229 72 77 / 0.16);
371
+ outline: var(--gp-ov-flag-w) solid var(--gp-ov-error);
372
+ background: color-mix(in srgb, var(--gp-ov-error) 18%, transparent);
337
373
  }
338
374
  /* placed on purpose (.gp-bleed, .gp-pin, …): present, not a problem */
339
375
  .gp-ov-flag[data-kind="intended"] {
340
- outline: 1px dotted var(--gp-ov-intent);
376
+ outline: var(--gp-ov-flag-w) dotted var(--gp-ov-intent);
341
377
  }
342
378
 
343
379
  /* designer mode ------------------------------------------------------- */
@@ -1436,6 +1472,13 @@
1436
1472
  }
1437
1473
  }
1438
1474
  function runPageBox(model, name, warnings = []) {
1475
+ const { right, agree, r, l } = pageSides(model, name);
1476
+ if (agree)
1477
+ return right;
1478
+ 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.`);
1479
+ return resolvePage(model, { name }).geometry;
1480
+ }
1481
+ function pageSides(model, name) {
1439
1482
  const right = resolvePage(model, { name, pseudos: ["right"] }).geometry;
1440
1483
  const left = resolvePage(model, { name, pseudos: ["left"] }).geometry;
1441
1484
  const box = (g) => ({
@@ -1444,10 +1487,23 @@
1444
1487
  });
1445
1488
  const r = box(right);
1446
1489
  const l = box(left);
1447
- if (Math.abs(r.w - l.w) < 0.01 && Math.abs(r.h - l.h) < 0.01)
1448
- return right;
1449
- 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.`);
1450
- return resolvePage(model, { name }).geometry;
1490
+ return {
1491
+ right,
1492
+ left,
1493
+ r,
1494
+ l,
1495
+ agree: Math.abs(r.w - l.w) < 0.01 && Math.abs(r.h - l.h) < 0.01
1496
+ };
1497
+ }
1498
+ function versoShiftPt(model, name, flushEdges = []) {
1499
+ const { right, left, agree } = pageSides(model, name);
1500
+ if (!agree)
1501
+ return 0;
1502
+ const r = flushMargins(right.margin, flushEdges);
1503
+ const l = flushMargins(left.margin, flushEdges);
1504
+ if (Math.abs(r.left + r.right - (l.left + l.right)) >= 0.01)
1505
+ return 0;
1506
+ return r.left - l.left;
1451
1507
  }
1452
1508
  function buildStrips(model, opts = {}, warnings = []) {
1453
1509
  const doc = document;
@@ -1473,6 +1529,10 @@
1473
1529
  strip.style.setProperty("--gp-margin-right", `${pt(margin.right)}px`);
1474
1530
  strip.style.setProperty("--gp-margin-bottom", `${pt(margin.bottom)}px`);
1475
1531
  strip.style.setProperty("--gp-margin-left", `${pt(margin.left)}px`);
1532
+ const rawShift = versoShiftPt(model, run.page, run.flushEdges);
1533
+ const versoShift = Math.abs(rawShift) >= 0.01 ? rawShift : 0;
1534
+ if (versoShift)
1535
+ strip.style.setProperty("--gp-verso-shift", `${pt(versoShift)}px`);
1476
1536
  run.nodes[0].before(strip);
1477
1537
  for (const n of run.nodes)
1478
1538
  strip.appendChild(n);
@@ -1481,6 +1541,7 @@
1481
1541
  page: run.page,
1482
1542
  geometry,
1483
1543
  flushEdges: run.flushEdges,
1544
+ versoShift,
1484
1545
  pages: 0,
1485
1546
  offset: 0
1486
1547
  });
@@ -2170,6 +2231,7 @@
2170
2231
  const h = g.height * scale;
2171
2232
  const b = Math.max(0, bleed) * scale;
2172
2233
  return {
2234
+ host: { x: 0, y: 0, w, h },
2173
2235
  trim: { x: 0, y: 0, w, h },
2174
2236
  content: {
2175
2237
  x: margin.left * scale,
@@ -2181,6 +2243,17 @@
2181
2243
  bleed: b
2182
2244
  };
2183
2245
  }
2246
+ function insetPageBoxes(g, margin, bleed, side, scale = 1) {
2247
+ const sheet = pageBoxes(g, margin, 0, scale);
2248
+ const b = Math.max(0, bleed) * scale;
2249
+ return {
2250
+ host: sheet.host,
2251
+ trim: { x: side === "verso" ? b : 0, y: b, w: sheet.host.w - b, h: sheet.host.h - 2 * b },
2252
+ content: sheet.content,
2253
+ bleedBox: sheet.host,
2254
+ bleed: b
2255
+ };
2256
+ }
2184
2257
  var SEVERITY = { inside: 0, margin: 1, bleed: 2, "off-page": 3 };
2185
2258
  var within = (r, outer, tol) => r.x >= outer.x - tol && r.y >= outer.y - tol && r.x + r.w <= outer.x + outer.w + tol && r.y + r.h <= outer.y + outer.h + tol;
2186
2259
  function classifyRect(r, boxes, tol = 0) {
@@ -2196,9 +2269,26 @@
2196
2269
  return kinds.reduce((worst, k) => SEVERITY[k] > SEVERITY[worst] ? k : worst, "inside");
2197
2270
  }
2198
2271
  var INTENT_SELECTOR = ".gp-bleed, .gp-pin, .gp-flush, .gp-behind, .gp-page-full-bleed";
2272
+ var OVERLAY_STRENGTHS = ["subtle", "normal", "strong"];
2273
+ var HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
2274
+ function sanitizeOverlayStyle(style) {
2275
+ const s = style && typeof style === "object" ? style : {};
2276
+ const out = {};
2277
+ if (typeof s.color === "string" && HEX_COLOR.test(s.color.trim()))
2278
+ out.color = s.color.trim();
2279
+ if (OVERLAY_STRENGTHS.includes(s.strength))
2280
+ out.strength = s.strength;
2281
+ return out;
2282
+ }
2199
2283
  var OVERLAY_OFF = { on: false, issues: 0, intended: 0, bleedPt: 0 };
2200
- function marginsDiffer(a, b) {
2201
- return ["top", "right", "bottom", "left"].some((k) => Math.abs(a[k] - b[k]) > 0.5);
2284
+ function pageFrameMargin(margin, versoShift, side) {
2285
+ if (side === "recto" || !versoShift)
2286
+ return margin;
2287
+ return { ...margin, left: margin.left - versoShift, right: margin.right + versoShift };
2288
+ }
2289
+ function configInsetBleedPt() {
2290
+ const v = parseFloat(document.querySelector('meta[name="gp-inset-bleed"]')?.getAttribute("content") ?? "");
2291
+ return Number.isFinite(v) && v > 0 ? v : 0;
2202
2292
  }
2203
2293
  var SKIP = new Set(["SCRIPT", "STYLE", "LINK", "TEMPLATE", "BR", "WBR", "SOURCE", "TRACK", "META"]);
2204
2294
  var TOL_PX = 1;
@@ -2215,11 +2305,25 @@
2215
2305
  el.style.width = `${r.w}px`;
2216
2306
  el.style.height = `${r.h}px`;
2217
2307
  }
2308
+ function marginTint(b) {
2309
+ const el = div("gp-ov-tint");
2310
+ const t = b.trim;
2311
+ const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
2312
+ const l = clamp(b.content.x, t.x, t.x + t.w);
2313
+ const r = clamp(b.content.x + b.content.w, t.x, t.x + t.w);
2314
+ const top = clamp(b.content.y, t.y, t.y + t.h);
2315
+ const bot = clamp(b.content.y + b.content.h, t.y, t.y + t.h);
2316
+ const x2 = t.x + t.w;
2317
+ const y2 = t.y + t.h;
2318
+ el.style.clipPath = `polygon(evenodd, ${t.x}px ${t.y}px, ${x2}px ${t.y}px, ${x2}px ${y2}px, ${t.x}px ${y2}px, ${t.x}px ${t.y}px, ` + `${l}px ${top}px, ${l}px ${bot}px, ${r}px ${bot}px, ${r}px ${top}px, ${l}px ${top}px)`;
2319
+ return el;
2320
+ }
2218
2321
  function clearOverlay() {
2219
2322
  for (const layer of Array.from(document.querySelectorAll(".gp-ov")))
2220
2323
  layer.remove();
2221
2324
  }
2222
- function paintOverlay(pages, strips, bleedPt) {
2325
+ function paintOverlay(pages, strips, bleedPt, insetBleedPt = 0) {
2326
+ const inSheet = bleedPt <= 0 && insetBleedPt > 0;
2223
2327
  clearOverlay();
2224
2328
  const layers = new Map;
2225
2329
  const painted = new Map;
@@ -2233,22 +2337,21 @@
2233
2337
  run.appendChild(layer);
2234
2338
  layers.set(run, layer);
2235
2339
  }
2236
- const boxes = pageBoxes(p.geometry, p.margin, bleedPt, PX_PER_PT);
2237
- const host = div("gp-ov-page", boxes.trim);
2340
+ const boxes = inSheet ? insetPageBoxes(p.geometry, p.margin, insetBleedPt, p.sheet.dataset.side === "verso" ? "verso" : "recto", PX_PER_PT) : pageBoxes(p.geometry, p.margin, bleedPt, PX_PER_PT);
2341
+ const host = div("gp-ov-page", boxes.host);
2238
2342
  host.style.left = p.sheet.style.left;
2239
2343
  host.style.top = p.sheet.style.top;
2240
2344
  host.dataset.page = String(p.index + 1);
2241
2345
  if (boxes.bleed > 0) {
2242
- const band = div("gp-ov-bleed");
2243
- band.style.inset = `${-boxes.bleed}px`;
2244
- band.style.borderWidth = `${boxes.bleed}px`;
2346
+ const bb = boxes.bleedBox;
2347
+ const t = boxes.trim;
2348
+ const band = div("gp-ov-bleed", bb);
2349
+ band.style.borderWidth = `${t.y - bb.y}px ${bb.x + bb.w - (t.x + t.w)}px ${bb.y + bb.h - (t.y + t.h)}px ${t.x - bb.x}px`;
2245
2350
  host.appendChild(band);
2246
- host.appendChild(div("gp-ov-trim"));
2351
+ host.appendChild(div("gp-ov-trim", t));
2247
2352
  }
2353
+ host.appendChild(marginTint(boxes));
2248
2354
  host.appendChild(div("gp-ov-margin", boxes.content));
2249
- if (marginsDiffer(p.printMargin, p.margin)) {
2250
- host.appendChild(div("gp-ov-print", pageBoxes(p.geometry, p.printMargin, 0, PX_PER_PT).content));
2251
- }
2252
2355
  layer.appendChild(host);
2253
2356
  painted.set(p.index, { host, boxes, sheet: p.sheet });
2254
2357
  }
@@ -2306,7 +2409,7 @@
2306
2409
  el.dataset.kind = f.kind;
2307
2410
  f.host.appendChild(el);
2308
2411
  }
2309
- return { on: true, issues, intended, bleedPt };
2412
+ return inSheet ? { on: true, issues, intended, bleedPt: insetBleedPt, bleedInSheet: true } : { on: true, issues, intended, bleedPt };
2310
2413
  }
2311
2414
 
2312
2415
  // src/engine/viewer/decorate.ts
@@ -2341,7 +2444,7 @@
2341
2444
  let overlayPages = [];
2342
2445
  let overlaySummary = OVERLAY_OFF;
2343
2446
  const paintOverlayNow = () => {
2344
- overlaySummary = paintOverlay(overlayPages, layout.strips, documentBleedPt(model));
2447
+ overlaySummary = paintOverlay(overlayPages, layout.strips, documentBleedPt(model), configInsetBleedPt());
2345
2448
  window.dispatchEvent(new CustomEvent("gp:overlay", { detail: overlaySummary }));
2346
2449
  };
2347
2450
  const api = {
@@ -2354,12 +2457,21 @@
2354
2457
  setDesigner(on) {
2355
2458
  document.body.dataset.designer = on ? "on" : "off";
2356
2459
  },
2357
- setOverlay(on) {
2460
+ setOverlay(on, style) {
2461
+ if (style) {
2462
+ const { color, strength } = sanitizeOverlayStyle(style);
2463
+ if (strength)
2464
+ document.body.dataset.overlayStrength = strength;
2465
+ if (color)
2466
+ document.body.style.setProperty("--gp-ov-color", color);
2467
+ }
2468
+ const wasOn = overlayOn;
2358
2469
  overlayOn = on;
2359
2470
  document.body.dataset.overlay = on ? "on" : "off";
2360
- if (on)
2361
- paintOverlayNow();
2362
- else {
2471
+ if (on) {
2472
+ if (!(wasOn && style))
2473
+ paintOverlayNow();
2474
+ } else {
2363
2475
  clearOverlay();
2364
2476
  overlaySummary = OVERLAY_OFF;
2365
2477
  window.dispatchEvent(new CustomEvent("gp:overlay", { detail: overlaySummary }));
@@ -2528,6 +2640,8 @@
2528
2640
  buildMaps();
2529
2641
  fillXrefs();
2530
2642
  let prevPageH = 0;
2643
+ let prevVersoShift = 0;
2644
+ let prevRunX = 0;
2531
2645
  let first = true;
2532
2646
  for (const strip of layout.strips) {
2533
2647
  const run = ensureRun(strip);
@@ -2544,12 +2658,13 @@
2544
2658
  const slot = i + shift;
2545
2659
  const row = Math.floor(slot / perRow);
2546
2660
  const colVisual = slot % perRow;
2547
- const sheetLeft = colVisual * stride;
2661
+ const side = bookIndex % 2 === 0 ? "recto" : "verso";
2662
+ const sheetLeft = colVisual * stride + (side === "verso" ? strip.versoShift * PX_PER_PT : 0);
2548
2663
  const sheetTop = row * rowStride;
2549
2664
  const sheet = document.createElement("div");
2550
2665
  sheet.className = "gp-sheet";
2551
2666
  sheet.dataset.page = String(bookIndex + 1);
2552
- sheet.dataset.side = bookIndex % 2 === 0 ? "recto" : "verso";
2667
+ sheet.dataset.side = side;
2553
2668
  sheet.style.left = `${sheetLeft}px`;
2554
2669
  sheet.style.top = `${sheetTop}px`;
2555
2670
  sheet.style.setProperty("--gp-page-w", px(ctx.geometry.width));
@@ -2568,15 +2683,19 @@
2568
2683
  sheet,
2569
2684
  strip,
2570
2685
  geometry: ctx.geometry,
2571
- printMargin: ctx.geometry.margin,
2572
- margin: ctx.padded || blankPages.has(bookIndex) ? ctx.geometry.margin : strip.flushEdges?.length ? flushMargins(g.margin, strip.flushEdges) : g.margin
2686
+ margin: ctx.padded || blankPages.has(bookIndex) ? ctx.geometry.margin : pageFrameMargin(flushMargins(g.margin, strip.flushEdges ?? []), strip.versoShift, side)
2573
2687
  });
2574
2688
  }
2575
2689
  const rows = Math.max(1, Math.ceil((strip.pages + shift) / perRow));
2576
2690
  run.style.height = `${rowStride * (rows - 1) + PX_PER_PT * g.height}px`;
2577
2691
  run.style.width = `${stride * perRow}px`;
2578
- run.style.marginTop = strip.wrapCols && shift === 1 && !first ? `${-(prevPageH + sheetGap)}px` : "";
2692
+ const composed = !!strip.wrapCols && shift === 1 && !first;
2693
+ run.style.marginTop = composed ? `${-(prevPageH + sheetGap)}px` : "";
2694
+ const runX = composed ? prevRunX + (prevVersoShift - strip.versoShift) * PX_PER_PT : 0;
2695
+ run.style.marginLeft = runX ? `${runX}px` : "";
2579
2696
  prevPageH = PX_PER_PT * g.height;
2697
+ prevVersoShift = strip.versoShift;
2698
+ prevRunX = runX;
2580
2699
  first = false;
2581
2700
  if (opts.designer)
2582
2701
  checkOverflow(strip, warnings);
@@ -148,22 +148,22 @@ 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-gh0jjve9.json";
151
+ var manifest_schema_default = "./manifest.schema-8m82f37q.json";
152
152
 
153
153
  // src/assets/preview/scripts/preview-interface.js
154
- var preview_interface_default = "./preview-interface-dm6wbdq6.js";
154
+ var preview_interface_default = "./preview-interface-2n1d9pbf.js";
155
155
 
156
156
  // src/assets/preview/scripts/preview-bridge.js
157
- var preview_bridge_default = "./preview-bridge-ga409pgz.js";
157
+ var preview_bridge_default = "./preview-bridge-evecsh4w.js";
158
158
 
159
159
  // src/assets/preview/scripts/preview-shell.js
160
- var preview_shell_default = "./preview-shell-c909krk2.js";
160
+ var preview_shell_default = "./preview-shell-hyp7g158.js";
161
161
 
162
162
  // profiles/CGATS21_CRPC1.icc
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-p50c4f08.js";
166
+ var gutterpress_viewer_default = "./gutterpress-viewer-k4mpqjh0.js";
167
167
 
168
168
  // src/assets/engine/gutterpress-agent.js
169
169
  var gutterpress_agent_default = "./gutterpress-agent-cazqstr1.js";
@@ -19,7 +19,7 @@ import {
19
19
  stringValueAt,
20
20
  toFolioPage,
21
21
  wantsRecto
22
- } from "./index-jg1r9t4w.js";
22
+ } from "./index-b81jc99n.js";
23
23
  import {
24
24
  launchChromium
25
25
  } from "./index-rzm1phg0.js";
@@ -9,7 +9,7 @@ import {
9
9
  getAssetsDir,
10
10
  lastPageMode,
11
11
  parseSize
12
- } from "./index-jg1r9t4w.js";
12
+ } from "./index-b81jc99n.js";
13
13
  import {
14
14
  INSTALL_HINTS,
15
15
  execCapture,
@@ -155,7 +155,8 @@ var DTRPG_PRESET = {
155
155
  page: {
156
156
  width: 621,
157
157
  height: 810,
158
- tolerance: 0.5
158
+ tolerance: 0.5,
159
+ bleed: 9
159
160
  },
160
161
  ink: {
161
162
  maxTac: 240,
@@ -231,7 +232,8 @@ var BOOK_PRESET = {
231
232
  page: {
232
233
  width: 432,
233
234
  height: 648,
234
- tolerance: 0.5
235
+ tolerance: 0.5,
236
+ bleed: undefined
235
237
  },
236
238
  ink: {
237
239
  maxTac: 400,
@@ -584,7 +586,8 @@ function resolveCustomPage(c, m) {
584
586
  return {
585
587
  width,
586
588
  height,
587
- tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5
589
+ tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
590
+ bleed: c.page?.bleed ?? m.page?.bleed
588
591
  };
589
592
  }
590
593
  function resolveWithPreset(cliOverrides, manifest, preset) {
@@ -5747,7 +5750,7 @@ import git from "isomorphic-git";
5747
5750
  // package.json
5748
5751
  var package_default = {
5749
5752
  name: "gutterpress",
5750
- version: "0.11.18-alpha.1",
5753
+ version: "0.11.18-alpha.3",
5751
5754
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5752
5755
  author: "itlackey",
5753
5756
  license: "MPL-2.0",
@@ -10303,7 +10306,7 @@ class PdfOutput {
10303
10306
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
10304
10307
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
10305
10308
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
10306
- const { buildNativePdf } = await import("./engine-s2caze6b.js");
10309
+ const { buildNativePdf } = await import("./engine-68avsbe2.js");
10307
10310
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
10308
10311
  title: config.title,
10309
10312
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -10525,8 +10528,10 @@ async function renderPreviewBook(inputPath, config, opts) {
10525
10528
  }
10526
10529
  });
10527
10530
  }
10528
- function injectPreviewScripts(html) {
10529
- const scripts = ` <script src="/engine/gutterpress-viewer.js"></script>
10531
+ function injectPreviewScripts(html, insetBleedPt = 0) {
10532
+ const meta = insetBleedPt > 0 ? ` <meta name="gp-inset-bleed" content="${insetBleedPt}">
10533
+ ` : "";
10534
+ const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
10530
10535
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
10531
10536
  ` + `<script src="/preview/scripts/preview-bridge.js"></script>
10532
10537
  `;
@@ -10560,7 +10565,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
10560
10565
  for (const ref of nextImageRefs)
10561
10566
  imageRefs.add(ref);
10562
10567
  }
10563
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html), "utf-8");
10568
+ await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page?.bleed), "utf-8");
10564
10569
  }
10565
10570
  function isDotPathUnderRoot(candidatePath, root) {
10566
10571
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
package/dist/index.js CHANGED
@@ -153,14 +153,14 @@ import {
153
153
  testRemoteAccess,
154
154
  updateExtensions,
155
155
  validateProjectExtensions
156
- } from "./index-pdanxwn1.js";
156
+ } from "./index-rpc74pqb.js";
157
157
  import"./index-ht1bcqdd.js";
158
158
  import {
159
159
  BUILD_DIAGNOSTIC_CODES
160
- } from "./index-3pnmf25p.js";
160
+ } from "./index-q88g67zg.js";
161
161
  import {
162
162
  BuildError
163
- } from "./index-jg1r9t4w.js";
163
+ } from "./index-b81jc99n.js";
164
164
  import {
165
165
  DEFAULT_PRINT_OPTS,
166
166
  assertMilestone,
@@ -21,6 +21,17 @@ export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" |
21
21
  /** The built-in preset ids, in the order pickers should offer them. */
22
22
  export declare const PRESET_IDS: readonly ["dtrpg", "book", "custom"];
23
23
  export type PresetId = (typeof PRESET_IDS)[number];
24
+ /**
25
+ * DriveThruRPG print-on-demand interior. Where each value comes from:
26
+ *
27
+ * SPEC = DriveThru Partners, "Quick Specifications for Print Books"
28
+ * (help.drivethrupartners.com/hc/en-us/articles/12780800178583)
29
+ * GUIDE = third-party DriveThruRPG layout guides (consistent with SPEC's
30
+ * bleed rule; not stated by DriveThru itself)
31
+ * CONVENTION = Gutterpress's own choice or a value DERIVED from a spec
32
+ * requirement (e.g. PDF/X-1a forbids transparency) — NOT a DriveThru
33
+ * requirement in its own right.
34
+ */
24
35
  export declare const DTRPG_PRESET: VendorPreset;
25
36
  /**
26
37
  * A neutral, vendor-agnostic default for authors who aren't targeting a
@@ -200,6 +200,12 @@
200
200
  "description": "Allowed deviation, in points, between the expected and the actual page size.",
201
201
  "default": 0.5,
202
202
  "minimum": 0
203
+ },
204
+ "bleed": {
205
+ "type": "number",
206
+ "description": "A bleed, in points, that the page width x height ALREADY CONTAINS on the top, bottom and OUTER edges (none on the binding edge) — how DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in book). Preview overlay only: it tells the margin & bleed overlay where the trim is and changes nothing in the PDF. CSS `@page { bleed }` wins when the stylesheet declares one. Preset default: dtrpg 9 (0.125in); book and custom none.",
207
+ "minimum": 0,
208
+ "examples": [9]
203
209
  }
204
210
  }
205
211
  },
@@ -7,17 +7,17 @@ import {
7
7
  TARGET_IDS,
8
8
  scaffoldExtension,
9
9
  scaffoldProject
10
- } from "./cli-1msxnhzy.js";
10
+ } from "./cli-72r4c2k4.js";
11
11
  import {
12
12
  resolveGhostscript
13
- } from "./cli-gv97c2a6.js";
13
+ } from "./cli-12aq78aq.js";
14
14
  import {
15
15
  UsageError,
16
16
  rejectExtraPositionals,
17
17
  rejectUnknownFlags
18
- } from "./cli-bzz33hjz.js";
18
+ } from "./cli-z835nehj.js";
19
19
  import"./cli-ht1bcqdd.js";
20
- import"./cli-th1ve2cx.js";
20
+ import"./cli-zrs92eef.js";
21
21
  import {
22
22
  EXIT_CODES
23
23
  } from "./cli-46ycxe6r.js";
@@ -2,17 +2,17 @@ import {
2
2
  executeValidation,
3
3
  publishTargetFor,
4
4
  reportMissingTools
5
- } from "./cli-1msxnhzy.js";
5
+ } from "./cli-72r4c2k4.js";
6
6
  import {
7
7
  log
8
- } from "./cli-gv97c2a6.js";
8
+ } from "./cli-12aq78aq.js";
9
9
  import {
10
10
  UsageError,
11
11
  rejectExtraPositionals,
12
12
  rejectUnknownFlags
13
- } from "./cli-bzz33hjz.js";
13
+ } from "./cli-z835nehj.js";
14
14
  import"./cli-ht1bcqdd.js";
15
- import"./cli-th1ve2cx.js";
15
+ import"./cli-zrs92eef.js";
16
16
  import {
17
17
  BuildError,
18
18
  EXIT_CODES
@@ -22,7 +22,7 @@ import type { PrintPadding } from '../engine/shared/synthesis.ts';
22
22
  * no per-source wrappers or forced breaks are added, so preview pagination
23
23
  * never diverges from the PDF where a source file begins mid-page.
24
24
  */
25
- export declare function injectPreviewScripts(html: string): string;
25
+ export declare function injectPreviewScripts(html: string, insetBleedPt?: number): string;
26
26
  /**
27
27
  * Generate HTML from markdown and write book.html to the temp directory.
28
28
  * The desktop's iframe loads `book.html` via a relative URL — same name in
@@ -45,6 +45,9 @@ export declare function generateAndWriteHtml(inputPath: string, tempDir: string,
45
45
  };
46
46
  extensions?: ResolvedExtensionConfig[];
47
47
  print?: PrintPadding;
48
+ page?: {
49
+ bleed?: number;
50
+ };
48
51
  }, cssAssets: Map<string, string>, imageRefs?: Set<string>): Promise<void>;
49
52
  /**
50
53
  * Chokidar 5's `ignored` matcher (regex or function) is tested against the
@@ -7,10 +7,10 @@ import {
7
7
  runBuild,
8
8
  splitOutPath,
9
9
  startPreviewServer
10
- } from "./cli-1msxnhzy.js";
10
+ } from "./cli-72r4c2k4.js";
11
11
  import {
12
12
  log
13
- } from "./cli-gv97c2a6.js";
13
+ } from "./cli-12aq78aq.js";
14
14
  import {
15
15
  UsageError,
16
16
  parseFormat,
@@ -18,9 +18,9 @@ import {
18
18
  rejectExtraPositionals,
19
19
  rejectUnknownFlags,
20
20
  resolvePort
21
- } from "./cli-bzz33hjz.js";
21
+ } from "./cli-z835nehj.js";
22
22
  import"./cli-ht1bcqdd.js";
23
- import"./cli-th1ve2cx.js";
23
+ import"./cli-zrs92eef.js";
24
24
  import {
25
25
  BuildError
26
26
  } from "./cli-46ycxe6r.js";
@@ -14,7 +14,7 @@
14
14
  //
15
15
  // Commands map 1:1 to previewAPI methods: getTotalPages, getCurrentPage,
16
16
  // goToPage, firstPage, prevPage, nextPage, lastPage, setViewMode, setZoom,
17
- // toggleDebugMode, setMarginOverlay. Plus a synthetic 'print' command that calls window.print().
17
+ // toggleDebugMode, setMarginOverlay(on, {color, strength}). Plus a synthetic 'print' command that calls window.print().
18
18
  //
19
19
  // Additional messages:
20
20
  // parent -> iframe: { type: 'gutterpress:inject-styles', id: <attr-name>, css: <string> }
@@ -1101,15 +1101,20 @@
1101
1101
  // the overlayChanged event (margin & bleed overlay, engine/viewer/
1102
1102
  // overlay.ts). A v9 lib with a pre-v9 SPA simply never turns it on; a v9
1103
1103
  // SPA feature-detects on the version before offering the toggle.
1104
+ // (v9 is unreleased as of this writing, so setMarginOverlay's optional
1105
+ // second argument — the look, below — is part of v9, not a bump.)
1104
1106
  getProtocolVersion: function () { return 9; },
1105
1107
 
1106
1108
  // Margin & bleed overlay. Viewer chrome only — it adds no box to the book
1107
1109
  // and no attribute to its elements, so pagination is identical on or off.
1108
1110
  // Returns the summary {on, issues, intended, bleedPt}, or null when the
1109
- // viewer predates it.
1110
- setMarginOverlay: function (on) {
1111
+ // viewer predates it. `style` ({color: '#rrggbb', strength: 'subtle' |
1112
+ // 'normal' | 'strong'}) is optional: it only sets the viewer's own
1113
+ // `--gp-ov-*` tokens, and a call that carries it while the overlay is
1114
+ // already on restyles without repainting.
1115
+ setMarginOverlay: function (on, style) {
1111
1116
  var d = window.Gutterpress && window.Gutterpress.decoration;
1112
- return d && typeof d.setOverlay === 'function' ? d.setOverlay(!!on) : null;
1117
+ return d && typeof d.setOverlay === 'function' ? d.setOverlay(!!on, style) : null;
1113
1118
  },
1114
1119
  toggleMarginOverlay: function () {
1115
1120
  var d = window.Gutterpress && window.Gutterpress.decoration;
@@ -1650,7 +1655,8 @@
1650
1655
  var parts = ['Margins & bleed'];
1651
1656
  parts.push(summary.issues ? summary.issues + ' overflowing' : 'nothing overflows');
1652
1657
  if (summary.intended) parts.push(summary.intended + ' placed on purpose');
1653
- if (!summary.bleedPt) parts.push('no @page bleed set');
1658
+ if (summary.bleedInSheet) parts.push('bleed inside the page (print preset)');
1659
+ if (!summary.bleedPt) parts.push('no bleed set \u2014 add @page { bleed: 0.125in } to see the bleed edge');
1654
1660
  badge.textContent = parts.join(' \u00b7 ') + ' (Alt+M hides)';
1655
1661
  }
1656
1662
 
@@ -108,7 +108,13 @@
108
108
  if (sourceBody.classList.contains('debug')) api.toggleDebugMode();
109
109
  // The margin & bleed overlay is a review aid the author switched on: a
110
110
  // hot reload must not drop it.
111
- if (sourceBody.dataset.overlay === 'on' && api.setMarginOverlay) api.setMarginOverlay(true);
111
+ // Its look (colour, strength) travels with it.
112
+ if (sourceBody.dataset.overlay === 'on' && api.setMarginOverlay) {
113
+ api.setMarginOverlay(true, {
114
+ color: sourceBody.style.getPropertyValue('--gp-ov-color').trim(),
115
+ strength: sourceBody.dataset.overlayStrength
116
+ });
117
+ }
112
118
  }
113
119
  var zoom = source.documentElement.style.getPropertyValue('--gutterpress-zoom');
114
120
  // Carry the fit flag too (setZoom's third argument clips sideways overflow).
@@ -11,18 +11,18 @@ import {
11
11
  publishProviderFor,
12
12
  resolvePublishFormat,
13
13
  runPublish
14
- } from "./cli-1msxnhzy.js";
14
+ } from "./cli-72r4c2k4.js";
15
15
  import {
16
16
  FileTokenStore,
17
17
  log
18
- } from "./cli-gv97c2a6.js";
18
+ } from "./cli-12aq78aq.js";
19
19
  import {
20
20
  UsageError,
21
21
  rejectExtraPositionals,
22
22
  rejectUnknownFlags
23
- } from "./cli-bzz33hjz.js";
23
+ } from "./cli-z835nehj.js";
24
24
  import"./cli-ht1bcqdd.js";
25
- import"./cli-th1ve2cx.js";
25
+ import"./cli-zrs92eef.js";
26
26
  import {
27
27
  EXIT_CODES
28
28
  } from "./cli-46ycxe6r.js";
@@ -146,6 +146,15 @@ export interface GutterpressManifest {
146
146
  width?: number;
147
147
  height?: number;
148
148
  tolerance?: number;
149
+ /**
150
+ * A bleed (pt) the page `width` x `height` ALREADY CONTAINS, on the top,
151
+ * bottom and OUTER edges only (the binding edge has none) — how
152
+ * DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in
153
+ * book). It changes nothing in the PDF: it only tells the preview's margin
154
+ * & bleed overlay where the trim is. CSS `@page { bleed }` (symmetric,
155
+ * grows the page) wins when the stylesheet declares one.
156
+ */
157
+ bleed?: number;
149
158
  };
150
159
  ink?: {
151
160
  maxTac?: number;
@@ -261,6 +270,8 @@ export interface ResolvedConfig {
261
270
  width: number;
262
271
  height: number;
263
272
  tolerance: number;
273
+ /** In-sheet bleed in pt (top, bottom, outer edge; see the manifest field); absent = none. */
274
+ bleed?: number;
264
275
  };
265
276
  ink: {
266
277
  maxTac: number;
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  executeAndReport
3
- } from "./cli-1msxnhzy.js";
3
+ } from "./cli-72r4c2k4.js";
4
4
  import {
5
5
  log
6
- } from "./cli-gv97c2a6.js";
6
+ } from "./cli-12aq78aq.js";
7
7
  import {
8
8
  UsageError,
9
9
  rejectExtraPositionals,
10
10
  rejectUnknownFlags
11
- } from "./cli-bzz33hjz.js";
11
+ } from "./cli-z835nehj.js";
12
12
  import"./cli-ht1bcqdd.js";
13
- import"./cli-th1ve2cx.js";
13
+ import"./cli-zrs92eef.js";
14
14
  import {
15
15
  BuildError,
16
16
  EXIT_CODES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gutterpress",
3
- "version": "0.11.18-alpha.1",
3
+ "version": "0.11.18-alpha.3",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",