gutterpress 0.11.23-beta.1 → 0.11.23-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +13 -3
  2. package/dist/api/index.d.ts +5 -5
  3. package/dist/api/index.js +19 -7
  4. package/dist/{build-dg8a566j.js → build-fq3mexhy.js} +9 -9
  5. package/dist/checks/asset/approved-fonts.d.ts +8 -0
  6. package/dist/checks/asset/extensions.d.ts +1 -1
  7. package/dist/checks/asset/index.d.ts +1 -0
  8. package/dist/{cli-e31jshz6.js → cli-5zbdb6mt.js} +296 -65
  9. package/dist/{cli-ttj95aex.js → cli-7mt0dftw.js} +47 -18
  10. package/dist/{cli-989eg8jz.js → cli-ah93yyzb.js} +163 -55
  11. package/dist/{cli-31anr0r9.js → cli-eymdsefg.js} +69 -29
  12. package/dist/{cli-1fy1pm9d.js → cli-fh685nqs.js} +1 -1
  13. package/dist/{cli-k087gj0p.js → cli-fsjre61x.js} +123 -12
  14. package/dist/{cli-s1zt779w.js → cli-q7t04gjt.js} +1 -1
  15. package/dist/{cli-vhav8g82.js → cli-rybz2dfs.js} +1 -1
  16. package/dist/cli.js +12 -12
  17. package/dist/{doctor-g4a42tcz.js → doctor-tg9ht1n3.js} +3 -3
  18. package/dist/engine/compiler/postprocess.d.ts +3 -0
  19. package/dist/engine/shared/pdf-inspect.d.ts +33 -0
  20. package/dist/{engine-jvkcahr1.js → engine-17v87vq5.js} +1 -1
  21. package/dist/{engine-wgkaqjsc.js → engine-y9fnarfa.js} +2 -2
  22. package/dist/{ext-w9d5ftcg.js → ext-esz03jz4.js} +27 -10
  23. package/dist/{extension-restore-5x4mj79d.js → extension-restore-91gy1e28.js} +3 -3
  24. package/dist/{extension-restore-as0g38p1.js → extension-restore-q70wqah5.js} +5 -5
  25. package/dist/{gutterpress-agent-d29ry3ak.js → gutterpress-agent-wyryqehx.js} +13 -4
  26. package/dist/{gutterpress-viewer-ze691rrd.js → gutterpress-viewer-ywkf49q5.js} +38 -153
  27. package/dist/{index-a4j3xcfp.js → index-67g8f7r4.js} +47 -18
  28. package/dist/{index-798jt0qc.js → index-d9w3r89w.js} +3 -3
  29. package/dist/{index-rj5vrhz6.js → index-fntpr44x.js} +321 -66
  30. package/dist/{index-5x42amh8.js → index-g3te5ey1.js} +2 -1
  31. package/dist/{index-cw03e9p4.js → index-kw4tbthz.js} +69 -29
  32. package/dist/{index-fk2kpf2t.js → index-santcs4q.js} +121 -10
  33. package/dist/{index-c6y0p7jy.js → index-z6vzj3s5.js} +243 -73
  34. package/dist/index.js +35 -8
  35. package/dist/lib/check-options.d.ts +70 -0
  36. package/dist/lib/extension-manager.d.ts +16 -1
  37. package/dist/lib/extension-scaffold.d.ts +15 -2
  38. package/dist/lib/extension-specifier.d.ts +8 -1
  39. package/dist/lib/manifest-config.d.ts +3 -1
  40. package/dist/lib/manifest-old-spellings.d.ts +32 -9
  41. package/dist/lib/markdown/gutterpress-css.d.ts +1 -1
  42. package/dist/lib/markdown/math.d.ts +1 -1
  43. package/dist/lib/markdown/typography.d.ts +1 -0
  44. package/dist/lib/page-size.d.ts +12 -4
  45. package/dist/lib/plugin-vendor.d.ts +6 -0
  46. package/dist/lib/presets.d.ts +2 -2
  47. package/dist/lib/root-tokens.d.ts +2 -1
  48. package/dist/lib/source-provider.d.ts +27 -0
  49. package/dist/lib/style-resolver.d.ts +2 -1
  50. package/dist/lib/version-changes.d.ts +22 -6
  51. package/dist/{manifest.schema-0jgj0jt1.json → manifest.schema-5cm13vr3.json} +215 -5
  52. package/dist/{new-jtk028rf.js → new-rmfyb7va.js} +22 -15
  53. package/dist/{plugins-bcbe7xs8.js → plugins-gs21xz5s.js} +2 -2
  54. package/dist/{plugins-zsm6wv79.js → plugins-tqv1s16f.js} +4 -4
  55. package/dist/{preflight-cs6mj1d6.js → preflight-bjyeeq0d.js} +9 -9
  56. package/dist/{preview-d1j1g4qf.js → preview-ke03fk5d.js} +9 -9
  57. package/dist/{publish-7yw4jyrt.js → publish-wpnz0qng.js} +9 -9
  58. package/dist/render.js +55 -13
  59. package/dist/schema/manifest.types.d.ts +45 -7
  60. package/dist/{source-provider-n7vehh6t.js → source-provider-3g3yrn1f.js} +6 -2
  61. package/dist/{source-provider-tgzet3j5.js → source-provider-azg9ywhb.js} +6 -2
  62. package/dist/test-helpers/vocabulary.d.ts +12 -0
  63. package/dist/{validate-rkcdnzhw.js → validate-zcwykb43.js} +9 -9
  64. package/package.json +1 -1
  65. /package/dist/{cli-y28j019d.js → cli-1dha1nxk.js} +0 -0
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
7
7
  BUILT_IN_TEMPLATE_IDS,
