gutterpress 0.11.18-alpha.3 → 0.11.18-alpha.5
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.js +2 -2
- package/dist/{build-5n7wpqg3.js → build-hc743bws.js} +4 -4
- package/dist/{cli-12aq78aq.js → cli-bx24fj78.js} +1 -1
- package/dist/{cli-zrs92eef.js → cli-dm3pa0cw.js} +2 -2
- package/dist/{cli-72r4c2k4.js → cli-dxs69beq.js} +415 -44
- package/dist/{cli-z835nehj.js → cli-q90qmcvp.js} +1 -1
- package/dist/cli.js +12 -12
- package/dist/{doctor-e3zkvkkh.js → doctor-vq5xetqk.js} +2 -2
- package/dist/{engine-17gjf7t0.js → engine-vvtkew4q.js} +1 -1
- package/dist/{engine-68avsbe2.js → engine-wjz5yhmp.js} +2 -2
- package/dist/{ext-rcf6hfhq.js → ext-zspsqk4y.js} +4 -4
- package/dist/{index-q88g67zg.js → index-89zg3jtk.js} +1 -1
- package/dist/{index-b81jc99n.js → index-pb4fj8m2.js} +2 -2
- package/dist/{index-rpc74pqb.js → index-r2ry6j3x.js} +416 -45
- package/dist/index.js +3 -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/printsafe.d.ts +2 -0
- package/dist/lib/unknown-classes.d.ts +12 -0
- package/dist/{manifest.schema-8m82f37q.json → manifest.schema-nc30dyfs.json} +1 -1
- package/dist/{new-6pm52vh7.js → new-zrvm9vpe.js} +4 -4
- package/dist/{preflight-d84a2ab9.js → preflight-h003cdh9.js} +4 -4
- package/dist/{preview-9s4ktkbk.js → preview-xd02xc2h.js} +4 -4
- package/dist/{publish-wcrtgxgk.js → publish-1yzay4z4.js} +4 -4
- package/dist/render.js +199 -28
- package/dist/{theme-j2bagrfx.css → theme-5729pyq1.css} +4 -1
- package/dist/{validate-q9c00m8g.js → validate-gmma4d2t.js} +4 -4
- package/package.json +1 -1
package/dist/api/index.js
CHANGED
|
@@ -136,11 +136,11 @@ import {
|
|
|
136
136
|
testRemoteAccess,
|
|
137
137
|
updateExtensions,
|
|
138
138
|
validateProjectExtensions
|
|
139
|
-
} from "../index-
|
|
139
|
+
} from "../index-r2ry6j3x.js";
|
|
140
140
|
import"../index-ht1bcqdd.js";
|
|
141
141
|
import {
|
|
142
142
|
BuildError
|
|
143
|
-
} from "../index-
|
|
143
|
+
} from "../index-pb4fj8m2.js";
|
|
144
144
|
import {
|
|
145
145
|
DEFAULT_PRINT_OPTS,
|
|
146
146
|
assertMilestone,
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runBuild,
|
|
3
3
|
splitOutPath
|
|
4
|
-
} from "./cli-
|
|
4
|
+
} from "./cli-dxs69beq.js";
|
|
5
5
|
import {
|
|
6
6
|
log
|
|
7
|
-
} from "./cli-
|
|
7
|
+
} from "./cli-bx24fj78.js";
|
|
8
8
|
import {
|
|
9
9
|
UsageError,
|
|
10
10
|
parseFormat,
|
|
11
11
|
parsePdfxFlavor,
|
|
12
12
|
rejectExtraPositionals,
|
|
13
13
|
rejectUnknownFlags
|
|
14
|
-
} from "./cli-
|
|
14
|
+
} from "./cli-q90qmcvp.js";
|
|
15
15
|
import"./cli-ht1bcqdd.js";
|
|
16
|
-
import"./cli-
|
|
16
|
+
import"./cli-dm3pa0cw.js";
|
|
17
17
|
import {
|
|
18
18
|
BuildError
|
|
19
19
|
} from "./cli-46ycxe6r.js";
|
|
@@ -1116,7 +1116,7 @@ import { fileURLToPath } from "node:url";
|
|
|
1116
1116
|
var favicon_default = "./favicon-272ga6y0.ico";
|
|
1117
1117
|
|
|
1118
1118
|
// src/assets/manifest.schema.json
|
|
1119
|
-
var manifest_schema_default = "./manifest.schema-
|
|
1119
|
+
var manifest_schema_default = "./manifest.schema-nc30dyfs.json";
|
|
1120
1120
|
|
|
1121
1121
|
// src/assets/preview/scripts/preview-interface.js
|
|
1122
1122
|
var preview_interface_default = "./preview-interface-2n1d9pbf.js";
|
|
@@ -1209,7 +1209,7 @@ var page_rules_default = "./page-rules-nsj3aw42.css";
|
|
|
1209
1209
|
var book_default = "./book-ted3tzrp.css";
|
|
1210
1210
|
|
|
1211
1211
|
// src/assets/themes/clean-book/theme.css
|
|
1212
|
-
var theme_default = "./theme-
|
|
1212
|
+
var theme_default = "./theme-5729pyq1.css";
|
|
1213
1213
|
|
|
1214
1214
|
// src/assets/themes/clean-book/package.json
|
|
1215
1215
|
var package_default3 = "./package-97gm31sg.json";
|
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
stampCreator,
|
|
14
14
|
stripAnnotations,
|
|
15
15
|
warn
|
|
16
|
-
} from "./cli-
|
|
16
|
+
} from "./cli-bx24fj78.js";
|
|
17
17
|
import {
|
|
18
18
|
DEBOUNCE,
|
|
19
19
|
UsageError,
|
|
20
20
|
resolvePort
|
|
21
|
-
} from "./cli-
|
|
21
|
+
} from "./cli-q90qmcvp.js";
|
|
22
22
|
import {
|
|
23
23
|
blackTextToGray
|
|
24
24
|
} from "./cli-ht1bcqdd.js";
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
getAssetPath,
|
|
28
28
|
getAssetsDir,
|
|
29
29
|
lastPageMode
|
|
30
|
-
} from "./cli-
|
|
30
|
+
} from "./cli-dm3pa0cw.js";
|
|
31
31
|
import {
|
|
32
32
|
BuildError,
|
|
33
33
|
EXIT_CODES
|
|
@@ -826,10 +826,11 @@ function parseMarkerLine(line, declaredWords) {
|
|
|
826
826
|
const kind = head.slice(1);
|
|
827
827
|
if (!KNOWN_KINDS.includes(kind) && !(declaredWords && declaredWords.has(kind)))
|
|
828
828
|
return null;
|
|
829
|
-
if (kind === "
|
|
829
|
+
if (kind === "continue" || kind.startsWith("end-")) {
|
|
830
830
|
return { kind, name: null, attrs: {} };
|
|
831
831
|
}
|
|
832
|
-
const
|
|
832
|
+
const isBreak = kind === "page-break" || kind === "column-break";
|
|
833
|
+
const body = isBreak ? tokens.slice(1).filter((token) => !isBareToken(token)) : tokens.slice(1);
|
|
833
834
|
const hasExplicitAttrsOrShorthand = body.some((token) => token.includes("=") || token.startsWith(".") || token.startsWith("#"));
|
|
834
835
|
const bareTokens = body.filter(isBareToken);
|
|
835
836
|
let name = null;
|
|
@@ -897,6 +898,8 @@ function parseMarkerLine(line, declaredWords) {
|
|
|
897
898
|
}
|
|
898
899
|
if (classes.length)
|
|
899
900
|
attrs.class = classes.join(" ");
|
|
901
|
+
if (isBreak)
|
|
902
|
+
return { kind, name: null, attrs };
|
|
900
903
|
const marker = { kind, name, attrs };
|
|
901
904
|
if (hasAmbiguousBareToken)
|
|
902
905
|
marker.__ambiguousBareToken = true;
|
|
@@ -1079,6 +1082,11 @@ function attachDataAttrs(token, kind, name, attrs) {
|
|
|
1079
1082
|
token.attrSet(k.startsWith("data-") ? k : `data-${k}`, v);
|
|
1080
1083
|
}
|
|
1081
1084
|
}
|
|
1085
|
+
function attachBreak(token, baseClass, attrs = {}) {
|
|
1086
|
+
addClasses(token, baseClass, attrs.class);
|
|
1087
|
+
token.attrSet("aria-hidden", "true");
|
|
1088
|
+
attachDataAttrs(token, "break", null, attrs);
|
|
1089
|
+
}
|
|
1082
1090
|
var DECLARED_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
1083
1091
|
function validateDeclaredMarkerName(name, pluginName) {
|
|
1084
1092
|
if (!DECLARED_NAME_RE.test(name)) {
|
|
@@ -1635,7 +1643,11 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1635
1643
|
attachDataAttrs(t, component.kind, component.variant, meta.attrs || {});
|
|
1636
1644
|
else
|
|
1637
1645
|
attachDataAttrs(t, "section", meta.name, meta.attrs || {});
|
|
1638
|
-
t.meta =
|
|
1646
|
+
t.meta = {
|
|
1647
|
+
line: meta.__line,
|
|
1648
|
+
...component || {},
|
|
1649
|
+
...meta.__continued ? { continued: true } : {}
|
|
1650
|
+
};
|
|
1639
1651
|
stack.open({
|
|
1640
1652
|
kind: "section",
|
|
1641
1653
|
classes: meta.attrs && meta.attrs.class || "",
|
|
@@ -1733,12 +1745,12 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1733
1745
|
drainDeclaredFrames(keep);
|
|
1734
1746
|
const contMeta = {
|
|
1735
1747
|
name: section.meta.name,
|
|
1736
|
-
attrs: { ...section.meta.attrs || {} }
|
|
1748
|
+
attrs: { ...section.meta.attrs || {} },
|
|
1749
|
+
__continued: true
|
|
1737
1750
|
};
|
|
1738
|
-
|
|
1751
|
+
delete contMeta.attrs.id;
|
|
1752
|
+
if (section.meta.component)
|
|
1739
1753
|
contMeta.__component = section.meta.component;
|
|
1740
|
-
contMeta.__continued = true;
|
|
1741
|
-
}
|
|
1742
1754
|
contMeta.__line = line;
|
|
1743
1755
|
const cls = (contMeta.attrs.class || "").split(/\s+/).filter(Boolean);
|
|
1744
1756
|
if (!cls.includes("gp-continued"))
|
|
@@ -1751,16 +1763,14 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1751
1763
|
if (kind === "page-break") {
|
|
1752
1764
|
const t = new state.Token("layout_page_break", "div", 0);
|
|
1753
1765
|
t.meta = { line };
|
|
1754
|
-
t
|
|
1755
|
-
t.attrSet("aria-hidden", "true");
|
|
1766
|
+
attachBreak(t, "gp-page-break", meta.attrs);
|
|
1756
1767
|
out.push(t);
|
|
1757
1768
|
continue;
|
|
1758
1769
|
}
|
|
1759
1770
|
if (kind === "column-break") {
|
|
1760
1771
|
const t = new state.Token("layout_column_break", "div", 0);
|
|
1761
1772
|
t.meta = { line };
|
|
1762
|
-
t
|
|
1763
|
-
t.attrSet("aria-hidden", "true");
|
|
1773
|
+
attachBreak(t, "gp-column-break", meta.attrs);
|
|
1764
1774
|
out.push(t);
|
|
1765
1775
|
continue;
|
|
1766
1776
|
}
|
|
@@ -1824,24 +1834,10 @@ function plugin(md, pluginOptions = {}) {
|
|
|
1824
1834
|
}
|
|
1825
1835
|
});
|
|
1826
1836
|
}
|
|
1827
|
-
|
|
1828
|
-
const cls = tokens[idx].attrGet("class") || "gp-page-break";
|
|
1829
|
-
const rangeAttr = tokens[idx].attrGet("data-source-range");
|
|
1830
|
-
const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
|
|
1831
|
-
const chapterAttr = tokens[idx].attrGet("data-chapter-src");
|
|
1832
|
-
const chapterHtml = chapterAttr ? ` data-chapter-src="${escapeAttr(chapterAttr)}"` : "";
|
|
1833
|
-
return `<div class="${escapeAttr(cls)}" aria-hidden="true"${rangeHtml}${chapterHtml}></div>
|
|
1837
|
+
const renderBreak = (tokens, idx, options2, env, self) => `<div${self.renderAttrs(tokens[idx])}></div>
|
|
1834
1838
|
`;
|
|
1835
|
-
|
|
1836
|
-
md.renderer.rules.layout_column_break =
|
|
1837
|
-
const cls = tokens[idx].attrGet("class") || "gp-column-break";
|
|
1838
|
-
const rangeAttr = tokens[idx].attrGet("data-source-range");
|
|
1839
|
-
const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
|
|
1840
|
-
const chapterAttr = tokens[idx].attrGet("data-chapter-src");
|
|
1841
|
-
const chapterHtml = chapterAttr ? ` data-chapter-src="${escapeAttr(chapterAttr)}"` : "";
|
|
1842
|
-
return `<div class="${escapeAttr(cls)}" aria-hidden="true"${rangeHtml}${chapterHtml}></div>
|
|
1843
|
-
`;
|
|
1844
|
-
};
|
|
1839
|
+
md.renderer.rules.layout_page_break = renderBreak;
|
|
1840
|
+
md.renderer.rules.layout_column_break = renderBreak;
|
|
1845
1841
|
md.renderer.rules.layout_marker = () => "";
|
|
1846
1842
|
}
|
|
1847
1843
|
var NO_MARGIN_BOXES = [
|
|
@@ -2292,6 +2288,177 @@ function gpPinScope(md) {
|
|
|
2292
2288
|
});
|
|
2293
2289
|
}
|
|
2294
2290
|
|
|
2291
|
+
// src/lib/slug.ts
|
|
2292
|
+
function slugify(name, fallback = "") {
|
|
2293
|
+
return name.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || fallback;
|
|
2294
|
+
}
|
|
2295
|
+
function prettify(slug) {
|
|
2296
|
+
const spaced = slug.replace(/[-_]+/g, " ").trim();
|
|
2297
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// src/lib/markdown/block-names.ts
|
|
2301
|
+
var BLOCK_OPEN = /^layout_(chapter|spread|page|section|component)_open$/;
|
|
2302
|
+
var BLOCK_CLOSE = /^layout_(chapter|spread|page|section|component)_close$/;
|
|
2303
|
+
var FOLDED = {
|
|
2304
|
+
ß: "ss",
|
|
2305
|
+
æ: "ae",
|
|
2306
|
+
œ: "oe",
|
|
2307
|
+
ø: "o",
|
|
2308
|
+
đ: "d",
|
|
2309
|
+
ð: "d",
|
|
2310
|
+
þ: "th",
|
|
2311
|
+
ł: "l",
|
|
2312
|
+
ı: "i"
|
|
2313
|
+
};
|
|
2314
|
+
function nameSlug(text) {
|
|
2315
|
+
return slugify(text.replace(/['’]/g, "").replace(/[ßæœøđðþłı]/gi, (c) => FOLDED[c.toLowerCase()] ?? c));
|
|
2316
|
+
}
|
|
2317
|
+
function nameFromSrc(src) {
|
|
2318
|
+
if (/^data:/i.test(src))
|
|
2319
|
+
return "";
|
|
2320
|
+
const path2 = src.split(/[?#]/, 1)[0] ?? "";
|
|
2321
|
+
const base = path2.split(/[\\/]/).pop() ?? "";
|
|
2322
|
+
let decoded = base;
|
|
2323
|
+
try {
|
|
2324
|
+
decoded = decodeURIComponent(base);
|
|
2325
|
+
} catch {}
|
|
2326
|
+
return nameSlug(decoded.replace(/\.[^.]*$/, ""));
|
|
2327
|
+
}
|
|
2328
|
+
function takeExplicitName(token) {
|
|
2329
|
+
const data = token.attrGet("data-name");
|
|
2330
|
+
const plain = token.attrGet("name");
|
|
2331
|
+
if (plain !== null && token.attrs)
|
|
2332
|
+
token.attrs = token.attrs.filter(([k]) => k !== "name");
|
|
2333
|
+
if (data)
|
|
2334
|
+
return data;
|
|
2335
|
+
if (plain) {
|
|
2336
|
+
token.attrSet("data-name", plain);
|
|
2337
|
+
return plain;
|
|
2338
|
+
}
|
|
2339
|
+
return "";
|
|
2340
|
+
}
|
|
2341
|
+
function inlineText2(inline) {
|
|
2342
|
+
if (!inline || inline.type !== "inline")
|
|
2343
|
+
return "";
|
|
2344
|
+
const parts = [];
|
|
2345
|
+
for (const child of inline.children ?? []) {
|
|
2346
|
+
if (child.type === "text" || child.type === "text_special" || child.type === "code_inline" || child.type === "image") {
|
|
2347
|
+
parts.push(child.content);
|
|
2348
|
+
} else if (child.type === "softbreak" || child.type === "hardbreak") {
|
|
2349
|
+
parts.push(" ");
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
return parts.join("");
|
|
2353
|
+
}
|
|
2354
|
+
function claim(used, slug) {
|
|
2355
|
+
let name = slug;
|
|
2356
|
+
for (let n = 2;used.has(name); n++)
|
|
2357
|
+
name = `${slug}-${n}`;
|
|
2358
|
+
used.add(name);
|
|
2359
|
+
return name;
|
|
2360
|
+
}
|
|
2361
|
+
function nameBlocksAndHeadings(tokens) {
|
|
2362
|
+
const blocks = [];
|
|
2363
|
+
const open = [];
|
|
2364
|
+
const headings = [];
|
|
2365
|
+
const headingSlugs = new Map;
|
|
2366
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
2367
|
+
const tok = tokens[i];
|
|
2368
|
+
const opened = BLOCK_OPEN.exec(tok.type);
|
|
2369
|
+
if (opened) {
|
|
2370
|
+
const block = { token: tok, type: opened[1], heading: "" };
|
|
2371
|
+
blocks.push(block);
|
|
2372
|
+
open.push(block);
|
|
2373
|
+
continue;
|
|
2374
|
+
}
|
|
2375
|
+
const closed = BLOCK_CLOSE.exec(tok.type);
|
|
2376
|
+
if (closed) {
|
|
2377
|
+
for (let j = open.length - 1;j >= 0; j--) {
|
|
2378
|
+
if (open[j].type === closed[1]) {
|
|
2379
|
+
open.splice(j, 1);
|
|
2380
|
+
break;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
continue;
|
|
2384
|
+
}
|
|
2385
|
+
if (tok.type === "heading_open") {
|
|
2386
|
+
const slug = nameSlug(inlineText2(tokens[i + 1]));
|
|
2387
|
+
headings.push(tok);
|
|
2388
|
+
headingSlugs.set(tok, slug);
|
|
2389
|
+
const owner = open[open.length - 1];
|
|
2390
|
+
if (owner && !owner.heading)
|
|
2391
|
+
owner.heading = slug;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
const usedBlocks = new Set;
|
|
2395
|
+
const usedHeadings = new Set;
|
|
2396
|
+
const unnamedBlocks = new Set;
|
|
2397
|
+
for (const block of blocks) {
|
|
2398
|
+
const explicit = block.token.attrGet("data-name");
|
|
2399
|
+
if (explicit)
|
|
2400
|
+
usedBlocks.add(explicit);
|
|
2401
|
+
else
|
|
2402
|
+
unnamedBlocks.add(block);
|
|
2403
|
+
}
|
|
2404
|
+
const unnamedHeadings = [];
|
|
2405
|
+
for (const heading of headings) {
|
|
2406
|
+
const explicit = takeExplicitName(heading);
|
|
2407
|
+
if (explicit)
|
|
2408
|
+
usedHeadings.add(explicit);
|
|
2409
|
+
else
|
|
2410
|
+
unnamedHeadings.push(heading);
|
|
2411
|
+
}
|
|
2412
|
+
let previousSection = null;
|
|
2413
|
+
for (const block of blocks) {
|
|
2414
|
+
const { token } = block;
|
|
2415
|
+
if (block.type === "section") {
|
|
2416
|
+
if (token.meta?.continued && previousSection) {
|
|
2417
|
+
const inherited = previousSection.token.attrGet("data-name");
|
|
2418
|
+
if (inherited)
|
|
2419
|
+
token.attrSet("data-name", inherited);
|
|
2420
|
+
previousSection = block;
|
|
2421
|
+
continue;
|
|
2422
|
+
}
|
|
2423
|
+
previousSection = block;
|
|
2424
|
+
}
|
|
2425
|
+
if (!unnamedBlocks.has(block))
|
|
2426
|
+
continue;
|
|
2427
|
+
const slug = nameSlug(token.attrGet("data-label") ?? "") || block.heading;
|
|
2428
|
+
if (slug)
|
|
2429
|
+
token.attrSet("data-name", claim(usedBlocks, slug));
|
|
2430
|
+
}
|
|
2431
|
+
for (const heading of unnamedHeadings) {
|
|
2432
|
+
const slug = headingSlugs.get(heading);
|
|
2433
|
+
if (slug)
|
|
2434
|
+
heading.attrSet("data-name", claim(usedHeadings, slug));
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
function gpBlockNames(md) {
|
|
2438
|
+
md.core.ruler.push("gp_block_names", (state) => nameBlocksAndHeadings(state.tokens));
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
// src/lib/markdown/table-align.ts
|
|
2442
|
+
var ALIGN = /^text-align:(left|center|right)$/;
|
|
2443
|
+
function gpTableAlign(md) {
|
|
2444
|
+
md.core.ruler.after("block", "gp_table_align", (state) => {
|
|
2445
|
+
for (const token of state.tokens) {
|
|
2446
|
+
if (token.type !== "th_open" && token.type !== "td_open")
|
|
2447
|
+
continue;
|
|
2448
|
+
const match = ALIGN.exec(token.attrGet("style") ?? "");
|
|
2449
|
+
if (!match)
|
|
2450
|
+
continue;
|
|
2451
|
+
token.attrs = (token.attrs ?? []).filter(([key]) => key !== "style");
|
|
2452
|
+
token.attrSet("data-align", match[1]);
|
|
2453
|
+
}
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
var TABLE_ALIGN_CSS = `
|
|
2457
|
+
th[data-align="left"], td[data-align="left"] { text-align: left; }
|
|
2458
|
+
th[data-align="center"], td[data-align="center"] { text-align: center; }
|
|
2459
|
+
th[data-align="right"], td[data-align="right"] { text-align: right; }
|
|
2460
|
+
`;
|
|
2461
|
+
|
|
2295
2462
|
// src/lib/markdown/renderer.ts
|
|
2296
2463
|
import markdownItSourceMap from "markdown-it-source-map";
|
|
2297
2464
|
import markdownItDeflist from "markdown-it-deflist";
|
|
@@ -2458,6 +2625,10 @@ function registerImageRule(md) {
|
|
|
2458
2625
|
if (srcIdx >= 0 && token.attrs) {
|
|
2459
2626
|
const src = token.attrs[srcIdx][1];
|
|
2460
2627
|
if (src) {
|
|
2628
|
+
const explicit = takeExplicitName(token);
|
|
2629
|
+
const named = explicit || nameFromSrc(src);
|
|
2630
|
+
if (named)
|
|
2631
|
+
token.attrSet("data-name", named);
|
|
2461
2632
|
const bucket = env ?? {};
|
|
2462
2633
|
(bucket.imageRefs ??= []).push(src);
|
|
2463
2634
|
const cls = token.attrGet("class");
|
|
@@ -2833,6 +3004,8 @@ function createMarkdownRenderer(customPlugins) {
|
|
|
2833
3004
|
const declaredMarkers = buildDeclaredMarkerRegistry(declaredMarkerSources);
|
|
2834
3005
|
md.use(plugin, { declaredMarkers });
|
|
2835
3006
|
md.use(gpPinScope);
|
|
3007
|
+
md.use(gpBlockNames);
|
|
3008
|
+
md.use(gpTableAlign);
|
|
2836
3009
|
registerImageRule(md);
|
|
2837
3010
|
if (customPlugins && customPlugins.length > 0) {
|
|
2838
3011
|
applyPlugins(md, customPlugins);
|
|
@@ -4004,15 +4177,6 @@ async function rollbackNpmPluginInstall(installed) {
|
|
|
4004
4177
|
// src/lib/extension-manager.ts
|
|
4005
4178
|
import { gt as semverGt, rcompare as semverRcompare } from "semver";
|
|
4006
4179
|
|
|
4007
|
-
// src/lib/slug.ts
|
|
4008
|
-
function slugify(name, fallback = "") {
|
|
4009
|
-
return name.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || fallback;
|
|
4010
|
-
}
|
|
4011
|
-
function prettify(slug) {
|
|
4012
|
-
const spaced = slug.replace(/[-_]+/g, " ").trim();
|
|
4013
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
4014
|
-
}
|
|
4015
|
-
|
|
4016
4180
|
// src/lib/project-gitignore.ts
|
|
4017
4181
|
import { readFile as readFile5, writeFile } from "node:fs/promises";
|
|
4018
4182
|
import path4 from "node:path";
|
|
@@ -6186,6 +6350,8 @@ var ruleRemoteUrls = "printsafe/no-remote-urls";
|
|
|
6186
6350
|
var ruleRiskyProps = "printsafe/no-risky-print-effects";
|
|
6187
6351
|
var ruleSyntax = "printsafe/syntax-error";
|
|
6188
6352
|
var rulePageContainment = "printsafe/page-containment";
|
|
6353
|
+
var rulePreviewCannotFollow = "printsafe/preview-cannot-follow";
|
|
6354
|
+
var ruleEngineHyphenation = "printsafe/engine-hyphenation";
|
|
6189
6355
|
function extractUrls(value) {
|
|
6190
6356
|
const urls = [];
|
|
6191
6357
|
const re = /url\(\s*(['"]?)(.*?)\1\s*\)/gi;
|
|
@@ -6331,6 +6497,66 @@ function clipsDescendants(decl) {
|
|
|
6331
6497
|
const v = decl.value.trim().toLowerCase();
|
|
6332
6498
|
return v !== "" && !isInertValue(p, v);
|
|
6333
6499
|
}
|
|
6500
|
+
var PREVIEW_AT_RULES = new Set(["media", "supports", "layer", "scope", "container", "document"]);
|
|
6501
|
+
var FORCED_BREAK_VALUE = /^(page|left|right|recto|verso)$/i;
|
|
6502
|
+
var LEGACY_FORCED_VALUE = /^(always|left|right)$/i;
|
|
6503
|
+
function previewBlindSpot(decl) {
|
|
6504
|
+
const prop = decl.prop.toLowerCase();
|
|
6505
|
+
const value = decl.value.trim();
|
|
6506
|
+
const written = `${decl.prop}: ${decl.value}`;
|
|
6507
|
+
const isBreak = prop === "break-before" || prop === "break-after";
|
|
6508
|
+
const isLegacy = prop === "page-break-before" || prop === "page-break-after";
|
|
6509
|
+
if (!isBreak && !isLegacy && prop !== "page")
|
|
6510
|
+
return null;
|
|
6511
|
+
const rules = [];
|
|
6512
|
+
for (let p = decl.parent;p; p = p.parent) {
|
|
6513
|
+
if (p.type === "rule")
|
|
6514
|
+
rules.push(p);
|
|
6515
|
+
else if (p.type === "atrule") {
|
|
6516
|
+
const name = p.name.toLowerCase();
|
|
6517
|
+
if (!PREVIEW_AT_RULES.has(name))
|
|
6518
|
+
return null;
|
|
6519
|
+
}
|
|
6520
|
+
}
|
|
6521
|
+
if (rules.length === 0)
|
|
6522
|
+
return null;
|
|
6523
|
+
const consequence = "the PDF honours it but the live preview does not show it, so the two page counts drift apart";
|
|
6524
|
+
if (isLegacy) {
|
|
6525
|
+
if (!LEGACY_FORCED_VALUE.test(value))
|
|
6526
|
+
return null;
|
|
6527
|
+
const modern = prop.replace("page-break-", "break-");
|
|
6528
|
+
const siblings = decl.parent.nodes ?? [];
|
|
6529
|
+
if (siblings.some((n) => n.type === "decl" && n.prop.toLowerCase() === modern))
|
|
6530
|
+
return null;
|
|
6531
|
+
const keyword = /^always$/i.test(value) ? "page" : value.toLowerCase();
|
|
6532
|
+
return `"${written}" is the legacy spelling, and the live preview reads only ${modern}: ${consequence}. Write "${modern}: ${keyword}".`;
|
|
6533
|
+
}
|
|
6534
|
+
const viaVar = /\bvar\(/i.test(value);
|
|
6535
|
+
if (isBreak && !viaVar && !FORCED_BREAK_VALUE.test(value))
|
|
6536
|
+
return null;
|
|
6537
|
+
if (prop === "page" && !viaVar && (value === "" || /^auto$/i.test(value)))
|
|
6538
|
+
return null;
|
|
6539
|
+
const what = prop === "page" ? "page assignment" : "forced break";
|
|
6540
|
+
if (viaVar) {
|
|
6541
|
+
return `The ${what} "${written}" uses var(): the live preview reads it only as a literal ${prop === "page" ? "page name" : "keyword (page, left, right, recto or verso)"}, so ${consequence}. Write the literal value.`;
|
|
6542
|
+
}
|
|
6543
|
+
const parent = decl.parent;
|
|
6544
|
+
if (rules.length >= 2 || parent.type === "atrule") {
|
|
6545
|
+
const outer = rules[rules.length - 1].selector;
|
|
6546
|
+
return `The ${what} "${written}" is inside a nested rule in "${outer}": the live preview reads only top-level rules and rules inside @media, @supports, @layer, @scope, @container and @document, so ${consequence}. Move it to a flat rule of its own.`;
|
|
6547
|
+
}
|
|
6548
|
+
let prev = decl.prev();
|
|
6549
|
+
while (prev && prev.type === "comment")
|
|
6550
|
+
prev = prev.prev();
|
|
6551
|
+
if (prev && (prev.type === "rule" || prev.type === "atrule" && prev.nodes)) {
|
|
6552
|
+
return `The ${what} "${written}" comes right after a nested rule in "${rules[0].selector}": the live preview's CSS reader drops the first declaration after a nested block, so ${consequence}. Move the declaration above the nested rule.`;
|
|
6553
|
+
}
|
|
6554
|
+
return null;
|
|
6555
|
+
}
|
|
6556
|
+
function isAutoHyphenation(decl) {
|
|
6557
|
+
const prop = decl.prop.toLowerCase();
|
|
6558
|
+
return (prop === "hyphens" || prop === "-webkit-hyphens") && decl.value.trim().toLowerCase() === "auto";
|
|
6559
|
+
}
|
|
6334
6560
|
function nodeLoc(node) {
|
|
6335
6561
|
return {
|
|
6336
6562
|
line: node.source?.start?.line ?? 1,
|
|
@@ -6443,6 +6669,22 @@ function checkCssRaw(css, from) {
|
|
|
6443
6669
|
});
|
|
6444
6670
|
}
|
|
6445
6671
|
});
|
|
6672
|
+
root.walkDecls((decl) => {
|
|
6673
|
+
const message = previewBlindSpot(decl);
|
|
6674
|
+
if (message) {
|
|
6675
|
+
warnings.push({ rule: rulePreviewCannotFollow, severity: "warning", message, ...nodeLoc(decl) });
|
|
6676
|
+
}
|
|
6677
|
+
});
|
|
6678
|
+
root.walkDecls((decl) => {
|
|
6679
|
+
if (!isAutoHyphenation(decl))
|
|
6680
|
+
return;
|
|
6681
|
+
warnings.push({
|
|
6682
|
+
rule: ruleEngineHyphenation,
|
|
6683
|
+
severity: "warning",
|
|
6684
|
+
message: `"${decl.prop}: auto" depends on the browser's hyphenation dictionaries, so the desktop app and the CLI (and different operating systems) can break lines and pages differently. Use "hyphens: manual" with soft hyphens (­) for identical pages everywhere.`,
|
|
6685
|
+
...nodeLoc(decl)
|
|
6686
|
+
});
|
|
6687
|
+
});
|
|
6446
6688
|
root.walkRules((rule) => {
|
|
6447
6689
|
if (rule.parent?.type === "atrule") {
|
|
6448
6690
|
const at = rule.parent.name.toLowerCase();
|
|
@@ -7621,10 +7863,117 @@ registerCheck(check25);
|
|
|
7621
7863
|
|
|
7622
7864
|
// src/checks/source/layout-markers.ts
|
|
7623
7865
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
7866
|
+
|
|
7867
|
+
// src/lib/unknown-classes.ts
|
|
7868
|
+
import postcss4 from "postcss";
|
|
7869
|
+
var EMITTED = new Set([
|
|
7870
|
+
"chapter",
|
|
7871
|
+
"spread",
|
|
7872
|
+
"page",
|
|
7873
|
+
"section",
|
|
7874
|
+
"footnotes",
|
|
7875
|
+
"footnotes-sep",
|
|
7876
|
+
"footnote-ref",
|
|
7877
|
+
"footnote-item",
|
|
7878
|
+
"footnote-backref"
|
|
7879
|
+
]);
|
|
7880
|
+
function classesInTokens(tokens) {
|
|
7881
|
+
const uses = new Map;
|
|
7882
|
+
const note = (token, line) => {
|
|
7883
|
+
const cls = token.attrs?.find(([key]) => key === "class")?.[1];
|
|
7884
|
+
if (!cls)
|
|
7885
|
+
return;
|
|
7886
|
+
for (const c of cls.split(/\s+/)) {
|
|
7887
|
+
if (!c || c.startsWith("gp-") || EMITTED.has(c) || /^chapter-\d+$/.test(c))
|
|
7888
|
+
continue;
|
|
7889
|
+
const seen = uses.get(c);
|
|
7890
|
+
if (seen)
|
|
7891
|
+
seen.count++;
|
|
7892
|
+
else
|
|
7893
|
+
uses.set(c, { line, count: 1 });
|
|
7894
|
+
}
|
|
7895
|
+
};
|
|
7896
|
+
for (const token of tokens) {
|
|
7897
|
+
const meta = token.meta;
|
|
7898
|
+
const line = token.map ? token.map[0] + 1 : typeof meta?.line === "number" ? meta.line : 0;
|
|
7899
|
+
note(token, line);
|
|
7900
|
+
for (const child of token.children ?? [])
|
|
7901
|
+
note(child, line);
|
|
7902
|
+
}
|
|
7903
|
+
return uses;
|
|
7904
|
+
}
|
|
7905
|
+
var CLASS_RE = /\.((?:[\w --]|\\.)+)/g;
|
|
7906
|
+
function addSelectorClasses(selector, into) {
|
|
7907
|
+
const bare = selector.replace(/\[(?:[^\]"']|"[^"]*"|'[^']*')*\]/g, "").replace(/"[^"]*"|'[^']*'/g, "");
|
|
7908
|
+
for (const m of bare.matchAll(CLASS_RE)) {
|
|
7909
|
+
const name = m[1].replace(/\\(.)/g, "$1");
|
|
7910
|
+
if (!/^\d/.test(name))
|
|
7911
|
+
into.add(name);
|
|
7912
|
+
}
|
|
7913
|
+
}
|
|
7914
|
+
function addCssClasses(css, into) {
|
|
7915
|
+
try {
|
|
7916
|
+
postcss4.parse(css).walk((node) => {
|
|
7917
|
+
if (node.type === "rule")
|
|
7918
|
+
addSelectorClasses(node.selector, into);
|
|
7919
|
+
else if (node.type === "atrule") {
|
|
7920
|
+
const at = node;
|
|
7921
|
+
if (/^(scope|container)$/i.test(at.name))
|
|
7922
|
+
addSelectorClasses(at.params, into);
|
|
7923
|
+
}
|
|
7924
|
+
});
|
|
7925
|
+
} catch {
|
|
7926
|
+
addSelectorClasses(css.replace(/\/\*[\s\S]*?\*\//g, ""), into);
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
function editDistance3(a, b) {
|
|
7930
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
7931
|
+
for (let i = 1;i <= a.length; i++) {
|
|
7932
|
+
const cur = [i];
|
|
7933
|
+
for (let j = 1;j <= b.length; j++) {
|
|
7934
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
7935
|
+
}
|
|
7936
|
+
prev = cur;
|
|
7937
|
+
}
|
|
7938
|
+
return prev[b.length];
|
|
7939
|
+
}
|
|
7940
|
+
function nearestKnownClass(cls, known) {
|
|
7941
|
+
const limit = cls.length >= 6 ? 2 : 1;
|
|
7942
|
+
let best = null;
|
|
7943
|
+
let bestDistance = limit + 1;
|
|
7944
|
+
for (const candidate of known) {
|
|
7945
|
+
if (Math.abs(candidate.length - cls.length) > limit)
|
|
7946
|
+
continue;
|
|
7947
|
+
const d = editDistance3(cls, candidate);
|
|
7948
|
+
if (d > 0 && d < bestDistance) {
|
|
7949
|
+
best = candidate;
|
|
7950
|
+
bestDistance = d;
|
|
7951
|
+
}
|
|
7952
|
+
}
|
|
7953
|
+
return best;
|
|
7954
|
+
}
|
|
7955
|
+
|
|
7956
|
+
// src/checks/source/layout-markers.ts
|
|
7957
|
+
async function collectKnownClasses(ctx, plugins) {
|
|
7958
|
+
const names = new Set;
|
|
7959
|
+
addCssClasses(MARKER_CSS, names);
|
|
7960
|
+
addCssClasses(GUTTERPRESS_CSS, names);
|
|
7961
|
+
for (const p of plugins)
|
|
7962
|
+
if (p.css)
|
|
7963
|
+
addCssClasses(p.css, names);
|
|
7964
|
+
const roots = [...ctx.cssFiles ?? [], ...plugins.flatMap((p) => p.styles ?? [])];
|
|
7965
|
+
const sheets = (await collectStyleDependencies(ctx.inputDir, roots)).filter((f) => /\.css$/i.test(f));
|
|
7966
|
+
for (const sheet of sheets) {
|
|
7967
|
+
try {
|
|
7968
|
+
addCssClasses(await readFile18(sheet, "utf8"), names);
|
|
7969
|
+
} catch {}
|
|
7970
|
+
}
|
|
7971
|
+
return names;
|
|
7972
|
+
}
|
|
7624
7973
|
var check26 = {
|
|
7625
7974
|
id: "source.markdown.layout-markers",
|
|
7626
7975
|
name: "Layout Markers",
|
|
7627
|
-
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class",
|
|
7976
|
+
description: "Reports @page/@section/@chapter marker arguments Gutterpress could not understand, plus any unknown gp-* class and any other class no stylesheet mentions",
|
|
7628
7977
|
category: "source",
|
|
7629
7978
|
phase: "pre-build",
|
|
7630
7979
|
async run(ctx) {
|
|
@@ -7636,11 +7985,31 @@ var check26 = {
|
|
|
7636
7985
|
results.push(inspectionFailed(check26.id, `Plugin "${ref}" could not be loaded, so markers it defines were not checked: ${error2.message}`));
|
|
7637
7986
|
});
|
|
7638
7987
|
const md = createMarkdownRenderer(plugins);
|
|
7988
|
+
let known;
|
|
7989
|
+
const knownClasses = () => known ??= collectKnownClasses(ctx, plugins);
|
|
7639
7990
|
for (const file of files) {
|
|
7640
7991
|
try {
|
|
7641
7992
|
const content = await readFile18(file, "utf8");
|
|
7642
7993
|
const env = {};
|
|
7643
|
-
md.
|
|
7994
|
+
const tokens = md.parse(content, env);
|
|
7995
|
+
md.renderer.render(tokens, md.options, env);
|
|
7996
|
+
const uses = classesInTokens(tokens);
|
|
7997
|
+
if (uses.size > 0) {
|
|
7998
|
+
const names = await knownClasses();
|
|
7999
|
+
const warnedLines = new Set((env.layoutWarnings ?? []).map((w) => w.line));
|
|
8000
|
+
for (const [cls, use] of uses) {
|
|
8001
|
+
if (names.has(cls) || warnedLines.has(use.line))
|
|
8002
|
+
continue;
|
|
8003
|
+
const guess = nearestKnownClass(cls, names);
|
|
8004
|
+
results.push(finding(check26.id, {
|
|
8005
|
+
severity: "warning",
|
|
8006
|
+
message: `Class ".${cls}" is not in any stylesheet (Gutterpress's own, the plugins', or this book's), so it styles nothing.` + (guess ? ` Did you mean ".${guess}"?` : "") + (use.count > 1 ? ` Used ${use.count} times in this file; the first is reported.` : ""),
|
|
8007
|
+
file,
|
|
8008
|
+
...use.line > 0 ? { line: use.line } : {},
|
|
8009
|
+
code: "unknown_class"
|
|
8010
|
+
}));
|
|
8011
|
+
}
|
|
8012
|
+
}
|
|
7644
8013
|
for (const w of env.layoutWarnings ?? []) {
|
|
7645
8014
|
results.push(finding(check26.id, {
|
|
7646
8015
|
severity: w.severity ?? "warning",
|
|
@@ -8615,6 +8984,8 @@ ${GUTTERPRESS_CSS.trim()}
|
|
|
8615
8984
|
}`,
|
|
8616
8985
|
pluginCss ? `/* extension css */
|
|
8617
8986
|
${pluginCss.trim()}` : null,
|
|
8987
|
+
`/* column alignment */
|
|
8988
|
+
${TABLE_ALIGN_CSS.trim()}`,
|
|
8618
8989
|
projectCss ? `/* project css */
|
|
8619
8990
|
${projectCss.trim()}` : null
|
|
8620
8991
|
].filter(Boolean).join(`
|
|
@@ -9726,7 +10097,7 @@ class PdfOutput {
|
|
|
9726
10097
|
const rawPdf = pdfxMode ? path13.join(stage, "raw.pdf") : path13.resolve(pdfFile);
|
|
9727
10098
|
await fsp2.mkdir(path13.dirname(path13.resolve(pdfFile)), { recursive: true });
|
|
9728
10099
|
log.info("Rendering HTML to PDF via the Gutterpress engine (native Chromium pagination)");
|
|
9729
|
-
const { buildNativePdf } = await import("./engine-
|
|
10100
|
+
const { buildNativePdf } = await import("./engine-vvtkew4q.js");
|
|
9730
10101
|
const engineDiagnostics = await buildNativePdf(htmlFile, rawPdf, {
|
|
9731
10102
|
title: config.title,
|
|
9732
10103
|
author: config.authors.length > 0 ? config.authors.join(", ") : undefined,
|
|
@@ -10952,7 +11323,7 @@ import { constants as FS, existsSync as existsSync12 } from "node:fs";
|
|
|
10952
11323
|
import path18 from "node:path";
|
|
10953
11324
|
|
|
10954
11325
|
// src/lib/page-size.ts
|
|
10955
|
-
import
|
|
11326
|
+
import postcss5 from "postcss";
|
|
10956
11327
|
import { Scalar as Scalar2 } from "yaml";
|
|
10957
11328
|
function isPositivePoints(value) {
|
|
10958
11329
|
return typeof value === "number" && Number.isFinite(value) && value > 0;
|