gutterpress 0.11.23-alpha.3 → 0.11.23-alpha.4

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 (69) hide show
  1. package/README.md +4 -6
  2. package/dist/{README-8db7btxb.md → README-syg2hn83.md} +1 -1
  3. package/dist/api/index.js +5 -5
  4. package/dist/{build-m78jcpf7.js → build-c48eer6f.js} +8 -8
  5. package/dist/checks/asset/extensions.d.ts +1 -1
  6. package/dist/checks/asset/index.d.ts +0 -1
  7. package/dist/checks/policy.d.ts +5 -4
  8. package/dist/checks/source/config-file.d.ts +9 -6
  9. package/dist/checks/source/dangling-links.d.ts +1 -1
  10. package/dist/checks/types.d.ts +2 -2
  11. package/dist/{cli-xsrffqzt.js → cli-54xt7pjg.js} +8 -8
  12. package/dist/{cli-2ppw6q9v.js → cli-5crpmz42.js} +80 -1
  13. package/dist/{cli-1h17ktsg.js → cli-93mkaw6v.js} +96 -209
  14. package/dist/{cli-n2tsxqpn.js → cli-d9mm3vvh.js} +7 -2
  15. package/dist/{cli-5jzyw06s.js → cli-gnpea9dx.js} +1 -1
  16. package/dist/{cli-s6djx4ma.js → cli-pspjxnc8.js} +1 -1
  17. package/dist/{cli-t8be9yeb.js → cli-rqh295j9.js} +12 -7
  18. package/dist/{cli-h4x81x97.js → cli-y6d1arem.js} +207 -142
  19. package/dist/cli.js +12 -12
  20. package/dist/{doctor-j71jqce3.js → doctor-ptb346k8.js} +4 -3
  21. package/dist/{engine-m4y10shc.js → engine-4rzdx6af.js} +1 -1
  22. package/dist/{engine-1r6q80tq.js → engine-6txhxmmv.js} +2 -2
  23. package/dist/{ext-fq2hg9va.js → ext-gy8szg3x.js} +8 -8
  24. package/dist/{extension-restore-9dh7c4tp.js → extension-restore-dymzcasz.js} +2 -2
  25. package/dist/{extension-restore-tmxtzkm0.js → extension-restore-m8tj43e7.js} +4 -4
  26. package/dist/{index-2ppw6q9v.js → index-5crpmz42.js} +80 -1
  27. package/dist/{index-nedd07bk.js → index-5kh4c78h.js} +8 -8
  28. package/dist/{index-0wee4408.js → index-8ywkd2sv.js} +12 -7
  29. package/dist/{index-prqvz3xm.js → index-ea94zbq4.js} +132 -228
  30. package/dist/{index-7pck1q9c.js → index-kaks3mm2.js} +206 -141
  31. package/dist/{index-nvmf22rt.js → index-yc3xj266.js} +1 -1
  32. package/dist/index.js +6 -6
  33. package/dist/lib/build-preflight.d.ts +4 -10
  34. package/dist/lib/build-runner.d.ts +1 -1
  35. package/dist/lib/ghostscript.d.ts +5 -2
  36. package/dist/lib/lint-rules.d.ts +5 -1
  37. package/dist/lib/manifest-config.d.ts +3 -2
  38. package/dist/lib/manifest-old-spellings.d.ts +64 -0
  39. package/dist/lib/markdown/chapter-id.d.ts +1 -1
  40. package/dist/lib/markdown/index.d.ts +1 -1
  41. package/dist/lib/markdown/table.d.ts +5 -3
  42. package/dist/lib/page-size.d.ts +14 -9
  43. package/dist/lib/pdf-inspect.d.ts +27 -0
  44. package/dist/lib/presets.d.ts +10 -8
  45. package/dist/lib/printsafe.d.ts +1 -1
  46. package/dist/lib/project-scaffold.d.ts +1 -1
  47. package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
  48. package/dist/lib/targets.d.ts +4 -4
  49. package/dist/lib/validation-exec.d.ts +4 -4
  50. package/dist/{manifest-my7mdv8j.yaml → manifest-2kc56wvw.yaml} +3 -4
  51. package/dist/{manifest-8afhrk30.yaml → manifest-41ddzm13.yaml} +3 -4
  52. package/dist/{manifest-gf13sjp8.yaml → manifest-79zsgkpn.yaml} +3 -4
  53. package/dist/{manifest-d4f9q79e.yaml → manifest-sqzm4681.yaml} +3 -4
  54. package/dist/{manifest.schema-650pe0d1.json → manifest.schema-w86c1xw1.json} +19 -309
  55. package/dist/{new-s4zcaepg.js → new-t2t28ccb.js} +9 -15
  56. package/dist/{page-rules-fj09xct5.css → page-rules-ytj9p04v.css} +1 -1
  57. package/dist/{pdf-inspect-wnw4za71.js → pdf-inspect-b3mf81ex.js} +3 -1
  58. package/dist/{plugins-80emnf31.js → plugins-fec8t2bh.js} +1 -1
  59. package/dist/{plugins-4vaf27n9.js → plugins-sqa4msdk.js} +3 -3
  60. package/dist/{preflight-c7w1xdn3.js → preflight-srq8fktp.js} +8 -8
  61. package/dist/preview/file-watcher.d.ts +1 -3
  62. package/dist/{preview-mzytcwyk.js → preview-zsp9hstd.js} +8 -8
  63. package/dist/{publish-fez14ad4.js → publish-hhy31gr9.js} +8 -8
  64. package/dist/render.js +6 -1
  65. package/dist/schema/manifest.types.d.ts +40 -107
  66. package/dist/{tokens-ye64p899.css → tokens-n82t4k9h.css} +11 -4
  67. package/dist/{validate-p2chr6hr.js → validate-r9jdz85h.js} +8 -8
  68. package/package.json +1 -1
  69. package/dist/checks/asset/approved-fonts.d.ts +0 -3