8
8
  EXTENSION_KINDS,
9
+ EXTENSION_KIND_ALIASES,
9
10
  FileTokenStore,
10
11
  GDRIVE_HOST,
11
12
  GITHUB_HOST,
@@ -42,6 +43,7 @@ import {
42
43
  executeAndReport,
43
44
  executeValidation,
44
45
  exportVersionTree,
46
+ extensionKindFor,
45
47
  extractUrlCredential,
46
48
  extractVariables,
47
49
  forgeKindForHost,
@@ -122,14 +124,16 @@ import {
122
124
  substituteVariables,
123
125
  syncProject,
124
126
  testRemoteAccess
125
- } from "./index-c6y0p7jy.js";
127
+ } from "./index-z6vzj3s5.js";
126
128
  import {
129
+ BUILT_IN_LOOKS,
127
130
  DEFAULT_NPM_REGISTRY,
128
131
  MANIFEST_FILENAMES,
129
132
  PRESETS,
130
133
  PRESET_IDS,
131
134
  TARGETS,
132
135
  TARGET_IDS,
136
+ addBuiltInStyleSet,
133
137
  addExtension,
134
138
  checkExtensionUpdates,
135
139
  describeExtension,
@@ -152,9 +156,10 @@ import {
152
156
  setExtensionEnabled,
153
157
  updateExtensions,
154
158
  validateProjectExtensions
155
- } from "./index-rj5vrhz6.js";
159
+ } from "./index-fntpr44x.js";
156
160
  import {
157
161
  BUNDLED_EXTENSIONS,
162
+ BUNDLED_EXTENSION_DETAILS,
158
163
  EXTENSION_MANIFEST_FILENAME,
159
164
  assertExtensionContained,
160
165
  bundledExtension,
@@ -169,12 +174,12 @@ import {
169
174
  pathEscapesFolder,
170
175
  readExtensionMeta,
171
176
  resolveExtension
172
- } from "./index-a4j3xcfp.js";
177
+ } from "./index-67g8f7r4.js";
173
178
  import"./index-ht1bcqdd.js";
174
179
  import {
175
180
  BUILD_DIAGNOSTIC_CODES
176
- } from "./index-fk2kpf2t.js";
177
- import"./index-798jt0qc.js";
181
+ } from "./index-santcs4q.js";
182
+ import"./index-d9w3r89w.js";
178
183
  import {
179
184
  BuildError
180
185
  } from "./index-tayzfr9c.js";
