gutterpress 0.11.23-beta.1 → 0.11.23-beta.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 (65) hide show
  1. package/README.md +13 -3
  2. package/dist/api/index.d.ts +5 -5
  3. package/dist/api/index.js +19 -7
  4. package/dist/{build-dg8a566j.js → build-fq3mexhy.js} +9 -9
  5. package/dist/checks/asset/approved-fonts.d.ts +8 -0
  6. package/dist/checks/asset/extensions.d.ts +1 -1
  7. package/dist/checks/asset/index.d.ts +1 -0
  8. package/dist/{cli-e31jshz6.js → cli-5zbdb6mt.js} +296 -65
  9. package/dist/{cli-ttj95aex.js → cli-7mt0dftw.js} +47 -18
  10. package/dist/{cli-989eg8jz.js → cli-ah93yyzb.js} +163 -55
  11. package/dist/{cli-31anr0r9.js → cli-eymdsefg.js} +69 -29
  12. package/dist/{cli-1fy1pm9d.js → cli-fh685nqs.js} +1 -1
  13. package/dist/{cli-k087gj0p.js → cli-fsjre61x.js} +123 -12
  14. package/dist/{cli-s1zt779w.js → cli-q7t04gjt.js} +1 -1
  15. package/dist/{cli-vhav8g82.js → cli-rybz2dfs.js} +1 -1
  16. package/dist/cli.js +12 -12
  17. package/dist/{doctor-g4a42tcz.js → doctor-tg9ht1n3.js} +3 -3
  18. package/dist/engine/compiler/postprocess.d.ts +3 -0
  19. package/dist/engine/shared/pdf-inspect.d.ts +33 -0
  20. package/dist/{engine-jvkcahr1.js → engine-17v87vq5.js} +1 -1
  21. package/dist/{engine-wgkaqjsc.js → engine-y9fnarfa.js} +2 -2
  22. package/dist/{ext-w9d5ftcg.js → ext-esz03jz4.js} +27 -10
  23. package/dist/{extension-restore-5x4mj79d.js → extension-restore-91gy1e28.js} +3 -3
  24. package/dist/{extension-restore-as0g38p1.js → extension-restore-q70wqah5.js} +5 -5
  25. package/dist/{gutterpress-agent-d29ry3ak.js → gutterpress-agent-wyryqehx.js} +13 -4
  26. package/dist/{gutterpress-viewer-ze691rrd.js → gutterpress-viewer-ywkf49q5.js} +38 -153
  27. package/dist/{index-a4j3xcfp.js → index-67g8f7r4.js} +47 -18
  28. package/dist/{index-798jt0qc.js → index-d9w3r89w.js} +3 -3
  29. package/dist/{index-rj5vrhz6.js → index-fntpr44x.js} +321 -66
  30. package/dist/{index-5x42amh8.js → index-g3te5ey1.js} +2 -1
  31. package/dist/{index-cw03e9p4.js → index-kw4tbthz.js} +69 -29
  32. package/dist/{index-fk2kpf2t.js → index-santcs4q.js} +121 -10
  33. package/dist/{index-c6y0p7jy.js → index-z6vzj3s5.js} +243 -73
  34. package/dist/index.js +35 -8
  35. package/dist/lib/check-options.d.ts +70 -0
  36. package/dist/lib/extension-manager.d.ts +16 -1
  37. package/dist/lib/extension-scaffold.d.ts +15 -2
  38. package/dist/lib/extension-specifier.d.ts +8 -1
  39. package/dist/lib/manifest-config.d.ts +3 -1
  40. package/dist/lib/manifest-old-spellings.d.ts +32 -9
  41. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  42. package/dist/lib/markdown/math.d.ts +1 -1
  43. package/dist/lib/markdown/typography.d.ts +1 -0
  44. package/dist/lib/page-size.d.ts +12 -4
  45. package/dist/lib/plugin-vendor.d.ts +6 -0
  46. package/dist/lib/presets.d.ts +2 -2
  47. package/dist/lib/root-tokens.d.ts +2 -1
  48. package/dist/lib/source-provider.d.ts +27 -0
  49. package/dist/lib/style-resolver.d.ts +2 -1
  50. package/dist/lib/version-changes.d.ts +22 -6
  51. package/dist/{manifest.schema-0jgj0jt1.json → manifest.schema-5cm13vr3.json} +215 -5
  52. package/dist/{new-jtk028rf.js → new-rmfyb7va.js} +22 -15
  53. package/dist/{plugins-bcbe7xs8.js → plugins-gs21xz5s.js} +2 -2
  54. package/dist/{plugins-zsm6wv79.js → plugins-tqv1s16f.js} +4 -4
  55. package/dist/{preflight-cs6mj1d6.js → preflight-bjyeeq0d.js} +9 -9
  56. package/dist/{preview-d1j1g4qf.js → preview-ke03fk5d.js} +9 -9
  57. package/dist/{publish-7yw4jyrt.js → publish-wpnz0qng.js} +9 -9
  58. package/dist/render.js +55 -13
  59. package/dist/schema/manifest.types.d.ts +45 -7
  60. package/dist/{source-provider-n7vehh6t.js → source-provider-3g3yrn1f.js} +6 -2
  61. package/dist/{source-provider-tgzet3j5.js → source-provider-azg9ywhb.js} +6 -2
  62. package/dist/test-helpers/vocabulary.d.ts +12 -0
  63. package/dist/{validate-rkcdnzhw.js → validate-zcwykb43.js} +9 -9
  64. package/package.json +1 -1
  65. /package/dist/{cli-y28j019d.js → cli-1dha1nxk.js} +0 -0
