gutterpress 0.11.20-alpha.3 → 0.11.20-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 (36) hide show
  1. package/dist/api/index.js +2 -2
  2. package/dist/{build-a8ycsg80.js → build-epdcrm2c.js} +4 -4
  3. package/dist/{cli-9k2tb493.js → cli-0ey902sx.js} +90 -28
  4. package/dist/{cli-x4p99w8d.js → cli-61vd4ews.js} +83 -41
  5. package/dist/{cli-hn9pk0hp.js → cli-fmksazej.js} +1 -1
  6. package/dist/{cli-y9bcya7r.js → cli-h4tfmpg3.js} +2 -2
  7. package/dist/cli.js +12 -12
  8. package/dist/{doctor-wbjkrcpc.js → doctor-78es5scm.js} +2 -2
  9. package/dist/engine/compiler/build.d.ts +1 -1
  10. package/dist/engine/shared/margin-box-support.d.ts +2 -2
  11. package/dist/engine/shared/pin-edges.d.ts +127 -0
  12. package/dist/engine/shared/print-area.d.ts +68 -0
  13. package/dist/{engine-yxnnp3hx.js → engine-67f68bxs.js} +1 -1
  14. package/dist/{engine-fdtv01a2.js → engine-7ha27n2b.js} +2 -2
  15. package/dist/{ext-pesyrs68.js → ext-9mc7sr0d.js} +4 -4
  16. package/dist/{gutterpress-agent-cazqstr1.js → gutterpress-agent-mrdnwktc.js} +78 -13
  17. package/dist/{gutterpress-viewer-1yse8cx3.js → gutterpress-viewer-jr30yw8t.js} +143 -52
  18. package/dist/{index-r87y7wzw.js → index-ad95fws8.js} +82 -40
  19. package/dist/{index-zagagnwq.js → index-gm5xbx5w.js} +3 -3
  20. package/dist/{index-tjvrchxj.js → index-t13p8y08.js} +199 -137
  21. package/dist/index.js +3 -3
  22. package/dist/lib/extension-manifest.d.ts +13 -0
  23. package/dist/lib/markdown/assemble.d.ts +12 -2
  24. package/dist/lib/markdown/gutterpress-css.d.ts +46 -30
  25. package/dist/lib/markdown/index.d.ts +5 -0
  26. package/dist/{manifest.schema-c9x8912a.json → manifest.schema-13dfax80.json} +3 -3
  27. package/dist/{new-8yah8z91.js → new-s4f0tnya.js} +4 -4
  28. package/dist/{preflight-8ccx4kdc.js → preflight-7ck4rbsq.js} +4 -4
  29. package/dist/preview/file-watcher.d.ts +4 -10
  30. package/dist/{preview-w7v9w36q.js → preview-j91sxadd.js} +4 -4
  31. package/dist/{publish-g5nh5myh.js → publish-f12xaenm.js} +4 -4
  32. package/dist/render.js +57 -6
  33. package/dist/schema/manifest.types.d.ts +8 -6
  34. package/dist/{validate-1yh59x0x.js → validate-pb5gysvr.js} +4 -4
  35. package/package.json +1 -1
  36. package/dist/engine/shared/flush.d.ts +0 -59
