gutterpress 0.11.23-alpha.2 → 0.11.23-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +10 -13
  2. package/dist/{README-8ef336pp.md → README-syg2hn83.md} +13 -19
  3. package/dist/api/index.d.ts +1 -1
  4. package/dist/api/index.js +5 -5
  5. package/dist/{build-encgnv0c.js → build-c48eer6f.js} +8 -8
  6. package/dist/checks/asset/extensions.d.ts +1 -1
  7. package/dist/checks/asset/index.d.ts +0 -1
  8. package/dist/checks/policy.d.ts +5 -4
  9. package/dist/checks/source/config-file.d.ts +9 -6
  10. package/dist/checks/source/dangling-links.d.ts +1 -1
  11. package/dist/checks/types.d.ts +2 -2
  12. package/dist/{cli-pz4bn98t.js → cli-54xt7pjg.js} +19 -13
  13. package/dist/{index-2ppw6q9v.js → cli-5crpmz42.js} +80 -1
  14. package/dist/{cli-xq6djqbj.js → cli-93mkaw6v.js} +166 -308
  15. package/dist/{cli-evg8sbht.js → cli-d9mm3vvh.js} +7 -2
  16. package/dist/{cli-kh8v4vvw.js → cli-gnpea9dx.js} +1 -1
  17. package/dist/{cli-aqh6brew.js → cli-pspjxnc8.js} +1 -1
  18. package/dist/{cli-pwwfxdgb.js → cli-rqh295j9.js} +15 -21
  19. package/dist/{index-x3ema2w3.js → cli-y6d1arem.js} +321 -272
  20. package/dist/cli.js +12 -12
  21. package/dist/{doctor-5gtpgpd1.js → doctor-ptb346k8.js} +4 -3
  22. package/dist/engine/shared/gcpm-extract.d.ts +8 -0
  23. package/dist/engine/shared/print-area.d.ts +16 -4
  24. package/dist/{engine-mc5br8g8.js → engine-4rzdx6af.js} +1 -1
  25. package/dist/{engine-tn13gk0b.js → engine-6txhxmmv.js} +2 -2
  26. package/dist/{ext-wn2a5afq.js → ext-gy8szg3x.js} +8 -8
  27. package/dist/{extension-restore-s3x1618g.js → extension-restore-dymzcasz.js} +2 -2
  28. package/dist/{extension-restore-g8jnmmzq.js → extension-restore-m8tj43e7.js} +4 -4
  29. package/dist/{gutterpress-viewer-701vf4qp.js → gutterpress-viewer-mafsby5k.js} +9 -2
  30. package/dist/{cli-2ppw6q9v.js → index-5crpmz42.js} +80 -1
  31. package/dist/{index-0skhjx3e.js → index-5kh4c78h.js} +19 -13
  32. package/dist/{index-2bjcghze.js → index-8ywkd2sv.js} +15 -21
  33. package/dist/{index-j5z86r4a.js → index-ea94zbq4.js} +329 -488
  34. package/dist/{cli-sdmv4bba.js → index-kaks3mm2.js} +448 -156
  35. package/dist/{index-4x6y8krm.js → index-yc3xj266.js} +1 -1
  36. package/dist/index.js +6 -6
  37. package/dist/lib/build-preflight.d.ts +4 -10
  38. package/dist/lib/build-runner.d.ts +1 -1
  39. package/dist/lib/extension-manager.d.ts +8 -7
  40. package/dist/lib/extension-manifest.d.ts +1 -13
  41. package/dist/lib/ghostscript.d.ts +5 -2
  42. package/dist/lib/lint-rules.d.ts +5 -1
  43. package/dist/lib/manifest-config.d.ts +3 -2
  44. package/dist/lib/manifest-old-spellings.d.ts +64 -0
  45. package/dist/lib/manifest.d.ts +15 -1
  46. package/dist/lib/markdown/assemble.d.ts +4 -3
  47. package/dist/lib/markdown/chapter-id.d.ts +1 -1
  48. package/dist/lib/markdown/index.d.ts +1 -1
  49. package/dist/lib/markdown/table.d.ts +5 -3
  50. package/dist/lib/page-size.d.ts +45 -33
  51. package/dist/lib/pdf-inspect.d.ts +27 -0
  52. package/dist/lib/presets.d.ts +10 -8
  53. package/dist/lib/printsafe.d.ts +2 -1
  54. package/dist/lib/project-scaffold.d.ts +1 -1
  55. package/dist/lib/project-templates.d.ts +9 -0
  56. package/dist/lib/remote-auth/sync-messages.d.ts +2 -2
  57. package/dist/lib/targets.d.ts +4 -4
  58. package/dist/lib/validation-exec.d.ts +4 -4
  59. package/dist/manifest-2kc56wvw.yaml +22 -0
  60. package/dist/manifest-41ddzm13.yaml +18 -0
  61. package/dist/manifest-79zsgkpn.yaml +26 -0
  62. package/dist/manifest-sqzm4681.yaml +22 -0
  63. package/dist/{manifest.schema-t7nx2stg.json → manifest.schema-w86c1xw1.json} +30 -308
  64. package/dist/{new-mhs46pw3.js → new-t2t28ccb.js} +9 -15
  65. package/dist/{package-aenwjew3.json → package-awwkt7sa.json} +0 -1
  66. package/dist/{page-rules-nsj3aw42.css → page-rules-ytj9p04v.css} +12 -40
  67. package/dist/{pdf-inspect-wnw4za71.js → pdf-inspect-b3mf81ex.js} +3 -1
  68. package/dist/{plugin.test.js-xgxrd8hf.tpl → plugin.test.js-gxzqqfdh.tpl} +0 -1
  69. package/dist/{plugins-2cek4mz1.js → plugins-fec8t2bh.js} +1 -1
  70. package/dist/{plugins-1sxe0dkz.js → plugins-sqa4msdk.js} +3 -3
  71. package/dist/{preflight-3k2b1x8e.js → preflight-srq8fktp.js} +8 -8
  72. package/dist/preview/file-watcher.d.ts +1 -3
  73. package/dist/{preview-6p9v6rht.js → preview-zsp9hstd.js} +8 -8
  74. package/dist/{publish-qgsngy8s.js → publish-hhy31gr9.js} +8 -8
  75. package/dist/render.js +30 -562
  76. package/dist/schema/manifest.types.d.ts +60 -105
  77. package/dist/{tokens-7984461t.css → tokens-n82t4k9h.css} +12 -12
  78. package/dist/{validate-0v7xw6wa.js → validate-r9jdz85h.js} +8 -8
  79. package/package.json +1 -1
  80. package/dist/checks/asset/approved-fonts.d.ts +0 -3
  81. package/dist/manifest-eagfhba6.yaml +0 -19
  82. package/dist/manifest-n1gh3qw5.yaml +0 -9