@@ -1,4 +1,5 @@
1
1
  import {
2
+ BUNDLED_EXTENSION_DETAILS,
2
3
  EXTENSION_MANIFEST_FILENAME,
3
4
  TWO_VERSIONS_OPEN,
4
5
  bundledExtension,
@@ -11,10 +12,10 @@ import {
11
12
  prettify,
12
13
  readExtensionMeta,
13
14
  slugify
14
- } from "./cli-ttj95aex.js";
15
+ } from "./cli-7mt0dftw.js";
15
16
  import {
16
17
  log
17
- } from "./cli-1fy1pm9d.js";
18
+ } from "./cli-fh685nqs.js";
18
19
  import {
19
20
  UsageError
20
21
  } from "./cli-d8xjns66.js";
@@ -37,7 +38,7 @@ import {
37
38
  vendoredNpmPluginRoot,
38
39
  vendoredNpmPluginRoots,
39
40
  windowsPathKey
40
- } from "./cli-y28j019d.js";
41
+ } from "./cli-1dha1nxk.js";
41
42
  import {
42
43
  __require
43
44
  } from "./cli-37x76zdn.js";
@@ -79,10 +80,10 @@ async function withFetchTimeout(options, run) {
79
80
  }
80
81
 
81
82
  // src/lib/extension-manager.ts
