gutterpress 0.11.23-alpha.1 → 0.11.23-alpha.2

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 (62) hide show
  1. package/README.md +21 -22
  2. package/dist/api/index.d.ts +4 -4
  3. package/dist/api/index.js +16 -22
  4. package/dist/{build-09cd1n7s.js → build-encgnv0c.js} +9 -10
  5. package/dist/{cli-habkc5ng.js → cli-a35w7wzg.js} +5 -4
  6. package/dist/{cli-b0vyjvmt.js → cli-aqh6brew.js} +1 -1
  7. package/dist/{cli-ccp7sxz3.js → cli-evg8sbht.js} +1 -1
  8. package/dist/{cli-nr6j6300.js → cli-kh8v4vvw.js} +1 -1
  9. package/dist/{cli-h17e3mtz.js → cli-pwwfxdgb.js} +385 -10
  10. package/dist/{cli-dtckwda1.js → cli-pz4bn98t.js} +185 -9
  11. package/dist/{cli-dd115fa8.js → cli-sdmv4bba.js} +47 -123
  12. package/dist/{cli-5hkmp2wy.js → cli-xq6djqbj.js} +55 -88
  13. package/dist/{index-r7jcsk6h.js → cli-y28j019d.js} +19 -10
  14. package/dist/cli.js +12 -12
  15. package/dist/{doctor-nddspx4n.js → doctor-5gtpgpd1.js} +3 -3
  16. package/dist/{engine-892swzzc.js → engine-mc5br8g8.js} +1 -2
  17. package/dist/{engine-yjzknt0g.js → engine-tn13gk0b.js} +3 -3
  18. package/dist/{ext-zjpen4yx.js → ext-wn2a5afq.js} +18 -33
  19. package/dist/{extension-restore-w8xt4kbq.js → extension-restore-g8jnmmzq.js} +5 -6
  20. package/dist/{extension-restore-jvs60x9b.js → extension-restore-s3x1618g.js} +4 -4
  21. package/dist/{index-apc8w9w8.js → index-0skhjx3e.js} +180 -1
  22. package/dist/{index-2jpk25en.js → index-2bjcghze.js} +385 -10
  23. package/dist/index-46ycxe6r.js +18 -0
  24. package/dist/{index-0w2azsk1.js → index-4x6y8krm.js} +2 -4
  25. package/dist/{cli-h3phdstf.js → index-5x42amh8.js} +19 -10
  26. package/dist/{index-93pqkvty.js → index-h6yk5tjx.js} +5 -4
  27. package/dist/{index-zrhyb967.js → index-j5z86r4a.js} +335 -349
  28. package/dist/{index-m4hhzg3c.js → index-x3ema2w3.js} +59 -134
  29. package/dist/index.js +17 -23
  30. package/dist/lib/extension-manager.d.ts +16 -43
  31. package/dist/lib/extension-scaffold.d.ts +3 -3
  32. package/dist/lib/extension-specifier.d.ts +23 -6
  33. package/dist/lib/markdown/math.d.ts +1 -1
  34. package/dist/lib/npm-plugin-installer.d.ts +1 -1
  35. package/dist/lib/plugin-vendor.d.ts +11 -3
  36. package/dist/lib/project-gitignore.d.ts +2 -2
  37. package/dist/lib/project-scaffold.d.ts +5 -7
  38. package/dist/lib/project-templates.d.ts +0 -3
  39. package/dist/lib/snippets.d.ts +2 -1
  40. package/dist/lib/style-resolver.d.ts +1 -2
  41. package/dist/{manifest-8f10dr4f.yaml → manifest-eagfhba6.yaml} +2 -0
  42. package/dist/{manifest.schema-9aeaered.json → manifest.schema-t7nx2stg.json} +5 -5
  43. package/dist/{new-xhq8wczn.js → new-mhs46pw3.js} +22 -21
  44. package/dist/{plugins-9en8m9y0.js → plugins-1sxe0dkz.js} +4 -4
  45. package/dist/{plugins-gf0gkjmv.js → plugins-2cek4mz1.js} +2 -2
  46. package/dist/{preflight-negnxg7b.js → preflight-3k2b1x8e.js} +9 -10
  47. package/dist/{preview-cep2s9cg.js → preview-6p9v6rht.js} +9 -10
  48. package/dist/{publish-xery6qc6.js → publish-qgsngy8s.js} +9 -10
  49. package/dist/{source-provider-qbfknhyy.js → source-provider-98k5deda.js} +2 -2
  50. package/dist/{source-provider-2r74a7wp.js → source-provider-knmcam50.js} +2 -2
  51. package/dist/{validate-3d0sgf4g.js → validate-0v7xw6wa.js} +9 -10
  52. package/package.json +1 -1
  53. package/dist/cli-fe43evmf.js +0 -212
  54. package/dist/index-dsvk7q6s.js +0 -229
  55. package/dist/package-97gm31sg.json +0 -10
  56. package/dist/package-gag6ykwn.json +0 -10
  57. package/dist/package-nrhkwf2m.json +0 -10
  58. package/dist/package-zttkgydq.json +0 -10
  59. package/dist/theme-9c58td2d.css +0 -76
  60. package/dist/theme-h5recz6c.css +0 -61
  61. package/dist/theme-j2bagrfx.css +0 -59
  62. package/dist/theme-nn6d53zy.css +0 -71