@@ -1,3 +1,6 @@
1
+ import {
2
+ pagesWithVisibleShadingPaints
3
+ } from "./cli-5crpmz42.js";
1
4
  import {
2
5
  enhancedPath,
3
6
  execCapture,
@@ -11,7 +14,7 @@ import {
11
14
  import {
12
15
  PACKAGE_VERSION,
13
16
  log
14
- } from "./cli-s6djx4ma.js";
17
+ } from "./cli-pspjxnc8.js";
15
18
  import {
16
19
  __require
17
20
  } from "./cli-37x76zdn.js";
@@ -189,7 +192,9 @@ async function findPostScriptShadingPages(pdfBytes) {
189
192
  if (usesPostScriptShading(asDict(page.node.Resources())))
190
193
  pages.push(i + 1);
191
194
  });
192
- return pages;
195
+ if (pages.length === 0)
196
+ return pages;
197
+ return pagesWithVisibleShadingPaints(pdfBytes, pages).catch(() => pages);
193
198
  }
194
199
  async function stripAnnotations(pdfPath, stagingDir) {
195
200
  const tmp = join(stagingDir, "annotations-stripped.pdf");
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "gutterpress",
4
- version: "0.11.23-alpha.3",
4
+ version: "0.11.23-alpha.4",
5
5
  description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
6
6
  author: "itlackey",
7
7
  license: "MPL-2.0",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "./cli-5jzyw06s.js";
3
+ } from "./cli-gnpea9dx.js";
4
4
 
5
5
  // src/utils/logger.ts
6
6
  var currentLevel = "INFO";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PACKAGE_VERSION,
3
3
  debug