@@ -19,7 +19,7 @@ import {
19
19
  stringValueAt,
20
20
  toFolioPage,
21
21
  wantsRecto
22
- } from "./index-0skhjx3e.js";
22
+ } from "./index-5kh4c78h.js";
23
23
  import {
24
24
  launchChromium
25
25
  } from "./index-bt0qws5c.js";
package/dist/index.js CHANGED
@@ -116,7 +116,7 @@ import {
116
116
  substituteVariables,
117
117
  syncProject,
118
118
  testRemoteAccess
119
- } from "./index-j5z86r4a.js";
119
+ } from "./index-ea94zbq4.js";
120
120
  import {
121
121
  DEFAULT_NPM_REGISTRY,
122
122
  MANIFEST_FILENAMES,
@@ -146,7 +146,7 @@ import {
146
146
  setExtensionEnabled,
147
147
  updateExtensions,
148
148
  validateProjectExtensions
149
- } from "./index-x3ema2w3.js";
149
+ } from "./index-kaks3mm2.js";
150
150
  import {
151
151
  BUNDLED_EXTENSIONS,
152
152
  EXTENSION_MANIFEST_FILENAME,
@@ -162,16 +162,16 @@ import {
162
162
  pathEscapesFolder,
163
163
  readExtensionMeta,
164
164
  resolveExtension
165
- } from "./index-2bjcghze.js";
165
+ } from "./index-8ywkd2sv.js";
166
166
  import"./index-ht1bcqdd.js";
167
167
  import {
168
168
  BUILD_DIAGNOSTIC_CODES
169
- } from "./index-4x6y8krm.js";
170
- import"./index-0skhjx3e.js";
169
+ } from "./index-yc3xj266.js";
170
+ import"./index-5kh4c78h.js";
171
171
  import {
172
172
  BuildError
173
173
  } from "./index-46ycxe6r.js";