@@ -1,28 +1,29 @@
1
1
  import {
2
2
  EXTENSION_MANIFEST_FILENAME,
3
3
  TWO_VERSIONS_OPEN,
4
+ bundledExtension,
4
5
  extensionCarries,
5
6
  extensionStyleList,
7
+ isPathSpecifier,
6
8
  loadPlugin,
9
+ parseExtensionSpecifier,
10
+ pinnedNpmSpecifier,
7
11
  prettify,
8
12
  readExtensionMeta,
9
13
  slugify
10
- } from "./cli-h17e3mtz.js";
14
+ } from "./cli-pwwfxdgb.js";
11
15
  import {
12
16
  log
13
- } from "./cli-b0vyjvmt.js";
17
+ } from "./cli-aqh6brew.js";
14
18
  import {
15
19
  UsageError
16
20
  } from "./cli-d8xjns66.js";
17
- import {
18
- getAssetsDir
19
- } from "./cli-fe43evmf.js";
20
21
  import {
21
22
  BuildError,
22
23
  EXIT_CODES
23
24
  } from "./cli-46ycxe6r.js";
24
25
  import {
25
- PLUGINS_DIR,
26
+ EXTENSIONS_DIR,
26
27
  VENDORED_NPM_DIR,
27
28
  assertWindowsSafeRelativePath,
28
29
  isExactNpmVersion,
@@ -31,8 +32,9 @@ import {
31
32
  resolveVendoredPluginInstallRoot,
32
33
  vendoredNpmPluginPackageDir,
33
34
  vendoredNpmPluginRoot,
35
+ vendoredNpmPluginRoots,
34
36
  windowsPathKey
35
- } from "./cli-h3phdstf.js";
37
+ } from "./cli-y28j019d.js";
36
38
  import {
37
39
  __require
38
40
  } from "./cli-37x76zdn.js";
@@ -74,7 +76,7 @@ async function withFetchTimeout(options, run) {
74
76
  }
75
77
 
76
78
  // src/lib/extension-manager.ts
