gutterpress 0.11.17 → 0.11.18-alpha.10
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.
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +4 -2
- package/dist/{build-gxy185pv.js → build-2tgs5j4d.js} +4 -4
- package/dist/{cli-zfgvssqz.js → cli-d0cq87s5.js} +1 -1
- package/dist/{cli-a4qtctrw.js → cli-gf1ywzeg.js} +411 -50
- package/dist/{cli-ycbg7mxk.js → cli-ptez7zcc.js} +53 -5
- package/dist/{cli-8nw6d915.js → cli-ytck518d.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-fygmzevd.js → doctor-y4yg51zy.js} +2 -2
- package/dist/engine/compiler/build.d.ts +1 -1
- package/dist/{engine-yf2w9hj7.js → engine-wtnakejg.js} +1 -1
- package/dist/{engine-5d1rpxxq.js → engine-zy6fzs5a.js} +2 -2
- package/dist/{ext-c48s56jm.js → ext-t2sye4as.js} +4 -4
- package/dist/{gutterpress-viewer-ez7k4ha7.js → gutterpress-viewer-k4mpqjh0.js} +406 -14
- package/dist/{index-3k3fyrec.js → index-cf1kanp0.js} +454 -52
- package/dist/{index-vmcfahe2.js → index-jt552wtf.js} +5 -5
- package/dist/{index-09h3n36d.js → index-n8vs6pss.js} +51 -2
- package/dist/index.js +5 -3
- package/dist/lib/markdown/block-names.d.ts +82 -0
- package/dist/lib/markdown/renderer.d.ts +3 -2
- package/dist/lib/markdown/table-align.d.ts +32 -0
- package/dist/lib/presets.d.ts +11 -0
- package/dist/lib/printsafe.d.ts +1 -0
- package/dist/lib/remote-auth/sync.d.ts +21 -0
- package/dist/lib/unknown-classes.d.ts +12 -0
- package/dist/{manifest.schema-gh0jjve9.json → manifest.schema-5pzhscqh.json} +7 -1
- package/dist/{new-a9m2jwyy.js → new-pbd5zrds.js} +4 -4
- package/dist/{preflight-w3tnbtb8.js → preflight-vnkb8tj7.js} +4 -4
- package/dist/preview/file-watcher.d.ts +4 -1
- package/dist/{preview-bridge-8jhd1wpb.js → preview-bridge-evecsh4w.js} +5 -1
- package/dist/{preview-7z8tt0b4.js → preview-g1hp0jt8.js} +4 -4
- package/dist/{preview-interface-c44qk17f.js → preview-interface-2n1d9pbf.js} +77 -1
- package/dist/{preview-shell-xwn84kqr.js → preview-shell-hyp7g158.js} +9 -0
- package/dist/{publish-bbftdxh2.js → publish-vkbmf3tj.js} +4 -4
- package/dist/render.js +199 -28
- package/dist/schema/manifest.types.d.ts +11 -0
- package/dist/{validate-x1ha1gqj.js → validate-q1xj2qxn.js} +4 -4
- package/package.json +1 -1
|
@@ -148,22 +148,22 @@ import { fileURLToPath } from "node:url";
|
|
|
148
148
|
var favicon_default = "./favicon-272ga6y0.ico";
|
|
149
149
|
|
|
150
150
|
// src/assets/manifest.schema.json
|
|
151
|
-
var manifest_schema_default = "./manifest.schema-
|
|
151
|
+
var manifest_schema_default = "./manifest.schema-5pzhscqh.json";
|
|
152
152
|
|
|
153
153
|
// src/assets/preview/scripts/preview-interface.js
|
|
154
|
-
var preview_interface_default = "./preview-interface-
|
|
154
|
+
var preview_interface_default = "./preview-interface-2n1d9pbf.js";
|
|
155
155
|
|
|
156
156
|
// src/assets/preview/scripts/preview-bridge.js
|
|
157
|
-
var preview_bridge_default = "./preview-bridge-
|
|
157
|
+
var preview_bridge_default = "./preview-bridge-evecsh4w.js";
|
|
158
158
|
|
|
159
159
|
// src/assets/preview/scripts/preview-shell.js
|
|
160
|
-
var preview_shell_default = "./preview-shell-
|
|
160
|
+
var preview_shell_default = "./preview-shell-hyp7g158.js";
|
|
161
161
|
|
|
162
162
|
// profiles/CGATS21_CRPC1.icc
|
|
163
163
|
var CGATS21_CRPC1_default = "./CGATS21_CRPC1-g0e3k7kr.icc";
|
|
164
164
|
|
|
165
165
|
// src/assets/engine/gutterpress-viewer.js
|
|
166
|
-
var gutterpress_viewer_default = "./gutterpress-viewer-
|
|
166
|
+
var gutterpress_viewer_default = "./gutterpress-viewer-k4mpqjh0.js";
|
|
167
167
|
|
|
168
168
|
// src/assets/engine/gutterpress-agent.js
|
|
169
169
|
var gutterpress_agent_default = "./gutterpress-agent-cazqstr1.js";
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
stringValueAt,
|
|
20
20
|
toFolioPage,
|
|
21
21
|
wantsRecto
|
|
22
|
-
} from "./index-
|
|
22
|
+
} from "./index-jt552wtf.js";
|
|
23
23
|
import {
|
|
24
24
|
launchChromium
|
|
25
25
|
} from "./index-rzm1phg0.js";
|
|
@@ -724,7 +724,8 @@ var BUILD_DIAGNOSTIC_CODES = [
|
|
|
724
724
|
"engine.content.overheight",
|
|
725
725
|
"engine.image.low-dpi",
|
|
726
726
|
"engine.flush.margin-box",
|
|
727
|
-
"engine.page-background.unreferenced"
|
|
727
|
+
"engine.page-background.unreferenced",
|
|
728
|
+
"engine.hyphenation.unsupported"
|
|
728
729
|
];
|
|
729
730
|
async function build(opts) {
|
|
730
731
|
const log = opts.onProgress ?? (() => {});
|
|
@@ -1333,6 +1334,54 @@ ${mapCss}`;
|
|
|
1333
1334
|
if (multicol.length)
|
|
1334
1335
|
log(`audit: ${multicol.length} fragmenting multicol warning(s)`);
|
|
1335
1336
|
}
|
|
1337
|
+
{
|
|
1338
|
+
const missing = JSON.parse(await page.evaluate(`(() => {
|
|
1339
|
+
const words = new Map();
|
|
1340
|
+
const styled = new Map();
|
|
1341
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
1342
|
+
for (let n = walker.nextNode(); n; n = walker.nextNode()) {
|
|
1343
|
+
const found = n.data.match(/\\p{L}{7,}/gu);
|
|
1344
|
+
const el = n.parentElement;
|
|
1345
|
+
if (!found || !el) continue;
|
|
1346
|
+
if (!styled.has(el)) styled.set(el, getComputedStyle(el).hyphens === "auto");
|
|
1347
|
+
if (!styled.get(el)) continue;
|
|
1348
|
+
const host = el.closest("[lang]");
|
|
1349
|
+
const lang = host ? host.getAttribute("lang") : null;
|
|
1350
|
+
if (!words.has(lang)) words.set(lang, new Set());
|
|
1351
|
+
const set = words.get(lang);
|
|
1352
|
+
for (const w of found) if (set.size < 200) set.add(w);
|
|
1353
|
+
}
|
|
1354
|
+
if (!words.size) return "[]";
|
|
1355
|
+
const box = document.createElement("div");
|
|
1356
|
+
box.style.cssText = "position:absolute;left:-10000px;top:0;visibility:hidden";
|
|
1357
|
+
const probes = [];
|
|
1358
|
+
for (const [lang, set] of words) {
|
|
1359
|
+
for (const w of [...set].sort((a, b) => b.length - a.length).slice(0, 8)) {
|
|
1360
|
+
const pair = ["auto", "manual"].map((h) => {
|
|
1361
|
+
const d = document.createElement("div");
|
|
1362
|
+
if (lang !== null) d.setAttribute("lang", lang);
|
|
1363
|
+
d.style.cssText = "width:1px;font:16px/16px serif;word-break:normal;overflow-wrap:normal;hyphens:" + h;
|
|
1364
|
+
d.textContent = w;
|
|
1365
|
+
box.appendChild(d);
|
|
1366
|
+
return d;
|
|
1367
|
+
});
|
|
1368
|
+
probes.push({ lang, pair });
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
document.body.appendChild(box);
|
|
1372
|
+
const hyphenates = new Set();
|
|
1373
|
+
for (const { lang, pair } of probes)
|
|
1374
|
+
if (pair[0].offsetHeight > pair[1].offsetHeight) hyphenates.add(lang);
|
|
1375
|
+
box.remove();
|
|
1376
|
+
const ua = navigator.userAgent;
|
|
1377
|
+
const browser = (/Electron\\/[\\d.]+/.exec(ua) || /(?:HeadlessChrome|Chrome)\\/[\\d.]+/.exec(ua) || [ua])[0];
|
|
1378
|
+
return JSON.stringify([...words.keys()].filter((l) => !hyphenates.has(l)).map((lang) => ({ lang, browser })));
|
|
1379
|
+
})()`));
|
|
1380
|
+
for (const { lang, browser: browser2 } of missing)
|
|
1381
|
+
diagnose("engine.hyphenation.unsupported", `Your stylesheets set hyphens: auto, but the browser making this PDF (${browser2}) has no hyphenation dictionary for ${lang ? `"${lang}"` : "the document's language"}, so no word is hyphenated automatically and lines (and pages) break differently than in a browser that has one. Build in a browser that hyphenates, such as Chrome (which the gutterpress CLI uses), or add soft hyphens (­) where a word may break.`);
|
|
1382
|
+
if (missing.length)
|
|
1383
|
+
log(`audit: hyphenation unavailable for ${missing.length} language(s)`);
|
|
1384
|
+
}
|
|
1336
1385
|
if (bytes === undefined) {
|
|
1337
1386
|
prints++;
|
|
1338
1387
|
bytes = await printPdf(page);
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
autoSyncDelayMs,
|
|
39
39
|
checkCss,
|
|
40
40
|
checkExtensionUpdates,
|
|
41
|
+
checkRemoteUpdates,
|
|
41
42
|
checkToolAvailability,
|
|
42
43
|
cloneRepository,
|
|
43
44
|
connectGenericHost,
|
|
@@ -153,14 +154,14 @@ import {
|
|
|
153
154
|
testRemoteAccess,
|
|
154
155
|
updateExtensions,
|
|
155
156
|
validateProjectExtensions
|
|
156
|
-
} from "./index-
|
|
157
|
+
} from "./index-cf1kanp0.js";
|
|
157
158
|
import"./index-ht1bcqdd.js";
|
|
158
159
|
import {
|
|
159
160
|
BUILD_DIAGNOSTIC_CODES
|
|
160
|
-
} from "./index-
|
|
161
|
+
} from "./index-n8vs6pss.js";
|
|
161
162
|
import {
|
|
162
163
|
BuildError
|
|
163
|
-
} from "./index-
|
|
164
|
+
} from "./index-jt552wtf.js";
|
|
164
165
|
import {
|
|
165
166
|
DEFAULT_PRINT_OPTS,
|
|
166
167
|
assertMilestone,
|
|
@@ -318,6 +319,7 @@ export {
|
|
|
318
319
|
connectGenericHost,
|
|
319
320
|
cloneRepository,
|
|
320
321
|
checkToolAvailability,
|
|
322
|
+
checkRemoteUpdates,
|
|
321
323
|
checkExtensionUpdates,
|
|
322
324
|
checkCss,
|
|
323
325
|
capabilitiesFor,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `data-name` — a stable hook for targeting ONE block from a stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* A book's markdown holds content and semantic markers; its look lives in the
|
|
5
|
+
* plugin's and the book's CSS. What CSS needs, to restyle a single block
|
|
6
|
+
* without a markdown edit, is something on that block to select by. Class
|
|
7
|
+
* names are shared by every instance, an `id` is something the author must
|
|
8
|
+
* type, and position selectors break on any insertion. So every block this
|
|
9
|
+
* pipeline can name gets a `data-name`:
|
|
10
|
+
*
|
|
11
|
+
* [data-name="sporos"] { break-inside: avoid; }
|
|
12
|
+
* img[data-name="founder-house"] { float: right; width: 40%; }
|
|
13
|
+
*
|
|
14
|
+
* Plain HTML: a `data-*` attribute is standard, selected with the standard
|
|
15
|
+
* attribute selector, and nothing here is a behaviour a future browser
|
|
16
|
+
* feature would replace. The viewer never reads it; it exists for the
|
|
17
|
+
* author's CSS.
|
|
18
|
+
*
|
|
19
|
+
* WHAT GETS A NAME
|
|
20
|
+
*
|
|
21
|
+
* Marker-opened blocks (`.chapter`, `.spread`, `.page`, `.section`, every
|
|
22
|
+
* declared marker, `@columns` included, their variants too) take, in order:
|
|
23
|
+
* 1. the marker's explicit `name=` (core already emits it as `data-name`);
|
|
24
|
+
* 2. its `label=`, slugged;
|
|
25
|
+
* 3. the slug of the block's OWN first heading: the first heading whose
|
|
26
|
+
* innermost marker block is this one, so a `@page` that holds a
|
|
27
|
+
* `@section` does not borrow the section's title and the two never
|
|
28
|
+
* share a name;
|
|
29
|
+
* 4. nothing. A block with no explicit name, label or own heading stays
|
|
30
|
+
* unnamed rather than getting a position-based name that moves when
|
|
31
|
+
* a block is inserted.
|
|
32
|
+
* Headings take an explicit `{name=…}`, else the slug of their own text.
|
|
33
|
+
* Images take an explicit `{name=…}`, else the slug of the file's basename
|
|
34
|
+
* without its extension (see {@link nameFromSrc}, applied in images.ts).
|
|
35
|
+
*
|
|
36
|
+
* SLUGS: lower-case, accents folded to ASCII, apostrophes (' and ’) dropped,
|
|
37
|
+
* every run of other characters becomes one `-`, trimmed. `slug.ts`'s `slugify` plus a few letters NFKD
|
|
38
|
+
* does not decompose.
|
|
39
|
+
*
|
|
40
|
+
* COLLISIONS. Names are unique within one source file, because a file is
|
|
41
|
+
* rendered on its own (a book is the concatenation of its files):
|
|
42
|
+
* - blocks share ONE namespace, so `[data-name="x"]` is exactly one block
|
|
43
|
+
* in a file whatever its kind; headings have their own namespace, so a
|
|
44
|
+
* section and its title can both be `sporos`. Pair the attribute with an
|
|
45
|
+
* element (`h3[data-name="sporos"]`) or a class to say which;
|
|
46
|
+
* - a later duplicate gets `-2`, `-3`, … in document order. Explicit names
|
|
47
|
+
* are authored and never rewritten; they are reserved first, so a
|
|
48
|
+
* derived name steps around them;
|
|
49
|
+
* - images are NOT numbered: two images from the same file share a name,
|
|
50
|
+
* which is usually what a book wants.
|
|
51
|
+
* A book that needs a name unique across files scopes the selector by chapter
|
|
52
|
+
* (`#ch-06 [data-name="x"]`) or types `name=`.
|
|
53
|
+
*
|
|
54
|
+
* CONTINUATIONS. A `gp-continued` section (made by `@continue`) is the SAME
|
|
55
|
+
* block, so it keeps its original's `data-name` and carries the
|
|
56
|
+
* `gp-continued` class; `:not(.gp-continued)` picks the first piece. It does
|
|
57
|
+
* NOT repeat the `id` (an id names one element; see markers.js).
|
|
58
|
+
*
|
|
59
|
+
* NO `id`s ARE ADDED. Ids feed `target-counter()` and links; inventing them
|
|
60
|
+
* would change what a document links to.
|
|
61
|
+
*
|
|
62
|
+
* Runs last among the built-in core rules and before any loaded plugin's own
|
|
63
|
+
* rules, so it sees `markdown-it-attrs`' result and a plugin sees the names.
|
|
64
|
+
*/
|
|
65
|
+
import type MarkdownIt from "markdown-it";
|
|
66
|
+
import type Token from "markdown-it/lib/token.mjs";
|
|
67
|
+
/** The `data-name` slug of `text`: `""` when nothing usable is left. */
|
|
68
|
+
export declare function nameSlug(text: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* The image name for `src`: its file's basename without extension, slugged.
|
|
71
|
+
* `images/chapter-00/Founder%20House.png?v=2` is `founder-house`. A `data:`
|
|
72
|
+
* URI has no file name, so it has none.
|
|
73
|
+
*/
|
|
74
|
+
export declare function nameFromSrc(src: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* The author's explicit name on a token: `data-name`, or the `name` a
|
|
77
|
+
* `{name=…}` brace leaves behind (not a valid attribute on these elements, so
|
|
78
|
+
* it is moved to `data-name`). `""` when there is none.
|
|
79
|
+
*/
|
|
80
|
+
export declare function takeExplicitName(token: Token): string;
|
|
81
|
+
/** Register the rule. Called by `createMarkdownRenderer` right after the marker plugin. */
|
|
82
|
+
export declare function gpBlockNames(md: MarkdownIt): void;
|
|
@@ -89,8 +89,9 @@ export interface GutterpressMarkerLabel {
|
|
|
89
89
|
*
|
|
90
90
|
* The close token has no meta. A `section: true` marker produces the core
|
|
91
91
|
* `layout_section_open` / `layout_section_close` pair instead; its open token
|
|
92
|
-
* carries `{ line, component, kind, variant, attrs }`
|
|
93
|
-
*
|
|
92
|
+
* carries `{ line, component, kind, variant, attrs }` and a plain `@section`'s
|
|
93
|
+
* carries only `{ line }`; a `@continue` reopening of either adds
|
|
94
|
+
* `continued: true`.
|
|
94
95
|
*
|
|
95
96
|
* See `markers.js`'s header comment for a worked example and
|
|
96
97
|
* `buildDeclaredMarkerRegistry` for the full validation/resolution contract
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table column alignment as `data-align`, not an inline style.
|
|
3
|
+
*
|
|
4
|
+
* markdown-it writes `|:--|--:|` as `style="text-align:left"` on every cell.
|
|
5
|
+
* An inline style beats every stylesheet, so a book could never restyle a
|
|
6
|
+
* column without `!important`. The same fact as a plain attribute,
|
|
7
|
+
* `data-align="left|center|right"`, is applied by {@link TABLE_ALIGN_CSS} and
|
|
8
|
+
* loses to the book's own rules.
|
|
9
|
+
*
|
|
10
|
+
* Only markdown-it's own exact `text-align:` value is converted. An author's
|
|
11
|
+
* own `{style=…}` on a cell is left untouched.
|
|
12
|
+
*/
|
|
13
|
+
import type MarkdownIt from "markdown-it";
|
|
14
|
+
export declare function gpTableAlign(md: MarkdownIt): void;
|
|
15
|
+
/**
|
|
16
|
+
* The rules that apply `data-align`. `assemble.ts` injects them UNLAYERED,
|
|
17
|
+
* just before the book's own stylesheets, and with the natural specificity of
|
|
18
|
+
* an element plus an attribute (0,1,1). That puts them where the inline style
|
|
19
|
+
* they replace sat against the two things that used to lose to it:
|
|
20
|
+
*
|
|
21
|
+
* - a plugin's default (`th { text-align: left }`, always in a cascade
|
|
22
|
+
* layer): unlayered beats layered, so a markdown `:-:` still wins;
|
|
23
|
+
* - a book's generic element rule (`th { text-align: left }`, specificity
|
|
24
|
+
* 0,0,1): the attribute selector outranks it, so the same book prints the
|
|
25
|
+
* same. Measured on the user guide and the design guide, whose `th` rule
|
|
26
|
+
* centred headers only until a layered version of these rules let it win.
|
|
27
|
+
*
|
|
28
|
+
* Unlike an inline style they can be overridden without `!important`: any
|
|
29
|
+
* book rule at least as specific that comes later wins, e.g.
|
|
30
|
+
* `td[data-align="right"] { text-align: center }` or `.stats td { … }`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const TABLE_ALIGN_CSS = "\nth[data-align=\"left\"], td[data-align=\"left\"] { text-align: left; }\nth[data-align=\"center\"], td[data-align=\"center\"] { text-align: center; }\nth[data-align=\"right\"], td[data-align=\"right\"] { text-align: right; }\n";
|
package/dist/lib/presets.d.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface VendorPreset extends Omit<ResolvedConfig, "title" | "authors" |
|
|
|
21
21
|
/** The built-in preset ids, in the order pickers should offer them. */
|
|
22
22
|
export declare const PRESET_IDS: readonly ["dtrpg", "book", "custom"];
|
|
23
23
|
export type PresetId = (typeof PRESET_IDS)[number];
|
|
24
|
+
/**
|
|
25
|
+
* DriveThruRPG print-on-demand interior. Where each value comes from:
|
|
26
|
+
*
|
|
27
|
+
* SPEC = DriveThru Partners, "Quick Specifications for Print Books"
|
|
28
|
+
* (help.drivethrupartners.com/hc/en-us/articles/12780800178583)
|
|
29
|
+
* GUIDE = third-party DriveThruRPG layout guides (consistent with SPEC's
|
|
30
|
+
* bleed rule; not stated by DriveThru itself)
|
|
31
|
+
* CONVENTION = Gutterpress's own choice or a value DERIVED from a spec
|
|
32
|
+
* requirement (e.g. PDF/X-1a forbids transparency) — NOT a DriveThru
|
|
33
|
+
* requirement in its own right.
|
|
34
|
+
*/
|
|
24
35
|
export declare const DTRPG_PRESET: VendorPreset;
|
|
25
36
|
/**
|
|
26
37
|
* A neutral, vendor-agnostic default for authors who aren't targeting a
|
package/dist/lib/printsafe.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const ruleRemoteUrls = "printsafe/no-remote-urls";
|
|
|
2
2
|
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
|
+
export declare const rulePreviewCannotFollow = "printsafe/preview-cannot-follow";
|
|
5
6
|
export interface PrintSafeWarning {
|
|
6
7
|
rule: string;
|
|
7
8
|
severity: "error" | "warning";
|
|
@@ -61,3 +61,24 @@ export declare function refreshRemoteCopies(options: {
|
|
|
61
61
|
refreshed: boolean;
|
|
62
62
|
reason?: "no-remote" | "auth" | "offline";
|
|
63
63
|
}>;
|
|
64
|
+
/**
|
|
65
|
+
* Has the book's branch moved online since this copy last heard from it?
|
|
66
|
+
* Reads only the server's advertised refs (`listServerRefs`: no objects
|
|
67
|
+
* downloaded, no history walked, nothing written), so it is cheap enough to
|
|
68
|
+
* poll and safe on a dirty tree. The branch's online tip is compared with the
|
|
69
|
+
* remote-tracking ref (the last tip this copy fetched or pushed) and with the
|
|
70
|
+
* local tip: if it matches neither, someone else sent changes the author has
|
|
71
|
+
* not got. A local copy ahead of the remote is not "updates available".
|
|
72
|
+
*
|
|
73
|
+
* Best-effort like refreshRemoteCopies: it never throws; it reports why it
|
|
74
|
+
* could not tell.
|
|
75
|
+
*/
|
|
76
|
+
export declare function checkRemoteUpdates(options: {
|
|
77
|
+
projectDir: string;
|
|
78
|
+
credential?: HostCredential;
|
|
79
|
+
tokenStore?: TokenStore;
|
|
80
|
+
httpClient?: typeof defaultGitHttp;
|
|
81
|
+
}): Promise<{
|
|
82
|
+
available: boolean;
|
|
83
|
+
reason?: "no-remote" | "auth" | "offline";
|
|
84
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type Token from "markdown-it/lib/token.mjs";
|
|
2
|
+
/** One class as the markdown uses it: where it first appears, and how often. */
|
|
3
|
+
export interface ClassUse {
|
|
4
|
+
line: number;
|
|
5
|
+
count: number;
|
|
6
|
+
}
|
|
7
|
+
/** The author-written classes the tokens carry (`gp-*` and pipeline-emitted ones left out), keyed by class. */
|
|
8
|
+
export declare function classesInTokens(tokens: readonly Token[]): Map<string, ClassUse>;
|
|
9
|
+
/** Add every class a selector in `css` names to `into`. Unparseable CSS contributes what a text scan finds. */
|
|
10
|
+
export declare function addCssClasses(css: string, into: Set<string>): void;
|
|
11
|
+
/** The known class `cls` is most likely a typo of, or null: one edit for short names, two for longer ones. */
|
|
12
|
+
export declare function nearestKnownClass(cls: string, known: ReadonlySet<string>): string | null;
|
|
@@ -200,6 +200,12 @@
|
|
|
200
200
|
"description": "Allowed deviation, in points, between the expected and the actual page size.",
|
|
201
201
|
"default": 0.5,
|
|
202
202
|
"minimum": 0
|
|
203
|
+
},
|
|
204
|
+
"bleed": {
|
|
205
|
+
"type": "number",
|
|
206
|
+
"description": "A bleed, in points, that the page width x height ALREADY CONTAINS on the top, bottom and OUTER edges (none on the binding edge) — how DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in book). Preview overlay only: it tells the margin & bleed overlay where the trim is and changes nothing in the PDF. CSS `@page { bleed }` wins when the stylesheet declares one. Preset default: dtrpg 9 (0.125in); book and custom none.",
|
|
207
|
+
"minimum": 0,
|
|
208
|
+
"examples": [9]
|
|
203
209
|
}
|
|
204
210
|
}
|
|
205
211
|
},
|
|
@@ -237,7 +243,7 @@
|
|
|
237
243
|
},
|
|
238
244
|
"rules": {
|
|
239
245
|
"type": "object",
|
|
240
|
-
"description": "Per-rule level overrides, keyed by rule id: a printsafe rule (printsafe/no-risky-print-effects, printsafe/page-containment, printsafe/no-remote-urls) or a check id such as source.accessibility.heading-order. \"off\" silences the rule; \"warn\" / \"error\" set its severity. An id that matches no rule is ignored. To silence one site instead, put a `gutterpress-disable-next-line <rule-id> -- reason` comment (CSS `/* */` or markdown `<!-- -->`) on the line above it.",
|
|
246
|
+
"description": "Per-rule level overrides, keyed by rule id: a printsafe rule (printsafe/no-risky-print-effects, printsafe/page-containment, printsafe/preview-cannot-follow, printsafe/no-remote-urls) or a check id such as source.accessibility.heading-order. \"off\" silences the rule; \"warn\" / \"error\" set its severity. An id that matches no rule is ignored. To silence one site instead, put a `gutterpress-disable-next-line <rule-id> -- reason` comment (CSS `/* */` or markdown `<!-- -->`) on the line above it.",
|
|
241
247
|
"additionalProperties": { "enum": ["off", "warn", "error"] },
|
|
242
248
|
"examples": [{ "printsafe/no-risky-print-effects": "off" }]
|
|
243
249
|
}
|
|
@@ -7,17 +7,17 @@ import {
|
|
|
7
7
|
TARGET_IDS,
|
|
8
8
|
scaffoldExtension,
|
|
9
9
|
scaffoldProject
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-gf1ywzeg.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveGhostscript
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-d0cq87s5.js";
|
|
14
14
|
import {
|
|
15
15
|
UsageError,
|
|
16
16
|
rejectExtraPositionals,
|
|
17
17
|
rejectUnknownFlags
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-ytck518d.js";
|
|
19
19
|
import"./cli-ht1bcqdd.js";
|
|
20
|
-
import"./cli-
|
|
20
|
+
import"./cli-ptez7zcc.js";
|
|
21
21
|
import {
|
|
22
22
|
EXIT_CODES
|
|
23
23
|
} from "./cli-46ycxe6r.js";
|
|
@@ -2,17 +2,17 @@ import {
|
|
|
2
2
|
executeValidation,
|
|
3
3
|
publishTargetFor,
|
|
4
4
|
reportMissingTools
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-gf1ywzeg.js";
|
|
6
6
|
import {
|
|
7
7
|
log
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-d0cq87s5.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-ytck518d.js";
|
|
14
14
|
import"./cli-ht1bcqdd.js";
|
|
15
|
-
import"./cli-
|
|
15
|
+
import"./cli-ptez7zcc.js";
|
|
16
16
|
import {
|
|
17
17
|
BuildError,
|
|
18
18
|
EXIT_CODES
|
|
@@ -22,7 +22,7 @@ import type { PrintPadding } from '../engine/shared/synthesis.ts';
|
|
|
22
22
|
* no per-source wrappers or forced breaks are added, so preview pagination
|
|
23
23
|
* never diverges from the PDF where a source file begins mid-page.
|
|
24
24
|
*/
|
|
25
|
-
export declare function injectPreviewScripts(html: string): string;
|
|
25
|
+
export declare function injectPreviewScripts(html: string, insetBleedPt?: number): string;
|
|
26
26
|
/**
|
|
27
27
|
* Generate HTML from markdown and write book.html to the temp directory.
|
|
28
28
|
* The desktop's iframe loads `book.html` via a relative URL — same name in
|
|
@@ -45,6 +45,9 @@ export declare function generateAndWriteHtml(inputPath: string, tempDir: string,
|
|
|
45
45
|
};
|
|
46
46
|
extensions?: ResolvedExtensionConfig[];
|
|
47
47
|
print?: PrintPadding;
|
|
48
|
+
page?: {
|
|
49
|
+
bleed?: number;
|
|
50
|
+
};
|
|
48
51
|
}, cssAssets: Map<string, string>, imageRefs?: Set<string>): Promise<void>;
|
|
49
52
|
/**
|
|
50
53
|
* Chokidar 5's `ignored` matcher (regex or function) is tested against the
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// Commands map 1:1 to previewAPI methods: getTotalPages, getCurrentPage,
|
|
16
16
|
// goToPage, firstPage, prevPage, nextPage, lastPage, setViewMode, setZoom,
|
|
17
|
-
// toggleDebugMode. Plus a synthetic 'print' command that calls window.print().
|
|
17
|
+
// toggleDebugMode, setMarginOverlay(on, {color, strength}). Plus a synthetic 'print' command that calls window.print().
|
|
18
18
|
//
|
|
19
19
|
// Additional messages:
|
|
20
20
|
// parent -> iframe: { type: 'gutterpress:inject-styles', id: <attr-name>, css: <string> }
|
|
@@ -82,6 +82,10 @@
|
|
|
82
82
|
window.addEventListener('contextMenuRequested', function (e) {
|
|
83
83
|
post({ type: 'gutterpress:event', name: 'contextMenuRequested', detail: e.detail });
|
|
84
84
|
});
|
|
85
|
+
// Margin & bleed overlay result (protocol v9): {on, issues, intended, bleedPt}.
|
|
86
|
+
window.addEventListener('overlayChanged', function (e) {
|
|
87
|
+
post({ type: 'gutterpress:event', name: 'overlayChanged', detail: e.detail });
|
|
88
|
+
});
|
|
85
89
|
// In-flow block editing (protocol v8). `Requested` is the double-click entry
|
|
86
90
|
// point — the SPA answers it with a beginBlockEdit command. `Finished`
|
|
87
91
|
// carries the edited text back for an end the AUTHOR initiated inside the
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
runBuild,
|
|
8
8
|
splitOutPath,
|
|
9
9
|
startPreviewServer
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-gf1ywzeg.js";
|
|
11
11
|
import {
|
|
12
12
|
log
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-d0cq87s5.js";
|
|
14
14
|
import {
|
|
15
15
|
UsageError,
|
|
16
16
|
parseFormat,
|
|
@@ -18,9 +18,9 @@ import {
|
|
|
18
18
|
rejectExtraPositionals,
|
|
19
19
|
rejectUnknownFlags,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-ytck518d.js";
|
|
22
22
|
import"./cli-ht1bcqdd.js";
|
|
23
|
-
import"./cli-
|
|
23
|
+
import"./cli-ptez7zcc.js";
|
|
24
24
|
import {
|
|
25
25
|
BuildError
|
|
26
26
|
} from "./cli-46ycxe6r.js";
|
|
@@ -1070,6 +1070,12 @@
|
|
|
1070
1070
|
refreshPages();
|
|
1071
1071
|
currentPage = detectVisiblePage();
|
|
1072
1072
|
recordVisibleSource();
|
|
1073
|
+
// A hot-reload swap makes this frame the active one AFTER its overlay was
|
|
1074
|
+
// switched on (while hidden, where nothing is relayed): say so now.
|
|
1075
|
+
var overlay = api.getMarginOverlay();
|
|
1076
|
+
if (overlay && overlay.on) {
|
|
1077
|
+
window.dispatchEvent(new CustomEvent('overlayChanged', { detail: overlay }));
|
|
1078
|
+
}
|
|
1073
1079
|
return api.notifyPageChange();
|
|
1074
1080
|
},
|
|
1075
1081
|
|
|
@@ -1091,7 +1097,33 @@
|
|
|
1091
1097
|
// floating edit panel this replaces. A v8 lib with a pre-v8 SPA loses the
|
|
1092
1098
|
// "Edit this block" action and nothing else; a v8 SPA feature-detects on
|
|
1093
1099
|
// the version before offering it.
|
|
1094
|
-
|
|
1100
|
+
// v9: ADDED setMarginOverlay()/toggleMarginOverlay()/getMarginOverlay() and
|
|
1101
|
+
// the overlayChanged event (margin & bleed overlay, engine/viewer/
|
|
1102
|
+
// overlay.ts). A v9 lib with a pre-v9 SPA simply never turns it on; a v9
|
|
1103
|
+
// SPA feature-detects on the version before offering the toggle.
|
|
1104
|
+
// (v9 is unreleased as of this writing, so setMarginOverlay's optional
|
|
1105
|
+
// second argument — the look, below — is part of v9, not a bump.)
|
|
1106
|
+
getProtocolVersion: function () { return 9; },
|
|
1107
|
+
|
|
1108
|
+
// Margin & bleed overlay. Viewer chrome only — it adds no box to the book
|
|
1109
|
+
// and no attribute to its elements, so pagination is identical on or off.
|
|
1110
|
+
// Returns the summary {on, issues, intended, bleedPt}, or null when the
|
|
1111
|
+
// viewer predates it. `style` ({color: '#rrggbb', strength: 'subtle' |
|
|
1112
|
+
// 'normal' | 'strong'}) is optional: it only sets the viewer's own
|
|
1113
|
+
// `--gp-ov-*` tokens, and a call that carries it while the overlay is
|
|
1114
|
+
// already on restyles without repainting.
|
|
1115
|
+
setMarginOverlay: function (on, style) {
|
|
1116
|
+
var d = window.Gutterpress && window.Gutterpress.decoration;
|
|
1117
|
+
return d && typeof d.setOverlay === 'function' ? d.setOverlay(!!on, style) : null;
|
|
1118
|
+
},
|
|
1119
|
+
toggleMarginOverlay: function () {
|
|
1120
|
+
var d = window.Gutterpress && window.Gutterpress.decoration;
|
|
1121
|
+
return d && typeof d.setOverlay === 'function' ? d.setOverlay(document.body.dataset.overlay !== 'on') : null;
|
|
1122
|
+
},
|
|
1123
|
+
getMarginOverlay: function () {
|
|
1124
|
+
var d = window.Gutterpress && window.Gutterpress.decoration;
|
|
1125
|
+
return d && typeof d.overlay === 'function' ? d.overlay() : null;
|
|
1126
|
+
},
|
|
1095
1127
|
|
|
1096
1128
|
// Resolve the annotated element/selection at a viewport point (protocol
|
|
1097
1129
|
// v4). Pure read; see buildContextTarget() above for the full contract.
|
|
@@ -1596,4 +1628,48 @@
|
|
|
1596
1628
|
// The Gutterpress engine viewer dispatches this when its pagination
|
|
1597
1629
|
// completes (engine/viewer/index.ts's mount()).
|
|
1598
1630
|
window.addEventListener('gp:layout', onRenderingComplete);
|
|
1631
|
+
|
|
1632
|
+
// Margin & bleed overlay: relay the viewer's result as 'overlayChanged' (the
|
|
1633
|
+
// bridge posts it to a host toolbar) and, where no host toolbar exists, give
|
|
1634
|
+
// the standalone CLI preview its own switch: Alt+M, plus a small status pill.
|
|
1635
|
+
// "Standalone" = the shell is top level (the CLI); under the desktop app the
|
|
1636
|
+
// shell's parent is the app, which owns the toggle and the count.
|
|
1637
|
+
var standalone = false;
|
|
1638
|
+
try { standalone = window.parent === window || window.parent.parent === window.parent; } catch (_e) {}
|
|
1639
|
+
|
|
1640
|
+
function updateOverlayBadge(summary) {
|
|
1641
|
+
var badge = document.getElementById('gp-ov-badge');
|
|
1642
|
+
if (!standalone || !summary || !summary.on) {
|
|
1643
|
+
if (badge) badge.remove();
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
if (!badge) {
|
|
1647
|
+
badge = document.createElement('div');
|
|
1648
|
+
badge.id = 'gp-ov-badge';
|
|
1649
|
+
badge.setAttribute('role', 'status');
|
|
1650
|
+
badge.style.cssText = 'position:fixed;top:8px;right:8px;z-index:2147483000;pointer-events:none;' +
|
|
1651
|
+
'background:rgba(20,20,24,.88);color:#fff;font:600 11px/1.4 ui-sans-serif,system-ui,sans-serif;' +
|
|
1652
|
+
'padding:4px 10px;border-radius:999px;box-shadow:0 1px 6px rgba(0,0,0,.4)';
|
|
1653
|
+
document.documentElement.appendChild(badge);
|
|
1654
|
+
}
|
|
1655
|
+
var parts = ['Margins & bleed'];
|
|
1656
|
+
parts.push(summary.issues ? summary.issues + ' overflowing' : 'nothing overflows');
|
|
1657
|
+
if (summary.intended) parts.push(summary.intended + ' placed on purpose');
|
|
1658
|
+
if (summary.bleedInSheet) parts.push('bleed inside the page (print preset)');
|
|
1659
|
+
if (!summary.bleedPt) parts.push('no bleed set \u2014 add @page { bleed: 0.125in } to see the bleed edge');
|
|
1660
|
+
badge.textContent = parts.join(' \u00b7 ') + ' (Alt+M hides)';
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
window.addEventListener('gp:overlay', function (e) {
|
|
1664
|
+
updateOverlayBadge(e.detail);
|
|
1665
|
+
window.dispatchEvent(new CustomEvent('overlayChanged', { detail: e.detail }));
|
|
1666
|
+
});
|
|
1667
|
+
|
|
1668
|
+
if (standalone) {
|
|
1669
|
+
document.addEventListener('keydown', function (e) {
|
|
1670
|
+
if (e.code !== 'KeyM' || !e.altKey || e.ctrlKey || e.metaKey || e.defaultPrevented) return;
|
|
1671
|
+
e.preventDefault();
|
|
1672
|
+
api.toggleMarginOverlay();
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1599
1675
|
})();
|
|
@@ -106,6 +106,15 @@
|
|
|
106
106
|
else if (sourceBody.classList.contains('view-spread')) api.setViewMode('spread', true);
|
|
107
107
|
else if (sourceBody.classList.contains('view-two-column')) api.setViewMode('two-column', true);
|
|
108
108
|
if (sourceBody.classList.contains('debug')) api.toggleDebugMode();
|
|
109
|
+
// The margin & bleed overlay is a review aid the author switched on: a
|
|
110
|
+
// hot reload must not drop it.
|
|
111
|
+
// Its look (colour, strength) travels with it.
|
|
112
|
+
if (sourceBody.dataset.overlay === 'on' && api.setMarginOverlay) {
|
|
113
|
+
api.setMarginOverlay(true, {
|
|
114
|
+
color: sourceBody.style.getPropertyValue('--gp-ov-color').trim(),
|
|
115
|
+
strength: sourceBody.dataset.overlayStrength
|
|
116
|
+
});
|
|
117
|
+
}
|
|
109
118
|
}
|
|
110
119
|
var zoom = source.documentElement.style.getPropertyValue('--gutterpress-zoom');
|
|
111
120
|
// Carry the fit flag too (setZoom's third argument clips sideways overflow).
|
|
@@ -11,18 +11,18 @@ import {
|
|
|
11
11
|
publishProviderFor,
|
|
12
12
|
resolvePublishFormat,
|
|
13
13
|
runPublish
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-gf1ywzeg.js";
|
|
15
15
|
import {
|
|
16
16
|
FileTokenStore,
|
|
17
17
|
log
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-d0cq87s5.js";
|
|
19
19
|
import {
|
|
20
20
|
UsageError,
|
|
21
21
|
rejectExtraPositionals,
|
|
22
22
|
rejectUnknownFlags
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-ytck518d.js";
|
|
24
24
|
import"./cli-ht1bcqdd.js";
|
|
25
|
-
import"./cli-
|
|
25
|
+
import"./cli-ptez7zcc.js";
|
|
26
26
|
import {
|
|
27
27
|
EXIT_CODES
|
|
28
28
|
} from "./cli-46ycxe6r.js";
|