@@ -9,7 +9,7 @@ import {
9
9
  getAssetsDir,
10
10
  lastPageMode,
11
11
  parseSize
12
- } from "./index-zagagnwq.js";
12
+ } from "./index-gm5xbx5w.js";
13
13
  import {
14
14
  countLinkAnnotations,
15
15
  getImageResolutions,
@@ -1989,7 +1989,9 @@ var GUTTERPRESS_CSS = `
1989
1989
  cap at equal specificity */
1990
1990
  .gp-small { width: 25%; max-width: 100%; }
1991
1991
  .gp-medium { width: 50%; max-width: 100%; }
1992
- .gp-large { width: 75%; max-width: 100%; }
1992
+ .gp-large { width: 60%; max-width: 100%; }
1993
+ .gp-xl { width: 80%; max-width: 100%; }
1994
+ .gp-xxl { width: 90%; max-width: 100%; }
1993
1995
 
1994
1996
  /* float clearance presets — consumed by var(--gp-gap) in the float rules
1995
1997
  above and by .gp-shape's shape-margin below; --gp-gap itself is
@@ -2047,10 +2049,16 @@ img.gp-shape {
2047
2049
  }
2048
2050
 
2049
2051
  /* pin — within the nearest positioned ancestor (.page/.spread, rule above).
2050
- inset:0 and the explicit centers are load-bearing; see doctrine comment. */
2052
+ The four insets are the pin box: 0 (the text block's edge) unless an
2053
+ engine set --gp-pin-<edge> on the page root for an edge a pin uses (the
2054
+ art-safe line). Non-auto insets and the explicit centers are
2055
+ load-bearing; see doctrine comment. */
2051
2056
  .gp-pin {
2052
2057
  position: absolute;
2053
- inset: 0;
2058
+ top: var(--gp-pin-top, 0px);
2059
+ right: var(--gp-pin-right, 0px);
2060
+ bottom: var(--gp-pin-bottom, 0px);
2061
+ left: var(--gp-pin-left, 0px);
2054
2062
  align-self: center;
2055
2063
  justify-self: center;
2056
2064
  margin: 0;
@@ -2065,6 +2073,16 @@ img.gp-shape {
2065
2073
  .gp-left { justify-self: start; }
2066
2074
  .gp-right { justify-self: end; }
2067
2075
 
2076
+ /* the escape hatch — the paper's edge, past the printer's safe area: the
2077
+ pin box loses its art-safe insets. Set on the pin itself, so it beats the
2078
+ values the page root hands down. */
2079
+ .gp-pin.gp-flush {
2080
+ --gp-pin-top: 0px;
2081
+ --gp-pin-right: 0px;
2082
+ --gp-pin-bottom: 0px;
2083
+ --gp-pin-left: 0px;
2084
+ }
2085
+
2068
2086
  /* wrapper-margin neutralization (same pattern and rationale as the
2069
2087
  .gp-bleed paragraph-margin note in the doctrine comment; for pin, the
2070
2088
  emptied paragraph would otherwise leave a phantom margin gap in flow) */
@@ -2174,6 +2192,8 @@ var GP_CLASSES = new Set([
2174
2192
  "gp-small",
2175
2193
  "gp-medium",
2176
2194
  "gp-large",
2195
+ "gp-xl",
2196
+ "gp-xxl",
2177
2197
  "gp-tight",
2178
2198
  "gp-loose",
2179
2199
  "gp-columns",
@@ -2190,6 +2210,7 @@ var GP_CLASSES = new Set([
2190
2210
  "gp-pin",
2191
2211
  "gp-top",
2192
2212
  "gp-bottom",
2213
+ "gp-flush",
2193
2214
  "gp-behind",
2194
2215
  "gp-base",
2195
2216
  "gp-raised",
@@ -2203,8 +2224,7 @@ var GP_CLASSES = new Set([
2203
2224
  "gp-alert-caution",
2204
2225
  "gp-page-break",
2205
2226
  "gp-column-break",
2206
- "gp-continued",
2207
- "gp-flush"
2227
+ "gp-continued"
2208
2228
  ]);
2209
2229
 
2210
2230
  // src/lib/markdown/table-align.ts
@@ -2331,6 +2351,21 @@ function hasPinClass(token) {
2331
2351
  const cls = token.attrGet && token.attrGet("class");
2332
2352
  return typeof cls === "string" && cls.split(/\s+/).includes("gp-pin");
2333
2353
  }
2354
+ var PIN_EDGES = ["gp-top", "gp-right", "gp-bottom", "gp-left"];
2355
+ var FLUSH_ART_MSG = "`.gp-flush` puts this image on the paper's edge, past the printer's safe area. Printers trim up to 0.125in there, so keep it for art that is meant to bleed off the page.";
2356
+ function checkFlushArt(token, env, line) {
2357
+ if (token.type !== "image")
2358
+ return;
2359
+ const cls = token.attrGet && token.attrGet("class");
2360
+ if (typeof cls !== "string")
2361
+ return;
2362
+ const classes = cls.split(/\s+/);
2363
+ if (!classes.includes("gp-pin") || !classes.includes("gp-flush"))
2364
+ return;
2365
+ if (!classes.some((c) => PIN_EDGES.includes(c)))
2366
+ return;
2367
+ warn3(env, line, "flush_art", FLUSH_ART_MSG);
2368
+ }
2334
2369
  function elementNameFor(token) {
2335
2370
  switch (token.type) {
2336
2371
  case "layout_chapter_open":
@@ -2390,6 +2425,7 @@ function gpPinScope(md) {
2390
2425
  if (token.type === "inline" && token.children) {
2391
2426
  for (const child of token.children) {
2392
2427
  checkUnknownGpClasses(child, state.env, line);
2428
+ checkFlushArt(child, state.env, line);
2393
2429
  }
2394
2430
  }
2395
2431
  }
@@ -3141,6 +3177,20 @@ function layerSlug(name) {
3141
3177
  return /^[a-z]/.test(slug) ? slug : `x${slug}`;
3142
3178
  }
3143
3179
 
3180
+ // src/engine/shared/print-area.ts
3181
+ function printAreaMeta(page) {
3182
+ const area = JSON.stringify({
3183
+ width: page.width,
3184
+ height: page.height,
3185
+ bleed: page.bleed ?? 0,
3186
+ safe: page.safe ?? 0,
3187
+ artSafe: page.artSafe,
3188
+ preset: page.preset,
3189
+ spec: page.spec
3190
+ });
3191
+ return `<meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>`;
3192
+ }
3193
+
3144
3194
  // src/lib/markdown/assemble.ts
3145
3195
  async function assembleBookHtml(opts) {
3146
3196
  const title = opts.title ?? "Document";
@@ -3206,7 +3256,8 @@ ${projectCss.trim()}` : null
3206
3256
  <html lang="en"${printAttrs}>
3207
3257
  <head>
3208
3258
  <meta charset="UTF-8">
3209
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
3259
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">${opts.printArea ? `
3260
+ ${printAreaMeta(opts.printArea)}` : ""}
3210
3261
  <title>${title}</title>${preloadTags}
3211
3262
  <style data-project-css>
3212
3263
  ${inlineCss}
@@ -3663,6 +3714,7 @@ ${css.trim()}
3663
3714
  preloadImages: cssAssetCopies.map((c) => c.to),
3664
3715
  title: opts.title,
3665
3716
  print: opts.print,
3717
+ printArea: opts.printArea,
3666
3718
  plugins: opts.plugins,
3667
3719
  pluginCss,
3668
3720
  annotateSourceChapters: opts.annotateSourceChapters,
@@ -3675,6 +3727,7 @@ async function renderChaptersToFile(inputDir, outDir, opts = {}) {
3675
3727
  const html = await renderChapters(inputDir, {
3676
3728
  title: opts.title,
3677
3729
  print: opts.print,
3730
+ printArea: opts.printArea,
3678
3731
  styles: opts.styles,
3679
3732
  files: opts.files,
3680
3733
  plugins: opts.plugins,
@@ -3756,6 +3809,118 @@ function resolveDeclaredStyles(rawStyles, baseDir, subject) {
3756
3809
  // src/lib/extension-manifest.ts
3757
3810
  import { readFile as readFile4 } from "node:fs/promises";
3758
3811
  import path3 from "node:path";
3812
+ import { satisfies, validRange } from "semver";
3813
+ // package.json
3814
+ var package_default = {
3815
+ name: "gutterpress",
3816
+ version: "0.11.20-alpha.4",
3817
+ description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
3818
+ author: "itlackey",
3819
+ license: "MPL-2.0",
3820
+ keywords: [
3821
+ "markdown",
3822
+ "pdf",
3823
+ "print",
3824
+ "ghostscript",
3825
+ "book",
3826
+ "publishing",
3827
+ "zine"
3828
+ ],
3829
+ repository: {
3830
+ type: "git",
3831
+ url: "https://github.com/dimm-city/gutterpress.git"
3832
+ },
3833
+ homepage: "https://github.com/dimm-city/gutterpress#readme",
3834
+ bugs: {
3835
+ url: "https://github.com/dimm-city/gutterpress/issues"
3836
+ },
3837
+ type: "module",
3838
+ main: "./dist/index.js",
3839
+ types: "./dist/index.d.ts",
3840
+ bin: {
3841
+ gutterpress: "./dist/cli.js"
3842
+ },
3843
+ exports: {
3844
+ ".": {
3845
+ types: "./dist/index.d.ts",
3846
+ default: "./dist/index.js"
3847
+ },
3848
+ "./api": {
3849
+ types: "./dist/api/index.d.ts",
3850
+ default: "./dist/api/index.js"
3851
+ },
3852
+ "./render": {
3853
+ types: "./dist/render.d.ts",
3854
+ default: "./dist/render.js"
3855
+ }
3856
+ },
3857
+ files: [
3858
+ "dist/",
3859
+ "README.md",
3860
+ "LICENSE"
3861
+ ],
3862
+ scripts: {
3863
+ "build:library": "bun scripts/build-engine-bundles.mjs && rimraf dist && bun build src/index.ts src/api/index.ts --target=node --packages=external --splitting --outdir=dist && bun build src/render.ts --target=node --packages=external --outdir=dist && node scripts/check-render-pure.mjs && tsc -p tsconfig.build.json",
3864
+ build: "bun run build:library && bun build src/cli.ts --target=node --packages=external --splitting --outdir=dist",
3865
+ prepublishOnly: "bun run build",
3866
+ test: "bun test",
3867
+ typecheck: "tsc --noEmit",
3868
+ "typecheck:engine-browser": "tsc --noEmit -p src/engine/browser.tsconfig.json",
3869
+ "parity:gate": "bun scripts/native-parity-gate.ts"
3870
+ },
3871
+ engines: {
3872
+ bun: ">=1.0.0",
3873
+ node: ">=22.0.0"
3874
+ },
3875
+ dependencies: {
3876
+ chokidar: "^5.0.0",
3877
+ citty: "^0.2.0",
3878
+ diff3: "0.0.3",
3879
+ fflate: "0.8.3",
3880
+ glob: "^13.0.0",
3881
+ htmlhint: "^1.9.2",
3882
+ ignore: "^5.3.2",
3883
+ "isomorphic-git": "^1.38.4",
3884
+ "markdown-it": "^14.3.2",
3885
+ "markdown-it-abbr": "^2.0.0",
3886
+ "markdown-it-attrs": "^4.3.0",
3887
+ "markdown-it-deflist": "^3.0.1",
3888
+ "markdown-it-footnote": "^4.0.0",
3889
+ "markdown-it-mark": "^4.0.0",
3890
+ "markdown-it-source-map": "^0.1.1",
3891
+ "markdown-it-sub": "^2.0.0",
3892
+ "markdown-it-sup": "^2.0.0",
3893
+ markdownlint: "^0.40.0",
3894
+ "pdf-lib": "^1.17.1",
3895
+ postcss: "^8.5.18",
3896
+ "resolve.exports": "2.0.3",
3897
+ semver: "7.8.0",
3898
+ tar: "7.5.22",
3899
+ unpdf: "^1.6.2",
3900
+ ws: "^8.21.0",
3901
+ yaml: "^2.8.2"
3902
+ },
3903
+ devDependencies: {
3904
+ "@types/bun": "1.3.14",
3905
+ "@types/markdown-it": "^14.1.2",
3906
+ "@types/node": "^22.0.0",
3907
+ "@types/semver": "^7.7.1",
3908
+ "@types/ws": "^8.5.13",
3909
+ playwright: "^1.60.0",
3910
+ rimraf: "^6.0.0",
3911
+ typescript: "^5"
3912
+ }
3913
+ };
3914
+
3915
+ // src/lib/version.ts
3916
+ var PACKAGE_META = {
3917
+ version: package_default.version ?? "unknown",
3918
+ dependencies: package_default.dependencies ?? {},
3919
+ devDependencies: package_default.devDependencies ?? {}
3920
+ };
3921
+ var PACKAGE_VERSION = PACKAGE_META.version;
3922
+
3923
+ // src/lib/extension-manifest.ts
3759
3924
  var EXTENSION_MANIFEST_FILENAME = "package.json";
3760
3925
  var GUTTERPRESS_KEY = "gutterpress";
3761
3926
  function object(value) {
@@ -3779,6 +3944,7 @@ function metaFromPackageJson(raw) {
3779
3944
  ...str(pkg.description) ? { description: str(pkg.description) } : {},
3780
3945
  ...keywords ? { keywords } : {},
3781
3946
  ...str(pkg.main) ? { main: str(pkg.main) } : {},
3947
+ ...str(object(pkg.engines)?.gutterpress) ? { gutterpressRange: str(object(pkg.engines)?.gutterpress) } : {},
3782
3948
  ..."preview" in gp ? { preview: str(gp.preview) ?? null } : {},
3783
3949
  ...Array.isArray(gp.styles) ? { styles: declaredList(gp.styles) } : {},
3784
3950
  ...str(gp.tokensFile) ? { tokensFile: str(gp.tokensFile) } : {},
@@ -3815,6 +3981,17 @@ function extensionCarries(meta) {
3815
3981
  function pathEscapesFolder(rel) {
3816
3982
  return path3.isAbsolute(rel) || rel.split(/[\\/]/).includes("..");
3817
3983
  }
3984
+ function assertSupportsThisGutterpress(meta, ref) {
3985
+ const range = meta.gutterpressRange;
3986
+ if (range === undefined)
3987
+ return;
3988
+ if (!validRange(range)) {
3989
+ throw new Error(`Extension "${ref}" has an invalid engines.gutterpress range "${range}".`);
3990
+ }
3991
+ if (!satisfies(PACKAGE_VERSION, range, { includePrerelease: true })) {
3992
+ throw new Error(`Extension "${ref}" needs Gutterpress ${range} (its package.json engines.gutterpress); ` + `this is Gutterpress ${PACKAGE_VERSION}. Update Gutterpress, or pin an older version of the extension.`);
3993
+ }
3994
+ }
3818
3995
  function assertExtensionContained(meta) {
3819
3996
  if (meta.engineStyles !== undefined) {
3820
3997
  throw new Error("The extension's metadata declares `engineStyles`, which was removed — " + "move its entries to the end of `styles`.");
@@ -4103,6 +4280,7 @@ This folder still has a ${stale}. Gutterpress no longer reads it — ` + `descri
4103
4280
  async function loadExtensionFromDir(extensionDir, config, pluginRef) {
4104
4281
  const meta = await readExtensionMeta(extensionDir);
4105
4282
  assertExtensionContained(meta);
4283
+ assertSupportsThisGutterpress(meta, pluginRef);
4106
4284
  const resolved = resolveExtension(extensionDir, meta, `Plugin "${pluginRef}"`);
4107
4285
  const extensionStyles = resolved.styles ?? [];
4108
4286
  const name = config.name ?? meta.name ?? pluginRef;
@@ -4177,6 +4355,7 @@ async function loadPlugin(config, baseDir) {
4177
4355
  if (loaded.packageDir) {
4178
4356
  const meta = await readExtensionMeta(loaded.packageDir);
4179
4357
  assertExtensionContained(meta);
4358
+ assertSupportsThisGutterpress(meta, pluginRef);
4180
4359
  const resolved = resolveExtension(loaded.packageDir, { ...meta, main: undefined }, `Plugin "${pluginRef}"`);
4181
4360
  if (resolved.markdown && loaded.entryPath && resolve3(resolved.markdown) !== resolve3(loaded.entryPath)) {
4182
4361
  throw new Error(`its package.json declares gutterpress.markdown "${meta.markdown}" but the package entry is "${relative(loaded.packageDir, loaded.entryPath)}" — an npm extension's ` + "markdown-it plugin is its package entry, so declare that file or drop the field.");
@@ -4307,7 +4486,7 @@ import path5 from "node:path";
4307
4486
  import { Transform } from "node:stream";
4308
4487
  import { pipeline } from "node:stream/promises";
4309
4488
  import { createGunzip } from "node:zlib";
4310
- import { maxSatisfying, satisfies, validRange } from "semver";
4489
+ import { maxSatisfying, satisfies as satisfies2, validRange as validRange2 } from "semver";
4311
4490
 
4312
4491
  // src/lib/npm-registry.ts
4313
4492
  var DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org";
@@ -4515,7 +4694,7 @@ async function selectPackage(ctx, name, selector) {
4515
4694
  version = selector;
4516
4695
  } else if (typeof tags[selector] === "string") {
4517
4696
  version = tags[selector];
4518
- } else if (validRange(selector)) {
4697
+ } else if (validRange2(selector)) {
4519
4698
  version = maxSatisfying(Object.keys(versions), selector) ?? undefined;
4520
4699
  }
4521
4700
  if (!version || !isExactNpmVersion(version)) {
@@ -4733,7 +4912,7 @@ async function installPackage(ctx, name, selector, parentPackagePath, ancestors,
4733
4912
  }
4734
4913
  throw error2;
4735
4914
  }
4736
- const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ? satisfies(candidate.version, selector) : false)));
4915
+ const ancestor = ancestors.find((candidate) => candidate.name === name && (candidate.version === selected.version || (selector ? satisfies2(candidate.version, selector) : false)));
4737
4916
  if (ancestor)