77
- import { cp, mkdir as mkdir3, readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
79
+ import { readdir, readFile as readFile5, rm as rm3, stat } from "node:fs/promises";
78
80
  import { existsSync as existsSync3 } from "node:fs";
79
81
  import path4 from "node:path";
80
82
  import { isSeq as isSeq2, isMap, isScalar, YAMLMap, Scalar } from "yaml";
@@ -92,44 +94,6 @@ import { existsSync, statSync } from "node:fs";
92
94
  import { resolve, dirname } from "node:path";
93
95
  import { parse as parseYaml, YAMLParseError } from "yaml";
94
96
 
95
- // src/lib/extension-specifier.ts
96
- var BUNDLED_EXTENSIONS = [
97
- "markdown-it-mark",
98
- "markdown-it-sub",
99
- "markdown-it-sup",
100
- "markdown-it-abbr",
101
- "gutterpress-gfm-alerts",
102
- "gutterpress-math"
103
- ];
104
- function isBundledExtension(name) {
105
- return BUNDLED_EXTENSIONS.includes(name);
106
- }
107
- function isPathSpecifier(use) {
108
- return use.startsWith("./") || use.startsWith("../") || use.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(use);
109
- }
110
- function parseExtensionSpecifier(use) {
111
- const spec = use.trim();
112
- if (!spec) {
113
- throw new Error("An extension specifier is required — an npm package name, or a ./ path.");
114
- }
115
- if (isPathSpecifier(spec))
116
- return { kind: "path", path: spec };
117
- if (/\.(m?js|cjs)$/i.test(spec) || !spec.startsWith("@") && spec.includes("/")) {
118
- throw new Error(`"${spec}" looks like a path; write it as "./${spec}". A specifier that does not ` + "start with ./, ../ or / is an npm package name.");
119
- }
120
- const parsed = parseNpmPluginSpec(spec);
121
- if (isBundledExtension(parsed.name)) {
122
- if (parsed.selector !== undefined) {
123
- throw new Error(`"${parsed.name}" is bundled with Gutterpress and always resolves to the bundled copy — ` + `drop "@${parsed.selector}".`);
124
- }
125
- return { kind: "bundled", name: parsed.name };
126
- }
127
- return parsed.selector === undefined ? { kind: "npm", name: parsed.name } : { kind: "npm", name: parsed.name, version: parsed.selector };
128
- }
129
- function pinnedNpmSpecifier(name, version) {
130
- return `${name}@${version}`;
131
- }
132
-
133
97
  // src/lib/presets.ts
134
98
  var PRESET_IDS = ["dtrpg", "book", "custom"];
135
99
  var DTRPG_PRESET = {
@@ -1283,13 +1247,13 @@ async function removeEmptyDirs(dirs) {
1283
1247
  }
1284
1248
  }
1285
1249
  async function prepareVendorParent(projectDir, name) {
1286
- const plugins = path2.join(projectDir, PLUGINS_DIR);
1287
- const npm = path2.join(plugins, VENDORED_NPM_DIR);
1250
+ const extensions = path2.join(projectDir, EXTENSIONS_DIR);
1251
+ const npm = path2.join(extensions, VENDORED_NPM_DIR);
1288
1252
  const packageParent = path2.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1289
1253
  await mkdir2(projectDir, { recursive: true });
1290
1254
  const createdDirs = [];
1291
1255
  try {
1292
- for (const dir of [plugins, npm, packageParent]) {
1256
+ for (const dir of [extensions, npm, packageParent]) {
1293
1257
  if (await ensureDirectory(dir))
1294
1258
  createdDirs.push(dir);
1295
1259
  }
@@ -1408,7 +1372,7 @@ function ignoreStatus(gitignore, entry) {
1408
1372
  return "ignored";
1409
1373
  return dir.unignored || file.unignored ? "negated" : "open";
1410
1374
  }
1411
- var PROJECT_GITIGNORE_ENTRIES = ["dist/", "plugins/npm/"];
1375
+ var PROJECT_GITIGNORE_ENTRIES = ["dist/", "extensions/npm/"];
1412
1376
  async function ensureProjectGitignore(projectDir) {
1413
1377
  const gitignorePath = path3.join(projectDir, ".gitignore");
1414
1378
  let existing = null;
@@ -1436,40 +1400,6 @@ async function ensureProjectGitignore(projectDir) {
1436
1400
  }
1437
1401
 
1438
1402
  // src/lib/extension-manager.ts
1439
- var EXTENSIONS_DIR = "extensions";
1440
- var RECOMMENDED_EXTENSIONS = [
1441
- {
1442
- use: "markdown-it-mark",
1443
- label: "Highlight",
1444
- description: "Highlight text by wrapping it in `==double equals==`."
1445
- },
1446
- {
1447
- use: "markdown-it-sub",
1448
- label: "Subscript",
1449
- description: "Lower text below the line, like the 2 in `H~2~O`."
1450
- },
1451
- {
1452
- use: "markdown-it-sup",
1453
- label: "Superscript",
1454
- description: "Raise text above the line, like the th in `29^th^`."
1455
- },
1456
- {
1457
- use: "markdown-it-abbr",
1458
- label: "Abbreviations",
1459
- description: "Explain an abbreviation when readers hover over it. Define it once, like `*[GPS]: Global Positioning System`."
1460
- },
1461
- {
1462
- use: "gutterpress-gfm-alerts",
1463
- label: "Callouts",
1464
- description: "Turn a quote into a boxed note: start it with `> [!NOTE]` (or `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`)."
1465
- },
1466
- {
1467
- use: "gutterpress-math",
1468
- label: "Math",
1469
- description: "Write LaTeX math between dollar signs: `$E = mc^2$` in a line, or `$$` … `$$` on lines of their own."
1470
- }
1471
- ];
1472
- var BUILT_IN_STYLE_SET_IDS = ["clean-book", "zine", "technical-doc", "academic"];
1473
1403
  var mutationQueues = new Map;
1474
1404
  function projectKey(projectDir) {
1475
1405
  const resolved = path4.resolve(projectDir);
@@ -1585,15 +1515,14 @@ async function describeEntry(projectDir, raw) {
1585
1515
  return withWarnings({ ...common, kind: "npm", name: raw.use, label: raw.use, carries: NO_CARRIES });
1586
1516
  }
1587
1517
  if (parsed.kind === "bundled") {
1588
- const bundledName = parsed.name;
1589
- const rec = RECOMMENDED_EXTENSIONS.find((r) => r.use === bundledName);
1518
+ const bundled = bundledExtension(parsed.name);
1590
1519
  return withWarnings({
1591
1520
  ...common,
1592
1521
  kind: "bundled",
1593
- name: bundledName,
1594
- label: rec?.label ?? bundledName,
1595
- ...rec ? { description: rec.description } : {},
1596
- carries: { ...NO_CARRIES, markdown: true }
1522
+ name: parsed.name,
1523
+ label: bundled.label,
1524
+ description: bundled.description,
1525
+ carries: { ...NO_CARRIES, ...bundled.css !== undefined ? { styles: true } : { markdown: true } }
1597
1526
  });
1598
1527
  }
1599
1528
  if (parsed.kind === "path") {
@@ -1715,9 +1644,10 @@ async function removeExtension(projectDir, use) {
1715
1644
  return;
1716
1645
  }
1717
1646
  if (parsed.kind === "npm" && parsed.version) {
1718
- const root = vendoredNpmPluginRoot(projectDir, parsed.name, parsed.version);
1719
- await rm3(root, { recursive: true, force: true });
1720
- await removeEmptyDirs([path4.dirname(root)]);
1647
+ for (const root of vendoredNpmPluginRoots(projectDir, parsed.name, parsed.version)) {
1648
+ await rm3(root, { recursive: true, force: true });
1649
+ await removeEmptyDirs([path4.dirname(root)]);
1650
+ }
1721
1651
  }
1722
1652
  });
1723
1653
  }
@@ -1835,8 +1765,8 @@ async function vendorNpmExtension(projectDir, input, exportName, options, commit
1835
1765
  });
1836
1766
  try {
1837
1767
  const { negated } = await ensureProjectGitignore(projectDir);
1838
- if (negated.includes("plugins/npm/")) {
1839
- warnings.push("Your .gitignore re-includes plugins/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1768
+ if (negated.includes("extensions/npm/")) {
1769
+ warnings.push("Your .gitignore re-includes extensions/npm/, so downloaded extensions will be committed with " + "your book. They shouldn't be: the manifest pins each version and a build downloads a " + "missing copy again.");
1840
1770
  }
1841
1771
  } catch (error) {
1842
1772
  warnings.push(`The extension was installed, but .gitignore could not be updated to keep it out of version history: ` + `${error instanceof Error ? error.message : String(error)}`);
@@ -1862,24 +1792,32 @@ async function pinnedVersionsOf(projectDir, name) {
1862
1792
  return versions;
1863
1793
  }
1864
1794
  async function pruneStaleVersions(projectDir, name, keep) {
1865
- const parent = path4.dirname(vendoredNpmPluginRoot(projectDir, name, "0.0.0"));
1795
+ const parents = vendoredNpmPluginRoots(projectDir, name, "0.0.0").map((root) => path4.dirname(root));
1866
1796
  try {
1867
- for (const entry of await readdir(parent, { withFileTypes: true })) {
1868
- if (!entry.isDirectory())
1869
- continue;
1870
- let version;
1797
+ for (const parent of parents) {
1798
+ let entries;
1871
1799
  try {
1872
- version = decodeURIComponent(entry.name);
1873
- } catch {
1874
- continue;
1800
+ entries = await readdir(parent, { withFileTypes: true });
1801
+ } catch (error) {
1802
+ if (error.code === "ENOENT")
1803
+ continue;
1804
+ throw error;
1805
+ }
1806
+ for (const entry of entries) {
1807
+ if (!entry.isDirectory())
1808
+ continue;
1809
+ let version;
1810
+ try {
1811
+ version = decodeURIComponent(entry.name);
1812
+ } catch {
1813
+ continue;
1814
+ }
1815
+ if (!isExactNpmVersion(version) || keep.includes(version))
1816
+ continue;
1817
+ await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1875
1818
  }
1876
- if (!isExactNpmVersion(version) || keep.includes(version))
1877
- continue;
1878
- await rm3(path4.join(parent, entry.name), { recursive: true, force: true });
1879
1819
  }
1880
1820
  } catch (error) {
1881
- if (error.code === "ENOENT")
1882
- return [];
1883
1821
  return [
1884
1822
  `The extension was installed, but a previous version's downloaded copy could not be removed: ` + `${error instanceof Error ? error.message : String(error)}`
1885
1823
  ];
@@ -1970,20 +1908,6 @@ async function uniqueExtensionId(projectDir, base) {
1970
1908
  }
1971
1909
  return candidate;
1972
1910
  }
1973
- async function addBuiltInStyleSet(projectDir, id) {
1974
- if (!BUILT_IN_STYLE_SET_IDS.includes(id)) {
1975
- throw new Error(`Unknown built-in look "${id}" (one of: ${BUILT_IN_STYLE_SET_IDS.join(", ")}).`);
1976
- }
1977
- return withMutationLock(projectDir, async () => {
1978
- const dest = path4.join(projectDir, EXTENSIONS_DIR, id);
1979
- if (!existsSync3(dest)) {
1980
- const src = path4.join(await getAssetsDir(), "themes", id);
1981
- await mkdir3(path4.dirname(dest), { recursive: true });
1982
- await cp(src, dest, { recursive: true });
1983
- }
1984
- return addExtensionUnlocked(projectDir, `./${EXTENSIONS_DIR}/${id}`, {});
1985
- });
1986
- }
1987
1911
 
1988
1912
  // src/lib/extension-restore.ts
1989
1913
  var OFFLINE_REASON = "you appear to be offline (npm can't be reached)";
@@ -2102,4 +2026,4 @@ async function restoreForCommand(dir, options) {
2102
2026
  log.warn(problem);
2103
2027
  }
2104
2028
 
2105
- export { isPathSpecifier, parseExtensionSpecifier, PRESET_IDS, PRESETS, TARGETS, TARGET_IDS, publishTargetFor, resolveTargets, applyDefaultPdfStrictChecks, MANIFEST_FILENAMES, loadManifest, loadManifestWithPath, resolveConfig, resolveConfigForTarget, FriendlyHttpError, withFetchTimeout, loadManifestDoc, writeManifestDoc, npmRegistryUrl, ensureProjectGitignore, EXTENSIONS_DIR, BUILT_IN_STYLE_SET_IDS, listProjectExtensions, setExtensionEnabled, removeExtension, addExtension, checkExtensionUpdates, updateExtensions, uniqueExtensionId, addBuiltInStyleSet, restorePinnedExtensions, restoreFailureMessage, restoreOfflineMessage, restoreForCommand };
2029
+ 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 };
@@ -8,16 +8,17 @@ import {
8
8
  stampCreator,
9
9
  stripAnnotations,
10
10
  transparentPages
11
- } from "./cli-ccp7sxz3.js";
11
+ } from "./cli-evg8sbht.js";
12
12
  import {
13
13
  blackTextToGray
14
14
  } from "./cli-ht1bcqdd.js";
15
15
  import {
16
16
  MARGIN_BOX_IGNORED_PROPERTIES,
17
17
  extract,
18
+ getAssetPath,
18
19
  lastPageMode,
19
20
  resolvePage
20
- } from "./cli-dtckwda1.js";
21
+ } from "./cli-pz4bn98t.js";
21
22
  import {
22
23
  countLinkAnnotations,
23
24
  getImageResolutions,
@@ -42,20 +43,18 @@ import {
42
43
  import {
43
44
  hasUncommittedChanges,
44
45
  listWorkdirChanges
45
- } from "./cli-habkc5ng.js";
46
+ } from "./cli-a35w7wzg.js";
46
47
  import {
47
48
  detectProjectSource,
48
49
  repoSubPath
49
50
  } from "./cli-v5mp7a6q.js";
50
51
  import {
51
- EXTENSIONS_DIR,
52
52
  FriendlyHttpError,
53
53
  MANIFEST_FILENAMES,
54
54
  PRESETS,
55
55
  PRESET_IDS,
56
56
  TARGETS,
57
57
  TARGET_IDS,
58
- addBuiltInStyleSet,
59
58
  addExtension,
60
59
  applyDefaultPdfStrictChecks,
61
60
  ensureProjectGitignore,
@@ -71,7 +70,7 @@ import {
71
70
  uniqueExtensionId,
72
71
  withFetchTimeout,
73
72
  writeManifestDoc
74
- } from "./cli-dd115fa8.js";
73
+ } from "./cli-sdmv4bba.js";
75
74
  import {
76
75
  EXTENSION_MANIFEST_FILENAME,
77
76
  GUTTERPRESS_CSS,
@@ -92,7 +91,7 @@ import {
92
91
  readExtensionMeta,
93
92
  resolveDeclaredStyles,
94
93
  slugify
95
- } from "./cli-h17e3mtz.js";
94
+ } from "./cli-pwwfxdgb.js";
96
95
  import {
97
96
  PACKAGE_META,
98
97
  PACKAGE_VERSION,
@@ -102,22 +101,20 @@ import {
102
101
  log,
103
102
  setLogLevel,
104
103
  warn
105
- } from "./cli-b0vyjvmt.js";
104
+ } from "./cli-aqh6brew.js";
106
105
  import {
107
106
  DEBOUNCE,
108
107
  UsageError,
109
108
  resolvePort
110
109
  } from "./cli-d8xjns66.js";
111
- import {
112
- getAssetPath
113
- } from "./cli-fe43evmf.js";
114
110
  import {
115
111
  BuildError
116
112
  } from "./cli-46ycxe6r.js";
117
113
  import {
114
+ EXTENSIONS_DIR,
118
115
  resolveVendoredPluginInstallRoot,
119
116
  vendoredNpmPluginPackageDir
120
- } from "./cli-h3phdstf.js";
117
+ } from "./cli-y28j019d.js";
121
118
  import {
122
119
  __require
123
120
  } from "./cli-37x76zdn.js";
@@ -1818,13 +1815,7 @@ import { existsSync as existsSync3 } from "node:fs";
1818
1815
  import { readdir } from "node:fs/promises";
1819
1816
  import path from "node:path";
1820
1817
  var STYLES_SUBDIR = "styles";
1821
- var FALLBACK_PRIORITY = [
1822
- "styles/book.css",
1823
- "css/print.css",
1824
- "css/index.css",
1825
- "css/style.css",
1826
- "css/main.css"
1827
- ];
1818
+ var DEFAULT_STYLESHEET = "styles/book.css";
1828
1819
  function relDisplay(projectDir, absPath) {
1829
1820
  return path.relative(projectDir, absPath).split(path.sep).join("/");
1830
1821
  }
@@ -1862,10 +1853,8 @@ async function resolveActiveStyles(projectDir, manifestStyles) {
1862
1853
  const manifest = configured.filter((s) => typeof s === "string" && s.trim().length > 0);
1863
1854
  if (manifest.length > 0)
1864
1855
  return manifest;
1865
- for (const rel of FALLBACK_PRIORITY) {
1866
- if (existsSync3(path.join(projectDir, rel)))
1867
- return [rel];
1868
- }
1856
+ if (existsSync3(path.join(projectDir, DEFAULT_STYLESHEET)))
1857
+ return [DEFAULT_STYLESHEET];
1869
1858
  const discovered = await discoverCssFiles(projectDir);
1870
1859
  if (discovered.length > 0) {
1871
1860
  const first = discovered.map((abs) => relDisplay(projectDir, abs)).sort((a, b) => a.localeCompare(b))[0];
@@ -5217,7 +5206,7 @@ class PdfOutput {
5217
5206
  const rawPdf = pdfxMode ? path7.join(stage, "raw.pdf") : path7.resolve(pdfFile);
5218
5207
  await fsp2.mkdir(path7.dirname(path7.resolve(pdfFile)), { recursive: true });
5219
5208
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
5220
- const { buildNativePdf } = await import("./engine-892swzzc.js");
5209
+ const { buildNativePdf } = await import("./engine-mc5br8g8.js");
5221
5210
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
5222
5211
  title: config.title,
5223
5212
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -5597,7 +5586,7 @@ function isGeneratedProjectPath(relative) {
5597
5586
  const segment = segments[i];
5598
5587
  if (segment === "dist" || segment === "node_modules")
5599
5588
  return true;
5600
- if (segment === "plugins" && segments[i + 1] === "npm")
5589
+ if ((segment === "extensions" || segment === "plugins") && segments[i + 1] === "npm")
5601
5590
  return true;
5602
5591
  }
5603
5592
  return false;
@@ -6506,43 +6495,6 @@ async function startPreviewServer(options) {
6506
6495
  };
6507
6496
  }
6508
6497
 
6509
- // src/lib/project-templates.ts
6510
- import { isMap, isScalar, isSeq, parseDocument } from "yaml";
6511
- var BUILT_IN_TEMPLATE_IDS = [
6512
- "book",
6513
- "zine",
6514
- "technical",
6515
- "paper"
6516
- ];
6517
- var BUILT_IN_META = {
6518
- book: {
6519
- label: "Book",
6520
- description: "A clean starting point for a novel, memoir, or any long-form book."
6521
- },
6522
- zine: {
6523
- label: "Zine",
6524
- description: "A short, personal zine made to be printed, folded, and shared."
6525
- },
6526
- technical: {
6527
- label: "Technical document",
6528
- description: "A manual or guide with steps, reference tables, and code examples."
6529
- },
6530
- paper: {
6531
- label: "Academic paper",
6532
- description: "A journal-style article with LaTeX math, numbered equations, figures, tables, notes and references.",
6533
- look: "academic"
6534
- }
6535
- };
6536
- var SKIP_ENTRIES = new Set([
6537
- ".git",
6538
- "node_modules",
6539
- "out",
6540
- "dist",
6541
- "build",
6542
- ".gutterpress",
6543
- ".DS_Store"
6544
- ]);
6545
-
6546
6498
  // src/lib/project-scaffold.ts
6547
6499
  import { access, copyFile, cp, mkdir as mkdir4, readdir as readdir5, readFile as readFile22, stat as stat4, writeFile as writeFile5 } from "node:fs/promises";
6548
6500
  import { constants as FS, existsSync as existsSync7 } from "node:fs";
@@ -6550,10 +6502,10 @@ import path12 from "node:path";
6550
6502
 
6551
6503
  // src/lib/page-size.ts
6552
6504
  import postcss5 from "postcss";
6553
- import { isMap as isMap2, Scalar } from "yaml";
6505
+ import { isMap, Scalar } from "yaml";
6554
6506
  function writePageBounds(doc, page) {
6555
6507
  const tolerance = page.tolerance ?? numberAt(doc, ["page", "tolerance"]);
6556
- if (!isMap2(doc.get("page", true)))
6508
+ if (!isMap(doc.get("page", true)))
6557
6509
  doc.set("page", doc.createNode({}));
6558
6510
  doc.setIn(["page", "width"], page.width);
6559
6511
  doc.setIn(["page", "height"], page.height);
@@ -6571,11 +6523,10 @@ function starterOverridesCss() {
6571
6523
  * Your book's own styles.
6572
6524
  *
6573
6525
  * This file loads AFTER every extension, so any rule you add here overrides
6574
- * a look's defaults. A new book has no look until you add one (the paper
6575
- * template adds the academic look for you):
6576
- * gutterpress ext add clean-book <book> --look (a built-in look)
6577
- * gutterpress ext add <package> <book> (a look from npm)
6578
- * A book has one look at a time; adding another turns the current one off.
6526
+ * a look's defaults. A new book has no look until its template or you add
6527
+ * one:
6528
+ * gutterpress ext add academic <book> (a built-in look)
6529
+ * gutterpress ext add <package> <book> (a look from npm)
6579
6530
  */
6580
6531
  `;
6581
6532
  }
@@ -6596,9 +6547,8 @@ function escapeYamlScalar(value) {
6596
6547
  }
6597
6548
  var DEFAULT_AUTHOR = "Anonymous";
6598
6549
  function requirePreset(preset, customPage) {
6599
- if (!preset || !PRESET_IDS.includes(preset)) {
6600
- const got = preset ? ` (got "${preset}")` : "";
6601
- throw new CreateProjectErrorImpl("preset-required", `A preset is required to create a book${got}. Choose one of: ${PRESET_IDS.join(", ")}.`);
6550
+ if (!PRESET_IDS.includes(preset)) {
6551
+ throw new CreateProjectErrorImpl("preset-required", `Unknown preset "${preset}". Choose one of: ${PRESET_IDS.join(", ")}.`);
6602
6552
  }
6603
6553
  if (preset === "custom") {
6604
6554
  const missing = [];
@@ -6656,7 +6606,7 @@ async function scaffoldProject(options) {
6656
6606
  }
6657
6607
  const template = options.template ?? "book";
6658
6608
  const customTemplateDir = options.templateDir;
6659
- const preset = customTemplateDir && options.preset === undefined ? undefined : requirePreset(options.preset, options.customPage);
6609
+ const preset = options.preset === undefined ? undefined : requirePreset(options.preset, options.customPage);
6660
6610
  const targets = preset ? resolveScaffoldTargets(options.targets, preset) : undefined;
6661
6611
  try {
6662
6612
  await mkdir4(projectDir, { recursive: true });
@@ -6694,13 +6644,12 @@ async function scaffoldProject(options) {
6694
6644
  let openFile = path12.join(projectDir, "chapter-01.md");
6695
6645
  try {
6696
6646
  await fillTemplateFile(manifestPath, substitutions);
6697
- const look = customTemplateDir ? undefined : BUILT_IN_META[template].look;
6698
- if (look)
6699
- await addBuiltInStyleSet(projectDir, look);
6700
- if (preset) {
6647
+ if (preset || options.customPage) {
6701
6648
  const { doc, file } = await loadManifestDoc(projectDir);
6702
- doc.set("preset", preset);
6703
- doc.set("targets", doc.createNode(targets ?? []));
6649
+ if (preset) {
6650
+ doc.set("preset", preset);
6651
+ doc.set("targets", doc.createNode(targets ?? []));
6652
+ }
6704
6653
  if (options.customPage)
6705
6654
  writePageBounds(doc, options.customPage);
6706
6655
  await writeManifestDoc(file, doc);
@@ -6717,7 +6666,7 @@ async function scaffoldProject(options) {
6717
6666
  let versionHistoryError;
6718
6667
  if (requested === "local-git") {
6719
6668
  try {
6720
- const { providerFor } = await import("./source-provider-2r74a7wp.js");
6669
+ const { providerFor } = await import("./source-provider-knmcam50.js");
6721
6670
  const provider = providerFor({ type: "local-folder", path: projectDir });
6722
6671
  await provider.initVersionHistory({
6723
6672
  projectDir,
@@ -6769,7 +6718,7 @@ async function fillTemplateFile(filePath, substitutions) {
6769
6718
  import { access as access2, mkdir as mkdir5, readFile as readFile23, writeFile as writeFile6 } from "node:fs/promises";
6770
6719
  import { constants as FS2 } from "node:fs";
6771
6720
  import path13 from "node:path";
6772
- var EXTENSION_KINDS = ["plugin", "theme"];
6721
+ var EXTENSION_KINDS = ["plugin", "look"];
6773
6722
  var RESERVED_PREFIX = "gp-";
6774
6723
  var TEMPLATE_FILES = {
6775
6724
  plugin: [
@@ -6782,7 +6731,7 @@ var TEMPLATE_FILES = {
6782
6731
  ["test/expected.html", "test/expected.html"],
6783
6732
  ["test/plugin.test.js.tpl", "test/plugin.test.js"]
6784
6733
  ],
6785
- theme: [
6734
+ look: [
6786
6735
  ["package.json", "package.json"],
6787
6736
  ["README.md", "README.md"],
6788
6737
  ["components.yaml", "components.yaml"],
@@ -6797,11 +6746,11 @@ var TEMPLATE_FILES = {
6797
6746
  };
6798
6747
  var OPEN_FILE = {
6799
6748
  plugin: "plugin.js",
6800
- theme: "styles/tokens.css"
6749
+ look: "styles/tokens.css"
6801
6750
  };
6802
6751
  var DEFAULT_DESCRIPTION = {
6803
6752
  plugin: "A Gutterpress plugin.",
6804
- theme: "A Gutterpress theme."
6753
+ look: "A Gutterpress look."
6805
6754
  };
6806
6755
  var DEFAULT_AUTHOR2 = "Anonymous";
6807
6756
 
@@ -6833,7 +6782,7 @@ function escapeJsonScalar(value) {
6833
6782
  }
6834
6783
  async function scaffoldExtension(options) {
6835
6784
  const kind = options.kind;
6836
- if (kind !== "plugin" && kind !== "theme") {
6785
+ if (kind !== "plugin" && kind !== "look") {
6837
6786
  throw new ExtensionScaffoldError("invalid-name", `Unknown extension kind "${String(kind)}". Choose one of: ${EXTENSION_KINDS.join(", ")}.`);
6838
6787
  }
6839
6788
  const name = (options.name ?? "").trim();
@@ -6909,6 +6858,24 @@ function substitute(text, table) {
6909
6858
  return out;
6910
6859
  }
6911
6860
 
6861
+ // src/lib/project-templates.ts
6862
+ import { isMap as isMap2, isScalar, isSeq, parseDocument } from "yaml";
6863
+ var BUILT_IN_TEMPLATE_IDS = [
6864
+ "book",
6865
+ "zine",
6866
+ "technical",
6867
+ "paper"
6868
+ ];
6869
+ var SKIP_ENTRIES = new Set([
6870
+ ".git",
6871
+ "node_modules",
6872
+ "out",
6873
+ "dist",
6874
+ "build",
6875
+ ".gutterpress",
6876
+ ".DS_Store"
6877
+ ]);
6878
+
6912
6879
  // src/lib/extension-import.ts
6913
6880
  import { existsSync as existsSync8 } from "node:fs";
6914
6881
  import { cp as cp2, mkdir as mkdir6, mkdtemp, readFile as readFile24, rm, writeFile as writeFile7 } from "node:fs/promises";
@@ -9053,7 +9020,7 @@ async function resolvePublishProvider(id, book) {
9053
9020
  const ext = await bookExtensions(book);
9054
9021
  const npmEntry = ext.extensions.some((c) => !c.path && c.name === id);
9055
9022
  if (npmEntry) {
9056
- const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-w8xt4kbq.js");
9023
+ const { restoreForCommand: restoreForCommand2 } = await import("./extension-restore-g8jnmmzq.js");
9057
9024
  await restoreForCommand2(book.projectDir, { manifestPath: book.manifestPath, failFast: true });
9058
9025
  }
9059
9026
  const publishers = await discover(ext);
@@ -9091,7 +9058,7 @@ function infoFor(p) {
9091
9058
  }
9092
9059
  function pluginProvider(p) {
9093
9060
  let loading;
9094
- const load = () => loading ??= import("./plugins-9en8m9y0.js").then((m) => m.loadPublishFunction(p));
9061
+ const load = () => loading ??= import("./plugins-1sxe0dkz.js").then((m) => m.loadPublishFunction(p));
9095
9062
  return {
9096
9063
  info: infoFor(p),
9097
9064
  async authenticate() {
@@ -9578,4 +9545,4 @@ function normalizeProjectKey(projectDir) {
9578
9545
  }
9579
9546
  // src/lib/publish/manifest-publish.ts
9580
9547
  import { isMap as isMap4 } from "yaml";
9581
- export { reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, BUILT_IN_TEMPLATE_IDS, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
9548
+ export { reportMissingTools, executeValidation, executeAndReport, splitOutPath, runBuild, openPath, startPreviewServer, scaffoldProject, EXTENSION_KINDS, scaffoldExtension, BUILT_IN_TEMPLATE_IDS, importExtensionFromFile, importExtensionFromUrl, searchNpmExtensions, publishCredentialKey, publishConnectionStatus, listPublishAccounts, connectGoogleDrive, listBookPublishProviders, resolvePublishProvider, resolvePublishFormat, runPublish, connectPublishProvider, disconnectPublishCredential };
@@ -2,7 +2,8 @@
2
2
  import { lstat, readFile, realpath } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { exports as resolveExports } from "resolve.exports";
5
- var PLUGINS_DIR = "plugins";
5
+ var EXTENSIONS_DIR = "extensions";
6
+ var LEGACY_PLUGINS_DIR = "plugins";
6
7
  var VENDORED_NPM_DIR = "npm";
7
8
  var NPM_SEGMENT = /^[a-z0-9][a-z0-9._~-]*$/;
8
9
  var EXACT_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
@@ -51,7 +52,10 @@ function encodePathPart(value) {
51
52
  return encodeURIComponent(value).replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
52
53
  }
53
54
  function vendoredNpmPluginRoot(projectDir, name, version) {
54
- return path.join(projectDir, PLUGINS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
55
+ return path.join(projectDir, EXTENSIONS_DIR, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version));
56
+ }
57
+ function vendoredNpmPluginRoots(projectDir, name, version) {
58
+ return [EXTENSIONS_DIR, LEGACY_PLUGINS_DIR].map((parent) => path.join(projectDir, parent, VENDORED_NPM_DIR, encodePathPart(name), encodePathPart(version)));
55
59
  }
56
60
  function vendoredNpmPluginPackageDir(installRoot, name) {
57
61
  return path.join(installRoot, "node_modules", ...name.split("/"));
@@ -175,15 +179,20 @@ async function resolvePackageEntry(packageDir, packageJson) {
175
179
  }
176
180
  async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expectedVersion) {
177
181
  const requestedProjectRoot = path.resolve(projectDir);
178
- const requestedInstallRoot = vendoredNpmPluginRoot(requestedProjectRoot, expectedName, expectedVersion);
182
+ let requestedInstallRoot = "";
179
183
  let installInfo;
180
- try {
181
- installInfo = await lstat(requestedInstallRoot);
182
- } catch (error) {
183
- if (error.code === "ENOENT")
184
- return null;
185
- throw error;
184
+ for (const candidate of vendoredNpmPluginRoots(requestedProjectRoot, expectedName, expectedVersion)) {
185
+ requestedInstallRoot = candidate;
186
+ try {
187
+ installInfo = await lstat(requestedInstallRoot);
188
+ break;
189
+ } catch (error) {
190
+ if (error.code !== "ENOENT")
191
+ throw error;
192
+ }
186
193
  }
194
+ if (!installInfo)
195
+ return null;
187
196
  if (!installInfo.isDirectory() || installInfo.isSymbolicLink()) {
188
197
  throw new Error("Vendor install root is not a normal directory.");
189
198
  }
@@ -197,4 +206,4 @@ async function resolveVendoredPluginInstallRoot(projectDir, expectedName, expect
197
206
  return installRoot;
198
207
  }
199
208
 
200
- export { PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginPackageDir, toPosixPath, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot };
209
+ export { EXTENSIONS_DIR, LEGACY_PLUGINS_DIR, VENDORED_NPM_DIR, isValidNpmPackageName, parseNpmPluginSpec, isExactNpmVersion, vendoredNpmPluginRoot, vendoredNpmPluginRoots, vendoredNpmPluginPackageDir, assertWindowsSafeRelativePath, windowsPathKey, packageResolutionTargets, safePackageTarget, resolvePackageEntry, resolveVendoredPluginInstallRoot };