gutterpress 0.11.21 → 0.11.22-alpha.2
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/README.md +11 -0
- package/dist/api/index.d.ts +5 -1
- package/dist/api/index.js +68 -47
- package/dist/{build-0neyk5h6.js → build-84retk4k.js} +16 -10
- package/dist/{index-sth219v1.js → cli-2ppw6q9v.js} +26 -6
- package/dist/cli-3twpesca.js +103 -0
- package/dist/cli-5et23z83.js +2089 -0
- package/dist/{cli-j79451n6.js → cli-5qhjwne5.js} +5 -76
- package/dist/cli-80t93x2r.js +75 -0
- package/dist/{cli-j0drzb6f.js → cli-885pcze8.js} +885 -5481
- package/dist/{cli-b57adna7.js → cli-9pk65fqf.js} +9 -201
- package/dist/{cli-sqkccnm7.js → cli-d8xjns66.js} +1 -102
- package/dist/cli-h3phdstf.js +200 -0
- package/dist/{index-qf7y4d85.js → cli-habkc5ng.js} +12 -258
- package/dist/cli-hndkg7bn.js +196 -0
- package/dist/cli-pn3z0y9p.js +3073 -0
- package/dist/cli.js +15 -13
- package/dist/{doctor-fptq8tvn.js → doctor-fmzpg7ag.js} +7 -4
- package/dist/engine/shared/print-area.d.ts +77 -1
- package/dist/{engine-x07q927h.js → engine-5p8tn9b4.js} +4 -3
- package/dist/{engine-0b8bt05n.js → engine-9710cd7x.js} +3 -2
- package/dist/{ext-gp624t5c.js → ext-hsdnad5c.js} +21 -14
- package/dist/extension-restore-05pae3cp.js +16 -0
- package/dist/extension-restore-3r7phztr.js +20 -0
- package/dist/{gutterpress-viewer-w7njnkj7.js → gutterpress-viewer-w9jzx6sb.js} +56 -52
- package/dist/{index-t7r8g75c.js → index-2d8j0enc.js} +4 -2
- package/dist/{cli-sth219v1.js → index-2ppw6q9v.js} +26 -6
- package/dist/{index-mr7zs8mv.js → index-3wcr69ba.js} +5579 -10055
- package/dist/{cli-5qr3z0qa.js → index-93pqkvty.js} +63 -207
- package/dist/{index-mr72vt8n.js → index-apc8w9w8.js} +55 -267
- package/dist/index-ew7cyk7c.js +2208 -0
- package/dist/index-jsz0s3dy.js +213 -0
- package/dist/index-kx03e4m4.js +3244 -0
- package/dist/index-r7jcsk6h.js +200 -0
- package/dist/index.js +71 -50
- package/dist/lib/extension-manifest.d.ts +22 -1
- package/dist/lib/markdown/assemble.d.ts +3 -2
- package/dist/lib/markdown/plugins.d.ts +20 -0
- package/dist/lib/markdown/two-versions.d.ts +32 -0
- package/dist/lib/page-size.d.ts +3 -4
- package/dist/lib/pdf-inspect.d.ts +12 -0
- package/dist/lib/project-scaffold.d.ts +3 -3
- package/dist/lib/publish/plugin-provider.d.ts +16 -0
- package/dist/lib/publish/run-publish.d.ts +13 -5
- package/dist/lib/publish/types.d.ts +34 -2
- package/dist/lib/text-diff.d.ts +43 -0
- package/dist/lib/two-versions-markers.d.ts +26 -0
- package/dist/lib/version-changes.d.ts +87 -0
- package/dist/{manifest.schema-n292sb77.json → manifest.schema-nvawq0xv.json} +2 -2
- package/dist/{new-dre6aw05.js → new-fqtt022k.js} +23 -16
- package/dist/{pdf-inspect-zdmy7qca.js → pdf-inspect-wnw4za71.js} +1 -1
- package/dist/plugins-gfvsgnxb.js +24 -0
- package/dist/plugins-nfxr831f.js +26 -0
- package/dist/{preflight-1k1zkg1p.js → preflight-gr9mscjw.js} +18 -11
- package/dist/{preview-dtrksvxf.js → preview-52w9pdps.js} +19 -13
- package/dist/{publish-g1ytnrmr.js → publish-esmcyhpb.js} +40 -23
- package/dist/render.js +706 -0
- package/dist/schema/manifest.types.d.ts +8 -3
- package/dist/{source-provider-pzmz0br9.js → source-provider-2r74a7wp.js} +2 -1
- package/dist/{source-provider-qbthnmqw.js → source-provider-qbfknhyy.js} +2 -1
- package/dist/{validate-yrrq8639.js → validate-vt1d7h6k.js} +16 -10
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -1477,6 +1477,124 @@ var GP_CLASSES = new Set([
|
|
|
1477
1477
|
"gp-continued"
|
|
1478
1478
|
]);
|
|
1479
1479
|
|
|
1480
|
+
// src/lib/two-versions-markers.ts
|
|
1481
|
+
var YOUR_VERSION_LABEL = "your version";
|
|
1482
|
+
var ONLINE_VERSION_LABEL = "online version";
|
|
1483
|
+
var TWO_VERSIONS_OPEN = `<<<<<<< ${YOUR_VERSION_LABEL}`;
|
|
1484
|
+
var TWO_VERSIONS_SEPARATOR = "=======";
|
|
1485
|
+
var TWO_VERSIONS_CLOSE = `>>>>>>> ${ONLINE_VERSION_LABEL}`;
|
|
1486
|
+
|
|
1487
|
+
// src/lib/markdown/two-versions.ts
|
|
1488
|
+
var TWO_VERSIONS_CSS = `
|
|
1489
|
+
.gp-two-versions {
|
|
1490
|
+
margin: 1em 0;
|
|
1491
|
+
border: 2px dashed #b7791f;
|
|
1492
|
+
border-radius: 6px;
|
|
1493
|
+
break-inside: avoid;
|
|
1494
|
+
}
|
|
1495
|
+
.gp-two-versions > .gp-version {
|
|
1496
|
+
padding: 0.5em 0.9em;
|
|
1497
|
+
}
|
|
1498
|
+
.gp-two-versions > .gp-version + .gp-version {
|
|
1499
|
+
border-top: 2px dashed #b7791f;
|
|
1500
|
+
}
|
|
1501
|
+
.gp-two-versions > .gp-version > .gp-version-text {
|
|
1502
|
+
margin: 0;
|
|
1503
|
+
white-space: pre-wrap;
|
|
1504
|
+
overflow-wrap: anywhere;
|
|
1505
|
+
font-size: 0.85em;
|
|
1506
|
+
}
|
|
1507
|
+
.gp-two-versions > .gp-version::before {
|
|
1508
|
+
content: attr(data-label);
|
|
1509
|
+
display: block;
|
|
1510
|
+
margin-bottom: 0.35em;
|
|
1511
|
+
font-family: system-ui, sans-serif;
|
|
1512
|
+
font-size: 0.7em;
|
|
1513
|
+
font-weight: 700;
|
|
1514
|
+
letter-spacing: 0.06em;
|
|
1515
|
+
text-transform: uppercase;
|
|
1516
|
+
color: #b7791f;
|
|
1517
|
+
}
|
|
1518
|
+
`;
|
|
1519
|
+
function lineText(state, line) {
|
|
1520
|
+
return state.src.slice(state.bMarks[line] + state.tShift[line], state.eMarks[line]);
|
|
1521
|
+
}
|
|
1522
|
+
var MARKER_LINE = /^@[a-z]/;
|
|
1523
|
+
function hasMarkerLine(state, from, to) {
|
|
1524
|
+
for (let line = from;line < to; line++) {
|
|
1525
|
+
if (MARKER_LINE.test(lineText(state, line)))
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
return false;
|
|
1529
|
+
}
|
|
1530
|
+
function pushVerbatim(state, from, to) {
|
|
1531
|
+
const raw = state.getLines(from, to, 0, false);
|
|
1532
|
+
const token = state.push("html_block", "", 0);
|
|
1533
|
+
token.map = [from, to];
|
|
1534
|
+
token.block = true;
|
|
1535
|
+
token.content = `<pre class="gp-version-text">${state.md.utils.escapeHtml(raw)}</pre>
|
|
1536
|
+
`;
|
|
1537
|
+
}
|
|
1538
|
+
function pushVersion(state, which, label, from, to) {
|
|
1539
|
+
const open = state.push("two_version_open", "div", 1);
|
|
1540
|
+
open.attrSet("class", `gp-version gp-version-${which}`);
|
|
1541
|
+
open.attrSet("data-label", label);
|
|
1542
|
+
open.map = [from, to];
|
|
1543
|
+
open.block = true;
|
|
1544
|
+
if (hasMarkerLine(state, from, to)) {
|
|
1545
|
+
pushVerbatim(state, from, to);
|
|
1546
|
+
} else {
|
|
1547
|
+
state.parentType = "container";
|
|
1548
|
+
state.lineMax = to;
|
|
1549
|
+
state.md.block.tokenize(state, from, to);
|
|
1550
|
+
}
|
|
1551
|
+
const close = state.push("two_version_close", "div", -1);
|
|
1552
|
+
close.block = true;
|
|
1553
|
+
}
|
|
1554
|
+
function twoVersions(state, startLine, endLine, silent) {
|
|
1555
|
+
if (state.sCount[startLine] - state.blkIndent >= 4)
|
|
1556
|
+
return false;
|
|
1557
|
+
if (state.src.charCodeAt(state.bMarks[startLine] + state.tShift[startLine]) !== 60)
|
|
1558
|
+
return false;
|
|
1559
|
+
if (lineText(state, startLine) !== TWO_VERSIONS_OPEN)
|
|
1560
|
+
return false;
|
|
1561
|
+
let separator = -1;
|
|
1562
|
+
let closer = -1;
|
|
1563
|
+
for (let line = startLine + 1;line < endLine; line++) {
|
|
1564
|
+
const text = lineText(state, line);
|
|
1565
|
+
if (separator < 0 && text === TWO_VERSIONS_SEPARATOR)
|
|
1566
|
+
separator = line;
|
|
1567
|
+
else if (text === TWO_VERSIONS_CLOSE) {
|
|
1568
|
+
closer = line;
|
|
1569
|
+
break;
|
|
1570
|
+
} else if (text === TWO_VERSIONS_OPEN)
|
|
1571
|
+
return false;
|
|
1572
|
+
}
|
|
1573
|
+
if (separator < 0 || closer < 0)
|
|
1574
|
+
return false;
|
|
1575
|
+
if (silent)
|
|
1576
|
+
return true;
|
|
1577
|
+
const oldParent = state.parentType;
|
|
1578
|
+
const oldLineMax = state.lineMax;
|
|
1579
|
+
const open = state.push("two_versions_open", "div", 1);
|
|
1580
|
+
open.attrSet("class", "gp-two-versions");
|
|
1581
|
+
open.map = [startLine, closer + 1];
|
|
1582
|
+
open.block = true;
|
|
1583
|
+
pushVersion(state, "yours", "Your version", startLine + 1, separator);
|
|
1584
|
+
pushVersion(state, "online", "Online version", separator + 1, closer);
|
|
1585
|
+
const close = state.push("two_versions_close", "div", -1);
|
|
1586
|
+
close.block = true;
|
|
1587
|
+
state.parentType = oldParent;
|
|
1588
|
+
state.lineMax = oldLineMax;
|
|
1589
|
+
state.line = closer + 1;
|
|
1590
|
+
return true;
|
|
1591
|
+
}
|
|
1592
|
+
function gpTwoVersions(md) {
|
|
1593
|
+
md.block.ruler.before("html_block", "two_versions", twoVersions, {
|
|
1594
|
+
alt: ["paragraph", "reference", "blockquote", "list"]
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1480
1598
|
// src/lib/markdown/table-align.ts
|
|
1481
1599
|
var ALIGN = /^text-align:(left|center|right)$/;
|
|
1482
1600
|
function gpTableAlign(md) {
|
|
@@ -2352,6 +2470,7 @@ function createMarkdownRenderer(customPlugins) {
|
|
|
2352
2470
|
md.use(gpPinScope);
|
|
2353
2471
|
md.use(gpBlockNames);
|
|
2354
2472
|
md.use(gpTableAlign);
|
|
2473
|
+
md.use(gpTwoVersions);
|
|
2355
2474
|
registerImageRule(md);
|
|
2356
2475
|
if (customPlugins && customPlugins.length > 0) {
|
|
2357
2476
|
applyPlugins(md, customPlugins);
|
|
@@ -2388,6 +2507,17 @@ function lastPageMode(print) {
|
|
|
2388
2507
|
var WHICH_VALUES = new Set(["first", "start", "last", "first-except"]);
|
|
2389
2508
|
|
|
2390
2509
|
// src/engine/shared/print-area.ts
|
|
2510
|
+
function isPositivePoints(value) {
|
|
2511
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
2512
|
+
}
|
|
2513
|
+
var round3 = (n) => Math.round(n * 1000) / 1000;
|
|
2514
|
+
function cssLength(pt) {
|
|
2515
|
+
const inches = round3(pt / 72);
|
|
2516
|
+
return Math.abs(inches * 72 - pt) < 0.000001 ? `${inches}in` : `${round3(pt)}pt`;
|
|
2517
|
+
}
|
|
2518
|
+
function formatPageSize(page) {
|
|
2519
|
+
return `${cssLength(page.width)} ${cssLength(page.height)}`;
|
|
2520
|
+
}
|
|
2391
2521
|
function printAreaMeta(page) {
|
|
2392
2522
|
const area = JSON.stringify({
|
|
2393
2523
|
width: page.width,
|
|
@@ -2402,7 +2532,578 @@ function printAreaMeta(page) {
|
|
|
2402
2532
|
return `<meta name="gp-print-area" content='${area.replace(/&/g, "&").replace(/'/g, "'")}'>`;
|
|
2403
2533
|
}
|
|
2404
2534
|
|
|
2535
|
+
// src/engine/shared/gcpm-extract.ts
|
|
2536
|
+
var NESTED_AT_RULES = /^@(media|supports|layer|scope|container|document)\b/i;
|
|
2537
|
+
function scanRules(css) {
|
|
2538
|
+
const out = [];
|
|
2539
|
+
let i = 0;
|
|
2540
|
+
let start = 0;
|
|
2541
|
+
let depth = 0;
|
|
2542
|
+
let bodyStart = -1;
|
|
2543
|
+
while (i < css.length) {
|
|
2544
|
+
const c = css[i];
|
|
2545
|
+
if (c === "/" && css[i + 1] === "*") {
|
|
2546
|
+
const end = css.indexOf("*/", i + 2);
|
|
2547
|
+
i = end === -1 ? css.length : end + 2;
|
|
2548
|
+
continue;
|
|
2549
|
+
}
|
|
2550
|
+
if (c === '"' || c === "'") {
|
|
2551
|
+
i = skipString(css, i);
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
if (c === "{") {
|
|
2555
|
+
if (depth === 0)
|
|
2556
|
+
bodyStart = i;
|
|
2557
|
+
depth++;
|
|
2558
|
+
i++;
|
|
2559
|
+
continue;
|
|
2560
|
+
}
|
|
2561
|
+
if (c === "}") {
|
|
2562
|
+
depth--;
|
|
2563
|
+
if (depth === 0) {
|
|
2564
|
+
out.push({
|
|
2565
|
+
prelude: stripComments(css.slice(start, bodyStart)).trim(),
|
|
2566
|
+
body: css.slice(bodyStart + 1, i)
|
|
2567
|
+
});
|
|
2568
|
+
start = i + 1;
|
|
2569
|
+
}
|
|
2570
|
+
i++;
|
|
2571
|
+
continue;
|
|
2572
|
+
}
|
|
2573
|
+
if (c === ";" && depth === 0) {
|
|
2574
|
+
const stmt = stripComments(css.slice(start, i)).trim();
|
|
2575
|
+
if (stmt)
|
|
2576
|
+
out.push({ statement: stmt });
|
|
2577
|
+
start = i + 1;
|
|
2578
|
+
i++;
|
|
2579
|
+
continue;
|
|
2580
|
+
}
|
|
2581
|
+
i++;
|
|
2582
|
+
}
|
|
2583
|
+
return out;
|
|
2584
|
+
}
|
|
2585
|
+
function stripComments(s) {
|
|
2586
|
+
let out = "";
|
|
2587
|
+
let i = 0;
|
|
2588
|
+
while (i < s.length) {
|
|
2589
|
+
if (s[i] === "/" && s[i + 1] === "*") {
|
|
2590
|
+
const end = s.indexOf("*/", i + 2);
|
|
2591
|
+
i = end === -1 ? s.length : end + 2;
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
2594
|
+
if (s[i] === '"' || s[i] === "'") {
|
|
2595
|
+
const end = skipString(s, i);
|
|
2596
|
+
out += s.slice(i, end);
|
|
2597
|
+
i = end;
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
out += s[i++];
|
|
2601
|
+
}
|
|
2602
|
+
return out;
|
|
2603
|
+
}
|
|
2604
|
+
function skipString(css, i) {
|
|
2605
|
+
const quote = css[i];
|
|
2606
|
+
i++;
|
|
2607
|
+
while (i < css.length) {
|
|
2608
|
+
if (css[i] === "\\")
|
|
2609
|
+
i += 2;
|
|
2610
|
+
else if (css[i] === quote)
|
|
2611
|
+
return i + 1;
|
|
2612
|
+
else
|
|
2613
|
+
i++;
|
|
2614
|
+
}
|
|
2615
|
+
return i;
|
|
2616
|
+
}
|
|
2617
|
+
function parseDeclarations(body) {
|
|
2618
|
+
const decls = {};
|
|
2619
|
+
let i = 0;
|
|
2620
|
+
let start = 0;
|
|
2621
|
+
let depth = 0;
|
|
2622
|
+
const push = (chunk) => {
|
|
2623
|
+
const s = stripComments(chunk).trim();
|
|
2624
|
+
if (!s)
|
|
2625
|
+
return;
|
|
2626
|
+
const colon = indexOfTopLevel(s, ":");
|
|
2627
|
+
if (colon <= 0)
|
|
2628
|
+
return;
|
|
2629
|
+
const rawProp = s.slice(0, colon).trim();
|
|
2630
|
+
const prop = rawProp.startsWith("--") ? rawProp : rawProp.toLowerCase();
|
|
2631
|
+
const value = s.slice(colon + 1).trim().replace(/\s*!important$/i, "");
|
|
2632
|
+
if (prop)
|
|
2633
|
+
decls[prop] = value;
|
|
2634
|
+
};
|
|
2635
|
+
while (i < body.length) {
|
|
2636
|
+
const c = body[i];
|
|
2637
|
+
if (c === "/" && body[i + 1] === "*") {
|
|
2638
|
+
const end = body.indexOf("*/", i + 2);
|
|
2639
|
+
i = end === -1 ? body.length : end + 2;
|
|
2640
|
+
continue;
|
|
2641
|
+
}
|
|
2642
|
+
if (c === '"' || c === "'") {
|
|
2643
|
+
i = skipString(body, i);
|
|
2644
|
+
continue;
|
|
2645
|
+
}
|
|
2646
|
+
if (c === "(" || c === "{" || c === "[")
|
|
2647
|
+
depth++;
|
|
2648
|
+
else if (c === ")" || c === "}" || c === "]")
|
|
2649
|
+
depth--;
|
|
2650
|
+
else if (c === ";" && depth === 0) {
|
|
2651
|
+
push(body.slice(start, i));
|
|
2652
|
+
start = i + 1;
|
|
2653
|
+
}
|
|
2654
|
+
i++;
|
|
2655
|
+
}
|
|
2656
|
+
push(body.slice(start));
|
|
2657
|
+
return decls;
|
|
2658
|
+
}
|
|
2659
|
+
function indexOfTopLevel(s, ch) {
|
|
2660
|
+
let depth = 0;
|
|
2661
|
+
for (let i = 0;i < s.length; i++) {
|
|
2662
|
+
const c = s[i];
|
|
2663
|
+
if (c === '"' || c === "'") {
|
|
2664
|
+
i = skipString(s, i) - 1;
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
if (c === "(")
|
|
2668
|
+
depth++;
|
|
2669
|
+
else if (c === ")")
|
|
2670
|
+
depth--;
|
|
2671
|
+
else if (c === ch && depth === 0)
|
|
2672
|
+
return i;
|
|
2673
|
+
}
|
|
2674
|
+
return -1;
|
|
2675
|
+
}
|
|
2676
|
+
function extract(css) {
|
|
2677
|
+
const model = {
|
|
2678
|
+
pageRules: [],
|
|
2679
|
+
stringSets: [],
|
|
2680
|
+
pageAssignments: [],
|
|
2681
|
+
breaks: [],
|
|
2682
|
+
xrefs: [],
|
|
2683
|
+
counterResets: [],
|
|
2684
|
+
pageNames: [],
|
|
2685
|
+
warnings: []
|
|
2686
|
+
};
|
|
2687
|
+
walk(css, model);
|
|
2688
|
+
resolveGeometryVars(model, collectRootCustomProperties(css));
|
|
2689
|
+
const names = new Set;
|
|
2690
|
+
for (const r of model.pageRules)
|
|
2691
|
+
if (r.name)
|
|
2692
|
+
names.add(r.name);
|
|
2693
|
+
for (const a of model.pageAssignments)
|
|
2694
|
+
names.add(a.page);
|
|
2695
|
+
model.pageNames = [...names];
|
|
2696
|
+
return model;
|
|
2697
|
+
}
|
|
2698
|
+
var GEOMETRY_PROPS = [
|
|
2699
|
+
"size",
|
|
2700
|
+
"margin",
|
|
2701
|
+
"margin-top",
|
|
2702
|
+
"margin-right",
|
|
2703
|
+
"margin-bottom",
|
|
2704
|
+
"margin-left",
|
|
2705
|
+
"bleed",
|
|
2706
|
+
"marks"
|
|
2707
|
+
];
|
|
2708
|
+
function collectRootCustomProperties(css) {
|
|
2709
|
+
const props = new Map;
|
|
2710
|
+
const walkForRoot = (body) => {
|
|
2711
|
+
for (const rule of scanRules(body)) {
|
|
2712
|
+
if ("statement" in rule)
|
|
2713
|
+
continue;
|
|
2714
|
+
const { prelude, body: ruleBody } = rule;
|
|
2715
|
+
if (NESTED_AT_RULES.test(prelude)) {
|
|
2716
|
+
walkForRoot(ruleBody);
|
|
2717
|
+
continue;
|
|
2718
|
+
}
|
|
2719
|
+
if (!prelude.startsWith("@")) {
|
|
2720
|
+
const selectors = splitTopLevel(prelude, ",");
|
|
2721
|
+
if (selectors.some((s) => s.trim() === ":root")) {
|
|
2722
|
+
const decls = parseDeclarations(ruleBody);
|
|
2723
|
+
for (const [k, v] of Object.entries(decls)) {
|
|
2724
|
+
if (k.startsWith("--"))
|
|
2725
|
+
props.set(k, v);
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
};
|
|
2731
|
+
walkForRoot(css);
|
|
2732
|
+
return props;
|
|
2733
|
+
}
|
|
2734
|
+
function matchParen(s, open) {
|
|
2735
|
+
let depth = 0;
|
|
2736
|
+
let i = open;
|
|
2737
|
+
while (i < s.length) {
|
|
2738
|
+
const c = s[i];
|
|
2739
|
+
if (c === '"' || c === "'") {
|
|
2740
|
+
i = skipString(s, i);
|
|
2741
|
+
continue;
|
|
2742
|
+
}
|
|
2743
|
+
if (c === "(")
|
|
2744
|
+
depth++;
|
|
2745
|
+
else if (c === ")") {
|
|
2746
|
+
depth--;
|
|
2747
|
+
if (depth === 0)
|
|
2748
|
+
return i;
|
|
2749
|
+
}
|
|
2750
|
+
i++;
|
|
2751
|
+
}
|
|
2752
|
+
return -1;
|
|
2753
|
+
}
|
|
2754
|
+
function resolveVarsInValue(value, customProps, stack = new Set) {
|
|
2755
|
+
let out = "";
|
|
2756
|
+
let i = 0;
|
|
2757
|
+
while (i < value.length) {
|
|
2758
|
+
const isVarStart = /^var\(/i.test(value.slice(i, i + 4)) && (i === 0 || !/[\w-]/.test(value[i - 1]));
|
|
2759
|
+
if (!isVarStart) {
|
|
2760
|
+
out += value[i];
|
|
2761
|
+
i++;
|
|
2762
|
+
continue;
|
|
2763
|
+
}
|
|
2764
|
+
const open = i + 3;
|
|
2765
|
+
const close = matchParen(value, open);
|
|
2766
|
+
if (close === -1) {
|
|
2767
|
+
out += value.slice(i);
|
|
2768
|
+
break;
|
|
2769
|
+
}
|
|
2770
|
+
const inner = value.slice(open + 1, close);
|
|
2771
|
+
const commaIdx = indexOfTopLevel(inner, ",");
|
|
2772
|
+
const name = (commaIdx === -1 ? inner : inner.slice(0, commaIdx)).trim();
|
|
2773
|
+
const fallback = commaIdx === -1 ? undefined : inner.slice(commaIdx + 1).trim();
|
|
2774
|
+
if (!/^--[\w-]+$/.test(name)) {
|
|
2775
|
+
return { text: out, unresolved: `var(${inner}) is not a valid custom property reference` };
|
|
2776
|
+
}
|
|
2777
|
+
if (stack.has(name)) {
|
|
2778
|
+
return { text: out, unresolved: `var(${name}) is circular` };
|
|
2779
|
+
}
|
|
2780
|
+
const rootValue = customProps.get(name);
|
|
2781
|
+
if (rootValue !== undefined) {
|
|
2782
|
+
const nested = resolveVarsInValue(rootValue, customProps, new Set(stack).add(name));
|
|
2783
|
+
if (nested.unresolved)
|
|
2784
|
+
return { text: out, unresolved: nested.unresolved };
|
|
2785
|
+
out += nested.text;
|
|
2786
|
+
} else if (fallback !== undefined) {
|
|
2787
|
+
if (/\bvar\(/i.test(fallback)) {
|
|
2788
|
+
return {
|
|
2789
|
+
text: out,
|
|
2790
|
+
unresolved: `var(${name}, ${fallback}) — a fallback containing another var() is not resolved`
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
out += fallback;
|
|
2794
|
+
} else {
|
|
2795
|
+
return {
|
|
2796
|
+
text: out,
|
|
2797
|
+
unresolved: `${name} is not defined at :root and var() has no fallback`
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
i = close + 1;
|
|
2801
|
+
}
|
|
2802
|
+
return { text: out };
|
|
2803
|
+
}
|
|
2804
|
+
function resolveGeometryVars(model, customProps) {
|
|
2805
|
+
for (const rule of model.pageRules) {
|
|
2806
|
+
for (const prop of GEOMETRY_PROPS) {
|
|
2807
|
+
const value = rule.decls[prop];
|
|
2808
|
+
if (!value || !/\bvar\(/i.test(value))
|
|
2809
|
+
continue;
|
|
2810
|
+
const { text, unresolved } = resolveVarsInValue(value, customProps);
|
|
2811
|
+
if (unresolved) {
|
|
2812
|
+
throw new Error(`${rule.raw} { ${prop}: ${value} } — cannot resolve ${unresolved}. ` + `Define the custom property at :root, add a literal var(--x, fallback) fallback, or use a literal value.`);
|
|
2813
|
+
}
|
|
2814
|
+
const unparsable = unparsableGeometry(prop, text);
|
|
2815
|
+
if (unparsable) {
|
|
2816
|
+
throw new Error(`${rule.raw} { ${prop}: ${value} } — resolves to \`${text.trim()}\`, ${unparsable}. ` + `Use a value this engine can read (a length like 0.75in/12pt/10mm, or a named page size for \`size\`).`);
|
|
2817
|
+
}
|
|
2818
|
+
rule.decls[prop] = text;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
function unparsableGeometry(prop, text) {
|
|
2823
|
+
const t = text.trim();
|
|
2824
|
+
if (prop === "size") {
|
|
2825
|
+
if (!t)
|
|
2826
|
+
return "which is empty";
|
|
2827
|
+
return parseSize(t) ? undefined : "which is not a page size";
|
|
2828
|
+
}
|
|
2829
|
+
if (prop === "margin" || prop.startsWith("margin-")) {
|
|
2830
|
+
if (!t)
|
|
2831
|
+
return "which is empty";
|
|
2832
|
+
const parts = t.split(/\s+/);
|
|
2833
|
+
if (prop !== "margin" && parts.length !== 1)
|
|
2834
|
+
return "which is not a single length";
|
|
2835
|
+
if (parts.length > 4)
|
|
2836
|
+
return "which is not a valid margin";
|
|
2837
|
+
const bad = parts.filter((p) => toPt(p) === undefined);
|
|
2838
|
+
if (bad.length)
|
|
2839
|
+
return `which this engine cannot read as a length (\`${bad[0]}\`)`;
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
if (prop === "bleed") {
|
|
2843
|
+
if (!t)
|
|
2844
|
+
return "which is empty";
|
|
2845
|
+
if (/^(auto|none)$/i.test(t))
|
|
2846
|
+
return;
|
|
2847
|
+
return toPt(t) === undefined ? "which this engine cannot read as a length" : undefined;
|
|
2848
|
+
}
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
function walk(css, model) {
|
|
2852
|
+
for (const rule of scanRules(css)) {
|
|
2853
|
+
if ("statement" in rule)
|
|
2854
|
+
continue;
|
|
2855
|
+
const { prelude, body } = rule;
|
|
2856
|
+
if (NESTED_AT_RULES.test(prelude)) {
|
|
2857
|
+
walk(body, model);
|
|
2858
|
+
continue;
|
|
2859
|
+
}
|
|
2860
|
+
if (/^@page\b/i.test(prelude)) {
|
|
2861
|
+
model.pageRules.push(parsePageRule(prelude, body));
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
if (prelude.startsWith("@"))
|
|
2865
|
+
continue;
|
|
2866
|
+
parseQualifiedRule(prelude, body, model);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
function parsePageRule(prelude, body) {
|
|
2870
|
+
const sel = prelude.replace(/^@page\s*/i, "").trim();
|
|
2871
|
+
const nameMatch = /^([A-Za-z_][\w-]*)/.exec(sel);
|
|
2872
|
+
const pseudos = [...sel.matchAll(/:([A-Za-z-]+)(\([^)]*\))?/g)].map((m) => m[1] + (m[2] ?? ""));
|
|
2873
|
+
const rule = {
|
|
2874
|
+
name: nameMatch?.[1],
|
|
2875
|
+
pseudos,
|
|
2876
|
+
decls: {},
|
|
2877
|
+
marginBoxes: {},
|
|
2878
|
+
raw: `@page ${sel}`.trim()
|
|
2879
|
+
};
|
|
2880
|
+
let rest = "";
|
|
2881
|
+
let i = 0;
|
|
2882
|
+
while (i < body.length) {
|
|
2883
|
+
const at = findNextAtRule(body, i);
|
|
2884
|
+
if (at === -1) {
|
|
2885
|
+
rest += body.slice(i);
|
|
2886
|
+
break;
|
|
2887
|
+
}
|
|
2888
|
+
rest += body.slice(i, at);
|
|
2889
|
+
const open = body.indexOf("{", at);
|
|
2890
|
+
if (open === -1) {
|
|
2891
|
+
rest += body.slice(at);
|
|
2892
|
+
break;
|
|
2893
|
+
}
|
|
2894
|
+
const name = body.slice(at + 1, open).trim().toLowerCase();
|
|
2895
|
+
let depth = 1;
|
|
2896
|
+
let j = open + 1;
|
|
2897
|
+
while (j < body.length && depth > 0) {
|
|
2898
|
+
const c = body[j];
|
|
2899
|
+
if (c === "/" && body[j + 1] === "*") {
|
|
2900
|
+
const end = body.indexOf("*/", j + 2);
|
|
2901
|
+
j = end === -1 ? body.length : end + 2;
|
|
2902
|
+
continue;
|
|
2903
|
+
}
|
|
2904
|
+
if (c === '"' || c === "'") {
|
|
2905
|
+
j = skipString(body, j);
|
|
2906
|
+
continue;
|
|
2907
|
+
}
|
|
2908
|
+
if (c === "{")
|
|
2909
|
+
depth++;
|
|
2910
|
+
else if (c === "}")
|
|
2911
|
+
depth--;
|
|
2912
|
+
j++;
|
|
2913
|
+
}
|
|
2914
|
+
rule.marginBoxes[`@${name}`] = parseDeclarations(body.slice(open + 1, j - 1));
|
|
2915
|
+
i = j;
|
|
2916
|
+
}
|
|
2917
|
+
rule.decls = parseDeclarations(rest);
|
|
2918
|
+
return rule;
|
|
2919
|
+
}
|
|
2920
|
+
function findNextAtRule(body, start) {
|
|
2921
|
+
let i = start;
|
|
2922
|
+
while (i < body.length) {
|
|
2923
|
+
const c = body[i];
|
|
2924
|
+
if (c === "/" && body[i + 1] === "*") {
|
|
2925
|
+
const end = body.indexOf("*/", i + 2);
|
|
2926
|
+
i = end === -1 ? body.length : end + 2;
|
|
2927
|
+
continue;
|
|
2928
|
+
}
|
|
2929
|
+
if (c === '"' || c === "'") {
|
|
2930
|
+
i = skipString(body, i);
|
|
2931
|
+
continue;
|
|
2932
|
+
}
|
|
2933
|
+
if (c === "@")
|
|
2934
|
+
return i;
|
|
2935
|
+
i++;
|
|
2936
|
+
}
|
|
2937
|
+
return -1;
|
|
2938
|
+
}
|
|
2939
|
+
function parseQualifiedRule(selector, body, model) {
|
|
2940
|
+
const decls = parseDeclarations(body);
|
|
2941
|
+
for (const [prop, value] of Object.entries(decls)) {
|
|
2942
|
+
if (prop === "string-set") {
|
|
2943
|
+
for (const entry of splitTopLevel(value, ",")) {
|
|
2944
|
+
const m = /^\s*([A-Za-z_][\w-]*)\s+(.+)$/.exec(entry);
|
|
2945
|
+
if (m && m[1] !== undefined && m[2] !== undefined)
|
|
2946
|
+
model.stringSets.push({ selector, name: m[1], value: m[2].trim() });
|
|
2947
|
+
}
|
|
2948
|
+
} else if (prop === "page") {
|
|
2949
|
+
if (value && value !== "auto")
|
|
2950
|
+
model.pageAssignments.push({ selector, page: value.trim() });
|
|
2951
|
+
} else if (prop === "break-before" || prop === "break-after" || prop === "break-inside") {
|
|
2952
|
+
model.breaks.push({ selector, prop, value });
|
|
2953
|
+
} else if (prop === "counter-reset") {
|
|
2954
|
+
const m = /\bpage\s+(-?\d+)/.exec(value);
|
|
2955
|
+
if (m)
|
|
2956
|
+
model.counterResets.push({ selector, start: Number(m[1]) });
|
|
2957
|
+
} else if (prop === "content") {
|
|
2958
|
+
for (const fn of ["target-counter", "target-text", "leader", "string"]) {
|
|
2959
|
+
if (new RegExp(`\\b${fn}\\s*\\(`).test(value)) {
|
|
2960
|
+
model.xrefs.push({ selector, content: value, fn });
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
function splitTopLevel(s, sep) {
|
|
2967
|
+
const out = [];
|
|
2968
|
+
let depth = 0;
|
|
2969
|
+
let start = 0;
|
|
2970
|
+
for (let i = 0;i < s.length; i++) {
|
|
2971
|
+
const c = s[i];
|
|
2972
|
+
if (c === '"' || c === "'") {
|
|
2973
|
+
i = skipString(s, i) - 1;
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
if (c === "(")
|
|
2977
|
+
depth++;
|
|
2978
|
+
else if (c === ")")
|
|
2979
|
+
depth--;
|
|
2980
|
+
else if (c === sep && depth === 0) {
|
|
2981
|
+
out.push(s.slice(start, i));
|
|
2982
|
+
start = i + 1;
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
out.push(s.slice(start));
|
|
2986
|
+
return out.map((x) => x.trim()).filter(Boolean);
|
|
2987
|
+
}
|
|
2988
|
+
var UNITS_PER_PT = {
|
|
2989
|
+
pt: 1,
|
|
2990
|
+
px: 0.75,
|
|
2991
|
+
in: 72,
|
|
2992
|
+
pc: 12,
|
|
2993
|
+
cm: 72 / 2.54,
|
|
2994
|
+
mm: 72 / 25.4,
|
|
2995
|
+
q: 72 / 101.6
|
|
2996
|
+
};
|
|
2997
|
+
function toPt(value) {
|
|
2998
|
+
const m = /^(-?[\d.]+)([a-z%]*)$/i.exec(value.trim());
|
|
2999
|
+
if (!m)
|
|
3000
|
+
return;
|
|
3001
|
+
const n = Number(m[1]);
|
|
3002
|
+
const unit = (m[2] || "px").toLowerCase();
|
|
3003
|
+
const factor = UNITS_PER_PT[unit];
|
|
3004
|
+
return factor === undefined ? undefined : n * factor;
|
|
3005
|
+
}
|
|
3006
|
+
var PAGE_SIZES = {
|
|
3007
|
+
a5: [419.53, 595.28],
|
|
3008
|
+
a4: [595.28, 841.89],
|
|
3009
|
+
a3: [841.89, 1190.55],
|
|
3010
|
+
b5: [498.9, 708.66],
|
|
3011
|
+
b4: [708.66, 1000.63],
|
|
3012
|
+
"jis-b5": [515.91, 728.5],
|
|
3013
|
+
"jis-b4": [728.5, 1031.81],
|
|
3014
|
+
letter: [612, 792],
|
|
3015
|
+
legal: [612, 1008],
|
|
3016
|
+
ledger: [1224, 792]
|
|
3017
|
+
};
|
|
3018
|
+
function parseSize(value) {
|
|
3019
|
+
const parts = value.trim().split(/\s+/);
|
|
3020
|
+
let landscape = false;
|
|
3021
|
+
const lens = [];
|
|
3022
|
+
let named;
|
|
3023
|
+
for (const p of parts) {
|
|
3024
|
+
const key = p.toLowerCase();
|
|
3025
|
+
if (key === "landscape")
|
|
3026
|
+
landscape = true;
|
|
3027
|
+
else if (key === "portrait")
|
|
3028
|
+
continue;
|
|
3029
|
+
else if (PAGE_SIZES[key])
|
|
3030
|
+
named = PAGE_SIZES[key];
|
|
3031
|
+
else {
|
|
3032
|
+
const pt = toPt(p);
|
|
3033
|
+
if (pt !== undefined)
|
|
3034
|
+
lens.push(pt);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
let w, h;
|
|
3038
|
+
if (named)
|
|
3039
|
+
[w, h] = named;
|
|
3040
|
+
else if (lens.length === 1)
|
|
3041
|
+
[w, h] = [lens[0], lens[0]];
|
|
3042
|
+
else if (lens.length >= 2)
|
|
3043
|
+
[w, h] = [lens[0], lens[1]];
|
|
3044
|
+
else
|
|
3045
|
+
return;
|
|
3046
|
+
if (landscape && h > w)
|
|
3047
|
+
[w, h] = [h, w];
|
|
3048
|
+
return { width: w, height: h };
|
|
3049
|
+
}
|
|
3050
|
+
function parseMargin(value) {
|
|
3051
|
+
const parts = value.trim().split(/\s+/).map((p) => toPt(p) ?? 0);
|
|
3052
|
+
const a = parts[0] ?? 0;
|
|
3053
|
+
const b = parts[1] ?? a;
|
|
3054
|
+
const c = parts[2] ?? a;
|
|
3055
|
+
const d = parts[3] ?? b;
|
|
3056
|
+
return { top: a, right: b, bottom: c, left: d };
|
|
3057
|
+
}
|
|
3058
|
+
function resolvePage(model, ctx = {}) {
|
|
3059
|
+
const wanted = new Set(ctx.pseudos ?? []);
|
|
3060
|
+
const applicable = model.pageRules.filter((r) => r.name ? r.name === ctx.name : true).filter((r) => r.pseudos.every((p) => wanted.has(p))).sort((a, b) => specificity(a) - specificity(b));
|
|
3061
|
+
const decls = {};
|
|
3062
|
+
const marginBoxes = {};
|
|
3063
|
+
const margin = { top: 72, right: 72, bottom: 72, left: 72 };
|
|
3064
|
+
for (const rule of applicable) {
|
|
3065
|
+
for (const [prop, value] of Object.entries(rule.decls)) {
|
|
3066
|
+
if (prop === "margin")
|
|
3067
|
+
Object.assign(margin, parseMargin(value));
|
|
3068
|
+
else if (prop === "margin-top" || prop === "margin-right" || prop === "margin-bottom" || prop === "margin-left") {
|
|
3069
|
+
const side = prop.slice(7);
|
|
3070
|
+
margin[side] = toPt(value) ?? margin[side];
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
Object.assign(decls, rule.decls);
|
|
3074
|
+
for (const [box, d] of Object.entries(rule.marginBoxes)) {
|
|
3075
|
+
marginBoxes[box] = { ...marginBoxes[box] ?? {}, ...d };
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
const size = parseSize(decls.size ?? "letter") ?? { width: 612, height: 792 };
|
|
3079
|
+
return {
|
|
3080
|
+
decls,
|
|
3081
|
+
marginBoxes,
|
|
3082
|
+
geometry: {
|
|
3083
|
+
width: size.width,
|
|
3084
|
+
height: size.height,
|
|
3085
|
+
margin,
|
|
3086
|
+
bleed: decls.bleed ? toPt(decls.bleed) ?? 0 : 0,
|
|
3087
|
+
marks: (decls.marks ?? "none").split(/\s+/).filter((m) => m && m !== "none")
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
function specificity(r) {
|
|
3092
|
+
return (r.name ? 2 : 0) + r.pseudos.length;
|
|
3093
|
+
}
|
|
3094
|
+
|
|
2405
3095
|
// src/lib/markdown/assemble.ts
|
|
3096
|
+
function defaultPageSizeCss(css, page) {
|
|
3097
|
+
if (!page || !isPositivePoints(page.width) || !isPositivePoints(page.height))
|
|
3098
|
+
return null;
|
|
3099
|
+
try {
|
|
3100
|
+
if (resolvePage(extract(css)).decls.size !== undefined)
|
|
3101
|
+
return null;
|
|
3102
|
+
} catch {
|
|
3103
|
+
return null;
|
|
3104
|
+
}
|
|
3105
|
+
return `@page { size: ${formatPageSize(page)}; }`;
|
|
3106
|
+
}
|
|
2406
3107
|
async function assembleBookHtml(opts) {
|
|
2407
3108
|
const title = opts.title ?? "Document";
|
|
2408
3109
|
const projectCss = opts.projectCss ?? "";
|
|
@@ -2440,16 +3141,21 @@ async function assembleBookHtml(opts) {
|
|
|
2440
3141
|
imageRefs.add(ref);
|
|
2441
3142
|
opts.onImageRefs([...imageRefs]);
|
|
2442
3143
|
}
|
|
3144
|
+
const pageSize = defaultPageSizeCss(`${pluginCss}
|
|
3145
|
+
${projectCss}`, opts.printArea);
|
|
2443
3146
|
const inlineCss = [
|
|
2444
3147
|
"@layer gp.marker, gp.vocab;",
|
|
2445
3148
|
`/* gutterpress markers */
|
|
2446
3149
|
@layer gp.marker {
|
|
2447
3150
|
${MARKER_CSS.trim()}
|
|
3151
|
+
${TWO_VERSIONS_CSS.trim()}
|
|
2448
3152
|
}`,
|
|
2449
3153
|
`/* gutterpress */
|
|
2450
3154
|
@layer gp.vocab {
|
|
2451
3155
|
${GUTTERPRESS_CSS.trim()}
|
|
2452
3156
|
}`,
|
|
3157
|
+
pageSize ? `/* page size (manifest) */
|
|
3158
|
+
${pageSize}` : null,
|
|
2453
3159
|
pluginCss ? `/* extension css */
|
|
2454
3160
|
${pluginCss.trim()}` : null,
|
|
2455
3161
|
`/* column alignment */
|