4738
4917
  return { path: ancestor.path };
4739
4918
  if (ctx.records.length >= ctx.limits.totalPackages) {
@@ -6140,117 +6319,6 @@ import { mkdir as mkdir5, readFile as readFile10, writeFile as writeFile4 } from
6140
6319
  import { createHash as createHash3 } from "node:crypto";
6141
6320
  import path10 from "node:path";
6142
6321
  import git from "isomorphic-git";
6143
- // package.json
6144
- var package_default = {
6145
- name: "gutterpress",
6146
- version: "0.11.20-alpha.3",
6147
- description: "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
6148
- author: "itlackey",
6149
- license: "MPL-2.0",
6150
- keywords: [
6151
- "markdown",
6152
- "pdf",
6153
- "print",
6154
- "ghostscript",
6155
- "book",
6156
- "publishing",
6157
- "zine"
6158
- ],
6159
- repository: {
6160
- type: "git",
6161
- url: "https://github.com/dimm-city/gutterpress.git"
6162
- },
6163
- homepage: "https://github.com/dimm-city/gutterpress#readme",
6164
- bugs: {
6165
- url: "https://github.com/dimm-city/gutterpress/issues"
6166
- },
6167
- type: "module",
6168
- main: "./dist/index.js",
6169
- types: "./dist/index.d.ts",
6170
- bin: {
6171
- gutterpress: "./dist/cli.js"
6172
- },
6173
- exports: {
6174
- ".": {
6175
- types: "./dist/index.d.ts",
6176
- default: "./dist/index.js"
6177
- },
6178
- "./api": {
6179
- types: "./dist/api/index.d.ts",
6180
- default: "./dist/api/index.js"
6181
- },
6182
- "./render": {
6183
- types: "./dist/render.d.ts",
6184
- default: "./dist/render.js"
6185
- }
6186
- },
6187
- files: [
6188
- "dist/",
6189
- "README.md",
6190
- "LICENSE"
6191
- ],
6192
- scripts: {
6193
- "build:library": "bun scripts/build-engine-bundles.mjs && rimraf dist && bun build src/index.ts src/api/index.ts --target=node --packages=external --splitting --outdir=dist && bun build src/render.ts --target=node --packages=external --outdir=dist && node scripts/check-render-pure.mjs && tsc -p tsconfig.build.json",
6194
- build: "bun run build:library && bun build src/cli.ts --target=node --packages=external --splitting --outdir=dist",
6195
- prepublishOnly: "bun run build",
6196
- test: "bun test",
6197
- typecheck: "tsc --noEmit",
6198
- "typecheck:engine-browser": "tsc --noEmit -p src/engine/browser.tsconfig.json",
6199
- "parity:gate": "bun scripts/native-parity-gate.ts"
6200
- },
6201
- engines: {
6202
- bun: ">=1.0.0",
6203
- node: ">=22.0.0"
6204
- },
6205
- dependencies: {
6206
- chokidar: "^5.0.0",
6207
- citty: "^0.2.0",
6208
- diff3: "0.0.3",
6209
- fflate: "0.8.3",
6210
- glob: "^13.0.0",
6211
- htmlhint: "^1.9.2",
6212
- ignore: "^5.3.2",
6213
- "isomorphic-git": "^1.38.4",
6214
- "markdown-it": "^14.3.2",
6215
- "markdown-it-abbr": "^2.0.0",
6216
- "markdown-it-attrs": "^4.3.0",
6217
- "markdown-it-deflist": "^3.0.1",
6218
- "markdown-it-footnote": "^4.0.0",
6219
- "markdown-it-mark": "^4.0.0",
6220
- "markdown-it-source-map": "^0.1.1",
6221
- "markdown-it-sub": "^2.0.0",
6222
- "markdown-it-sup": "^2.0.0",
6223
- markdownlint: "^0.40.0",
6224
- "pdf-lib": "^1.17.1",
6225
- postcss: "^8.5.18",
6226
- "resolve.exports": "2.0.3",
6227
- semver: "7.8.0",
6228
- tar: "7.5.22",
6229
- unpdf: "^1.6.2",
6230
- ws: "^8.21.0",
6231
- yaml: "^2.8.2"
6232
- },
6233
- devDependencies: {
6234
- "@types/bun": "1.3.14",
6235
- "@types/markdown-it": "^14.1.2",
6236
- "@types/node": "^22.0.0",
6237
- "@types/semver": "^7.7.1",
6238
- "@types/ws": "^8.5.13",
6239
- playwright: "^1.60.0",
6240
- rimraf: "^6.0.0",
6241
- typescript: "^5"
6242
- }
6243
- };
6244
-
6245
- // src/lib/version.ts
6246
- var PACKAGE_META = {
6247
- version: package_default.version ?? "unknown",
6248
- dependencies: package_default.dependencies ?? {},
6249
- devDependencies: package_default.devDependencies ?? {}
6250
- };
6251
- var PACKAGE_VERSION = PACKAGE_META.version;
6252
-
6253
- // src/lib/build-fingerprint.ts
6254
6322
  var FINGERPRINT_FILENAME = "build-fingerprint.json";
6255
6323
  var VERSION_TIMEOUT_MS = 4000;
6256
6324
  function toJsonValue(value) {
@@ -10646,6 +10714,7 @@ async function renderBook(ctx) {
10646
10714
  const htmlFile = await renderChaptersToFile(renderDir, workDir, {
10647
10715
  title: config.title,
10648
10716
  print: config.print,
10717
+ printArea: { ...config.page, preset: config.preset, spec: config.spec },
10649
10718
  styles: config.styles,
10650
10719
  files: config.source.files,
10651
10720
  plugins,
@@ -10798,7 +10867,7 @@ class PdfOutput {
10798
10867
  const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
10799
10868
  await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
10800
10869
  log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
10801
- const { buildNativePdf } = await import("./engine-fdtv01a2.js");
10870
+ const { buildNativePdf } = await import("./engine-7ha27n2b.js");
10802
10871
  const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
10803
10872
  title: config.title,
10804
10873
  author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
@@ -11072,6 +11141,7 @@ async function renderPreviewBook(inputPath, config, opts) {
11072
11141
  return renderChapters(inputPath, {
11073
11142
  title: config.title ?? "Document",
11074
11143
  print: config.print,
11144
+ printArea: config.printArea,
11075
11145
  styles: config.styles,
11076
11146
  files: opts.files,
11077
11147
  plugins,
@@ -11086,19 +11156,8 @@ async function renderPreviewBook(inputPath, config, opts) {
11086
11156
  }
11087
11157
  });
11088
11158
  }
11089
- function injectPreviewScripts(html, page) {
11090
- const area = page ? JSON.stringify({
11091
- width: page.width,
11092
- height: page.height,
11093
- bleed: page.bleed ?? 0,
11094
- safe: page.safe ?? 0,
11095
- artSafe: page.artSafe,
11096
- preset: page.preset,
11097
- spec: page.spec
11098
- }) : "";
11099
- const meta = area ? ` <meta name="gp-print-area" content='${area.replace(/&/g, "&amp;").replace(/'/g, "&#39;")}'>
11100
- ` : "";
11101
- const scripts = meta + ` <script src="/engine/gutterpress-viewer.js"></script>
11159
+ function injectPreviewScripts(html) {
11160
+ const scripts = ` <script src="/engine/gutterpress-viewer.js"></script>
11102
11161
  ` + `<script src="/preview/scripts/preview-interface.js"></script>
11103
11162
  ` + `<script src="/preview/scripts/preview-bridge.js"></script>
11104
11163
  `;
@@ -11113,7 +11172,10 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
11113
11172
  }
11114
11173
  const nextAssets = new Map;
11115
11174
  const nextImageRefs = new Set;
11116
- const html = await renderPreviewBook(inputPath, config, {
11175
+ const html = await renderPreviewBook(inputPath, {
11176
+ ...config,
11177
+ printArea: config.page && { ...config.page, preset: config.preset, spec: config.spec }
11178
+ }, {
11117
11179
  files: config.source?.files ?? null,
11118
11180
  onCssAssets: (copies) => {
11119
11181
  for (const copy of copies)
@@ -11132,7 +11194,7 @@ async function generateAndWriteHtml(inputPath, tempDir, config, cssAssets, image
11132
11194
  for (const ref of nextImageRefs)
11133
11195
  imageRefs.add(ref);
11134
11196
  }
11135
- await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html, config.page && { ...config.page, preset: config.preset, spec: config.spec }), "utf-8");
11197
+ await fsp3.writeFile(path15.join(tempDir, BOOK_HTML_FILENAME), injectPreviewScripts(html), "utf-8");
11136
11198
  }
11137
11199
  function isDotPathUnderRoot(candidatePath, root) {
11138
11200
  const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "");
package/dist/index.js CHANGED
@@ -154,14 +154,14 @@ import {
154
154
  testRemoteAccess,
155
155
  updateExtensions,
156
156
  validateProjectExtensions
157
- } from "./index-tjvrchxj.js";
157
+ } from "./index-t13p8y08.js";
158
158
  import"./index-ht1bcqdd.js";
159
159
  import {
160
160
  BUILD_DIAGNOSTIC_CODES
161
- } from "./index-r87y7wzw.js";
161
+ } from "./index-ad95fws8.js";
162
162
  import {
163
163
  BuildError
164
- } from "./index-zagagnwq.js";
164
+ } from "./index-gm5xbx5w.js";
165
165
  import"./index-sth219v1.js";
166
166
  import {
167
167
  DEFAULT_PRINT_OPTS,
@@ -70,6 +70,12 @@ export interface ExtensionMetadata {
70
70
  * does the merge.
71
71
  */
72
72
  snippets?: string;
73
+ /**
74
+ * npm `engines.gutterpress` — the Gutterpress versions the extension works
75
+ * with, as a semver range (e.g. `">=0.11.20-alpha.3"`). Checked when the
76
+ * extension loads ({@link assertSupportsThisGutterpress}).
77
+ */
78
+ gutterpressRange?: string;
73
79
  /**
74
80
  * The removed `engineStyles` field (#266), carried through from the
75
81
  * `gutterpress` block ONLY so {@link assertExtensionContained} can reject it
@@ -119,6 +125,13 @@ export declare function extensionCarries(meta: ExtensionMetadata): ExtensionCarr
119
125
  * doctrine {@link readExtensionMeta}'s tolerant JSON parse already follows).
120
126
  */
121
127
  export declare function pathEscapesFolder(rel: string): boolean;
128
+ /**
129
+ * Refuse to load an extension whose `engines.gutterpress` range excludes this
130
+ * Gutterpress, so a book built with too old a version fails with the fix
131
+ * instead of quietly printing differently. Prereleases count: 0.12.0-alpha.1
132
+ * satisfies `>=0.11.20`.
133
+ */
134
+ export declare function assertSupportsThisGutterpress(meta: ExtensionMetadata, ref: string): void;
122
135
  /**
123
136
  * Every path an extension declares must live INSIDE its own folder — an
124
137
  * extension is self-contained by contract (apply/install copies the whole
@@ -1,5 +1,6 @@
1
1
  import { type LoadedPlugin } from "./renderer";
2
2
  import { type PrintPadding } from "../../engine/shared/synthesis.ts";
3
+ import { type PrintAreaSource } from "../../engine/shared/print-area.ts";
3
4
  /** Reader injected by the host: resolve a project-root-relative file → its text. */
4
5
  export type ReadText = (relPath: string) => Promise<string>;
5
6
  /**
@@ -9,8 +10,8 @@ export type ReadText = (relPath: string) => Promise<string>;
9
10
  * `type`s (`ambiguous_marker_token`, `unrecognized_marker_token`,
10
11
  * `extra_bare_marker_token`, `unknown_marker`, `nested_spread`,
11
12
  * `continue_without_section`, `spread_without_pages`, `spread_eof_close`,
12
- * `page_outside_spread`, `pin_outside_page`, `unknown_gp_class` — the last
13
- * emitted by `gp-pin-scope.js`'s `gp_pin_scope_check`, same as
13
+ * `page_outside_spread`, `pin_outside_page`, `unknown_gp_class`, `flush_art` —
14
+ * the last two emitted by `gp-pin-scope.js`'s `gp_pin_scope_check`, same as
14
15
  * `pin_outside_page`, see #226).
15
16
  *
16
17
  * #240 (declarative container components) adds three more, all emitted by
@@ -126,6 +127,15 @@ export interface AssembleBookHtmlOptions {
126
127
  * engine/shared/synthesis.ts) and the two page counts agree.
127
128
  */
128
129
  print?: PrintPadding;
130
+ /**
131
+ * The project's print area — the resolved manifest's `page` block plus its
132
+ * preset id and printer spec — written as `<meta name="gp-print-area">`
133
+ * (engine/shared/print-area.ts) into EVERY assembled book, preview, build
134
+ * and publish alike: the preview's print overlays judge the ink against it,
135
+ * and both engines place pinned art on its art-safe line. Metadata only:
136
+ * nothing renders from it but what reads it.
137
+ */
138
+ printArea?: PrintAreaSource;
129
139
  plugins?: LoadedPlugin[];
130
140
  pluginCss?: string;
131
141
  /** Add a layout-neutral source-file id to source-mapped preview blocks. */