174
- import"./index-2ppw6q9v.js";
174
+ import"./index-5crpmz42.js";
175
175
  import {
176
176
  DEFAULT_PRINT_OPTS,
177
177
  assertMilestone,
@@ -28,9 +28,10 @@ export interface Gates {
28
28
  * Disable the `source.stylelint` check (CSS print-safety: remote urls,
29
29
  * risky print effects, page-containment) for this run, without touching
30
30
  * `preValidate` itself (#272 — one CSS gate, not two). `true` when
31
- * `--skip-lint` was passed or `config.lint.enabled === false`; consulted
32
- * only when `preValidate` is also true — with `preValidate` off, no
33
- * pre-build check (CSS included) runs at all, `--skip-lint` or not.
31
+ * `--skip-lint` was passed (a book switches it off for good with
32
+ * `checks: { source.stylelint: off }`); consulted only when `preValidate`
33
+ * is also true — with `preValidate` off, no pre-build check (CSS
34
+ * included) runs at all, `--skip-lint` or not.
34
35
  */
35
36
  skipStylelint: boolean;
36
37
  preValidate: boolean;
@@ -40,11 +41,4 @@ export declare function computeGates(format: BuildFormat, opts: {
40
41
  skipLint?: boolean;
41
42
  skipPreValidate?: boolean;
42
43
  skipPostValidate?: boolean;
43
- }, config: {
44
- lint: {
45
- enabled: boolean;
46
- };
47
- validate: {
48
- enabled: boolean;
49
- };
50
44
  }): Gates;
@@ -131,7 +131,7 @@ export interface BuildContext {
131
131
  inputDir: string;
132
132
  /**
133
133
  * THE anchor every manifest-relative path resolves against: `styles:`,
134
- * `source.files`, authored plugin `path:` entries, and the CSS print-safety
134
+ * `chapters`, authored plugin `path:` entries, and the CSS print-safety
135
135
  * check's stylesheet resolution. Equal to {@link BuildContext.manifestDir}.
136
136
  *
137
137
  * It is NOT `inputDir`. The two are identical in the normative layout (the
@@ -27,14 +27,11 @@ export interface ProjectExtensionEntry {
27
27
  author?: string;
28
28
  /** Preview image path relative to the extension folder, when declared. */
29
29
  preview?: string | null;
30
- /** The sheet carrying the `:root` token surface, relative to the folder,
31
- * when declared (package.json's `gutterpress.tokensFile`). */
32
- tokensFile?: string;
33
30
  /** Declared stylesheets relative to the folder, in cascade order
34
31
  * (`gutterpress.styles`). Absent when the folder declares none. */
35
32
  styles?: string[];
36
- /** What the extension declares — the desktop shows styles-carrying entries
37
- * in its Look view and markdown-carrying ones in Features; one list. */
33
+ /** What the extension declares — the badges on the desktop's Looks &
34
+ * features list. */
38
35
  carries: ExtensionCarries;
39
36
  /** Absolute folder its metadata was read from. Absent for a bundled name,
40
37
  * a bare JS file, an uninstalled npm entry, or a missing path. */
@@ -208,7 +205,11 @@ export declare function uniqueExtensionId(projectDir: string, base: string): Pro
208
205
  export declare function validateProjectExtensions(projectDir: string): Promise<ExtensionValidationResult[]>;
209
206
  /**
210
207
  * Read an extension's stylesheets, concatenated in cascade order, for a
211
- * preview thumbnail (the renderer never touches fs). Read-only, so
212
- * concatenation is fine even though it would not be for an editable surface.
208
+ * preview thumbnail (the renderer never touches fs). A bundled look answers
209
+ * whether or not the manifest lists it, so a book can show what a look is
210
+ * before adding it. Read-only, so concatenation is fine even though it would
211
+ * not be for an editable surface.
213
212
  */
214
213
  export declare function readExtensionCss(projectDir: string, use: string): Promise<string>;
214
+ /** The same, for an entry already described (`listProjectExtensions`), so a caller with the list in hand reads each once. */
215
+ export declare function readEntryCss(entry: Pick<ProjectExtensionEntry, "use" | "dir">): Promise<string>;
@@ -42,11 +42,6 @@ export interface ExtensionMetadata {
42
42
  * its sheets, it does not get an implicit `theme.css`.
43
43
  */
44
44
  styles?: string[];
45
- /** `gutterpress.tokensFile` — which declared sheet (a path from `styles`)
46
- * carries the author-facing `:root` token surface for the Design panel's
47
- * guided editor. Purely advisory — nothing in this module enforces or
48
- * existence-checks it. */
49
- tokensFile?: string;
50
45
  /**
51
46
  * `gutterpress.markdown` — an EXPLICIT markdown-it entry, relative to the
52
47
  * extension folder, for the one case npm's `main` can't express: a package
@@ -138,7 +133,7 @@ export declare function assertPublishOnly(meta: ExtensionMetadata, ref: string):
138
133
  * Exported (#242) for `snippets.ts`'s installed-extension snippet merge,
139
134
  * which needs this SAME single-field check but cannot use
140
135
  * {@link assertExtensionContained}: that guard throws on ANY escaping field
141
- * (styles/markdown/components/tokensFile too), which is correct for a
136
+ * (styles/markdown/components too), which is correct for a
142
137
  * write-boundary check at install/apply time but wrong for a tolerant
143
138
  * listing — an extension with a broken, unrelated `styles` entry must not
144
139
  * make its perfectly fine `snippets` folder disappear from the picker (the
@@ -158,8 +153,6 @@ export declare function assertSupportsThisGutterpress(meta: ExtensionMetadata, r
158
153
  * extension is self-contained by contract (apply/install copies the whole
159
154
  * folder), and an imported/vendored package is untrusted input: a `../` or
160
155
  * absolute entry would make apply/load read a file from anywhere on disk.
161
- * `tokensFile` is included even though it is advisory/unenforced elsewhere —
162
- * defense in depth against a future consumer reading it unchecked.
163
156
  *
164
157
  * A WRITE-BOUNDARY guard, not a read-path check: callers invoke this before
165
158
  * copying anything or wiring a manifest, never from a plain listing/read
@@ -176,17 +169,12 @@ export declare function assertExtensionContained(meta: ExtensionMetadata): void;
176
169
  * `styles` already resolve through, so a broken declaration (a missing file)
177
170
  * throws HERE, at load/apply time, instead of failing silently deep in the
178
171
  * render pipeline (or never, for `components`, which nothing yet reads).
179
- *
180
- * `tokensFile` is the one exception, kept as declared (relative, not
181
- * existence-checked) — it stays purely advisory.
182
172
  */
183
173
  export interface ResolvedExtension {
184
174
  /** Absolute path to the markdown-it entry module, when the folder has one. */
185
175
  markdown?: string;
186
176
  /** Absolute paths, in cascade order, when any are declared. */
187
177
  styles?: string[];
188
- /** Declared-relative path of the `:root` token surface — advisory. */
189
- tokensFile?: string;
190
178
  /** Absolute path to the component catalog file, when declared (#242). */
191
179
  components?: string;
192
180
  /** Absolute path to the snippets folder, when declared. */
@@ -1,4 +1,4 @@
1
- import type { UnsplittableReason } from "./pdf-inspect";
1
+ import { type UnsplittableReason } from "./pdf-inspect";
2
2
  /**
3
3
  * Resolve the Ghostscript command on every supported platform.
4
4
  *
@@ -57,7 +57,10 @@ export declare function transparentPages(pdfBytes: Uint8Array): Promise<number[]
57
57
  * these shadings.
58
58
  *
59
59
  * Walks page resources (and form XObjects they draw) rather than every
60
- * object, so only shadings a page can actually paint are reported. Like
60
+ * object, so only shadings a page can actually paint are reported; of those
61
+ * pages, only the ones that paint a shading with a visible area (#438: a box
62
+ * that opens the NEXT page is also in this page's content as a sliver —
63
+ * `pagesWithVisibleShadingPaints`, pdf-inspect.ts). Like
61
64
  * {@link transparentPages}, it never fails a build: an unparseable PDF
62
65
  * reports no pages.
63
66
  */
@@ -1,5 +1,9 @@
1
1
  export type LintLevel = "off" | "warn" | "error";
2
- /** Read one rule's configured level; unrecognised values are ignored. */
2
+ /**
3
+ * Read one rule's configured level; unrecognised values (and `on`) leave the
4
+ * rule at its own severity. A lint rule reports at warning or error, so
5
+ * `info` reads as warning.
6
+ */
3
7
  export declare function ruleLevel(rules: Readonly<Record<string, unknown>> | undefined, id: string): LintLevel | undefined;
4
8
  /**
5
9
  * The rule ids named by a `gutterpress-disable-next-line <id>[, <id>...]
@@ -2,7 +2,7 @@
2
2
  export interface ProjectConfigFields {
3
3
  title?: string;
4
4
  authors?: string[];
5
- /** `source.files` — the markdown inputs (null means "all chapter files"). */
5
+ /** `chapters` — the markdown inputs (null means "all chapter files"). */
6
6
  sourceFiles?: string[] | null;
7
7
  /**
8
8
  * `targets:` — the publish destinations this book is validated against
@@ -16,7 +16,8 @@ export interface ProjectConfigFields {
16
16
  * Apply the author-facing manifest field updates in a single yaml round-trip.
17
17
  * Only the keys present in `updates` are written; everything else is left
18
18
  * untouched (so unrelated comments + sections survive). `sourceFiles: null`
19
- * deletes the `source.files` entry (back to "all chapter files").
19
+ * deletes the `chapters` entry (back to "all chapter files"). Either way an
20
+ * older book's `source.files` is taken out, so the list has one spelling.
20
21
  */
21
22
  export declare function setManifestFields(projectDir: string, updates: ProjectConfigFields): Promise<ProjectConfigFields>;
22
23
  /**
@@ -0,0 +1,64 @@
1
+ /**
2
+ * The manifest spellings 0.11.23 replaced, read for this release only so a
3
+ * book written for 0.11.22 still builds. Every old key found is translated
4
+ * to its new spelling (or dropped, when nothing replaces it) and named in ONE
5
+ * notice. Delete this module, and its call in `loadManifestWithPath`, in the
6
+ * release after 0.11.23.
7
+ *
8
+ * source.files -> chapters
9
+ * validate.checks, lint.rules -> checks (off | on | info | warn | error)
10
+ * validate.source.<tool>: false -> checks: { source.<tool>: off }
11
+ * lint.enabled: false -> checks: { source.stylelint: off }
12
+ * validate.assets.allowAlpha,
13
+ * validate.assets.requireFontLicense,
14
+ * validate.pdf.requireBookmarks,
15
+ * validate.pdf.requireTocLinks,
16
+ * validate.pdf.requireBleed,
17
+ * validate.pdf.forbidTransparency -> the matching check, on or off
18
+ * validate.source.<tool>: <path> -> dropped: the config file is found by
19
+ * its usual name in the book's folder
20
+ * or a folder above it
21
+ * ink.*, page.tolerance,
22
+ * validate thresholds,
23
+ * validate.enabled, lint.configPath,
24
+ * and the keys
25
+ * 0.11.22 already ignored -> dropped: the preset and the publish
26
+ * target set the thresholds
27
+ */
28
+ import type { CheckLevelInput, GutterpressManifest } from "../schema/manifest.types";
29
+ type OldCheckEntry = boolean | {
30
+ enabled?: boolean;
31
+ severity?: string;
32
+ options?: unknown;
33
+ };
34
+ /** The keys a pre-0.11.23 manifest may carry. */
35
+ export interface OldSpellings {
36
+ source?: {
37
+ files?: string[] | null;
38
+ assets?: unknown;
39
+ } | null;
40
+ lint?: {
41
+ enabled?: boolean;
42
+ configPath?: string | null;
43
+ rules?: Record<string, CheckLevelInput>;
44
+ } | null;
45
+ validate?: {
46
+ enabled?: boolean;
47
+ checks?: Record<string, OldCheckEntry>;
48
+ source?: Record<string, unknown>;
49
+ assets?: Record<string, unknown>;
50
+ pdf?: Record<string, unknown>;
51
+ heuristics?: Record<string, unknown>;
52
+ } | null;
53
+ ink?: unknown;
54
+ page?: GutterpressManifest["page"] & {
55
+ tolerance?: unknown;
56
+ };
57
+ }
58
+ /**
59
+ * Translate a parsed manifest's old spellings to the current ones. A key
60
+ * written both ways keeps the new spelling. Throws for `source.assets`,
61
+ * removed before 0.11.23 with an actionable error.
62
+ */
63
+ export declare function readOldSpellings(raw: GutterpressManifest & OldSpellings): GutterpressManifest;
64
+ export {};
@@ -1,4 +1,4 @@
1
- import type { GutterpressManifest, ResolvedConfig } from "../schema/manifest.types";
1
+ import type { GutterpressManifest, PageMargin, ResolvedConfig } from "../schema/manifest.types";
2
2
  /**
3
3
  * The manifest file name Gutterpress recognizes.
4
4
  * Single source of truth — any code that needs to find/match a manifest file
@@ -44,3 +44,17 @@ export declare function resolveConfig(cliOverrides: Partial<GutterpressManifest>
44
44
  * the same sovereignty rule presets follow.
45
45
  */
46
46
  export declare function resolveConfigForTarget(cliOverrides: Partial<GutterpressManifest>, manifest: GutterpressManifest, targetId: string): ResolvedConfig;
47
+ /** The edges of `page.margin`, in the order they are shown and written. */
48
+ export declare const MARGIN_EDGES: readonly ["top", "bottom", "inside", "outside"];
49
+ /**
50
+ * The one rule for a legal `page.margin`, applied to a manifest as it is
51
+ * resolved and to a margin Book setup is about to write: all four edges or
52
+ * nothing (a partial block would leave the missing edges to whatever a
53
+ * stylesheet says, the two-places split the field exists to end), each a
54
+ * finite point value of 0 or more, and the four leaving room for text on the
55
+ * page. Returns the margin typed, or throws the message both callers show.
56
+ */
57
+ export declare function validatePageMargin(margin: unknown, size: {
58
+ width: number;
59
+ height: number;
60
+ }): PageMargin;
@@ -138,9 +138,10 @@ export interface AssembleBookHtmlOptions {
138
138
  * preset id and printer spec — written as `<meta name="gp-print-area">`
139
139
  * (engine/shared/print-area.ts) into EVERY assembled book, preview, build
140
140
  * and publish alike: the preview's print overlays judge the ink against it,
141
- * and both engines place pinned art on its art-safe line. Metadata only,
142
- * with one exception: its `width` x `height` is also the page size the book
143
- * prints at when no stylesheet sets one — see {@link defaultPageSizeCss}.
141
+ * and both engines place pinned art on its art-safe line. Metadata, except
142
+ * that its `width` x `height` is also the page size the book prints at, and
143
+ * its `margin`, when the manifest sets one, the margins — see
144
+ * {@link pageGeometryCss}.
144
145
  */
145
146
  printArea?: PrintAreaSource;
146
147
  plugins?: LoadedPlugin[];
@@ -13,7 +13,7 @@
13
13
  * - the desktop's editor↔preview sync scoping
14
14
  * (packages/desktop +page.svelte editorChapter)
15
15
  *
16
- * WHY: manifest `source.files` entries are author-written and arrive in many
16
+ * WHY: manifest `chapters` entries are author-written and arrive in many
17
17
  * spellings (`./chapters/03.md`, `chapters\03.md` on Windows, `chapters//03.md`),
18
18
  * while the watcher broadcasts `path.relative(inputPath, file)`. Before this
19
19
  * helper the build tagged the manifest string VERBATIM, so any `./`-prefixed
@@ -9,7 +9,7 @@ export type { LayoutWarning } from "./assemble";
9
9
  * resolver — markdown's counterpart to `resolveActiveStyles` (style-resolver.ts).
10
10
  * Both `renderChapters` below AND validation (validation-exec.ts) call this,
11
11
  * so what gets checked is always what gets rendered:
12
- * 1. `configuredFiles` (the manifest `source.files` list), if it has entries,
12
+ * 1. `configuredFiles` (the manifest `chapters` list), if it has entries,
13
13
  * in that order; else
14
14
  * 2. every `.md` file directly inside `inputDir` — NOT recursive, since a
15
15
  * book's chapters live at the project root by convention — alphabetically.
@@ -11,9 +11,11 @@ import type MarkdownIt from "markdown-it";
11
11
  * or a formula splits the cell, and the cells past the header's count
12
12
  * are silently dropped. Here an inline span is opaque to the split: a
13
13
  * code span always, and a `$…$` formula when the math feature is on
14
- * (math.ts re-registers this rule with its own scanner). `\|` still
15
- * escapes a pipe in plain cell text; inside a span nothing is touched,
16
- * so `\|` in a formula is LaTeX's own `\|`.
14
+ * (math.ts re-registers this rule with its own scanner). `\|` escapes
15
+ * a pipe in plain cell text and, as on GitHub, inside a code span (the
16
+ * GFM spec's "including inside other inline spans"), so a table that
17
+ * lints clean for markdownlint's MD056 prints `|`, not `\|`; a formula's
18
+ * text is not touched, so `\|` there is LaTeX's own `\|`.
17
19
  * - a row with MORE cells than the header is reported
18
20
  * (`table_extra_cells` on `env.layoutWarnings`, the channel Gutterpress's
19
21
  * marker parser uses) instead of losing them without a word.
@@ -1,67 +1,79 @@
1
1
  import type { Document } from "yaml";
2
2
  import { type PresetId } from "./presets.ts";
3
- import { formatPageSize, isPositivePoints } from "../engine/shared/print-area.ts";
4
- export { formatPageSize, isPositivePoints };
3
+ import { isPositivePoints } from "../engine/shared/print-area.ts";
4
+ import type { PageMargin } from "../schema/manifest.types.ts";
5
+ export { isPositivePoints };
6
+ export type { PageMargin };
5
7
  /** Page bounds written to the manifest's `page:` block (points; 72pt = 1in). */
6
8
  export interface CustomPageOptions {
7
9
  /** Trim width in points (72pt = 1in). */
8
10
  width: number;
9
11
  /** Trim height in points (72pt = 1in). */
10
12
  height: number;
11
- /** Allowed deviation in points when validating a built PDF. Default 0.5. */
12
- tolerance?: number;
13
13
  }
14
14
  /** A page size in points. */
15
15
  export interface PageSizePoints {
16
16
  width: number;
17
17
  height: number;
18
18
  }
19
- /** What an author picks: a preset, plus the trim when the preset is `custom`. */
19
+ /** What an author picks for the size: a preset, and the trim when the preset is `custom`. */
20
20
  export interface PageSizeChoice {
21
21
  preset: PresetId;
22
22
  /** Required for `custom`; ignored for `dtrpg` / `book` (the preset owns it). */
23
23
  page?: PageSizePoints;
24
24
  }
25
- /** A book's page size as currently recorded, read back for the settings UI. */
25
+ /**
26
+ * A change to a book's page geometry: the size, the margins, or both. A part
27
+ * left out stays as recorded, so a margin edit never rewrites the size (nor
28
+ * drops a preset's `page:` override) and a size change never invents margins.
29
+ */
30
+ export interface PageSetupChange {
31
+ size?: PageSizeChoice;
32
+ /** New margins, or null to remove `page.margin` so the stylesheets' (or the default) apply again. */
33
+ margins?: PageMargin | null;
34
+ }
35
+ /** A book's page geometry as currently recorded, read back for the settings UI. */
26
36
  export interface PageSetup {
27
37
  /** The manifest's preset (`dtrpg` when undeclared — the product default); null when unrecognised. */
28
38
  preset: PresetId | null;
29
- /** The validation bounds the manifest resolves to, or null (custom with no `page:`). */
39
+ /** The size the book prints at and is checked against, or null (custom with no `page:`). */
30
40
  bounds: (PageSizePoints & {
31
41
  tolerance: number;
32
42
  }) | null;
33
- /** What the book's stylesheets print at, or null when no plain `@page` sets a size (the book then prints at `bounds`). */
34
- css: (PageSizePoints & {
35
- file: string;
36
- }) | null;
43
+ /** The margins the book prints with (points, `inside` at the binding edge). */
44
+ margins: PageMargin;
45
+ /** Where `margins` come from: the manifest's `page.margin` (the author's own), a stylesheet's `@page` (a look's design), or the engine's 1in default. */
46
+ marginsFrom: "manifest" | "stylesheet" | "default";
47
+ /** What the book prints with without `page.margin`: a stylesheet's `@page` margins or the default. Equals `marginsFrom` unless the manifest sets margins. */
48
+ fallbackMarginsFrom: "stylesheet" | "default";
49
+ /** True when `preset:` is `dtrpg` or `book` and `page.width`/`height` override its size: Details shows the preset, and choosing a size there replaces the override. */
50
+ presetSizeOverridden: boolean;
37
51
  }
38
52
  /**
39
- * Write the manifest's `page:` bounds. `tolerance` defaults to whatever the
40
- * block already carries, so re-sizing a book never silently resets a
41
- * hand-tuned tolerance. The block's other keys stay as written; a `page:`
42
- * that is not a map is replaced.
53
+ * Write the manifest's `page:` bounds. A `tolerance` an older book carries
54
+ * goes (the preset sets it); the block's other keys stay as written; a
55
+ * `page:` that is not a map is replaced.
43
56
  */
44
57
  export declare function writePageBounds(doc: Document, page: CustomPageOptions): void;
58
+ /** Remove the bounds a preset owns and nothing else; a `page:` left empty goes too. */
59
+ export declare function deletePageBounds(doc: Document): void;
60
+ /** Remove the manifest's `page.margin` and nothing else; a `page:` left empty goes too. */
61
+ export declare function deletePageMargin(doc: Document): void;
62
+ /** Write the manifest's `page.margin`, all four edges; the block's other keys stay as written. */
63
+ export declare function writePageMargin(doc: Document, margin: PageMargin): void;
64
+ /** A positive point value at `keyPath` in the manifest, or undefined. */
65
+ export declare function numberAt(doc: Document, keyPath: string[]): number | undefined;
45
66
  /** The size (points) a choice means: `custom` brings its own, the others are the preset's. */
46
67
  export declare function pageSizeForChoice(choice: PageSizeChoice): PageSizePoints;
47
- /** The size the LAST plain `@page { size }` in the stylesheet sets (cascade: last wins), or null. */
48
- export declare function readPageSizeFromCss(css: string): PageSizePoints | null;
49
- /**
50
- * Point a stylesheet's plain `@page` rule at `page`. Rewrites the existing
51
- * `size` declaration's value in place — comments, whitespace, other rules and
52
- * `!important` untouched — or, when no plain `@page` sets a size, appends a
53
- * minimal rule. An already-matching size leaves the text exactly as it was.
54
- * Throws when the CSS does not parse (it must never be half-rewritten).
55
- */
56
- export declare function setPageSizeInCss(css: string, page: PageSizePoints): string;
57
- /** Read a book's page size back from its manifest and stylesheets. */
68
+ /** Read a book's page geometry back from its manifest (and, for margins it does not set, its stylesheets). */
58
69
  export declare function readPageSetup(projectDir: string): Promise<PageSetup>;
59
70
  /**
60
- * Change a book's page size: the manifest's `preset:` + `page:` bounds and the
61
- * stylesheet's `@page` size, together. Mirrors what the wizard records —
62
- * `dtrpg` / `book` carry their own bounds (the `page:` block's width, height
63
- * and tolerance are removed so they cannot shadow them; its print-area keys
64
- * stay), `custom` records the typed trim. Nothing is written
65
- * unless the whole choice validates and the stylesheet parses.
71
+ * Change a book's page geometry in the manifest. The size mirrors what the
72
+ * wizard records — `dtrpg` / `book` carry their own bounds (the `page:` block's
73
+ * width and height are removed so they cannot shadow them; its
74
+ * other keys stay), `custom` records the typed trim. The margins go to
75
+ * `page.margin`, validated against the size being written, else the recorded
76
+ * one; `margins: null` removes `page.margin`. Nothing is written unless the
77
+ * whole change validates.
66
78
  */
67
- export declare function setPageSetup(projectDir: string, choice: PageSizeChoice): Promise<PageSetup>;
79
+ export declare function setPageSetup(projectDir: string, change: PageSetupChange): Promise<PageSetup>;
@@ -191,6 +191,33 @@ export type UnsplittableReason = "text-transparency" | "type3-font" | "text-patt
191
191
  * XObject is an implicit save/restore).
192
192
  */
193
193
  export declare function unsplittableTextReasons(pdfBytes: Uint8Array, pages?: number[]): Promise<Map<number, UnsplittableReason>>;
194
+ /**
195
+ * Of `pages` (1-based), those on which a shading pattern paints a visible
196
+ * area: a path filled with a `/Pattern` fill colour, or a `sh`, whose box is
197
+ * at least {@link SLIVER_PT} wide and tall once the clip in force is applied.
198
+ * Text shown in a shading pattern, a stroke with one, and a `sh` under an
199
+ * unbounded clip are not sized here and count as visible.
200
+ *
201
+ * The one thing this excludes (#438): a box that starts exactly on a page
202
+ * break and whose ink overflow (a `box-shadow` blur, an `outline`) reaches
203
+ * above its top edge. Chromium paints it into the PREVIOUS page as well — the
204
+ * whole background, clipped to that page's content box — so a repeating or
205
+ * conic gradient on such a box also sits, as a sliver, in the resources and
206
+ * content of the page before it. Its clipped area is empty in exact
207
+ * arithmetic (under 0.01pt when the page's content height is not a whole
208
+ * pixel); a RIP under the any-part-of-pixel rule renders one device row of it.
209
+ * Measured on the ghostscript.flatten fixture with its 1in margins: page 3's
210
+ * content fills page 4's 636x56px gradient box at y = 738pt, the clip's own
211
+ * bottom edge, and a 300dpi raster of page 3 has exactly one non-white row.
212
+ *
213
+ * Walks PDF.js's operator list as {@link unsplittableTextReasons} does, with
214
+ * the CTM and the clip's bounding box tracked per save/restore (a form
215
+ * XObject is an implicit save/restore under its `/Matrix`, inside its
216
+ * `/BBox`). PDF.js reports `W`/`W*` as a `clip`/`eoClip` op ahead of the
217
+ * path it applies to, and every path as `constructPath` with its painting
218
+ * operator and its bounding box.
219
+ */
220
+ export declare function pagesWithVisibleShadingPaints(pdfBytes: Uint8Array, pages: number[]): Promise<number[]>;
194
221
  /**
195
222
  * Does page `pageNum` (1-based) paint anything at all? False for a page with
196
223
  * no content and for one whose operators only set state or clip. A path op
@@ -5,8 +5,10 @@ import type { ResolvedConfig } from "../schema/manifest.types";
5
5
  * book of this kind validates against when the manifest lists none. Where
6
6
  * the book is PUBLISHED is a separate concern — see ./targets.ts.
7
7
  *
8
- * Every preset value is overridable from the manifest, leaf by leaf
9
- * (resolveConfig's mergeShape; precedence cli > manifest > target > preset).
8
+ * Geometry, build and print values are overridable from the manifest, leaf
9
+ * by leaf (resolveConfig's mergeShape; precedence cli > manifest > target >
10
+ * preset). The check thresholds (`ink`, `validate`) are the preset's and the
11
+ * target's alone; a book changes a check's level in `checks:`.
10
12
  */
11
13
  export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" | "language" | "preset" | "targets" | "page"> {
12
14
  /** This preset's id: what the manifest's `preset:` names and a resolved config reports. */
@@ -43,16 +45,16 @@ export declare const DTRPG_PRESET: VendorPreset;
43
45
  * - `page`: 6in x 9in (432 x 648pt) — the common generic trade paperback trim.
44
46
  * Authors targeting a different size still set `page.width`/`page.height`
45
47
  * directly; this is a starting point, not a lock-in.
46
- * - `ink.maxTac: 400` — 400% is the physical ceiling for 4-color process ink
48
+ * - `ink.maxTac` 400 — 400% is the physical ceiling for 4-color process ink
47
49
  * (100% each of C+M+Y+K), so `pdf.print.ink-coverage` effectively never
48
50
  * fires unless the author opts into a real vendor limit.
49
- * - `validate.checks`: keeps the generic, vendor-agnostic PDF health checks
51
+ * - `checks`: keeps the generic, vendor-agnostic PDF health checks
50
52
  * (`pdf.structure.qpdf` parses/traverses, `pdf.print.embedded-fonts` are
51
- * embedded) but does NOT force `pdf.print.pdfx-markers` /
53
+ * embedded) but switches off `pdf.print.pdfx-markers` /
52
54
  * `pdf.print.pdfx-metadata` — those assert PDF/X OutputIntent/DOCINFO
53
- * structure, which only applies to a PDF/X build (`--format pdfx`).
54
- * - `validate.pdf.forbidTransparency: false` and `assets.allowAlpha: true` —
55
- * PDF/X forbids both; a plain PDF/HTML book has no such restriction.
55
+ * structure, which only applies to a PDF/X build (`--format pdfx`) — and
56
+ * the transparency and alpha-channel checks: PDF/X forbids both; a plain
57
+ * PDF/HTML book has no such restriction.
56
58
  */
57
59
  export declare const BOOK_PRESET: VendorPreset;
58
60
  /**
@@ -3,6 +3,7 @@ export declare const ruleRiskyProps = "printsafe/no-risky-print-effects";
3
3
  export declare const ruleSyntax = "printsafe/syntax-error";
4
4
  export declare const rulePageContainment = "printsafe/page-containment";
5
5
  export declare const rulePreviewCannotFollow = "printsafe/preview-cannot-follow";
6
+ export declare const rulePageSizeInStylesheet = "printsafe/page-size-in-stylesheet";
6
7
  export interface PrintSafeWarning {
7
8
  rule: string;
8
9
  severity: "error" | "warning";
@@ -11,7 +12,7 @@ export interface PrintSafeWarning {
11
12
  column: number;
12
13
  }
13
14
  export interface CheckCssOptions {
14
- /** The manifest's `lint.rules`: `off` drops a rule, `warn`/`error` set its severity. */
15
+ /** The book's `checks` levels: `off` drops a rule, `warn`/`error` set its severity. */
15
16
  rules?: Readonly<Record<string, unknown>>;
16
17
  }
17
18
  /**
@@ -134,7 +134,7 @@ export interface AdoptFolderOptions {
134
134
  /**
135
135
  * Adopt an EXISTING folder as a gutterpress project, in place (no new subfolder).
136
136
  * Writes a `manifest.yaml` (using the folder's existing top-level `.md` files as
137
- * `source.files`, or scaffolding a `chapter-01.md` when there are none), copies
137
+ * `chapters`, or scaffolding a `chapter-01.md` when there are none), copies
138
138
  * a starter `styles/book.css`, ensures `dist/` and `extensions/npm/` are gitignored
139
139
  * (see `ensureProjectGitignore`), and optionally initialises local version history.
140
140
  *
@@ -22,6 +22,15 @@ export interface TemplateInfo {
22
22
  preset?: string;
23
23
  /** The `targets:` this template's manifest declares, when it has one. */
24
24
  targets?: string[];
25
+ /**
26
+ * The `page:` size (points) this template's manifest declares, when it has
27
+ * one. A `custom`-preset template says what size it is designed for here,
28
+ * and the creation flows show that size pre-filled.
29
+ */
30
+ page?: {
31
+ width: number;
32
+ height: number;
33
+ };
25
34
  }
26
35
  export declare const BUILT_IN_META: Record<ProjectTemplateId, {
27
36
  label: string;
@@ -8,7 +8,7 @@ export declare const MSG_UP_TO_DATE = "Everything is in sync.";
8
8
  export declare const MSG_UP_TO_DATE_PULLED = "Everything is in sync. The latest online changes were downloaded to this computer.";
9
9
  export declare const MSG_SYNCED = "Your changes are online.";
10
10
  export declare const MSG_SYNCED_MERGED = "Your changes are online, combined with changes from the online copy.";
11
- export declare const MSG_OFFLINE = "Your changes are saved on this computer. gutterpress couldn't reach the online repository \u2014 try syncing again when you're back online.";
11
+ export declare const MSG_OFFLINE = "Your changes are saved on this computer. Gutterpress couldn't reach the online repository \u2014 try syncing again when you're back online.";
12
12
  export declare const MSG_AUTH = "The online repository didn't accept the saved connection. Reconnect and try again.";
13
13
  export declare const MSG_INSECURE_TRANSPORT = "This book's online address isn't secure, so the saved connection wasn't sent \u2014 connections are never sent over an insecure address. Switch the address to a secure one (starting with https), or to a local loopback address for a server on this computer, to sync.";
14
14
  export declare const MSG_BUSY = "The online copy is changing very quickly right now. Your work is saved on this computer \u2014 try Sync again in a moment.";
@@ -21,7 +21,7 @@ export declare const MSG_BUSY = "The online copy is changing very quickly right
21
21
  export declare const MSG_HISTORY_UNREADABLE = "This book's version history can't be read, so syncing can't continue. Your writing is safe \u2014 every file is still here on this computer. Use Repair online backup (in Where your work is kept) to get history and syncing working again.";
22
22
  export declare const MSG_UNRELATED = "This book and the online copy have no history in common, so they can't be combined. Either the online address points at a different book, or this book's own history was lost and started over. Your writing is safe \u2014 every file is still here on this computer. Check the online address; if it is correct, use Repair online backup (in Where your work is kept).";
23
23
  export declare const MSG_NO_REMOTE = "This book isn't connected to an online repository yet.";
24
- export declare const MSG_SSH_REMOTE = "This book's online address uses SSH (git@\u2026), which gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
24
+ export declare const MSG_SSH_REMOTE = "This book's online address uses SSH (git@\u2026), which Gutterpress can't sync to. Switch it to the web (HTTPS) address to sync from here.";
25
25
  export declare const MSG_NO_BRANCH = "This book's version history isn't on a named branch, so it can't be synced right now.";
26
26
  /** Message recorded on the automatic pre-sync snapshot (D5 invariant).
27
27
  * Pre-0.10.1 history carries the old spelling "Snapshot before syncing". */