4
- } from "./cli-s6djx4ma.js";
4
+ } from "./cli-pspjxnc8.js";
5
5
  import {
6
6
  isExactNpmVersion,
7
7
  isValidNpmPackageName,
@@ -24,7 +24,7 @@ import { pathToFileURL } from "node:url";
24
24
  var theme_default = `/* =============================================================================
25
25
  Clean Book — gutterpress built-in theme (theme.css)
26
26
  A calm, classic long-form book look. Change the :root tokens from the book's own stylesheet.
27
- The page SIZE comes from the manifest (Setup > Details), so it is deliberately not set here.
27
+ The page SIZE comes from the manifest (Setup > Book), so it is deliberately not set here.
28
28
  ============================================================================= */
29
29
 
30
30
  :root {
@@ -83,7 +83,7 @@ hr { border: 0; border-top: 1px solid var(--color-rule); margin: 1.5em 0; }
83
83
  var theme_default2 = `/* =============================================================================
84
84
  Zine — gutterpress built-in theme (theme.css)
85
85
  High-contrast, punchy, photocopier-friendly. Change the :root tokens from the book's own stylesheet.
86
- The page SIZE comes from the manifest (Setup > Details), so it is deliberately not set here.
86
+ The page SIZE comes from the manifest (Setup > Book), so it is deliberately not set here.
87
87
  ============================================================================= */
88
88
 
89
89
  :root {
@@ -144,7 +144,7 @@ code, pre { font-family: var(--font-mono); font-size: 0.95em; }
144
144
  var theme_default3 = `/* =============================================================================
145
145
  Technical Document — gutterpress built-in theme (theme.css)
146
146
  Clean sans-serif manual look with clear hierarchy. Change the :root tokens from the book's own stylesheet.
147
- The page SIZE comes from the manifest (Setup > Details), so it is deliberately not set here.
147
+ The page SIZE comes from the manifest (Setup > Book), so it is deliberately not set here.
148
148
  ============================================================================= */
149
149
 
150
150
  :root {
@@ -2335,7 +2335,12 @@ function splitCells(str2, spans) {
2335
2335
  lastPos = pos;
2336
2336
  }
2337
2337
  } else if (!isEscaped && spans[ch]) {
2338
- pos = spans[ch](str2, pos, max);
2338
+ const end = spans[ch](str2, pos, max);
2339
+ if (ch === 96) {
2340
+ current += str2.substring(lastPos, pos) + str2.substring(pos, end).replace(/\\\|/g, "|");
2341
+ lastPos = end;
2342
+ }
2343
+ pos = end;
2339
2344
  continue;
2340
2345
  }
2341
2346
  isEscaped = ch === 92;
@@ -3570,9 +3575,9 @@ async function loadNpmPackage(packageName, baseDir, version) {
3570
3575
  const looksLikeJsFilename = /\.(m?js|cjs)$/i.test(packageName);
3571
3576
  const suggestedPath = looksLikeJsFilename ? `./${packageName}` : `./plugins/${packageName}.js`;
3572
3577
  if (!looksLikeJsFilename && (!version || !isExactNpmVersion(version))) {
3573
- throw new Error(`Extension "${packageName}${version ? `@${version}` : ""}" is not pinned to an exact version, so it can't be downloaded automatically. Run \`gutterpress ext add ${packageName}\` (or use Book setup > Looks & features > Advanced in the desktop app) to download it and pin its version in your manifest.`);
3578
+ throw new Error(`Extension "${packageName}${version ? `@${version}` : ""}" is not pinned to an exact version, so it can't be downloaded automatically. Run \`gutterpress ext add ${packageName}\` (or use Book setup > Advanced in the desktop app) to download it and pin its version in your manifest.`);
3574
3579
  }
3575
- throw new Error(`Extension "${packageName}" not found. Install it with \`gutterpress ext add ${version ? `${packageName}@${version}` : packageName}\` (or Book setup > Looks & features > Advanced in the desktop app),
3580
+ throw new Error(`Extension "${packageName}" not found. Install it with \`gutterpress ext add ${version ? `${packageName}@${version}` : packageName}\` (or Book setup > Advanced in the desktop app),
3576
3581
  or reference a local file under \`extensions:\`:
3577
3582
  extensions:
3578
3583
  - ${suggestedPath}`);
@@ -11,10 +11,10 @@ import {
11
11
  prettify,
12
12
  readExtensionMeta,
13
13
  slugify
14
- } from "./cli-t8be9yeb.js";
14
+ } from "./cli-rqh295j9.js";
15
15
  import {
16
16
  log
17
- } from "./cli-s6djx4ma.js";
17
+ } from "./cli-pspjxnc8.js";
18
18
  import {
19
19
  UsageError
20
20
  } from "./cli-d8xjns66.js";
@@ -185,9 +185,7 @@ var DTRPG_PRESET = {
185
185
  },
186
186
  defaultTargets: ["dtrpg"],
187
187
  extensions: [],
188
- source: {
189
- files: null
190
- },
188
+ chapters: null,
191
189
  print: { signature: 4, reserveLastPage: true, lastPage: undefined },