82
- import { readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
83
+ import { mkdir as mkdir3, readdir, readFile as readFile5, rm as rm3, stat, writeFile as writeFile2 } from "node:fs/promises";
83
84
  import { existsSync as existsSync3 } from "node:fs";
84
85
  import path4 from "node:path";
85
- import { isSeq as isSeq2, isMap, isScalar, YAMLMap, Scalar } from "yaml";
86
+ import { isSeq as isSeq2, isMap as isMap3, isScalar, YAMLMap, Scalar } from "yaml";
86
87
 
87
88
  // src/lib/manifest-doc.ts
88
89
  import { existsSync as existsSync2 } from "node:fs";
@@ -145,7 +146,8 @@ var DTRPG_PRESET = {
145
146
  },
146
147
  validate: {
147
148
  assets: {
148
- maxImageSize: 1e7
149
+ maxImageSize: 1e7,
150
+ approvedFontFiles: []
149
151
  },
150
152
  pdf: {
151
153
  minImageResolution: 300,
@@ -202,7 +204,8 @@ var BOOK_PRESET = {
202
204
  },
203
205
  validate: {
204
206
  assets: {
205
- maxImageSize: 1e7
207
+ maxImageSize: 1e7,
208
+ approvedFontFiles: []
206
209
  },
207
210
  pdf: {
208
211
  minImageResolution: 300,
@@ -362,6 +365,124 @@ function applyDefaultPdfStrictChecks(config) {
362
365
  return next;
363
366
  }
364
367
 
368
+ // src/lib/manifest-old-spellings.ts
369
+ import { isMap as isMap2 } from "yaml";
370
+
371
+ // src/lib/check-options.ts
372
+ import { isMap } from "yaml";
373
+ var CHECK_OPTIONS = {
374
+ "pdf.print.ink-coverage": {
375
+ maxTac: { at: "ink.maxTac", kind: "number" },
376
+ tacTolerance: { at: "ink.tacTolerance", kind: "number" }
377
+ },
378
+ "pdf.print.page-size": {
379
+ tolerance: { at: "page.tolerance", kind: "number" }
380
+ },
381
+ "pdf.print.image-resolution": {
382
+ minImageResolution: { at: "validate.pdf.minImageResolution", kind: "number" }
383
+ },
384
+ "pdf.print.bleed": {
385
+ bleedSize: { at: "validate.pdf.bleedSize", kind: "number" }
386
+ },
387
+ "asset.image.file-size": {
388
+ maxImageSize: { at: "validate.assets.maxImageSize", kind: "number" }
389
+ },
390
+ "asset.font.approved-files": {
391
+ approvedFontFiles: { at: "validate.assets.approvedFontFiles", kind: "patterns" }
392
+ },
393
+ "heuristic.decoration.layer-count": {
394
+ maxDecorativeLayers: { at: "validate.heuristics.maxDecorativeLayers", kind: "number" }
395
+ },
396
+ "heuristic.whitespace.text-density": {
397
+ textDensityRange: { at: "validate.heuristics.textDensityRange", kind: "range" }
398
+ },
399
+ "heuristic.chunking.section-density": {
400
+ maxParagraphsPerSection: { at: "validate.heuristics.maxParagraphsPerSection", kind: "number" }
401
+ }
402
+ };
403
+ function optionForOldSpelling(at) {
404
+ for (const [id, options] of Object.entries(CHECK_OPTIONS)) {
405
+ for (const [option, spec] of Object.entries(options)) {
406
+ if (spec.at === at)
407
+ return { id, option };
408
+ }
409
+ }
410
+ return;
411
+ }
412
+ function isPlainObject(v) {
413
+ return typeof v === "object" && v !== null && !Array.isArray(v);
414
+ }
415
+ var isAmount = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
416
+ var isRangeEnd = ([k, v]) => (k === "min" || k === "max") && isAmount(v);
417
+ var MUST_BE = {
418
+ number: "a number, 0 or more",
419
+ range: "`{ min, max }` (numbers, 0 or more)",
420
+ patterns: "a list of file patterns"
421
+ };
422
+ function fits(kind, value) {
423
+ if (kind === "number")
424
+ return isAmount(value);
425
+ if (kind === "range")
426
+ return isPlainObject(value) && Object.entries(value).every(isRangeEnd);
427
+ return Array.isArray(value) && value.every((p) => typeof p === "string");
428
+ }
429
+ function checked(id, option, kind, value) {
430
+ if (fits(kind, value))
431
+ return value;
432
+ throw new UsageError(`Manifest field \`checks.${id}.${option}\` must be ${MUST_BE[kind]}, got ${JSON.stringify(value)}.`);
433
+ }
434
+ function isCheckLevelInput(value) {
435
+ return typeof value === "boolean" || ["off", "on", "info", "warn", "warning", "error"].includes(value);
436
+ }
437
+ function readOldThreshold(at, value) {
438
+ const moved = optionForOldSpelling(at);
439
+ if (!moved)
440
+ return;
441
+ const { kind } = CHECK_OPTIONS[moved.id][moved.option];
442
+ if (fits(kind, value))
443
+ return { ...moved, value };
444
+ const kept = kind === "range" && isPlainObject(value) ? Object.entries(value).filter(isRangeEnd) : [];
445
+ return { ...moved, ...kept.length > 0 ? { value: Object.fromEntries(kept) } : {}, mustBe: MUST_BE[kind] };
446
+ }
447
+ function readCheckOptions(id, entry) {
448
+ const known = CHECK_OPTIONS[id] ?? {};
449
+ const out = {};
450
+ for (const [option, value] of Object.entries(entry)) {
451
+ if (option === "level" || value === null || value === undefined)
452
+ continue;
453
+ const spec = known[option];
454
+ if (!spec) {
455
+ const takes = Object.keys(known);
456
+ throw new UsageError(`Manifest field \`checks.${id}\` has no option \`${option}\`. ` + (takes.length > 0 ? `It takes \`level\` and ${takes.map((o) => `\`${o}\``).join(", ")}.` : "It takes only `level` (off, on, info, warn or error)."));
457
+ }
458
+ out[option] = checked(id, option, spec.kind, value);
459
+ }
460
+ return out;
461
+ }
462
+ function applyCheckOptions(config, options) {
463
+ for (const [id, values] of Object.entries(options)) {
464
+ for (const [option, value] of Object.entries(values)) {
465
+ const spec = CHECK_OPTIONS[id][option];
466
+ const keys = spec.at.split(".");
467
+ const leaf = keys.pop();
468
+ let node = config;
469
+ for (const key of keys)
470
+ node = node[key];
471
+ node[leaf] = spec.kind === "range" ? { ...node[leaf], ...value } : value;
472
+ }
473
+ }
474
+ }
475
+ function setCheckOption(doc, id, option, value) {
476
+ if (!isMap(doc.get("checks", true)))
477
+ doc.set("checks", doc.createNode({}));
478
+ const current = doc.getIn(["checks", id], true);
479
+ if (!isMap(current)) {
480
+ const level = doc.getIn(["checks", id]);
481
+ doc.setIn(["checks", id], doc.createNode(level === undefined || level === null ? {} : { level }));
482
+ }
483
+ doc.setIn(["checks", id, option], value);
484
+ }
485
+
365
486
  // src/lib/manifest-old-spellings.ts
366
487
  var SOURCE_TOOL_CHECKS = {
367
488
  markdownlint: "source.markdownlint",
@@ -380,6 +501,7 @@ var FLAG_CHECKS = {
380
501
  function isObject(v) {
381
502
  return typeof v === "object" && v !== null && !Array.isArray(v);
382
503
  }
504
+ var optionSpelling = (id, option, value) => `checks: { ${id}: { ${option}: ${JSON.stringify(value)} } }`;
383
505
  function readOldSpellings(raw) {
384
506
  const { source, lint, validate, ink, ...manifest } = raw;
385
507
  const tolerance = isObject(raw.page) && "tolerance" in raw.page;
@@ -391,7 +513,32 @@ function readOldSpellings(raw) {
391
513
  }
392
514
  const renamed = [];
393
515
  const dropped = [];
394
- const checks = {};
516
+ const unusable = [];
517
+ const levels = {};
518
+ const options = {};
519
+ const threshold = (at, value) => {
520
+ const read = readOldThreshold(at, value);
521
+ if (!read) {
522
+ dropped.push(at);
523
+ return;
524
+ }
525
+ if (value === null || value === undefined)
526
+ return;
527
+ if (read.value !== undefined) {
528
+ (options[read.id] ??= {})[read.option] = read.value;
529
+ renamed.push(`${at} -> ${optionSpelling(read.id, read.option, read.value)}`);
530
+ }
531
+ if (read.mustBe) {
532
+ const kept = read.value !== undefined ? `kept ${Object.keys(read.value).join(" and ")}, ignored the rest` : "ignored";
533
+ unusable.push(`${at}: ${JSON.stringify(value)} is not ${read.mustBe} — ${kept}`);
534
+ }
535
+ };
536
+ const level = (at, value) => {
537
+ if (isCheckLevelInput(value))
538
+ return value;
539
+ unusable.push(`${at}: ${JSON.stringify(value)} is not off, on, info, warn or error — ignored`);
540
+ return;
541
+ };
395
542
  if (isObject(source) && "files" in source) {
396
543
  renamed.push("source.files -> chapters");
397
544
  if (manifest.chapters === undefined)
@@ -400,18 +547,37 @@ function readOldSpellings(raw) {
400
547
  if (isObject(validate)) {
401
548
  for (const [id, entry] of Object.entries(validate.checks ?? {})) {
402
549
  if (entry === false || isObject(entry) && entry.enabled === false)
403
- checks[id] = "off";
404
- else if (isObject(entry) && typeof entry.severity === "string")
405
- checks[id] = entry.severity;
406
- else
407
- checks[id] = "on";
550
+ levels[id] = "off";
551
+ else if (isObject(entry) && entry.severity !== undefined && entry.severity !== null) {
552
+ levels[id] = level(`validate.checks.${id}.severity`, entry.severity) ?? "on";
553
+ } else
554
+ levels[id] = "on";
408
555
  }
409
556
  if (validate.checks !== undefined)
410
557
  renamed.push("validate.checks -> checks");
558
+ }
559
+ if (isObject(lint)) {
560
+ for (const [id, value] of Object.entries(lint.rules ?? {})) {
561
+ const read = level(`lint.rules.${id}`, value);
562
+ if (read !== undefined)
563
+ levels[id] = read;
564
+ }
565
+ if (lint.rules !== undefined)
566
+ renamed.push("lint.rules -> checks");
567
+ if (lint.enabled === false) {
568
+ levels["source.stylelint"] = "off";
569
+ renamed.push("lint.enabled: false -> checks: { source.stylelint: off }");
570
+ } else if (lint.enabled !== undefined) {
571
+ dropped.push("lint.enabled");
572
+ }
573
+ if (lint.configPath !== undefined)
574
+ dropped.push("lint.configPath");
575
+ }
576
+ if (isObject(validate)) {
411
577
  for (const [tool, value] of Object.entries(validate.source ?? {})) {
412
578
  const id = SOURCE_TOOL_CHECKS[tool];
413
579
  if (id && value === false) {
414
- checks[id] = "off";
580
+ levels[id] = "off";
415
581
  renamed.push(`validate.source.${tool}: false -> checks: { ${id}: off }`);
416
582
  } else if (id && tool !== "stylelint" && typeof value === "string") {
417
583
  dropped.push(`validate.source.${tool} (the config file is found by its usual name in the book's folder or a folder above it)`);
@@ -423,11 +589,14 @@ function readOldSpellings(raw) {
423
589
  for (const [key, value] of Object.entries(validate[block] ?? {})) {
424
590
  const flag = FLAG_CHECKS[`${block}.${key}`];
425
591
  if (flag && typeof value === "boolean") {
426
- const level = value === flag.trueMeansOn ? "on" : "off";
427
- checks[flag.check] = level;
428
- renamed.push(`validate.${block}.${key}: ${value} -> checks: { ${flag.check}: ${level} }`);
592
+ const on = value === flag.trueMeansOn;
593
+ if (!on)
594
+ levels[flag.check] = "off";
595
+ else
596
+ levels[flag.check] ??= "on";
597
+ renamed.push(`validate.${block}.${key}: ${value} -> checks: { ${flag.check}: ${String(levels[flag.check])} }`);
429
598
  } else {
430
- dropped.push(`validate.${block}.${key} (the preset and the publish target set it)`);
599
+ threshold(`validate.${block}.${key}`, value);
431
600
  }
432
601
  }
433
602
  }
@@ -435,32 +604,28 @@ function readOldSpellings(raw) {
435
604
  dropped.push("validate.enabled (`gutterpress build --skip-pre-validate --skip-post-validate` skips validation for one build)");
436
605
  }
437
606
  }
438
- if (isObject(lint)) {
439
- for (const [id, level] of Object.entries(lint.rules ?? {}))
440
- checks[id] = level;
441
- if (lint.rules !== undefined)
442
- renamed.push("lint.rules -> checks");
443
- if (lint.enabled === false) {
444
- checks["source.stylelint"] = "off";
445
- renamed.push("lint.enabled: false -> checks: { source.stylelint: off }");
446
- } else if (lint.enabled !== undefined) {
447
- dropped.push("lint.enabled");
448
- }
449
- if (lint.configPath !== undefined)
450
- dropped.push("lint.configPath");
607
+ if (isObject(ink)) {
608
+ for (const [key, value] of Object.entries(ink))
609
+ threshold(`ink.${key}`, value);
610
+ } else if (ink !== undefined && ink !== null) {
611
+ dropped.push("ink");
451
612
  }
452
- if (ink !== undefined)
453
- dropped.push("ink (the preset and the publish target set it)");
454
613
  if (tolerance) {
455
- const { tolerance: _, ...page } = raw.page;
614
+ const { tolerance: value, ...page } = raw.page;
456
615
  manifest.page = page;
457
- dropped.push("page.tolerance (the preset sets it)");
616
+ threshold("page.tolerance", value);
617
+ }
618
+ const ids = new Set([...Object.keys(levels), ...Object.keys(options)]);
619
+ if (ids.size > 0) {
620
+ const checks = { ...manifest.checks };
621
+ for (const id of ids)
622
+ checks[id] = mergeEntry(levels[id], options[id], manifest.checks?.[id]);
623
+ manifest.checks = checks;
458
624
  }
459
- if (Object.keys(checks).length > 0)
460
- manifest.checks = { ...checks, ...manifest.checks };
461
625
  const lines = [
462
626
  ...renamed.map((r) => ` ${r}`),
463
- ...dropped.map((d) => ` ${d} — no longer a setting, ignored`)
627
+ ...dropped.map((d) => ` ${d} — no longer a setting, ignored`),
628
+ ...unusable.map((u) => ` ${u}`)
464
629
  ];
465
630
  if (lines.length > 0) {
466
631
  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:
@@ -469,6 +634,18 @@ function readOldSpellings(raw) {
469
634
  }
470
635
  return manifest;
471
636
  }
637
+ function mergeEntry(level, options, own) {
638
+ const ownEntry = isObject(own) ? own : own === undefined ? {} : { level: own };
639
+ const merged = {
640
+ ...level !== undefined ? { level } : {},
641
+ ...options,
642
+ ...ownEntry
643
+ };
644
+ if (ownEntry.level === undefined && level !== undefined)
645
+ merged.level = level;
646
+ const keys = Object.keys(merged);
647
+ return keys.length === 1 && keys[0] === "level" ? merged.level : merged;
648
+ }
472
649
 
473
650
  // src/lib/manifest.ts
474
651
  var MANIFEST_FILENAMES = [
@@ -577,7 +754,7 @@ function pluginsRemovedMessage(raw) {
577
754
  ` + (lines.length > 0 ? lines.join(`
578
755
  `) : " - ./plugins/my-plugin.js");
579
756
  }
580
- function isPlainObject(v) {
757
+ function isPlainObject2(v) {
581
758
  return typeof v === "object" && v !== null && !Array.isArray(v);
582
759
  }
583
760
  function mergeShape(cli, manifest, preset) {
@@ -586,7 +763,7 @@ function mergeShape(cli, manifest, preset) {
586
763
  const presetValue = preset[key];
587
764
  const manifestValue = manifest ? manifest[key] : undefined;
588
765
  const cliValue = cli ? cli[key] : undefined;
589
- out[key] = isPlainObject(presetValue) ? mergeShape(cliValue, manifestValue, presetValue) : cliValue ?? manifestValue ?? presetValue;
766
+ out[key] = isPlainObject2(presetValue) ? mergeShape(cliValue, manifestValue, presetValue) : cliValue ?? manifestValue ?? presetValue;
590
767
  }
591
768
  return out;
592
769
  }
@@ -634,7 +811,7 @@ function resolveCustomPage(c, m) {
634
811
  }
635
812
  var MARGIN_EDGES = ["top", "bottom", "inside", "outside"];
636
813
  function validatePageMargin(margin, size) {
637
- const edges = isPlainObject(margin) && Object.keys(margin).every((k) => MARGIN_EDGES.includes(k)) && MARGIN_EDGES.every((e) => isNonNegativePoints(margin[e]));
814
+ const edges = isPlainObject2(margin) && Object.keys(margin).every((k) => MARGIN_EDGES.includes(k)) && MARGIN_EDGES.every((e) => isNonNegativePoints(margin[e]));
638
815
  if (!edges) {
639
816
  throw new UsageError("Manifest field `page.margin` must set all four edges in points (72pt = 1in), for example\n" + ` page:
640
817
  margin:
@@ -691,7 +868,9 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
691
868
  const page = preset.page ? mergeShape(c.page, m.page, preset.page) : resolveCustomPage(c, m);
692
869
  if (page.margin !== undefined && page.margin !== null)
693
870
  validatePageMargin(page.margin, page);
694
- return {
871
+ const manifestChecks = resolveChecks(m.checks);
872
+ const cliChecks = resolveChecks(c.checks);
873
+ const config = {
695
874
  title: c.title ?? m.title ?? "Document",
696
875
  authors: c.authors ?? m.authors ?? [],
697
876
  language: resolveLanguage(c.language ?? m.language),
@@ -701,35 +880,47 @@ function resolveWithPreset(cliOverrides, manifest, preset) {
701
880
  extensions,
702
881
  targets: resolveTargets(c.targets ?? m.targets, preset.defaultTargets),
703
882
  chapters: c.chapters ?? m.chapters ?? preset.chapters,
704
- checks: { ...preset.checks, ...resolveCheckLevels(m.checks), ...resolveCheckLevels(c.checks) },
883
+ checks: { ...preset.checks, ...manifestChecks.levels, ...cliChecks.levels },
705
884
  pdfx: mergeShape(c.pdfx, m.pdfx, preset.pdfx),
706
885
  print: mergeShape(c.print, m.print, preset.print),
707
886
  page,
708
887
  ink: structuredClone(preset.ink),
709
888
  validate: structuredClone(preset.validate)
710
889
  };
711
- }
712
- function resolveCheckLevels(input) {
890
+ applyCheckOptions(config, manifestChecks.options);
891
+ applyCheckOptions(config, cliChecks.options);
892
+ return config;
893
+ }
894
+ function checkLevel(id, value) {
895
+ if (!isCheckLevelInput(value)) {
896
+ throw new UsageError(`Manifest field \`checks.${id}\` must be off, on, info, warn or error, got ${JSON.stringify(value)}.`);
897
+ }
898
+ if (value === false || value === "off")
899
+ return "off";
900
+ if (value === true || value === "on")
901
+ return "on";
902
+ return value === "warn" || value === "warning" ? "warning" : value;
903
+ }
904
+ function resolveChecks(input) {
905
+ const levels = {};
906
+ const options = {};
713
907
  if (input === undefined || input === null)
714
- return {};
715
- if (!isPlainObject(input)) {
908
+ return { levels, options };
909
+ if (!isPlainObject2(input)) {
716
910
  throw new UsageError("Manifest field `checks` must map check ids to off, on, info, warn or error.");
717
911
  }
718
- const out = {};
719
912
  for (const [id, value] of Object.entries(input)) {
720
- if (value === false || value === "off")
721
- out[id] = "off";
722
- else if (value === true || value === "on")
723
- out[id] = "on";
724
- else if (value === "warn" || value === "warning")
725
- out[id] = "warning";
726
- else if (value === "info" || value === "error")
727
- out[id] = value;
728
- else {
729
- throw new UsageError(`Manifest field \`checks.${id}\` must be off, on, info, warn or error, got ${JSON.stringify(value)}.`);
913
+ if (!isPlainObject2(value)) {
914
+ levels[id] = checkLevel(id, value);
915
+ continue;
730
916
  }
917
+ if (value.level !== undefined && value.level !== null)
918
+ levels[id] = checkLevel(`${id}.level`, value.level);
919
+ const own = readCheckOptions(id, value);
920
+ if (Object.keys(own).length > 0)
921
+ options[id] = own;
731
922
  }
732
- return out;
923
+ return { levels, options };
733
924
  }
734
925
 
735
926
  // src/lib/manifest-doc.ts
@@ -1502,7 +1693,7 @@ function withMutationLock(projectDir, mutation) {
1502
1693
  function entryUse(item) {
1503
1694
  if (isScalar(item))
1504
1695
  return typeof item.value === "string" ? item.value.trim() : null;
1505
- if (isMap(item)) {
1696
+ if (isMap3(item)) {
1506
1697
  const u = item.get("use");
1507
1698
  return typeof u === "string" ? u.trim() : null;
1508
1699
  }
@@ -1512,7 +1703,7 @@ function rawEntry(item) {
1512
1703
  const use = entryUse(item);
1513
1704
  if (!use)
1514
1705
  return null;
1515
- if (!isMap(item))
1706
+ if (!isMap3(item))
1516
1707
  return { use, enabled: true };
1517
1708
  const x = item.get("export");
1518
1709
  return {
@@ -1685,13 +1876,13 @@ async function setExtensionEnabled(projectDir, use, enabled) {
1685
1876
  throw new Error(`Extension "${use}" is not in the manifest.`);
1686
1877
  const item = seq.items[idx];
1687
1878
  if (enabled) {
1688
- if (isMap(item)) {
1879
+ if (isMap3(item)) {
1689
1880
  item.delete("enabled");
1690
1881
  if (item.items.length === 1 && typeof item.get("use") === "string") {
1691
1882
  seq.items[idx] = new Scalar(item.get("use"));
1692
1883
  }
1693
1884
  }
1694
- } else if (isMap(item)) {
1885
+ } else if (isMap3(item)) {
1695
1886
  item.set("enabled", false);
1696
1887
  } else {
1697
1888
  const map = new YAMLMap(doc.schema);
@@ -1734,7 +1925,7 @@ async function upsertEntry(projectDir, use, exportName) {
1734
1925
  const idx = indexOfUse(seq, use);
1735
1926
  if (idx >= 0) {
1736
1927
  const item = seq.items[idx];
1737
- if (isMap(item)) {
1928
+ if (isMap3(item)) {
1738
1929
  item.set("use", use);
1739
1930
  if (exportName)
1740
1931
  item.set("export", exportName);
@@ -1991,6 +2182,46 @@ async function updateExtensions(projectDir, options = {}) {
1991
2182
  }
1992
2183
  return updated;
1993
2184
  }
2185
+ var BUILT_IN_LOOKS = BUNDLED_EXTENSION_DETAILS.filter((b) => b.css !== undefined).map((b) => b.name);
2186
+ async function addBuiltInStyleSet(projectDir, id) {
2187
+ const look = bundledExtension(id);
2188
+ if (look?.css === undefined) {
2189
+ throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_LOOKS.join(", ")}).`);
2190
+ }
2191
+ const css = look.css;
2192
+ return withMutationLock(projectDir, async () => {
2193
+ const dest = path4.join(projectDir, EXTENSIONS_DIR, id);
2194
+ if (!existsSync3(dest)) {
2195
+ await mkdir3(dest, { recursive: true });
2196
+ const pkg = {
2197
+ name: id,
2198
+ description: look.description,
2199
+ author: "gutterpress",
2200
+ keywords: ["gutterpress", "gutterpress-look"],
2201
+ gutterpress: { styles: ["theme.css"] }
2202
+ };
2203
+ await writeFile2(path4.join(dest, EXTENSION_MANIFEST_FILENAME), `${JSON.stringify(pkg, null, 2)}
2204
+ `, "utf8");
2205
+ await writeFile2(path4.join(dest, "theme.css"), css, "utf8");
2206
+ }
2207
+ const use = `./${EXTENSIONS_DIR}/${id}`;
2208
+ await loadPlugin(extensionConfigFor({ use, kind: "path", name: use }), projectDir);
2209
+ const { doc, file } = await loadManifestDoc(projectDir);
2210
+ const seq = ensureSeq(doc, "extensions");
2211
+ if (indexOfUse(seq, use) < 0) {
2212
+ const at = indexOfUse(seq, id);
2213
+ const item = at < 0 ? undefined : seq.items[at];
2214
+ if (isMap3(item))
2215
+ item.set("use", use);
2216
+ else if (at >= 0)
2217
+ seq.items[at] = new Scalar(use);
2218
+ else
2219
+ seq.add(new Scalar(use));
2220
+ await writeManifestDoc(file, doc);
2221
+ }
2222
+ return await describeExtension(projectDir, use);
2223
+ });
2224
+ }
1994
2225
  async function uniqueExtensionId(projectDir, base) {
1995
2226
  const slug = slugify(base) || "extension";
1996
2227
  let candidate = slug;
@@ -2117,4 +2348,4 @@ async function restoreForCommand(dir, options) {
2117
2348
  log.warn(problem);
2118
2349
  }
2119
2350
 
2120
- export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
2351
+ export { PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, setCheckOption, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, BUILT_IN_LOOKS, addBuiltInStyleSet, uniqueExtensionId, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PACKAGE_VERSION,
3
3
  debug
4
- } from "./cli-1fy1pm9d.js";
4
+ } from "./cli-fh685nqs.js";
5
5
  import {
6
6
  isExactNpmVersion,
7
7
  isValidNpmPackageName,
@@ -11,7 +11,7 @@ import {
11
11
  resolveVendoredPluginInstallRoot,
12
12
  safePackageTarget,
13
13
  vendoredNpmPluginPackageDir
14
- } from "./cli-y28j019d.js";
14
+ } from "./cli-1dha1nxk.js";
15
15
 
16
16
  // src/lib/markdown/plugins.ts
17
17
  import { existsSync as existsSync2, readFileSync, statSync, unlinkSync } from "node:fs";
@@ -292,7 +292,7 @@ th, td { padding: 0.2em 0.8em; }
292
292
  `;
293
293
 
294
294
  // src/lib/extension-specifier.ts
295
- var BUNDLED_EXTENSIONS = [
295
+ var BUNDLED_EXTENSION_DETAILS = [
296
296
  {
297
297
  name: "markdown-it-mark",
298
298
  label: "Highlight",
@@ -348,8 +348,9 @@ var BUNDLED_EXTENSIONS = [
348
348
  css: theme_default4
349
349
  }
350
350
  ];
351
+ var BUNDLED_EXTENSIONS = BUNDLED_EXTENSION_DETAILS.map((e) => e.name);
351
352
  function bundledExtension(name) {
352
- return BUNDLED_EXTENSIONS.find((e) => e.name === name);
353
+ return BUNDLED_EXTENSION_DETAILS.find((e) => e.name === name);
353
354
  }
354
355
  function isBundledExtension(name) {
355
356
  return bundledExtension(name) !== undefined;
@@ -1950,8 +1951,9 @@ img.gp-shape {
1950
1951
 
1951
1952
  /* Math (the bundled gutterpress-math feature, math.ts): MathJax's own rules
1952
1953
  for the <mjx-container> SVG output it emits, from its SVG output jax's
1953
- stylesheet, less the ones for its interactive menu and tooltips. Inert in
1954
- a book without formulas. */
1954
+ stylesheet, less the ones for its interactive menu and tooltips, plus one
1955
+ bound of ours on the hidden MathML copy (#469, below). Inert in a book
1956
+ without formulas. */
1955
1957
  mjx-container[jax="SVG"] { direction: ltr; }
1956
1958
  mjx-container[jax="SVG"] > svg { overflow: visible; min-height: 1px; min-width: 1px; }
1957
1959
  mjx-container[jax="SVG"] > svg a { fill: blue; stroke: blue; }
@@ -1968,6 +1970,15 @@ mjx-assistive-mml {
1968
1970
  border: 0px !important;
1969
1971
  display: block !important;
1970
1972
  width: auto !important;
1973
+ /* Ours (#469). The browser lays the copy out in whatever math font it has,
1974
+ so it can be wider than the formula's SVG; ending a line, it crossed the
1975
+ column edge and Chromium shrank the whole print to fit. MathJax makes the
1976
+ container position: relative with the copy at its top left, so 100% is
1977
+ the formula's width. Clip and overflow still hide the copy and its MathML
1978
+ still reaches screen readers; only its box changes. Not the 1px
1979
+ visually-hidden box MathJax 2 used: its docs note VoiceOver on iOS could
1980
+ not reach a formula by touch in it. */
1981
+ max-width: 100% !important;
1971
1982
  overflow: hidden !important;
1972
1983
  user-select: none;
1973
1984
  }
@@ -2538,22 +2549,39 @@ th[data-align="right"], td[data-align="right"] { text-align: right; }
2538
2549
  `;
2539
2550
 
2540
2551
  // src/lib/markdown/typography.ts
2552
+ var SCOPED = /\((c|tm|r)\)/gi;
2553
+ var SYMBOLS = { c: "©", r: "®", tm: "™" };
2554
+ var LABEL_BEFORE = { c: "(b)", C: "(B)", r: "(q)", R: "(Q)" };
2555
+ var LABELS = /\((b|q)\)/gi;
2556
+ var RARE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/;
2541
2557
  var EM_DASH = /(^|[^-])---(?=[^-]|$)/gm;
2542
2558
  var EN_DASH_SPACED = /(^|\s)--(?=\s|$)/gm;
2543
2559
  var EN_DASH = /(^|[^-\s])--(?=[^-\s]|$)/gm;
2544
- var ELLIPSIS = /\.\.\./g;
2545
- var CANDIDATE = /--|\.\.\./;
2546
- function replaceInText(children) {
2560
+ var CANDIDATE = /\((?:c|tm|r|b|q)\)|\+-|\.\.|\?\?\?\?|!!!!|,,|--/i;
2561
+ function replaceRare(text) {
2562
+ return text.replace(/\+-/g, "±").replace(/\.{2,}/g, "…").replace(/([?!])…/g, "$1..").replace(/([?!]){4,}/g, "$1$1$1").replace(/,{2,}/g, ",").replace(EM_DASH, "$1—").replace(EN_DASH_SPACED, "$1–").replace(EN_DASH, "$1–");
2563
+ }
2564
+ function replaceInText(children, seen) {
2547
2565
  let insideAutolink = 0;
2548
- for (let i = children.length - 1;i >= 0; i--) {
2549
- const token = children[i];
2550
- if (token.type === "text" && !insideAutolink && CANDIDATE.test(token.content)) {
2551
- token.content = token.content.replace(ELLIPSIS, "…").replace(EM_DASH, "$1—").replace(EN_DASH_SPACED, "$1–").replace(EN_DASH, "$1–");
2552
- }
2553
- if (token.type === "link_close" && token.info === "auto")
2554
- insideAutolink++;
2566
+ for (const token of children) {
2555
2567
  if (token.type === "link_open" && token.info === "auto")
2568
+ insideAutolink++;
2569
+ if (token.type === "link_close" && token.info === "auto")
2556
2570
  insideAutolink--;
2571
+ if (token.type !== "text")
2572
+ continue;
2573
+ const typed = token.content;
2574
+ if (!insideAutolink) {
2575
+ let text = typed.replace(SCOPED, (match, name, offset) => {
2576
+ const label = LABEL_BEFORE[name];
2577
+ return label && (seen.has(label) || typed.slice(0, offset).includes(label)) ? match : SYMBOLS[name.toLowerCase()];
2578
+ });
2579
+ if (RARE.test(text))
2580
+ text = replaceRare(text);
2581
+ token.content = text;
2582
+ }
2583
+ for (const [label] of typed.matchAll(LABELS))
2584
+ seen.add(label);
2557
2585
  }
2558
2586
  }
2559
2587
  var QUOTES = {
@@ -2580,10 +2608,11 @@ function quotesFor(language) {
2580
2608
  function gpTypography(md) {
2581
2609
  md.disable("replacements");
2582
2610
  md.core.ruler.after("linkify", "gp_typography", (state) => {
2611
+ const seen = new Set;
2583
2612
  for (const token of state.tokens) {
2584
2613
  if (token.type !== "inline" || !token.children || !CANDIDATE.test(token.content))
2585
2614
  continue;
2586
- replaceInText(token.children);
2615
+ replaceInText(token.children, seen);
2587
2616
  }
2588
2617
  });
2589
2618
  }
@@ -3897,4 +3926,4 @@ async function loadPluginsWithCss(configs, baseDir, onError) {
3897
3926
  };
3898
3927
  }
3899
3928
 
3900
- export { bundledExtension, isPathSpecifier, parseExtensionSpecifier, pinnedNpmSpecifier, TWO_VERSIONS_OPEN, TWO_VERSIONS_CLOSE, MARKER_CSS, GUTTERPRESS_CSS, TWO_VERSIONS_CSS, TABLE_ALIGN_CSS, slugify, prettify, parseSrcsetUrlCandidates, collectHtmlImageRefs, createMarkdownRenderer, applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups, resolveDeclaredStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, assertExtensionContained, __resetPathPluginCacheForTests, loadPlugin, loadPublishFunction, loadPlugins, loadPluginsWithCss };
3929
+ export { BUNDLED_EXTENSION_DETAILS, bundledExtension, isPathSpecifier, parseExtensionSpecifier, pinnedNpmSpecifier, TWO_VERSIONS_OPEN, TWO_VERSIONS_CLOSE, MARKER_CSS, GUTTERPRESS_CSS, TWO_VERSIONS_CSS, TABLE_ALIGN_CSS, slugify, prettify, parseSrcsetUrlCandidates, collectHtmlImageRefs, createMarkdownRenderer, applyPlugins, collectPluginCss, collectPluginStylePaths, collectPluginStyleGroups, resolveDeclaredStyles, EXTENSION_MANIFEST_FILENAME, readExtensionMeta, extensionStyleList, extensionEntry, extensionCarries, assertExtensionContained, __resetPathPluginCacheForTests, loadPlugin, loadPublishFunction, loadPlugins, loadPluginsWithCss };