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.
Files changed (38) hide show
  1. package/dist/api/index.d.ts +1 -1
  2. package/dist/api/index.js +4 -2
  3. package/dist/{build-gxy185pv.js → build-2tgs5j4d.js} +4 -4
  4. package/dist/{cli-zfgvssqz.js → cli-d0cq87s5.js} +1 -1
  5. package/dist/{cli-a4qtctrw.js → cli-gf1ywzeg.js} +411 -50
  6. package/dist/{cli-ycbg7mxk.js → cli-ptez7zcc.js} +53 -5
  7. package/dist/{cli-8nw6d915.js → cli-ytck518d.js} +1 -1
  8. package/dist/cli.js +12 -12
  9. package/dist/{doctor-fygmzevd.js → doctor-y4yg51zy.js} +2 -2
  10. package/dist/engine/compiler/build.d.ts +1 -1
  11. package/dist/{engine-yf2w9hj7.js → engine-wtnakejg.js} +1 -1
  12. package/dist/{engine-5d1rpxxq.js → engine-zy6fzs5a.js} +2 -2
  13. package/dist/{ext-c48s56jm.js → ext-t2sye4as.js} +4 -4
  14. package/dist/{gutterpress-viewer-ez7k4ha7.js → gutterpress-viewer-k4mpqjh0.js} +406 -14
  15. package/dist/{index-3k3fyrec.js → index-cf1kanp0.js} +454 -52
  16. package/dist/{index-vmcfahe2.js → index-jt552wtf.js} +5 -5
  17. package/dist/{index-09h3n36d.js → index-n8vs6pss.js} +51 -2
  18. package/dist/index.js +5 -3
  19. package/dist/lib/markdown/block-names.d.ts +82 -0
  20. package/dist/lib/markdown/renderer.d.ts +3 -2
  21. package/dist/lib/markdown/table-align.d.ts +32 -0
  22. package/dist/lib/presets.d.ts +11 -0
  23. package/dist/lib/printsafe.d.ts +1 -0
  24. package/dist/lib/remote-auth/sync.d.ts +21 -0
  25. package/dist/lib/unknown-classes.d.ts +12 -0
  26. package/dist/{manifest.schema-gh0jjve9.json → manifest.schema-5pzhscqh.json} +7 -1
  27. package/dist/{new-a9m2jwyy.js → new-pbd5zrds.js} +4 -4
  28. package/dist/{preflight-w3tnbtb8.js → preflight-vnkb8tj7.js} +4 -4
  29. package/dist/preview/file-watcher.d.ts +4 -1
  30. package/dist/{preview-bridge-8jhd1wpb.js → preview-bridge-evecsh4w.js} +5 -1
  31. package/dist/{preview-7z8tt0b4.js → preview-g1hp0jt8.js} +4 -4
  32. package/dist/{preview-interface-c44qk17f.js → preview-interface-2n1d9pbf.js} +77 -1
  33. package/dist/{preview-shell-xwn84kqr.js → preview-shell-hyp7g158.js} +9 -0
  34. package/dist/{publish-bbftdxh2.js → publish-vkbmf3tj.js} +4 -4
  35. package/dist/render.js +199 -28
  36. package/dist/schema/manifest.types.d.ts +11 -0
  37. package/dist/{validate-x1ha1gqj.js → validate-q1xj2qxn.js} +4 -4
  38. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -86,10 +86,11 @@ function parseMarkerLine(line, declaredWords) {
86
86
  const kind = head.slice(1);
87
87
  if (!KNOWN_KINDS.includes(kind) && !(declaredWords && declaredWords.has(kind)))
88
88
  return null;
89
- if (kind === "page-break" || kind === "column-break" || kind === "continue" || kind.startsWith("end-")) {
89
+ if (kind === "continue" || kind.startsWith("end-")) {
90
90
  return { kind, name: null, attrs: {} };
91
91
  }
92
- const body = tokens.slice(1);
92
+ const isBreak = kind === "page-break" || kind === "column-break";
93
+ const body = isBreak ? tokens.slice(1).filter((token) => !isBareToken(token)) : tokens.slice(1);
93
94
  const hasExplicitAttrsOrShorthand = body.some((token) => token.includes("=") || token.startsWith(".") || token.startsWith("#"));
94
95
  const bareTokens = body.filter(isBareToken);
95
96
  let name = null;
@@ -157,6 +158,8 @@ function parseMarkerLine(line, declaredWords) {
157
158
  }
158
159
  if (classes.length)
159
160
  attrs.class = classes.join(" ");
161
+ if (isBreak)
162
+ return { kind, name: null, attrs };
160
163
  const marker = { kind, name, attrs };
161
164
  if (hasAmbiguousBareToken)
162
165
  marker.__ambiguousBareToken = true;
@@ -339,6 +342,11 @@ function attachDataAttrs(token, kind, name, attrs) {
339
342
  token.attrSet(k.startsWith("data-") ? k : `data-${k}`, v);
340
343
  }
341
344
  }
345
+ function attachBreak(token, baseClass, attrs = {}) {
346
+ addClasses(token, baseClass, attrs.class);
347
+ token.attrSet("aria-hidden", "true");
348
+ attachDataAttrs(token, "break", null, attrs);
349
+ }
342
350
  var DECLARED_NAME_RE = /^[a-z][a-z0-9-]*$/;
343
351
  function validateDeclaredMarkerName(name, pluginName) {
344
352
  if (!DECLARED_NAME_RE.test(name)) {
@@ -895,7 +903,11 @@ function plugin(md, pluginOptions = {}) {
895
903
  attachDataAttrs(t, component.kind, component.variant, meta.attrs || {});
896
904
  else
897
905
  attachDataAttrs(t, "section", meta.name, meta.attrs || {});
898
- t.meta = component ? { line: meta.__line, ...component, ...meta.__continued ? { continued: true } : {} } : { line: meta.__line };
906
+ t.meta = {
907
+ line: meta.__line,
908
+ ...component || {},
909
+ ...meta.__continued ? { continued: true } : {}
910
+ };
899
911
  stack.open({
900
912
  kind: "section",
901
913
  classes: meta.attrs && meta.attrs.class || "",
@@ -993,12 +1005,12 @@ function plugin(md, pluginOptions = {}) {
993
1005
  drainDeclaredFrames(keep);
994
1006
  const contMeta = {
995
1007
  name: section.meta.name,
996
- attrs: { ...section.meta.attrs || {} }
1008
+ attrs: { ...section.meta.attrs || {} },
1009
+ __continued: true
997
1010
  };
998
- if (section.meta.component) {
1011
+ delete contMeta.attrs.id;
1012
+ if (section.meta.component)
999
1013
  contMeta.__component = section.meta.component;
1000
- contMeta.__continued = true;
1001
- }
1002
1014
  contMeta.__line = line;
1003
1015
  const cls = (contMeta.attrs.class || "").split(/\s+/).filter(Boolean);
1004
1016
  if (!cls.includes("gp-continued"))
@@ -1011,16 +1023,14 @@ function plugin(md, pluginOptions = {}) {
1011
1023
  if (kind === "page-break") {
1012
1024
  const t = new state.Token("layout_page_break", "div", 0);
1013
1025
  t.meta = { line };
1014
- t.attrSet("class", "gp-page-break");
1015
- t.attrSet("aria-hidden", "true");
1026
+ attachBreak(t, "gp-page-break", meta.attrs);
1016
1027
  out.push(t);
1017
1028
  continue;
1018
1029
  }
1019
1030
  if (kind === "column-break") {
1020
1031
  const t = new state.Token("layout_column_break", "div", 0);
1021
1032
  t.meta = { line };
1022
- t.attrSet("class", "gp-column-break");
1023
- t.attrSet("aria-hidden", "true");
1033
+ attachBreak(t, "gp-column-break", meta.attrs);
1024
1034
  out.push(t);
1025
1035
  continue;
1026
1036
  }
@@ -1084,24 +1094,10 @@ function plugin(md, pluginOptions = {}) {
1084
1094
  }
1085
1095
  });
1086
1096
  }
1087
- md.renderer.rules.layout_page_break = (tokens, idx) => {
1088
- const cls = tokens[idx].attrGet("class") || "gp-page-break";
1089
- const rangeAttr = tokens[idx].attrGet("data-source-range");
1090
- const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
1091
- const chapterAttr = tokens[idx].attrGet("data-chapter-src");
1092
- const chapterHtml = chapterAttr ? ` data-chapter-src="${escapeAttr(chapterAttr)}"` : "";
1093
- return `<div class="${escapeAttr(cls)}" aria-hidden="true"${rangeHtml}${chapterHtml}></div>
1094
- `;
1095
- };
1096
- md.renderer.rules.layout_column_break = (tokens, idx) => {
1097
- const cls = tokens[idx].attrGet("class") || "gp-column-break";
1098
- const rangeAttr = tokens[idx].attrGet("data-source-range");
1099
- const rangeHtml = rangeAttr ? ` data-source-range="${escapeAttr(rangeAttr)}"` : "";
1100
- const chapterAttr = tokens[idx].attrGet("data-chapter-src");
1101
- const chapterHtml = chapterAttr ? ` data-chapter-src="${escapeAttr(chapterAttr)}"` : "";
1102
- return `<div class="${escapeAttr(cls)}" aria-hidden="true"${rangeHtml}${chapterHtml}></div>
1097
+ const renderBreak = (tokens, idx, options2, env, self) => `<div${self.renderAttrs(tokens[idx])}></div>
1103
1098
  `;
1104
- };
1099
+ md.renderer.rules.layout_page_break = renderBreak;
1100
+ md.renderer.rules.layout_column_break = renderBreak;
1105
1101
  md.renderer.rules.layout_marker = () => "";
1106
1102
  }
1107
1103
  var NO_MARGIN_BOXES = [
@@ -1453,6 +1449,27 @@ var GP_CLASSES = new Set([
1453
1449
  "gp-flush"
1454
1450
  ]);
1455
1451
 
1452
+ // src/lib/markdown/table-align.ts
1453
+ var ALIGN = /^text-align:(left|center|right)$/;
1454
+ function gpTableAlign(md) {
1455
+ md.core.ruler.after("block", "gp_table_align", (state) => {
1456
+ for (const token of state.tokens) {
1457
+ if (token.type !== "th_open" && token.type !== "td_open")
1458
+ continue;
1459
+ const match = ALIGN.exec(token.attrGet("style") ?? "");
1460
+ if (!match)
1461
+ continue;
1462
+ token.attrs = (token.attrs ?? []).filter(([key]) => key !== "style");
1463
+ token.attrSet("data-align", match[1]);
1464
+ }
1465
+ });
1466
+ }
1467
+ var TABLE_ALIGN_CSS = `
1468
+ th[data-align="left"], td[data-align="left"] { text-align: left; }
1469
+ th[data-align="center"], td[data-align="center"] { text-align: center; }
1470
+ th[data-align="right"], td[data-align="right"] { text-align: right; }
1471
+ `;
1472
+
1456
1473
  // src/lib/markdown/chapter-id.ts
1457
1474
  function canonicalChapterId(p) {
1458
1475
  let s = String(p).replace(/\\/g, "/");
@@ -1598,6 +1615,152 @@ function gpPinScope(md) {
1598
1615
  });
1599
1616
  }
1600
1617
 
1618
+ // src/lib/slug.ts
1619
+ function slugify(name, fallback = "") {
1620
+ return name.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || fallback;
1621
+ }
1622
+
1623
+ // src/lib/markdown/block-names.ts
1624
+ var BLOCK_OPEN = /^layout_(chapter|spread|page|section|component)_open$/;
1625
+ var BLOCK_CLOSE = /^layout_(chapter|spread|page|section|component)_close$/;
1626
+ var FOLDED = {
1627
+ ß: "ss",
1628
+ æ: "ae",
1629
+ œ: "oe",
1630
+ ø: "o",
1631
+ đ: "d",
1632
+ ð: "d",
1633
+ þ: "th",
1634
+ ł: "l",
1635
+ ı: "i"
1636
+ };
1637
+ function nameSlug(text) {
1638
+ return slugify(text.replace(/['’]/g, "").replace(/[ßæœøđðþłı]/gi, (c) => FOLDED[c.toLowerCase()] ?? c));
1639
+ }
1640
+ function nameFromSrc(src) {
1641
+ if (/^data:/i.test(src))
1642
+ return "";
1643
+ const path = src.split(/[?#]/, 1)[0] ?? "";
1644
+ const base = path.split(/[\\/]/).pop() ?? "";
1645
+ let decoded = base;
1646
+ try {
1647
+ decoded = decodeURIComponent(base);
1648
+ } catch {}
1649
+ return nameSlug(decoded.replace(/\.[^.]*$/, ""));
1650
+ }
1651
+ function takeExplicitName(token) {
1652
+ const data = token.attrGet("data-name");
1653
+ const plain = token.attrGet("name");
1654
+ if (plain !== null && token.attrs)
1655
+ token.attrs = token.attrs.filter(([k]) => k !== "name");
1656
+ if (data)
1657
+ return data;
1658
+ if (plain) {
1659
+ token.attrSet("data-name", plain);
1660
+ return plain;
1661
+ }
1662
+ return "";
1663
+ }
1664
+ function inlineText2(inline) {
1665
+ if (!inline || inline.type !== "inline")
1666
+ return "";
1667
+ const parts = [];
1668
+ for (const child of inline.children ?? []) {
1669
+ if (child.type === "text" || child.type === "text_special" || child.type === "code_inline" || child.type === "image") {
1670
+ parts.push(child.content);
1671
+ } else if (child.type === "softbreak" || child.type === "hardbreak") {
1672
+ parts.push(" ");
1673
+ }
1674
+ }
1675
+ return parts.join("");
1676
+ }
1677
+ function claim(used, slug) {
1678
+ let name = slug;
1679
+ for (let n = 2;used.has(name); n++)
1680
+ name = `${slug}-${n}`;
1681
+ used.add(name);
1682
+ return name;
1683
+ }
1684
+ function nameBlocksAndHeadings(tokens) {
1685
+ const blocks = [];
1686
+ const open = [];
1687
+ const headings = [];
1688
+ const headingSlugs = new Map;
1689
+ for (let i = 0;i < tokens.length; i++) {
1690
+ const tok = tokens[i];
1691
+ const opened = BLOCK_OPEN.exec(tok.type);
1692
+ if (opened) {
1693
+ const block = { token: tok, type: opened[1], heading: "" };
1694
+ blocks.push(block);
1695
+ open.push(block);
1696
+ continue;
1697
+ }
1698
+ const closed = BLOCK_CLOSE.exec(tok.type);
1699
+ if (closed) {
1700
+ for (let j = open.length - 1;j >= 0; j--) {
1701
+ if (open[j].type === closed[1]) {
1702
+ open.splice(j, 1);
1703
+ break;
1704
+ }
1705
+ }
1706
+ continue;
1707
+ }
1708
+ if (tok.type === "heading_open") {
1709
+ const slug = nameSlug(inlineText2(tokens[i + 1]));
1710
+ headings.push(tok);
1711
+ headingSlugs.set(tok, slug);
1712
+ const owner = open[open.length - 1];
1713
+ if (owner && !owner.heading)
1714
+ owner.heading = slug;
1715
+ }
1716
+ }
1717
+ const usedBlocks = new Set;
1718
+ const usedHeadings = new Set;
1719
+ const unnamedBlocks = new Set;
1720
+ for (const block of blocks) {
1721
+ const explicit = block.token.attrGet("data-name");
1722
+ if (explicit)
1723
+ usedBlocks.add(explicit);
1724
+ else
1725
+ unnamedBlocks.add(block);
1726
+ }
1727
+ const unnamedHeadings = [];
1728
+ for (const heading of headings) {
1729
+ const explicit = takeExplicitName(heading);
1730
+ if (explicit)
1731
+ usedHeadings.add(explicit);
1732
+ else
1733
+ unnamedHeadings.push(heading);
1734
+ }
1735
+ let previousSection = null;
1736
+ for (const block of blocks) {
1737
+ const { token } = block;
1738
+ if (block.type === "section") {
1739
+ if (token.meta?.continued && previousSection) {
1740
+ const inherited = previousSection.token.attrGet("data-name");
1741
+ if (inherited)
1742
+ token.attrSet("data-name", inherited);
1743
+ previousSection = block;
1744
+ continue;
1745
+ }
1746
+ previousSection = block;
1747
+ }
1748
+ if (!unnamedBlocks.has(block))
1749
+ continue;
1750
+ const slug = nameSlug(token.attrGet("data-label") ?? "") || block.heading;
1751
+ if (slug)
1752
+ token.attrSet("data-name", claim(usedBlocks, slug));
1753
+ }
1754
+ for (const heading of unnamedHeadings) {
1755
+ const slug = headingSlugs.get(heading);
1756
+ if (slug)
1757
+ heading.attrSet("data-name", claim(usedHeadings, slug));
1758
+ }
1759
+ }
1760
+ function gpBlockNames(md) {
1761
+ md.core.ruler.push("gp_block_names", (state) => nameBlocksAndHeadings(state.tokens));
1762
+ }
1763
+
1601
1764
  // src/lib/markdown/renderer.ts
1602
1765
  import markdownItSourceMap from "markdown-it-source-map";
1603
1766
  import markdownItDeflist from "markdown-it-deflist";
@@ -1764,6 +1927,10 @@ function registerImageRule(md) {
1764
1927
  if (srcIdx >= 0 && token.attrs) {
1765
1928
  const src = token.attrs[srcIdx][1];
1766
1929
  if (src) {
1930
+ const explicit = takeExplicitName(token);
1931
+ const named = explicit || nameFromSrc(src);
1932
+ if (named)
1933
+ token.attrSet("data-name", named);
1767
1934
  const bucket = env ?? {};
1768
1935
  (bucket.imageRefs ??= []).push(src);
1769
1936
  const cls = token.attrGet("class");
@@ -2139,6 +2306,8 @@ function createMarkdownRenderer(customPlugins) {
2139
2306
  const declaredMarkers = buildDeclaredMarkerRegistry(declaredMarkerSources);
2140
2307
  md.use(plugin, { declaredMarkers });
2141
2308
  md.use(gpPinScope);
2309
+ md.use(gpBlockNames);
2310
+ md.use(gpTableAlign);
2142
2311
  registerImageRule(md);
2143
2312
  if (customPlugins && customPlugins.length > 0) {
2144
2313
  applyPlugins(md, customPlugins);
@@ -2224,6 +2393,8 @@ ${GUTTERPRESS_CSS.trim()}
2224
2393
  }`,
2225
2394
  pluginCss ? `/* extension css */
2226
2395
  ${pluginCss.trim()}` : null,
2396
+ `/* column alignment */
2397
+ ${TABLE_ALIGN_CSS.trim()}`,
2227
2398
  projectCss ? `/* project css */
2228
2399
  ${projectCss.trim()}` : null
2229
2400
  ].filter(Boolean).join(`
@@ -146,6 +146,15 @@ export interface GutterpressManifest {
146
146
  width?: number;
147
147
  height?: number;
148
148
  tolerance?: number;
149
+ /**
150
+ * A bleed (pt) the page `width` x `height` ALREADY CONTAINS, on the top,
151
+ * bottom and OUTER edges only (the binding edge has none) — how
152
+ * DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in
153
+ * book). It changes nothing in the PDF: it only tells the preview's margin
154
+ * & bleed overlay where the trim is. CSS `@page { bleed }` (symmetric,
155
+ * grows the page) wins when the stylesheet declares one.
156
+ */
157
+ bleed?: number;
149
158
  };
150
159
  ink?: {
151
160
  maxTac?: number;
@@ -261,6 +270,8 @@ export interface ResolvedConfig {
261
270
  width: number;
262
271
  height: number;
263
272
  tolerance: number;
273
+ /** In-sheet bleed in pt (top, bottom, outer edge; see the manifest field); absent = none. */
274
+ bleed?: number;
264
275
  };
265
276
  ink: {
266
277
  maxTac: number;
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  executeAndReport
3
- } from "./cli-a4qtctrw.js";
3
+ } from "./cli-gf1ywzeg.js";
4
4
  import {
5
5
  log
6
- } from "./cli-zfgvssqz.js";
6
+ } from "./cli-d0cq87s5.js";
7
7
  import {
8
8
  UsageError,
9
9
  rejectExtraPositionals,
10
10
  rejectUnknownFlags
11
- } from "./cli-8nw6d915.js";
11
+ } from "./cli-ytck518d.js";
12
12
  import"./cli-ht1bcqdd.js";
13
- import"./cli-ycbg7mxk.js";
13
+ import"./cli-ptez7zcc.js";
14
14
  import {
15
15
  BuildError,
16
16
  EXIT_CODES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gutterpress",
3
- "version": "0.11.17",
3
+ "version": "0.11.18-alpha.10",
4
4
  "description": "Markdown-to-PDF converter for professional print layout using a native Chromium print engine and Ghostscript.",
5
5
  "author": "itlackey",
6
6
  "license": "MPL-2.0",