@@ -199,10 +204,11 @@ import {
199
204
  revisionNameFrom,
200
205
  switchBranch,
201
206
  whenGitIdle
202
- } from "./index-cw03e9p4.js";
207
+ } from "./index-kw4tbthz.js";
203
208
  import {
204
- EXTENSIONS_DIR
205
- } from "./index-5x42amh8.js";
209
+ EXTENSIONS_DIR,
210
+ PLUGINS_DIR
211
+ } from "./index-g3te5ey1.js";
206
212
  import {
207
213
  capabilitiesFor,
208
214
  detectProjectSource,
@@ -327,7 +333,22 @@ function insertDeclaration(root, name, value) {
327
333
  root.raws.after = `
328
334
  `;
329
335
  }
336
+ function assertDeclarationValue(name, value) {
337
+ let single = false;
338
+ try {
339
+ const nodes = postcss.parse(`:root{${name}:${value}}`).nodes;
340
+ const rule = nodes[0];
341
+ single = nodes.length === 1 && rule?.type === "rule" && rule.nodes.length === 1 && rule.nodes[0].type === "decl";
342
+ } catch {
343
+ single = false;
344
+ }
345
+ if (!single)
346
+ throw new Error(`"${value}" can't be saved as ${name}: it is not one CSS value.`);
347
+ }
330
348
  function applyTokenUpdates(sheets, target, updates) {
349
+ for (const { name, value } of updates)
350
+ if (value !== null)
351
+ assertDeclarationValue(name, value);
331
352
  const roots = sheets.map((css) => postcss.parse(css));
332
353
  const changed = new Set;
333
354
  for (const { name, value } of updates) {
@@ -550,6 +571,7 @@ export {
550
571
  extractVariables,
551
572
  extractUrlCredential,
552
573
  extensionStyleList,
574
+ extensionKindFor,
553
575
  extensionEntry,
554
576
  extensionCarries,
555
577
  exportVersionTree,
@@ -585,6 +607,7 @@ export {
585
607
  assertExtensionContained,
586
608
  adoptFolder,
587
609
  addExtension,
610
+ addBuiltInStyleSet,
588
611
  VERSION_CHANGES_SHOWN,
589
612
  TARGET_IDS,
590
613
  TARGETS,
@@ -598,6 +621,7 @@ export {
598
621
  PublishSelectionsStore,
599
622
  PRESET_IDS,
600
623
  PRESETS,
624
+ PLUGINS_DIR,
601
625
  MAX_SEARCH_BYTES,
602
626
  MANIFEST_FILENAMES,
603
627
  HISTORY_PAGE_LIMIT,
@@ -608,13 +632,16 @@ export {
608
632
  GDRIVE_HOST,
609
633
  FileTokenStore,
610
634
  EXTENSION_MANIFEST_FILENAME,
635
+ EXTENSION_KIND_ALIASES,
611
636
  EXTENSION_KINDS,
612
637
  EXTENSIONS_DIR,
613
638
  DEFAULT_PRINT_OPTS,
614
639
  DEFAULT_NPM_REGISTRY,
615
640
  BuildError,
641
+ BUNDLED_EXTENSION_DETAILS,
616
642
  BUNDLED_EXTENSIONS,
617
643
  BUILT_IN_TEMPLATE_IDS,
644
+ BUILT_IN_LOOKS,
618
645
  BUILD_DIAGNOSTIC_CODES,
619
646
  AUTO_SYNC_PUSH_INTERVAL_MINUTES,
620
647
  AUTO_SYNC_MIN_MINUTES,
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The options a check takes in its `checks:` entry (#443, #483): the numbers
3
+ * and the list it measures against. The preset and the publish target set
4
+ * them; a book overrides one in the same entry that sets the check's level.
5
+ *
6
+ * checks:
7
+ * pdf.print.ink-coverage: { maxTac: 300 }
8
+ * pdf.print.image-resolution: { level: error, minImageResolution: 200 }
9
+ *
10
+ * Each option lands at ONE place in the resolved config, `at`, which is what
11
+ * the check reads (and, for `maxTac`, what the PDF/X conversion holds the ink
12
+ * to). `at` is also how a 0.11.22 manifest spelled the option
13
+ * (`ink.maxTac`, `validate.pdf.minImageResolution`, `page.tolerance`), so
14
+ * manifest-old-spellings.ts translates the old keys from this same table.
15
+ *
16
+ * No filesystem or check-registry imports: manifest.ts resolves with it, and
17
+ * the Book setup writers use {@link setCheckOption}.
18
+ */
19
+ import { type Document } from "yaml";
20
+ import type { CheckLevelInput, ResolvedConfig } from "../schema/manifest.types";
21
+ /** What an option's value is: points/bytes/DPI/a count, a `{ min, max }` range, or a list of file patterns. */
22
+ type OptionKind = "number" | "range" | "patterns";
23
+ interface CheckOption {
24
+ /** Where the value lands in the resolved config, and the 0.11.22 manifest spelling. */
25
+ at: string;
26
+ kind: OptionKind;
27
+ }
28
+ /** Check id -> option name -> where it lands. */
29
+ export declare const CHECK_OPTIONS: Readonly<Record<string, Readonly<Record<string, CheckOption>>>>;
30
+ /** The check id and option name a 0.11.22 spelling (`ink.maxTac`) moved to, if it is one. */
31
+ export declare function optionForOldSpelling(at: string): {
32
+ id: string;
33
+ option: string;
34
+ } | undefined;
35
+ /** A `checks:` level as a manifest may write it: `off`, `on`, `info`, `warn` (`warning`), `error`, or a boolean. */
36
+ export declare function isCheckLevelInput(value: unknown): value is CheckLevelInput;
37
+ /**
38
+ * A 0.11.22 threshold (`validate.heuristics.maxDecorativeLayers`) read as
39
+ * the option it became, or undefined when the key was never a threshold.
40
+ * 0.11.22 passed any value through, and a check given one it could not use
41
+ * measured nothing, so a value the option cannot take is NOT an error here:
42
+ * `mustBe` says what it should have been and the caller reports it and
43
+ * ignores it, and a book that built in 0.11.22 still builds. A range keeps
44
+ * whichever of `min` and `max` are numbers, as 0.11.22's merge did.
45
+ */
46
+ export declare function readOldThreshold(at: string, value: unknown): {
47
+ id: string;
48
+ option: string;
49
+ value?: unknown;
50
+ mustBe?: string;
51
+ } | undefined;
52
+ /**
53
+ * Read the options of one `checks:` entry (every key but `level`). A key the
54
+ * check does not take is a usage error naming the ones it does, so a typo
55
+ * never silently keeps the preset's number. A `null` value is left unset.
56
+ */
57
+ export declare function readCheckOptions(id: string, entry: Record<string, unknown>): Record<string, unknown>;
58
+ /**
59
+ * Write read options into a resolved config, over the preset's and the
60
+ * target's values. A range merges key by key, so `{ max: 4000 }` keeps the
61
+ * preset's `min`; a list replaces the preset's whole.
62
+ */
63
+ export declare function applyCheckOptions(config: ResolvedConfig, options: Record<string, Record<string, unknown>>): void;
64
+ /**
65
+ * Set one option in a manifest Document, keeping the entry's level: a bare
66
+ * level (`warn`) becomes `{ level: warn, <option>: value }`. Used where Book
67
+ * setup and `gutterpress new` write a check's option.
68
+ */
69
+ export declare function setCheckOption(doc: Document, id: string, option: string, value: unknown): void;
70
+ export {};
@@ -4,7 +4,7 @@ import type { ResolvedExtensionConfig } from "../schema/manifest.types.ts";
4
4
  /** The folder (relative to the project root) a book's extensions live under:
5
5
  * vendored npm copies in `extensions/npm/`, zip and URL imports beside them
6
6
  * (extension-import.ts). Path entries elsewhere are never moved. */
7
- export { EXTENSIONS_DIR } from "./plugin-vendor.ts";
7
+ export { EXTENSIONS_DIR, PLUGINS_DIR } from "./plugin-vendor.ts";
8
8
  /** How an `extensions:` entry resolves — the form of its specifier decides. */
9
9
  export type ExtensionSourceKind = "bundled" | "path" | "npm";
10
10
  /** One configured extension, as surfaced to the CLI and the desktop. */
@@ -194,6 +194,21 @@ export interface BundledExtensionInfo {
194
194
  }
195
195
  /** The bundled extensions, in table order. */
196
196
  export declare function listBundledExtensions(): BundledExtensionInfo[];
197
+ /** The built-in looks' names (the bundled extensions that carry a stylesheet), in table order. */
198
+ export declare const BUILT_IN_LOOKS: readonly string[];
199
+ /**
200
+ * Make an editable copy of a built-in look (0.11.22's, restored in #482): its
201
+ * stylesheet and a package.json land in `extensions/<id>/`, the author's own
202
+ * files from then on, and the book lists the copy as `./extensions/<id>` — a
203
+ * plain path extension, loaded and built like any folder the author owns.
204
+ *
205
+ * A book that lists the built-in by name gets the copy in that entry's place
206
+ * (on or off as it was), so the look is not listed twice and the cascade
207
+ * order holds; otherwise the copy is added at the end. Idempotent: a folder
208
+ * already there is kept as it is — the author's edits survive — and only
209
+ * (re-)referenced.
210
+ */
211
+ export declare function addBuiltInStyleSet(projectDir: string, id: string): Promise<ProjectExtensionEntry>;
197
212
  /** A folder name under `extensions/` not yet taken: `<slug>`, `<slug>-2`, … */
198
213
  export declare function uniqueExtensionId(projectDir: string, base: string): Promise<string>;
199
214
  /**
@@ -2,6 +2,19 @@
2
2
  export type ExtensionKind = "plugin" | "look";
3
3
  /** The kinds `--kind` accepts, for CLI help text and pickers. */
4
4
  export declare const EXTENSION_KINDS: readonly ["plugin", "look"];
5
+ /**
6
+ * Older names for a kind, read wherever a kind is given (`scaffoldExtension`,
7
+ * `gutterpress new --kind`) and never offered: 0.11.22 called a look a
8
+ * `theme` (#483). Kept apart from {@link EXTENSION_KINDS} so help text and
9
+ * pickers keep showing one word per kind.
10
+ */
11
+ export declare const EXTENSION_KIND_ALIASES: {
12
+ readonly theme: "look";
13
+ };
14
+ /** An older name {@link EXTENSION_KIND_ALIASES} reads. */
15
+ export type ExtensionKindAlias = keyof typeof EXTENSION_KIND_ALIASES;
16
+ /** The kind a word names — a kind, or an older name for one — or undefined. */
17
+ export declare function extensionKindFor(word: string): ExtensionKind | undefined;
5
18
  /**
6
19
  * The class/property prefix a scaffolded extension is FORBIDDEN to take.
7
20
  * `gp-` is Gutterpress core's own vocabulary (CLAUDE.md §6); an extension
@@ -13,8 +26,8 @@ export declare const RESERVED_PREFIX = "gp-";
13
26
  export interface ScaffoldExtensionOptions {
14
27
  /** Human-friendly package name, e.g. "Field Notes". Required. */
15
28
  name: string;
16
- /** Which starter to create. Required. */
17
- kind: ExtensionKind;
29
+ /** Which starter to create. Required. `theme` (0.11.22's word) makes a look. */
30
+ kind: ExtensionKind | ExtensionKindAlias;
18
31
  /** Absolute path to the PARENT directory the folder is created in. */
19
32
  parentDir: string;
20
33
  /** Folder name under `parentDir`. Defaults to a slug of `name`. */
@@ -20,7 +20,14 @@ export interface BundledExtension {
20
20
  /** A look's stylesheet. Absent for a markdown feature. */
21
21
  css?: string;
22
22
  }
23
- export declare const BUNDLED_EXTENSIONS: readonly BundledExtension[];
23
+ export declare const BUNDLED_EXTENSION_DETAILS: readonly BundledExtension[];
24
+ /**
25
+ * The bundled names, in table order: the shape this export had in 0.11.22,
26
+ * so `BUNDLED_EXTENSIONS.includes(name)` still answers "is this bundled?".
27
+ * Each one's label, description and stylesheet are in
28
+ * {@link BUNDLED_EXTENSION_DETAILS}.
29
+ */
30
+ export declare const BUNDLED_EXTENSIONS: readonly string[];
24
31
  export declare function bundledExtension(name: string): BundledExtension | undefined;
25
32
  export declare function isBundledExtension(name: string): boolean;
26
33
  /** `./x`, `../x`, `/x`, or a Windows drive path (`C:\x`). Nothing else is a path. */
@@ -17,7 +17,9 @@ export interface ProjectConfigFields {
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
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
+ * older book's `source.files` is taken out, so the list has one spelling, and
21
+ * the thresholds it spells the 0.11.22 way are written as their checks'
22
+ * options (`rewriteOldThresholds`).
21
23
  */
22
24
  export declare function setManifestFields(projectDir: string, updates: ProjectConfigFields): Promise<ProjectConfigFields>;
23
25
  /**
@@ -2,8 +2,10 @@
2
2
  * The manifest spellings 0.11.23 replaced, read for this release only so a
3
3
  * book written for 0.11.22 still builds. Every old key found is translated
4
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.
5
+ * notice. Book setup writes the new spelling of a threshold when it saves
6
+ * (`rewriteOldThresholds`). Delete this module, its call in
7
+ * `loadManifestWithPath` and the two calls of `rewriteOldThresholds`
8
+ * (manifest-config.ts, page-size.ts) in the release after 0.11.23.
7
9
  *
8
10
  * source.files -> chapters
9
11
  * validate.checks, lint.rules -> checks (off | on | info | warn | error)
@@ -14,17 +16,27 @@
14
16
  * validate.pdf.requireBookmarks,
15
17
  * validate.pdf.requireTocLinks,
16
18
  * validate.pdf.requireBleed,
17
- * validate.pdf.forbidTransparency -> the matching check, on or off
19
+ * validate.pdf.forbidTransparency -> the matching check, on or off; a level
20
+ * the same book set for it is kept
21
+ * ink.*, page.tolerance and the
22
+ * validate.assets / pdf /
23
+ * heuristics thresholds -> an option of their check:
24
+ * checks: { <id>: { <option>: value } }
25
+ * (lib/check-options.ts)
18
26
  * validate.source.<tool>: <path> -> dropped: the config file is found by
19
27
  * its usual name in the book's folder
20
28
  * 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
29
+ * validate.enabled, lint.configPath
30
+ * and the keys 0.11.22 already
31
+ * ignored -> dropped
32
+ *
33
+ * Nothing read from an old spelling can stop a book from building: 0.11.22
34
+ * passed any value through, so a threshold, a severity or a rule level it
35
+ * could not have used (`maxDecorativeLayers: many`, `severity: critical`) is
36
+ * named in the notice and ignored. The new spelling, written directly, is
37
+ * checked strictly (`resolveConfig`).
27
38
  */
39
+ import { type Document } from "yaml";
28
40
  import type { CheckLevelInput, GutterpressManifest } from "../schema/manifest.types";
29
41
  type OldCheckEntry = boolean | {
30
42
  enabled?: boolean;
@@ -61,4 +73,15 @@ export interface OldSpellings {
61
73
  * removed before 0.11.23 with an actionable error.
62
74
  */
63
75
  export declare function readOldSpellings(raw: GutterpressManifest & OldSpellings): GutterpressManifest;
76
+ /**
77
+ * Book setup's save: move every 0.11.22 threshold key in a manifest Document
78
+ * (`ink.maxTac`, `validate.pdf.minImageResolution`, `page.tolerance`, …) to
79
+ * its check's option, the way `setManifestFields` moves `source.files` to
80
+ * `chapters`. An option the new spelling already sets keeps its value; a
81
+ * block left empty goes. A value the option cannot take stays where it is,
82
+ * as written: moved, the new spelling's strict check would stop the book
83
+ * from building, and left, it is named and ignored at load. Every other key
84
+ * stays as written.
85
+ */
86
+ export declare function rewriteOldThresholds(doc: Document): void;
64
87
  export {};
@@ -151,7 +151,7 @@
151
151
  * carrying gp-left could start shifting; that is standards-tracking per
152
152
  * CLAUDE.md ("Chrome wins once it ships"), not a bug in the author's book.
153
153
  */
154
- export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-size-10 { width: 10%; max-width: 100%; }\n.gp-size-20 { width: 20%; max-width: 100%; }\n.gp-size-30 { width: 30%; max-width: 100%; }\n.gp-size-40 { width: 40%; max-width: 100%; }\n.gp-size-50 { width: 50%; max-width: 100%; }\n.gp-size-60 { width: 60%; max-width: 100%; }\n.gp-size-70 { width: 70%; max-width: 100%; }\n.gp-size-80 { width: 80%; max-width: 100%; }\n.gp-size-90 { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n\n/* Math (the bundled gutterpress-math feature, math.ts): MathJax's own rules\n for the <mjx-container> SVG output it emits, from its SVG output jax's\n stylesheet, less the ones for its interactive menu and tooltips. Inert in\n a book without formulas. */\nmjx-container[jax=\"SVG\"] { direction: ltr; }\nmjx-container[jax=\"SVG\"] > svg { overflow: visible; min-height: 1px; min-width: 1px; }\nmjx-container[jax=\"SVG\"] > svg a { fill: blue; stroke: blue; }\nmjx-container[jax=\"SVG\"][display=\"true\"] { display: block; text-align: center; margin: 1em 0; }\nmjx-container[jax=\"SVG\"][display=\"true\"][width=\"full\"] { display: flex; }\nmjx-container[jax=\"SVG\"][justify=\"left\"] { text-align: left; }\nmjx-container[jax=\"SVG\"][justify=\"right\"] { text-align: right; }\nmjx-container[jax=\"SVG\"] path[data-c], mjx-container[jax=\"SVG\"] use[data-c] { stroke-width: 3; }\nmjx-assistive-mml {\n position: absolute !important;\n top: 0px; left: 0px;\n clip: rect(1px, 1px, 1px, 1px);\n padding: 1px 0px 0px 0px !important;\n border: 0px !important;\n display: block !important;\n width: auto !important;\n overflow: hidden !important;\n user-select: none;\n}\nmjx-assistive-mml[display=\"block\"] { width: 100% !important; }\ng[data-mml-node=\"merror\"] > g { fill: red; stroke: red; }\ng[data-mml-node=\"merror\"] > rect[data-background] { fill: yellow; stroke: none; }\ng[data-mml-node=\"mtable\"] > line[data-line], svg[data-table] > g > line[data-line] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > rect[data-frame], svg[data-table] > g > rect[data-frame] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed { stroke-dasharray: 140; }\ng[data-mml-node=\"mtable\"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted { stroke-linecap: round; stroke-dasharray: 0,140; }\ng[data-mml-node=\"mtable\"] > g > svg { overflow: visible; }\n";
154
+ export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-size-10 { width: 10%; max-width: 100%; }\n.gp-size-20 { width: 20%; max-width: 100%; }\n.gp-size-30 { width: 30%; max-width: 100%; }\n.gp-size-40 { width: 40%; max-width: 100%; }\n.gp-size-50 { width: 50%; max-width: 100%; }\n.gp-size-60 { width: 60%; max-width: 100%; }\n.gp-size-70 { width: 70%; max-width: 100%; }\n.gp-size-80 { width: 80%; max-width: 100%; }\n.gp-size-90 { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n\n/* Math (the bundled gutterpress-math feature, math.ts): MathJax's own rules\n for the <mjx-container> SVG output it emits, from its SVG output jax's\n stylesheet, less the ones for its interactive menu and tooltips, plus one\n bound of ours on the hidden MathML copy (#469, below). Inert in a book\n without formulas. */\nmjx-container[jax=\"SVG\"] { direction: ltr; }\nmjx-container[jax=\"SVG\"] > svg { overflow: visible; min-height: 1px; min-width: 1px; }\nmjx-container[jax=\"SVG\"] > svg a { fill: blue; stroke: blue; }\nmjx-container[jax=\"SVG\"][display=\"true\"] { display: block; text-align: center; margin: 1em 0; }\nmjx-container[jax=\"SVG\"][display=\"true\"][width=\"full\"] { display: flex; }\nmjx-container[jax=\"SVG\"][justify=\"left\"] { text-align: left; }\nmjx-container[jax=\"SVG\"][justify=\"right\"] { text-align: right; }\nmjx-container[jax=\"SVG\"] path[data-c], mjx-container[jax=\"SVG\"] use[data-c] { stroke-width: 3; }\nmjx-assistive-mml {\n position: absolute !important;\n top: 0px; left: 0px;\n clip: rect(1px, 1px, 1px, 1px);\n padding: 1px 0px 0px 0px !important;\n border: 0px !important;\n display: block !important;\n width: auto !important;\n /* Ours (#469). The browser lays the copy out in whatever math font it has,\n so it can be wider than the formula's SVG; ending a line, it crossed the\n column edge and Chromium shrank the whole print to fit. MathJax makes the\n container position: relative with the copy at its top left, so 100% is\n the formula's width. Clip and overflow still hide the copy and its MathML\n still reaches screen readers; only its box changes. Not the 1px\n visually-hidden box MathJax 2 used: its docs note VoiceOver on iOS could\n not reach a formula by touch in it. */\n max-width: 100% !important;\n overflow: hidden !important;\n user-select: none;\n}\nmjx-assistive-mml[display=\"block\"] { width: 100% !important; }\ng[data-mml-node=\"merror\"] > g { fill: red; stroke: red; }\ng[data-mml-node=\"merror\"] > rect[data-background] { fill: yellow; stroke: none; }\ng[data-mml-node=\"mtable\"] > line[data-line], svg[data-table] > g > line[data-line] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > rect[data-frame], svg[data-table] > g > rect[data-frame] { stroke-width: 70px; fill: none; }\ng[data-mml-node=\"mtable\"] > .mjx-dashed, svg[data-table] > g > .mjx-dashed { stroke-dasharray: 140; }\ng[data-mml-node=\"mtable\"] > .mjx-dotted, svg[data-table] > g > .mjx-dotted { stroke-linecap: round; stroke-dasharray: 0,140; }\ng[data-mml-node=\"mtable\"] > g > svg { overflow: visible; }\n";
155
155
  /**
156
156
  * GP_CLASSES — every class an author may legitimately write with a `gp-`
157
157
  * prefix: the `.gp-*` selectors in `GUTTERPRESS_CSS` above, the two
@@ -5,7 +5,7 @@ import type MarkdownIt from "markdown-it";
5
5
  *
6
6
  * Bundled, OPT-IN markdown feature (`gutterpress-math` in
7
7
  * `BUILTIN_OPTIONAL_PLUGINS`, renderer.ts; "Math" in
8
- * `BUNDLED_EXTENSIONS`, extension-specifier.ts), exactly like the Callouts
8
+ * `BUNDLED_EXTENSION_DETAILS`, extension-specifier.ts), exactly like the Callouts
9
9
  * feature: a book that never lists it renders `$` as the character it is.
10
10
  *
11
11
  * WHY SVG. Each formula is written as a drawing of its own glyphs, so the
@@ -9,4 +9,5 @@ import type MarkdownIt from "markdown-it";
9
9
  */
10
10
  export declare const QUOTES: Record<string, string | string[]>;
11
11
  export declare function quotesFor(language?: string): string | string[];
12
+ /** Replaces markdown-it's `replacements` rule, at its place in the core chain. */
12
13
  export declare function gpTypography(md: MarkdownIt): void;
@@ -10,6 +10,8 @@ export interface CustomPageOptions {
10
10
  width: number;
11
11
  /** Trim height in points (72pt = 1in). */
12
12
  height: number;
13
+ /** How far (pt) the built PDF may differ from it, written as the page-size check's option. Default: the preset's 0.5. */
14
+ tolerance?: number;
13
15
  }
14
16
  /** A page size in points. */
15
17
  export interface PageSizePoints {
@@ -50,9 +52,10 @@ export interface PageSetup {
50
52
  presetSizeOverridden: boolean;
51
53
  }
52
54
  /**
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.
55
+ * Write the manifest's `page:` bounds, and a `tolerance` given with them as
56
+ * the page-size check's option (one already recorded stays when none is
57
+ * given). The block's other keys stay as written; a `page:` that is not a
58
+ * map is replaced.
56
59
  */
57
60
  export declare function writePageBounds(doc: Document, page: CustomPageOptions): void;
58
61
  /** Remove the bounds a preset owns and nothing else; a `page:` left empty goes too. */
@@ -67,6 +70,8 @@ export declare function numberAt(doc: Document, keyPath: string[]): number | und
67
70
  export declare function pageSizeForChoice(choice: PageSizeChoice): PageSizePoints;
68
71
  /** Read a book's page geometry back from its manifest (and, for margins it does not set, its stylesheets). */
69
72
  export declare function readPageSetup(projectDir: string): Promise<PageSetup>;
73
+ /** @deprecated 0.11.22's call shape: pass `{ size: { preset, page } }`. */
74
+ export declare function setPageSetup(projectDir: string, choice: PageSizeChoice): Promise<PageSetup>;
70
75
  /**
71
76
  * Change a book's page geometry in the manifest. The size mirrors what the
72
77
  * wizard records — `dtrpg` / `book` carry their own bounds (the `page:` block's
@@ -74,6 +79,9 @@ export declare function readPageSetup(projectDir: string): Promise<PageSetup>;
74
79
  * other keys stay), `custom` records the typed trim. The margins go to
75
80
  * `page.margin`, validated against the size being written, else the recorded
76
81
  * one; `margins: null` removes `page.margin`. Nothing is written unless the
77
- * whole change validates.
82
+ * whole change validates. A 0.11.22 caller's `{ preset, page? }` (a size
83
+ * choice, before margins joined it) is read as `{ size: { preset, page } }`.
84
+ * The thresholds a 0.11.22 manifest spells the old way are written the new
85
+ * way (`rewriteOldThresholds`).
78
86
  */
79
87
  export declare function setPageSetup(projectDir: string, change: PageSetupChange): Promise<PageSetup>;
@@ -1,5 +1,11 @@
1
1
  /** Project-relative folder every extension a book carries lives under: vendored npm copies in `npm/`, imports beside them. */
2
2
  export declare const EXTENSIONS_DIR = "extensions";
3
+ /**
4
+ * @deprecated 0.11.22's name for the folder vendored npm extensions go in;
5
+ * the same folder as {@link EXTENSIONS_DIR}, kept so a caller of the lib keeps
6
+ * working. Books vendored before 0.11.23 keep theirs in {@link LEGACY_PLUGINS_DIR}.
7
+ */
8
+ export declare const PLUGINS_DIR = "extensions";
3
9
  /** Where books vendored before 0.11.23 keep their npm copies. Read, never written. */
4
10
  export declare const LEGACY_PLUGINS_DIR = "plugins";
5
11
  export declare const VENDORED_NPM_DIR = "npm";
@@ -7,8 +7,8 @@ import type { ResolvedConfig } from "../schema/manifest.types";
7
7
  *
8
8
  * Geometry, build and print values are overridable from the manifest, leaf
9
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
+ * preset). The check thresholds (`ink`, `validate`, `page.tolerance`) are
11
+ * overridden in the check's own `checks:` entry (lib/check-options.ts).
12
12
  */
13
13
  export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" | "language" | "preset" | "targets" | "page"> {
14
14
  /** This preset's id: what the manifest's `preset:` names and a resolved config reports. */
@@ -71,6 +71,7 @@ export declare function rootTokens(css: string): RootToken[];
71
71
  * added to `sheets[target]`. A null value removes the token from every
72
72
  * sheet, so what the looks set shows through. Returns the sheets' new text;
73
73
  * a sheet nothing changed comes back as it was given. Throws postcss's
74
- * `CssSyntaxError` for a sheet it cannot parse.
74
+ * `CssSyntaxError` for a sheet it cannot parse, and refuses the whole batch,
75
+ * writing nothing, when a value is not one declaration's value.
75
76
  */
76
77
  export declare function applyTokenUpdates(sheets: readonly string[], target: number, updates: readonly TokenUpdate[]): string[];
@@ -1,3 +1,4 @@
1
+ import { type ReadCommitResult } from "isomorphic-git";
1
2
  import type { ProjectCapabilities, ProjectSource } from "./project-source.ts";
2
3
  /**
3
4
  * isomorphic-git object cache scoped to ONE operation and released with it.
@@ -182,6 +183,32 @@ export declare function hasPendingChanges(dir: string, cache?: GitCache): Promis
182
183
  * as dirty (the WORKDIR-vs-STAGE-only check silently drops it).
183
184
  */
184
185
  export declare function hasUncommittedChanges(dir: string, cache?: GitCache): Promise<boolean>;
186
+ /**
187
+ * A walk back through a repo's history in the order `git.log` lists it (the
188
+ * newest commit time first), visiting each commit once (#472). `git.log` has
189
+ * no visited set: after a merge it lists a commit again, with everything older,
190
+ * and the repeats used up a page. Starting the next page's log at the last id
191
+ * also dropped the side of a merge that id does not descend from. Shared with
192
+ * the online-only versions (`commitsAhead`, #477), which walks two tips at once.
193
+ */
194
+ export interface HistoryWalk {
195
+ /** Commits read but not listed yet, sorted so the newest is last. */
196
+ tips: ReadCommitResult[];
197
+ /** Every commit ever put in `tips`. */
198
+ queued: Set<string>;
199
+ /** `.git/shallow`: commits whose parents a shallow clone does not have. */
200
+ shallow: Set<string>;
201
+ /** Commits read ahead (see {@link nextInHistory}), not queued yet. */
202
+ readAhead: Map<string, ReadCommitResult>;
203
+ }
204
+ /** A walk back from `heads` (the history of each of them, together). */
205
+ export declare function startHistoryWalk(dir: string, cache: GitCache, heads: string[]): Promise<HistoryWalk>;
206
+ /**
207
+ * The walk's next commit, newest first; undefined once the history is done.
208
+ * `needed` is how many more commits the page needs, to bound a read-ahead
209
+ * (none for 1: a caller that cannot tell how far it will go reads one by one).
210
+ */
211
+ export declare function nextInHistory(dir: string, walk: HistoryWalk, cache: GitCache, needed: number): Promise<ReadCommitResult | undefined>;
185
212
  /**
186
213
  * Lock-free snapshot of the full working tree (stage everything + commit).
187
214
  *
@@ -14,7 +14,8 @@ export declare function relDisplay(projectDir: string, absPath: string): string;
14
14
  * BOTH the renderer (to `<link>` them) and the editor (to edit them), so they
15
15
  * can never disagree. Returns project-relative paths:
16
16
  * 1. the manifest `styles:` list, if it has any entries; else
17
- * 2. `styles/book.css`, when the project has it; else
17
+ * 2. the first conventional stylesheet the project actually has
18
+ * (`FALLBACK_PRIORITY`); else
18
19
  * 3. the first discovered `.css` anywhere we scan (deterministic); else
19
20
  * 4. `[]` — the project has no stylesheet (an honest empty, never a phantom
20
21
  * link to a missing file).
@@ -22,6 +22,12 @@ export interface VersionEntry extends SnapshotEntry {
22
22
  /** True = online too; false = on this computer only; null = no online copy to compare with. */
23
23
  backedUp: boolean | null;
24
24
  }
25
+ /** A version in the online copy that this computer has not got, with the files it changed. */
26
+ export interface OnlineVersionEntry extends VersionEntry {
27
+ changes: VersionFileChange[];
28
+ /** Changed files beyond the ones listed. */
29
+ moreChanges: number;
30
+ }
25
31
  /** How many changed files a version card lists before "and N more". */
26
32
  export declare const VERSION_CHANGES_SHOWN = 8;
27
33
  /** The size hint for a text file that changed between two versions. */
@@ -45,15 +51,25 @@ export declare function listVersionChanges(projectDir: string, oid: string): Pro
45
51
  export declare function describeVersions(projectDir: string, entries: SnapshotEntry[]): Promise<VersionEntry[]>;
46
52
  /**
47
53
  * The commits reachable from `fromOid` that `baseOid`'s history does not
48
- * have, newest first, down to (not including) their merge base. None when
49
- * the two share no history.
54
+ * have (`git rev-list fromOid ^baseOid`), each once, newest first in the
55
+ * order Version history lists versions, up to `limit`. None when the two
56
+ * share no history: that is another book's online copy, which Sync refuses.
57
+ *
58
+ * One walk back from both tips, newest commit time first (#477). What
59
+ * `baseOid` reaches is marked, and a marked commit marks its parents, even
60
+ * ones already walked: a commit both tips reach is never listed, by whatever
61
+ * path `fromOid` reaches it (a criss-cross merge has two merge bases). The
62
+ * walk goes on while an unmarked commit is queued, so a version dated before
63
+ * the commits it builds on (a collaborator's clock behind) is still found;
64
+ * then until every marked commit queued is {@link CLOCK_SKEW_SECONDS} older
65
+ * than the oldest version listed.
50
66
  */
51
- export declare function commitsAhead(dir: string, fromOid: string, baseOid: string, cache?: object, limit?: number): Promise<SnapshotEntry[]>;
67
+ export declare function commitsAhead(dir: string, fromOid: string, baseOid: string, cache?: Record<string, unknown>, limit?: number): Promise<SnapshotEntry[]>;
52
68
  /**
53
69
  * What is online that this computer has not got: fetch the book's branch
54
70
  * (remote-tracking ref only — no merge, no checkout, safe on a dirty tree)
55
- * and describe the commits above the merge base. Quiet on every failure,
56
- * reporting why, like `checkRemoteUpdates`.
71
+ * and describe the commits this computer has not got, each with the files it
72
+ * changed. Quiet on every failure, reporting why, like `checkRemoteUpdates`.
57
73
  */
58
74
  export declare function listOnlineVersions(options: {
59
75
  projectDir: string;
@@ -61,7 +77,7 @@ export declare function listOnlineVersions(options: {
61
77
  tokenStore?: TokenStore;
62
78
  httpClient?: typeof defaultGitHttp;
63
79
  }): Promise<{
64
- entries: VersionEntry[];
80
+ entries: OnlineVersionEntry[];
65
81
  reason?: "no-remote" | "auth" | "offline";
66
82
  }>;
67
83
  /**