192
190
  pdfx: {
193
191
  flavor: "x1a",
@@ -212,53 +210,23 @@ var DTRPG_PRESET = {
212
210
  maxTac: 240,
213
211
  tacTolerance: 0.5
214
212
  },
215
- lint: {
216
- enabled: true,
217
- configPath: null,
218
- rules: {}
213
+ checks: {
214
+ "pdf.structure.qpdf": "error",
215
+ "pdf.print.pdfx-markers": "error",
216
+ "pdf.print.pdfx-metadata": "error",
217
+ "pdf.print.embedded-fonts": "error",
218
+ "pdf.print.safe-margins": "error",
219
+ "pdf.print.bleed": "off",
220
+ "pdf.nav.bookmarks": "off",
221
+ "pdf.nav.toc-links": "off",
222
+ "asset.font.license": "off"
219
223
  },
220
224
  validate: {
221
- enabled: true,
222
- checks: {
223
- "pdf.structure.qpdf": {
224
- enabled: true,
225
- severity: "error"
226
- },
227
- "pdf.print.pdfx-markers": {
228
- enabled: true,
229
- severity: "error"
230
- },
231
- "pdf.print.pdfx-metadata": {
232
- enabled: true,
233
- severity: "error"
234
- },
235
- "pdf.print.embedded-fonts": {
236
- enabled: true,
237
- severity: "error"
238
- },
239
- "pdf.print.safe-margins": {
240
- enabled: true,
241
- severity: "error"
242
- }
243
- },
244
- source: {
245
- markdownlint: null,
246
- htmlhint: null,
247
- stylelint: null,
248
- cssOwnership: null
249
- },
250
225
  assets: {
251
- maxImageSize: 1e7,
252
- allowAlpha: false,
253
- approvedFontFiles: [],
254
- requireFontLicense: false
226
+ maxImageSize: 1e7
255
227
  },
256
228
  pdf: {
257
- requireBookmarks: false,
258
- requireTocLinks: false,
259
229
  minImageResolution: 300,
260
- forbidTransparency: true,
261
- requireBleed: false,
262
230
  bleedSize: 9
263
231
  },
264
232
  heuristics: {
@@ -272,9 +240,7 @@ var BOOK_PRESET = {
272
240
  id: "book",
273
241
  defaultTargets: [],
274
242
  extensions: [],
275
- source: {
276
- files: null
277
- },
243
+ chapters: null,
278
244
  print: { signature: 1, reserveLastPage: false, lastPage: undefined },
279
245
  pdfx: {
280
246
  flavor: "x1a",
@@ -299,47 +265,25 @@ var BOOK_PRESET = {
299
265
  maxTac: 400,
300
266
  tacTolerance: 0.5
301
267
  },
302
- lint: {
303
- enabled: true,
304
- configPath: null,
305
- rules: {}
268
+ checks: {
269
+ "pdf.structure.qpdf": "error",
270
+ "pdf.print.embedded-fonts": "error",
271
+ "pdf.print.safe-margins": "warning",
272
+ "pdf.print.pdfx-markers": "off",
273
+ "pdf.print.pdfx-metadata": "off",
274
+ "pdf.print.transparency": "off",
275
+ "asset.image.alpha-channel": "off",
276
+ "pdf.print.bleed": "off",
277
+ "pdf.nav.bookmarks": "off",
278
+ "pdf.nav.toc-links": "off",
279
+ "asset.font.license": "off"
306
280
  },
307
281
  validate: {
308
- enabled: true,
309
- checks: {
310
- "pdf.structure.qpdf": {
311
- enabled: true,
312
- severity: "error"
313
- },
314
- "pdf.print.embedded-fonts": {
315
- enabled: true,
316
- severity: "error"
317
- },
318
- "pdf.print.safe-margins": {
319
- enabled: true,
320
- severity: "warning"
321
- },
322
- "pdf.print.pdfx-markers": false,
323
- "pdf.print.pdfx-metadata": false
324
- },
325
- source: {
326
- markdownlint: null,
327
- htmlhint: null,
328
- stylelint: null,
329
- cssOwnership: null
330
- },
331
282
  assets: {
332
- maxImageSize: 1e7,
333
- allowAlpha: true,
334
- approvedFontFiles: [],
335
- requireFontLicense: false
283
+ maxImageSize: 1e7
336
284
  },
337
285
  pdf: {
338
- requireBookmarks: false,
339
- requireTocLinks: false,
340
286
  minImageResolution: 300,
341
- forbidTransparency: false,
342
- requireBleed: false,
343
287
  bleedSize: 9
344
288
  },
345
289
  heuristics: {
@@ -392,16 +336,16 @@ var DTRPG_TARGET = {
392
336
  maxTac: DTRPG_PRESET.ink.maxTac,
393
337
  tacTolerance: DTRPG_PRESET.ink.tacTolerance
394
338
  },
339
+ checks: {
340
+ "pdf.structure.qpdf": "error",
341
+ "pdf.print.pdfx-markers": "error",
342
+ "pdf.print.pdfx-metadata": "error",
343
+ "pdf.print.embedded-fonts": "error",
344
+ "pdf.print.safe-margins": "error",
345
+ "pdf.print.transparency": "on"
346
+ },
395
347
  validate: {
396
- checks: {
397
- "pdf.structure.qpdf": { enabled: true, severity: "error" },
398
- "pdf.print.pdfx-markers": { enabled: true, severity: "error" },
399
- "pdf.print.pdfx-metadata": { enabled: true, severity: "error" },
400
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
401
- "pdf.print.safe-margins": { enabled: true, severity: "error" }
402
- },
403
348
  pdf: {
404
- forbidTransparency: DTRPG_PRESET.validate.pdf.forbidTransparency,
405
349
  minImageResolution: DTRPG_PRESET.validate.pdf.minImageResolution
406
350
  }
407
351
  }
@@ -419,22 +363,22 @@ var ITCH_TARGET = {
419
363
  id: "itch",
420
364
  label: "itch.io",
421
365
  overlay: {
366
+ checks: {
367
+ "pdf.structure.qpdf": "error",
368
+ "pdf.print.embedded-fonts": "error",
369
+ "pdf.print.pdfx-markers": "off",
370
+ "pdf.print.pdfx-metadata": "off",
371
+ "pdf.print.ink-coverage": "off",
372
+ "pdf.print.safe-margins": "off",
373
+ "pdf.print.transparency": "off",
374
+ "pdf.print.bleed": "off",
375
+ "asset.image.alpha-channel": "off"
376
+ },
422
377
  validate: {
423
- checks: {
424
- "pdf.structure.qpdf": { enabled: true, severity: "error" },
425
- "pdf.print.embedded-fonts": { enabled: true, severity: "error" },
426
- "pdf.print.pdfx-markers": false,
427
- "pdf.print.pdfx-metadata": false,
428
- "pdf.print.ink-coverage": false,
429
- "pdf.print.safe-margins": false
430
- },
431
378
  assets: {
432
- allowAlpha: true,
433
379
  maxImageSize: 0
434
380
  },
435
381
  pdf: {
436
- forbidTransparency: false,
437
- requireBleed: false,
438
382
  minImageResolution: 0
439
383
  }
440
384
  }
@@ -473,9 +417,9 @@ function overlayPreset(preset, target) {
473
417
  ...preset,
474
418
  pdfx: { ...preset.pdfx, ...o.pdfx },
475
419
  ink: { ...preset.ink, ...o.ink },
420
+ checks: { ...preset.checks, ...o.checks },
476
421
  validate: {
477
422
  ...preset.validate,
478
- checks: { ...preset.validate.checks, ...o.validate?.checks },
479
423
  assets: { ...preset.validate.assets, ...o.validate?.assets },
480
424
  pdf: { ...preset.validate.pdf, ...o.validate?.pdf }
481
425
  }
@@ -490,13 +434,120 @@ var DEFAULT_STRICT_PDF_CHECKS = [
490
434
  function applyDefaultPdfStrictChecks(config) {
491
435
  const next = structuredClone(config);
492
436
  for (const checkId of DEFAULT_STRICT_PDF_CHECKS) {
493
- if (next.validate.checks[checkId] === undefined) {
494
- next.validate.checks[checkId] = { enabled: true, severity: "error" };
495
- }
437
+ if (next.checks[checkId] === undefined)
438
+ next.checks[checkId] = "error";
496
439
  }
497
440
  return next;
498
441
  }
499
442
 
443
+ // src/lib/manifest-old-spellings.ts
444
+ var SOURCE_TOOL_CHECKS = {
445
+ markdownlint: "source.markdownlint",
446
+ htmlhint: "source.htmlhint",
447
+ stylelint: "source.stylelint",
448
+ cssOwnership: "source.css-ownership"
449
+ };
450
+ var FLAG_CHECKS = {
451
+ "assets.allowAlpha": { check: "asset.image.alpha-channel", trueMeansOn: false },
452
+ "assets.requireFontLicense": { check: "asset.font.license", trueMeansOn: true },
453
+ "pdf.requireBookmarks": { check: "pdf.nav.bookmarks", trueMeansOn: true },
454
+ "pdf.requireTocLinks": { check: "pdf.nav.toc-links", trueMeansOn: true },
455
+ "pdf.requireBleed": { check: "pdf.print.bleed", trueMeansOn: true },
456
+ "pdf.forbidTransparency": { check: "pdf.print.transparency", trueMeansOn: true }
457
+ };
458
+ function isObject(v) {
459
+ return typeof v === "object" && v !== null && !Array.isArray(v);
460
+ }
461
+ function readOldSpellings(raw) {
462
+ const { source, lint, validate, ink, ...manifest } = raw;
463
+ const tolerance = isObject(raw.page) && "tolerance" in raw.page;
464
+ if (source === undefined && lint === undefined && validate === undefined && ink === undefined && !tolerance) {
465
+ return raw;
466
+ }
467
+ if (isObject(source) && source.assets !== undefined) {
468
+ throw new UsageError("Manifest field `source.assets` is no longer supported — remove it.\n" + " Assets are discovered from what your book actually references, so no list is needed.");
469
+ }
470
+ const renamed = [];
471
+ const dropped = [];
472
+ const checks = {};
473
+ if (isObject(source) && "files" in source) {
474
+ renamed.push("source.files -> chapters");
475
+ if (manifest.chapters === undefined)
476
+ manifest.chapters = source.files ?? null;
477
+ }
478
+ if (isObject(validate)) {
479
+ for (const [id, entry] of Object.entries(validate.checks ?? {})) {
480
+ if (entry === false || isObject(entry) && entry.enabled === false)
481
+ checks[id] = "off";
482
+ else if (isObject(entry) && typeof entry.severity === "string")
483
+ checks[id] = entry.severity;
484
+ else
485
+ checks[id] = "on";
486
+ }
487
+ if (validate.checks !== undefined)
488
+ renamed.push("validate.checks -> checks");
489
+ for (const [tool, value] of Object.entries(validate.source ?? {})) {
490
+ const id = SOURCE_TOOL_CHECKS[tool];
491
+ if (id && value === false) {
492
+ checks[id] = "off";
493
+ renamed.push(`validate.source.${tool}: false -> checks: { ${id}: off }`);
494
+ } else if (id && tool !== "stylelint" && typeof value === "string") {
495
+ dropped.push(`validate.source.${tool} (the config file is found by its usual name in the book's folder or a folder above it)`);
496
+ } else {
497
+ dropped.push(`validate.source.${tool}`);
498
+ }
499
+ }
500
+ for (const block of ["assets", "pdf", "heuristics"]) {
501
+ for (const [key, value] of Object.entries(validate[block] ?? {})) {
502
+ const flag = FLAG_CHECKS[`${block}.${key}`];
503
+ if (flag && typeof value === "boolean") {
504
+ const level = value === flag.trueMeansOn ? "on" : "off";
505
+ checks[flag.check] = level;
506
+ renamed.push(`validate.${block}.${key}: ${value} -> checks: { ${flag.check}: ${level} }`);
507
+ } else {
508
+ dropped.push(`validate.${block}.${key} (the preset and the publish target set it)`);
509
+ }
510
+ }
511
+ }
512
+ if (validate.enabled !== undefined) {
513
+ dropped.push("validate.enabled (`gutterpress build --skip-pre-validate --skip-post-validate` skips validation for one build)");
514
+ }
515
+ }
516
+ if (isObject(lint)) {
517
+ for (const [id, level] of Object.entries(lint.rules ?? {}))
518
+ checks[id] = level;
519
+ if (lint.rules !== undefined)
520
+ renamed.push("lint.rules -> checks");
521
+ if (lint.enabled === false) {
522
+ checks["source.stylelint"] = "off";
523
+ renamed.push("lint.enabled: false -> checks: { source.stylelint: off }");
524
+ } else if (lint.enabled !== undefined) {
525
+ dropped.push("lint.enabled");
526
+ }
527
+ if (lint.configPath !== undefined)
528
+ dropped.push("lint.configPath");
529
+ }
530
+ if (ink !== undefined)
531
+ dropped.push("ink (the preset and the publish target set it)");
532
+ if (tolerance) {
533
+ const { tolerance: _, ...page } = raw.page;
534
+ manifest.page = page;
535
+ dropped.push("page.tolerance (the preset sets it)");
536
+ }
537
+ if (Object.keys(checks).length > 0)
538
+ manifest.checks = { ...checks, ...manifest.checks };
539
+ const lines = [
540
+ ...renamed.map((r) => ` ${r}`),
541
+ ...dropped.map((d) => ` ${d} — no longer a setting, ignored`)
542
+ ];
543
+ if (lines.length > 0) {
544
+ warnOnce(`old-manifest-spellings:${lines.join("|")}`, "[gutterpress] manifest.yaml uses spellings 0.11.23 replaced. They still work in this " + `release; rewrite them before the next one:
545
+ ` + lines.join(`
546
+ `));
547
+ }
548
+ return manifest;
549
+ }
550
+
500
551
  // src/lib/manifest.ts
501
552
  var MANIFEST_FILENAMES = [
502
553
  "manifest.yaml"
@@ -514,8 +565,8 @@ async function loadManifestWithPath(pathOrDir, opts) {
514
565
  throw new UsageError(`"${p}" has two versions of a setting — yours and the online version — left by a sync. Open it, keep the one you want and delete the marker lines.`);
515
566
  }
516
567
  try {
517
- const manifest = parseYaml(raw) ?? {};
518
- return { manifest, manifestDir: dirname(p), manifestPath: p };
568
+ const parsed = parseYaml(raw) ?? {};
569
+ return { manifest: readOldSpellings(parsed), manifestDir: dirname(p), manifestPath: p };
519
570
  } catch (error) {
520
571
  if (!(error instanceof YAMLParseError))
521
572
  throw error;
@@ -651,7 +702,7 @@ function resolveCustomPage(c, m) {
651
702
  return {
652
703
  width,
653
704
  height,
654
- tolerance: c.page?.tolerance ?? m.page?.tolerance ?? 0.5,
705
+ tolerance: 0.5,
655
706
  bleed: c.page?.bleed ?? m.page?.bleed,
656
707
  safe: c.page?.safe ?? m.page?.safe,
657
708
  artSafe: c.page?.artSafe ?? m.page?.artSafe,
@@ -680,22 +731,8 @@ function validatePageMargin(margin, size) {
680
731
  function resolveWithPreset(cliOverrides, manifest, preset) {
681
732
  const m = manifest;
682
733
  const c = cliOverrides;
683
- const legacy = m;
684
- if (legacy.output !== undefined || legacy.source?.assets !== undefined) {
685
- const stale = [
686
- legacy.output !== undefined ? "`output`" : null,
687
- legacy.source?.assets !== undefined ? "`source.assets`" : null
688
- ].filter(Boolean);
689
- throw new UsageError(`Manifest field(s) ${stale.join(" and ")} are no longer supported — remove them.
690
- ` + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).\n" + " Assets are discovered from what your book actually references, so no list is needed.");
691
- }
692
- if ((m.validate?.source?.allowedCallouts?.length ?? 0) > 0 || (c.validate?.source?.allowedCallouts?.length ?? 0) > 0) {
693
- warnOnce("allowed-callouts-deprecated", "[gutterpress] manifest field `validate.source.allowedCallouts` is " + "deprecated and ignored. The `:::` container syntax it gated was " + "removed 2026-05-17. See docs/migrations/2026-05-removing-container-syntax.md.");
694
- }
695
- for (const key of ["minImageDpi", "allowedColorSpaces"]) {
696
- if (m.validate?.assets?.[key] !== undefined || c.validate?.assets?.[key] !== undefined) {
697
- warnOnce(`assets-${key}-deprecated`, `[gutterpress] manifest field \`validate.assets.${key}\` is deprecated and ` + "ignored. The source-image check it configured was removed; the build " + "and the PDF checks measure what actually prints.");
698
- }
734
+ if (m.output !== undefined) {
735
+ throw new UsageError("Manifest field `output` is no longer supported — remove it.\n" + " Output goes to `dist/<title-slug>/` automatically (use `--out` for a one-off location).");
699
736
  }
700
737
  const removed = m;
701
738
  if (removed.engine !== undefined) {
@@ -741,25 +778,37 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
741
778
  styles: c.styles ?? m.styles,
742
779
  extensions,
743
780
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
744
- source: mergeShape(c.source, m.source, preset.source),
781
+ chapters: c.chapters ?? m.chapters ?? preset.chapters,
782
+ checks: { ...preset.checks, ...resolveCheckLevels(m.checks), ...resolveCheckLevels(c.checks) },
745
783
  pdfx: mergeShape(c.pdfx, m.pdfx, preset.pdfx),
746
784
  print: mergeShape(c.print, m.print, preset.print),
747
785
  page,
748
- ink: mergeShape(c.ink, m.ink, preset.ink),
749
- lint: {
750
- ...mergeShape(c.lint, m.lint, preset.lint),
751
- rules: { ...preset.lint.rules, ...m.lint?.rules, ...c.lint?.rules }
752
- },
753
- validate: {
754
- enabled: c.validate?.enabled ?? m.validate?.enabled ?? preset.validate.enabled,
755
- checks: { ...preset.validate.checks, ...m.validate?.checks, ...c.validate?.checks },
756
- source: mergeShape(c.validate?.source, m.validate?.source, preset.validate.source),
757
- assets: mergeShape(c.validate?.assets, m.validate?.assets, preset.validate.assets),
758
- pdf: mergeShape(c.validate?.pdf, m.validate?.pdf, preset.validate.pdf),
759
- heuristics: mergeShape(c.validate?.heuristics, m.validate?.heuristics, preset.validate.heuristics)
760
- }
786
+ ink: structuredClone(preset.ink),
787
+ validate: structuredClone(preset.validate)
761
788
  };
762
789
  }
790
+ function resolveCheckLevels(input) {
791
+ if (input === undefined || input === null)
792
+ return {};
793
+ if (!isPlainObject(input)) {
794
+ throw new UsageError("Manifest field `checks` must map check ids to off, on, info, warn or error.");
795
+ }
796
+ const out = {};
797
+ for (const [id, value] of Object.entries(input)) {
798
+ if (value === false || value === "off")
799
+ out[id] = "off";
800
+ else if (value === true || value === "on")
801
+ out[id] = "on";
802
+ else if (value === "warn" || value === "warning")
803
+ out[id] = "warning";
804
+ else if (value === "info" || value === "error")
805
+ out[id] = value;
806
+ else {
807
+ throw new UsageError(`Manifest field \`checks.${id}\` must be off, on, info, warn or error, got ${JSON.stringify(value)}.`);
808
+ }
809
+ }
810
+ return out;
811
+ }
763
812
 
764
813
  // src/lib/manifest-doc.ts
765
814
  function resolveManifestPath(projectDir) {
@@ -1478,6 +1527,7 @@ function ignoreStatus(gitignore, entry) {
1478
1527
  return dir.unignored || file.unignored ? "negated" : "open";
1479
1528
  }
1480
1529
  var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
1530
+ var LEGACY_NPM_LINE = /^\/?plugins\/npm\/?[ \t]*\r?\n?/gm;
1481
1531
  async function ensureProjectGitignore(projectDir) {
1482
1532
  const gitignorePath = path3.join(projectDir, ".gitignore");
1483
1533
  let existing = null;
@@ -1494,10 +1544,11 @@ async function ensureProjectGitignore(projectDir) {
1494
1544
  negated.push(entry);
1495
1545
  }
1496
1546
  if (missing.length > 0) {
1497
- const lead = existing !== null && existing.length > 0 && !existing.endsWith(`
1547
+ const kept = missing.includes("extensions/npm/") ? (existing ?? "").replace(LEGACY_NPM_LINE, "") : existing ?? "";
1548
+ const lead = kept.length > 0 && !kept.endsWith(`
1498
1549
  `) ? `
1499
1550
  ` : "";
1500
- await writeFile(gitignorePath, `${existing ?? ""}${lead}${missing.join(`
1551
+ await writeFile(gitignorePath, `${kept}${lead}${missing.join(`
1501
1552
  `)}
1502
1553
  `, "utf8");
1503
1554
  }
@@ -1867,6 +1918,7 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1867
1918
  await finalizeNpmPluginInstall(installed).catch((error) => {
1868
1919
  warnings.push(`The extension was installed, but an old backup could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`);
1869
1920
  });
1921
+ warnings.push(...await retireLegacyCopies(projectDir, installed.name));
1870
1922
  try {
1871
1923
  const { negated } = await ensureProjectGitignore(projectDir);
1872
1924
  if (negated.includes("extensions/npm/")) {
@@ -1877,6 +1929,19 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1877
1929
  }
1878
1930
  return { name: installed.name, version: installed.version, use, warnings };
1879
1931
  }
1932
+ async function retireLegacyCopies(projectDir, name) {
1933
+ const legacyPackageDir = path4.dirname(vendoredNpmPluginRoots(projectDir, name, "0.0.0")[1]);
1934
+ const legacyNpmDir = path4.dirname(legacyPackageDir);
1935
+ try {
1936
+ await rm3(legacyPackageDir, { recursive: true, force: true });
1937
+ await removeEmptyDirs([path4.dirname(legacyNpmDir), legacyNpmDir]);
1938
+ } catch (error) {
1939
+ return [
1940
+ `The extension was installed, but the copy an older version of Gutterpress kept under plugins/npm/ ` + `could not be removed: ${error instanceof Error ? error.message : String(error)}`
1941
+ ];
1942
+ }
1943
+ return [];
1944
+ }
1880
1945
  async function pinnedVersionsOf(projectDir, name) {
1881
1946
  const { doc } = await loadManifestDoc(projectDir);
1882
1947
  const seq = doc.get("extensions", true);
package/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "./cli-d8xjns66.js";
9
9
  import {
10
10
  package_default
11
- } from "./cli-5jzyw06s.js";
11
+ } from "./cli-gnpea9dx.js";
12
12
  import {
13
13
  EXIT_CODES
14
14
  } from "./cli-46ycxe6r.js";
@@ -20,14 +20,14 @@ import {
20
20
  import { defineCommand, parseArgs, runMain } from "citty";
21
21
  import { statSync } from "node:fs";
22
22
  var SUBCOMMANDS = {
23
- new: () => import("./new-s4zcaepg.js").then((m) => m.default),
24
- preview: () => import("./preview-mzytcwyk.js").then((m) => m.default),
25
- build: () => import("./build-m78jcpf7.js").then((m) => m.default),
26
- publish: () => import("./publish-fez14ad4.js").then((m) => m.default),
27
- validate: () => import("./validate-p2chr6hr.js").then((m) => m.default),
28
- preflight: () => import("./preflight-c7w1xdn3.js").then((m) => m.default),
29
- doctor: () => import("./doctor-j71jqce3.js").then((m) => m.default),
30
- ext: () => import("./ext-fq2hg9va.js").then((m) => m.default)
23
+ new: () => import("./new-t2t28ccb.js").then((m) => m.default),
24
+ preview: () => import("./preview-zsp9hstd.js").then((m) => m.default),
25
+ build: () => import("./build-c48eer6f.js").then((m) => m.default),
26
+ publish: () => import("./publish-hhy31gr9.js").then((m) => m.default),
27
+ validate: () => import("./validate-r9jdz85h.js").then((m) => m.default),
28
+ preflight: () => import("./preflight-srq8fktp.js").then((m) => m.default),
29
+ doctor: () => import("./doctor-ptb346k8.js").then((m) => m.default),
30
+ ext: () => import("./ext-gy8szg3x.js").then((m) => m.default)
31
31
  };
32
32
  var VERSION = package_default.version;
33
33
  var main = defineCommand({
@@ -52,7 +52,7 @@ async function preflightRequiredInvocations(rawArgs) {
52
52
  const [command, ...commandArgs] = rawArgs;
53
53
  try {
54
54
  if (command === "new") {
55
- const { newArgs } = await import("./new-s4zcaepg.js");
55
+ const { newArgs } = await import("./new-t2t28ccb.js");
56
56
  rejectUnknownFlags(commandArgs, newArgs, "new");
57
57
  const parsed = parseArgs(commandArgs, {
58
58
  ...newArgs,
@@ -64,7 +64,7 @@ async function preflightRequiredInvocations(rawArgs) {
64
64
  return;
65
65
  }
66
66
  if (command === "preflight") {
67
- const { preflightArgs } = await import("./preflight-c7w1xdn3.js");
67
+ const { preflightArgs } = await import("./preflight-srq8fktp.js");
68
68
  rejectUnknownFlags(commandArgs, preflightArgs, "preflight");
69
69
  const parsed = parseArgs(commandArgs, {
70
70
  ...preflightArgs,
@@ -84,7 +84,7 @@ async function preflightRequiredInvocations(rawArgs) {
84
84
  }
85
85
  if (subcommand.startsWith("-"))
86
86
  return;
87
- const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-fq2hg9va.js");
87
+ const { EXT_SUBCOMMANDS, extAddArgs, extRemoveArgs, extEnableArgs, extDisableArgs } = await import("./ext-gy8szg3x.js");
88
88
  if (!EXT_SUBCOMMANDS.includes(subcommand)) {
89
89
  throw new UsageError(`gutterpress ext: unknown command "${subcommand}"`);
90
90
  }