@masumdev/markforge 0.4.0 → 0.4.1

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/index.js CHANGED
@@ -52,6 +52,7 @@ __export(src_exports, {
52
52
  THEME_DEFAULT: () => THEME_DEFAULT,
53
53
  Theme: () => Theme,
54
54
  WatermarkPosition: () => WatermarkPosition,
55
+ applyHeadingNumbering: () => applyHeadingNumbering,
55
56
  buildDocxDocument: () => buildDocxDocument,
56
57
  buildHtmlDocument: () => buildHtmlDocument,
57
58
  buildPdfDocument: () => buildPdfDocument,
@@ -271,7 +272,16 @@ function parseInlineSpans(text) {
271
272
  remaining = remaining.slice(nextSpecial);
272
273
  }
273
274
  }
274
- return spans;
275
+ const merged = [];
276
+ for (const s of spans) {
277
+ const prev = merged[merged.length - 1];
278
+ if (prev && prev.type === "text" && s.type === "text") {
279
+ prev.content += s.content;
280
+ } else {
281
+ merged.push(s);
282
+ }
283
+ }
284
+ return merged;
275
285
  }
276
286
  function slugify(text) {
277
287
  return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -283,6 +293,7 @@ function applyHeadingNumbering(nodes, tocEntries, options) {
283
293
  const counters = [0, 0, 0, 0, 0, 0];
284
294
  for (const node of nodes) {
285
295
  if (node.type === "heading" && node.level) {
296
+ if (node._numbered) continue;
286
297
  const lvl = node.level;
287
298
  if (lvl > depth) continue;
288
299
  if (lvl === 1 && skipH1) continue;
@@ -298,6 +309,7 @@ function applyHeadingNumbering(nodes, tocEntries, options) {
298
309
  const originalText = node.text || "";
299
310
  node.text = fullPrefix + originalText;
300
311
  node.inlines = parseInlineSpans(node.text);
312
+ node._numbered = true;
301
313
  const toc = tocEntries.find((t) => t.id === node.id);
302
314
  if (toc) {
303
315
  toc.text = node.text;
@@ -1185,7 +1197,7 @@ var THEME_COMPONENTS = `
1185
1197
  .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
1186
1198
 
1187
1199
  /* Table of Contents */
1188
- .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
1200
+ .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; page-break-after: always; break-after: page; }
1189
1201
  .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
1190
1202
  .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
1191
1203
  .table-of-contents li { padding: 0.25rem 0; }
@@ -1258,11 +1270,13 @@ var THEME_CORPORATE = `
1258
1270
  }
1259
1271
  body { background-color: var(--mf-bg); color: var(--mf-text); font-family: var(--mf-font-family); font-size: 15px; line-height: 1.65; margin: 0; padding: 2.5rem; }
1260
1272
  .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1261
- h1, h2, h3, h4, h5, h6 { color: var(--mf-text); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
1262
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1273
+ h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
1274
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
1263
1275
  h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1264
- h3 { font-size: 1.3rem; }
1265
- h4 { font-size: 1.1rem; }
1276
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
1277
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
1278
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
1279
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
1266
1280
  p { margin: 0.8rem 0; }
1267
1281
  `;
1268
1282
  var THEME_DEFAULT = THEME_CORPORATE;
@@ -1304,11 +1318,13 @@ function generateThemeCss(theme) {
1304
1318
  }
1305
1319
  body { background-color: var(--mf-bg); color: var(--mf-text); font-family: var(--mf-font-family); font-size: 15px; line-height: 1.65; margin: 0; padding: 2.5rem; }
1306
1320
  .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1307
- h1, h2, h3, h4, h5, h6 { color: var(--mf-text); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
1308
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1321
+ h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
1322
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
1309
1323
  h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1310
- h3 { font-size: 1.3rem; }
1311
- h4 { font-size: 1.1rem; }
1324
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
1325
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
1326
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
1327
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
1312
1328
  p { margin: 0.8rem 0; }
1313
1329
  ${theme.customCss || ""}
1314
1330
  `;
@@ -1517,16 +1533,45 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
1517
1533
  if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1518
1534
  return str;
1519
1535
  }
1520
- function replaceDocumentTokens(template = "", meta) {
1521
- const currentYear = meta.year || (/* @__PURE__ */ new Date()).getFullYear().toString();
1522
- return template.replace(/\{title\}/gi, meta.title || "").replace(/\{subtitle\}/gi, meta.subtitle || "").replace(/\{author\}/gi, meta.author || "").replace(/\{version\}/gi, meta.version || "").replace(/\{date\}/gi, meta.date || "").replace(/\{year\}/gi, currentYear).replace(/\{company\}/gi, meta.company || "");
1536
+ function replaceDocumentTokens(template = "", meta = {}) {
1537
+ if (!template) return "";
1538
+ const currentYear = meta.year ? String(meta.year) : (/* @__PURE__ */ new Date()).getFullYear().toString();
1539
+ const tokenMap = {
1540
+ title: meta.title ? String(meta.title) : "",
1541
+ subtitle: meta.subtitle ? String(meta.subtitle) : "",
1542
+ author: meta.author ? Array.isArray(meta.author) ? meta.author.join(", ") : String(meta.author) : "",
1543
+ version: meta.version ? String(meta.version) : "",
1544
+ date: meta.date ? String(meta.date) : "",
1545
+ company: meta.company ? String(meta.company) : "",
1546
+ year: currentYear
1547
+ };
1548
+ if (meta.metadata && typeof meta.metadata === "object") {
1549
+ for (const [key, val] of Object.entries(meta.metadata)) {
1550
+ if (val !== void 0 && val !== null) {
1551
+ tokenMap[key.toLowerCase()] = String(val);
1552
+ }
1553
+ }
1554
+ }
1555
+ for (const [key, val] of Object.entries(meta)) {
1556
+ if (val !== void 0 && val !== null && typeof val !== "object") {
1557
+ tokenMap[key.toLowerCase()] = String(val);
1558
+ }
1559
+ }
1560
+ return template.replace(/\{([a-zA-Z0-9_\-]+)\}/gi, (match, tokenKey) => {
1561
+ const lowerKey = tokenKey.toLowerCase();
1562
+ if (lowerKey in tokenMap) {
1563
+ return tokenMap[lowerKey];
1564
+ }
1565
+ return match;
1566
+ });
1523
1567
  }
1524
- function normalizeWatermark(rawWatermark) {
1568
+ function normalizeWatermark(rawWatermark, tokens) {
1525
1569
  if (!rawWatermark) {
1526
1570
  return void 0;
1527
1571
  }
1528
1572
  if (typeof rawWatermark === "string") {
1529
- const text = rawWatermark.trim();
1573
+ let text = rawWatermark.trim();
1574
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1530
1575
  if (!text) return void 0;
1531
1576
  return {
1532
1577
  text,
@@ -1539,8 +1584,10 @@ function normalizeWatermark(rawWatermark) {
1539
1584
  }
1540
1585
  if (typeof rawWatermark === "object") {
1541
1586
  if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1587
+ let text = rawWatermark.text.trim();
1588
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1542
1589
  return {
1543
- text: rawWatermark.text.trim(),
1590
+ text,
1544
1591
  color: rawWatermark.color || "#94a3b8",
1545
1592
  opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1546
1593
  fontSize: rawWatermark.fontSize || 54,
@@ -1661,27 +1708,46 @@ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1661
1708
  const cfg = typeof rawCover === "object" ? rawCover : {};
1662
1709
  if (cfg.enabled === false) return void 0;
1663
1710
  const preset = cfg.preset || "modern";
1664
- const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title || "Document Title";
1665
- const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle;
1666
- const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author;
1667
- const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1668
- const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version;
1711
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title ? String(tokenCtx.title) : "Document Title";
1712
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle ? String(tokenCtx.subtitle) : void 0;
1713
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1714
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1715
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1669
1716
  let dateStr;
1670
1717
  if (typeof cfg.date === "string") {
1671
1718
  dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
1672
1719
  } else if (cfg.date === true) {
1673
- dateStr = tokenCtx.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1720
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1674
1721
  } else {
1675
- dateStr = tokenCtx.date;
1722
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : void 0;
1676
1723
  }
1677
1724
  const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1678
1725
  const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1679
1726
  const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1680
1727
  const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1681
1728
  const logoWidth = cfg.logoWidth;
1682
- const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1729
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1730
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1683
1731
  const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1732
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1733
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1734
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1684
1735
  const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1736
+ const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1737
+ const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1738
+ const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
1739
+ const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
1740
+ let socialMap;
1741
+ if (cfg.social && typeof cfg.social === "object") {
1742
+ socialMap = {};
1743
+ for (const [k, v] of Object.entries(cfg.social)) {
1744
+ if (typeof v === "string") {
1745
+ socialMap[k] = replaceDocumentTokens(v, tokenCtx);
1746
+ }
1747
+ }
1748
+ }
1749
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1750
+ const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : void 0;
1685
1751
  return {
1686
1752
  enabled: true,
1687
1753
  preset,
@@ -1697,8 +1763,18 @@ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1697
1763
  logo,
1698
1764
  logoWidth,
1699
1765
  bgGradient,
1766
+ backgroundColor,
1700
1767
  textColor,
1701
- footerText
1768
+ titleColor,
1769
+ subtitleColor,
1770
+ accentColor,
1771
+ footerText,
1772
+ address,
1773
+ email,
1774
+ phone,
1775
+ website,
1776
+ social: socialMap,
1777
+ copyright
1702
1778
  };
1703
1779
  }
1704
1780
  function normalizeBackCover(rawBack, tokenCtx = {}) {
@@ -1708,7 +1784,10 @@ function normalizeBackCover(rawBack, tokenCtx = {}) {
1708
1784
  const preset = cfg.preset || "modern";
1709
1785
  const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
1710
1786
  const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
1711
- const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1787
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1788
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1789
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1790
+ const date = typeof cfg.date === "string" ? replaceDocumentTokens(cfg.date, tokenCtx) : tokenCtx.date ? String(tokenCtx.date) : void 0;
1712
1791
  const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1713
1792
  const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1714
1793
  const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
@@ -1724,32 +1803,45 @@ function normalizeBackCover(rawBack, tokenCtx = {}) {
1724
1803
  }
1725
1804
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1726
1805
  const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
1806
+ const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1727
1807
  const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1728
1808
  const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1729
1809
  const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1730
1810
  const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1731
1811
  const logoWidth = cfg.logoWidth;
1732
- const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1812
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1813
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1733
1814
  const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1815
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1816
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1817
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1734
1818
  return {
1735
1819
  enabled: true,
1736
1820
  preset,
1737
1821
  title,
1738
1822
  subtitle,
1823
+ author,
1739
1824
  company,
1825
+ version,
1826
+ date,
1740
1827
  address,
1741
1828
  email,
1742
1829
  phone,
1743
1830
  website,
1744
1831
  social: socialMap,
1745
1832
  copyright,
1833
+ footerText,
1746
1834
  badge,
1747
1835
  badgeColor,
1748
1836
  badgeTextColor,
1749
1837
  logo,
1750
1838
  logoWidth,
1751
1839
  bgGradient,
1752
- textColor
1840
+ backgroundColor,
1841
+ textColor,
1842
+ titleColor,
1843
+ subtitleColor,
1844
+ accentColor
1753
1845
  };
1754
1846
  }
1755
1847
  function normalizeNumberHeadings(raw) {
@@ -1789,7 +1881,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1789
1881
  const version = mergedMeta.version || void 0;
1790
1882
  const company = mergedMeta.company || void 0;
1791
1883
  const lang = mergedMeta.lang || "en";
1792
- const tokenContext = { title, subtitle, author, version, date, company };
1884
+ const tokenContext = {
1885
+ ...mergedMeta,
1886
+ title,
1887
+ subtitle,
1888
+ author,
1889
+ version,
1890
+ date,
1891
+ company
1892
+ };
1793
1893
  const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1794
1894
  const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1795
1895
  const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
@@ -1818,7 +1918,7 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1818
1918
  const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1819
1919
  const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1820
1920
  const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1821
- const watermark = normalizeWatermark(rawWatermark);
1921
+ const watermark = normalizeWatermark(rawWatermark, tokenContext);
1822
1922
  const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1823
1923
  const signatures = normalizeSignatures(rawSignatures, tokenContext);
1824
1924
  const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
@@ -1914,7 +2014,7 @@ var KATEX_INLINE_CSS = `
1914
2014
  function escapeHtml(str) {
1915
2015
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1916
2016
  }
1917
- async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
2017
+ async function renderInlinesToHtml(spans = [], baseDir = process.cwd(), tokens) {
1918
2018
  let result = "";
1919
2019
  for (const span of spans) {
1920
2020
  if (span.type === "image" && span.url) {
@@ -1928,23 +2028,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1928
2028
  continue;
1929
2029
  }
1930
2030
  if (span.type === "link" && span.url) {
1931
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
2031
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1932
2032
  const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
1933
2033
  result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
1934
2034
  continue;
1935
2035
  }
1936
2036
  if (span.type === "bold") {
1937
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
2037
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1938
2038
  result += `<strong>${inner}</strong>`;
1939
2039
  continue;
1940
2040
  }
1941
2041
  if (span.type === "italic") {
1942
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
2042
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1943
2043
  result += `<em>${inner}</em>`;
1944
2044
  continue;
1945
2045
  }
1946
2046
  if (span.type === "strikethrough") {
1947
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
2047
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1948
2048
  result += `<del>${inner}</del>`;
1949
2049
  continue;
1950
2050
  }
@@ -1965,21 +2065,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1965
2065
  result += span.content;
1966
2066
  continue;
1967
2067
  }
1968
- result += escapeHtml(span.content);
2068
+ const content = tokens ? replaceDocumentTokens(span.content, tokens) : span.content;
2069
+ result += escapeHtml(content);
1969
2070
  }
1970
2071
  return result;
1971
2072
  }
1972
- async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
2073
+ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd(), tokens) {
1973
2074
  let bodyHtml = "";
2075
+ const tokenCtx = tokens || resolved;
1974
2076
  for (const node of nodes) {
1975
2077
  if (node.type === "heading") {
1976
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2078
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1977
2079
  bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
1978
2080
  `;
1979
2081
  continue;
1980
2082
  }
1981
2083
  if (node.type === "paragraph") {
1982
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2084
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1983
2085
  bodyHtml += ` <p>${inner}</p>
1984
2086
  `;
1985
2087
  continue;
@@ -1994,7 +2096,7 @@ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
1994
2096
  const gap = node.columnGap || "1.5rem";
1995
2097
  let colChildrenHtml = "";
1996
2098
  for (const col of node.children || []) {
1997
- const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir);
2099
+ const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir, tokenCtx);
1998
2100
  colChildrenHtml += ` <div class="markforge-col">
1999
2101
  ${colInner} </div>
2000
2102
  `;
@@ -2020,7 +2122,7 @@ ${escapeHtml(node.text || "")}
2020
2122
  continue;
2021
2123
  }
2022
2124
  if (node.type === "callout") {
2023
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2125
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
2024
2126
  const CALLOUT_STYLES = {
2025
2127
  NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
2026
2128
  TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
@@ -2040,7 +2142,7 @@ ${escapeHtml(node.text || "")}
2040
2142
  continue;
2041
2143
  }
2042
2144
  if (node.type === "blockquote") {
2043
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2145
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
2044
2146
  bodyHtml += ` <blockquote>${inner}</blockquote>
2045
2147
  `;
2046
2148
  continue;
@@ -2054,7 +2156,7 @@ ${escapeHtml(node.text || "")}
2054
2156
  for (const cell of row.children || []) {
2055
2157
  const tag = cell.isHeader ? "th" : "td";
2056
2158
  const align = cell.align ? ` align="${cell.align}"` : "";
2057
- const inner = await renderInlinesToHtml(cell.inlines, baseDir);
2159
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir, tokenCtx);
2058
2160
  bodyHtml += ` <${tag}${align}>${inner}</${tag}>
2059
2161
  `;
2060
2162
  }
@@ -2070,7 +2172,7 @@ ${escapeHtml(node.text || "")}
2070
2172
  bodyHtml += ` <${tag}>
2071
2173
  `;
2072
2174
  for (const item of node.children) {
2073
- const inner = await renderInlinesToHtml(item.inlines, baseDir);
2175
+ const inner = await renderInlinesToHtml(item.inlines, baseDir, tokenCtx);
2074
2176
  bodyHtml += ` <li>${inner}</li>
2075
2177
  `;
2076
2178
  }
@@ -2384,7 +2486,7 @@ async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
2384
2486
  return { html, css };
2385
2487
  }
2386
2488
  async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2387
- var _a;
2489
+ var _a, _b;
2388
2490
  const resolved = resolveDocumentConfig(doc.metadata, config);
2389
2491
  const baseThemeCss = generateThemeCss(resolved.theme);
2390
2492
  let customCss = "";
@@ -2480,6 +2582,9 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2480
2582
  bodyHtml += ` </header>
2481
2583
  `;
2482
2584
  }
2585
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
2586
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
2587
+ }
2483
2588
  if (resolved.toc && doc.tocEntries.length > 0) {
2484
2589
  bodyHtml += ` <nav class="table-of-contents">
2485
2590
  `;
@@ -2495,12 +2600,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2495
2600
  </nav>
2496
2601
  `;
2497
2602
  }
2498
- bodyHtml += await renderNodesToHtml(doc.nodes, resolved, baseDir);
2603
+ const mergedTokens = {
2604
+ ...config.metadata,
2605
+ ...doc.metadata,
2606
+ ...resolved,
2607
+ title: resolved.title,
2608
+ subtitle: resolved.subtitle,
2609
+ author: resolved.author,
2610
+ version: resolved.version,
2611
+ date: resolved.date,
2612
+ company: resolved.company
2613
+ };
2614
+ const nodesHtml = await renderNodesToHtml(doc.nodes, resolved, baseDir, mergedTokens);
2615
+ bodyHtml += ` <main class="markforge-content-body">
2616
+ ${nodesHtml} </main>
2617
+ `;
2499
2618
  let footnotesHtml = "";
2500
2619
  if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
2501
2620
  let fnListHtml = "";
2502
2621
  for (const def of doc.footnoteDefs) {
2503
- const defInner = await renderInlinesToHtml(def.inlines, baseDir);
2622
+ const defInner = await renderInlinesToHtml(def.inlines, baseDir, mergedTokens);
2504
2623
  fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">&#8617;</a></li>
2505
2624
  `;
2506
2625
  }
@@ -2538,17 +2657,7 @@ ${fnListHtml} </ol>
2538
2657
  }
2539
2658
  @media print {
2540
2659
  .document-watermark {
2541
- position: fixed;
2542
- top: 0;
2543
- left: 0;
2544
- right: 0;
2545
- bottom: 0;
2546
- width: 100vw;
2547
- height: 100vh;
2548
- pointer-events: none;
2549
- z-index: 0;
2550
- -webkit-print-color-adjust: exact;
2551
- print-color-adjust: exact;
2660
+ display: none !important;
2552
2661
  }
2553
2662
  }
2554
2663
  `;
@@ -2868,7 +2977,7 @@ function findChromeExecutable() {
2868
2977
  return null;
2869
2978
  }
2870
2979
  function injectPagedMediaStyles(html, config, metadata) {
2871
- var _a, _b, _c, _d, _e, _f, _g, _h;
2980
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2872
2981
  const resolved = resolveDocumentConfig(metadata || {}, config);
2873
2982
  const size = resolved.paperSize;
2874
2983
  const orientation = resolved.orientation;
@@ -2943,9 +3052,11 @@ function injectPagedMediaStyles(html, config, metadata) {
2943
3052
  min-height: 100vh;
2944
3053
  height: 100vh;
2945
3054
  box-sizing: border-box;
3055
+ break-before: page;
3056
+ break-after: avoid;
2946
3057
  }` : "";
2947
- const pagedCss = `
2948
- @page {
3058
+ const tocPageCss = resolved.toc ? `
3059
+ @page toc-page {
2949
3060
  size: ${size} ${orientation};
2950
3061
  margin-top: ${top};
2951
3062
  margin-bottom: ${bottom};
@@ -2956,9 +3067,36 @@ function injectPagedMediaStyles(html, config, metadata) {
2956
3067
  ${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
2957
3068
  ${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
2958
3069
  ${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
2959
- ${buildZoneCss("bottom-right", (_h = resolved.footer) == null ? void 0 : _h.right, true)}
3070
+ @bottom-right {
3071
+ content: counter(page, lower-roman);
3072
+ font-size: 9pt;
3073
+ color: #94a3b8;
3074
+ }
3075
+ }
3076
+ .table-of-contents {
3077
+ page: toc-page;
3078
+ page-break-after: always;
3079
+ break-after: page;
3080
+ }
3081
+ .markforge-content-body {
3082
+ counter-reset: page 1;
3083
+ }` : "";
3084
+ const pagedCss = `
3085
+ @page {
3086
+ size: ${size} ${orientation};
3087
+ margin-top: ${top};
3088
+ margin-bottom: ${bottom};
3089
+ margin-left: ${left};
3090
+ margin-right: ${right};
3091
+ ${buildZoneCss("top-left", (_h = resolved.header) == null ? void 0 : _h.left)}
3092
+ ${buildZoneCss("top-center", (_i = resolved.header) == null ? void 0 : _i.center)}
3093
+ ${buildZoneCss("top-right", (_j = resolved.header) == null ? void 0 : _j.right)}
3094
+ ${buildZoneCss("bottom-left", (_k = resolved.footer) == null ? void 0 : _k.left)}
3095
+ ${buildZoneCss("bottom-center", (_l = resolved.footer) == null ? void 0 : _l.center)}
3096
+ ${buildZoneCss("bottom-right", (_m = resolved.footer) == null ? void 0 : _m.right, true)}
2960
3097
  }
2961
3098
  ${coverPageCss}
3099
+ ${tocPageCss}
2962
3100
  ${backCoverCss}
2963
3101
  @media print {
2964
3102
  body { padding: 0; }
@@ -3089,9 +3227,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
3089
3227
  const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
3090
3228
  if (wmPng) {
3091
3229
  const embeddedPng = await pdfDoc.embedPng(wmPng);
3230
+ const totalPages = pdfDoc.getPageCount();
3092
3231
  const pages = pdfDoc.getPages();
3093
3232
  const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
3094
- const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? pages.length - 1 : pages.length;
3233
+ const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? totalPages - 1 : totalPages;
3095
3234
  for (let i = startPageIndex; i < endPageIndex; i++) {
3096
3235
  const page = pages[i];
3097
3236
  const { width, height } = page.getSize();
@@ -3431,7 +3570,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
3431
3570
  return runs;
3432
3571
  }
3433
3572
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3434
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3573
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3435
3574
  const resolved = resolveDocumentConfig(doc.metadata, config);
3436
3575
  const docElements = [];
3437
3576
  const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
@@ -3503,6 +3642,9 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3503
3642
  );
3504
3643
  }
3505
3644
  }
3645
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
3646
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
3647
+ }
3506
3648
  if (resolved.toc) {
3507
3649
  const headingNodes = doc.nodes.filter(
3508
3650
  (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
@@ -3572,7 +3714,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3572
3714
  ]
3573
3715
  });
3574
3716
  docElements.push(tocCard);
3575
- docElements.push(new import_docx.Paragraph({ spacing: { after: 200 } }));
3717
+ docElements.push(
3718
+ new import_docx.Paragraph({
3719
+ children: [new import_docx.PageBreak()]
3720
+ })
3721
+ );
3576
3722
  }
3577
3723
  }
3578
3724
  for (const node of doc.nodes) {
@@ -3582,7 +3728,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3582
3728
  font: defaultFont,
3583
3729
  size: 34,
3584
3730
  // 17pt
3585
- color: textHex,
3731
+ color: primaryDarkHex,
3586
3732
  bold: true
3587
3733
  });
3588
3734
  docElements.push(
@@ -3628,7 +3774,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3628
3774
  font: defaultFont,
3629
3775
  size: 22,
3630
3776
  // 11pt
3631
- color: textHex,
3777
+ color: primaryDarkHex,
3632
3778
  bold: true
3633
3779
  });
3634
3780
  docElements.push(
@@ -3858,7 +4004,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3858
4004
  }
3859
4005
  if (node.type === "table" && node.children) {
3860
4006
  const tableRows = [];
3861
- const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
4007
+ const numCols = ((_d = (_c = node.children[0]) == null ? void 0 : _c.children) == null ? void 0 : _d.length) || 1;
3862
4008
  const colWidth = Math.floor(9e3 / numCols);
3863
4009
  for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
3864
4010
  const rowNode = node.children[rowIdx];
@@ -3868,7 +4014,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3868
4014
  if (rowNode.children) {
3869
4015
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
3870
4016
  const cellNode = rowNode.children[colIdx];
3871
- const align = (_d = node.align) == null ? void 0 : _d[colIdx];
4017
+ const align = (_e = node.align) == null ? void 0 : _e[colIdx];
3872
4018
  let alignment = import_docx.AlignmentType.LEFT;
3873
4019
  if (align === "center") alignment = import_docx.AlignmentType.CENTER;
3874
4020
  if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
@@ -4175,7 +4321,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4175
4321
  const centerPos = Math.round(contentWidthTwip / 2);
4176
4322
  const rightPos = contentWidthTwip;
4177
4323
  const headerRuns = [];
4178
- if ((_e = resolved.header) == null ? void 0 : _e.left) {
4324
+ if ((_f = resolved.header) == null ? void 0 : _f.left) {
4179
4325
  headerRuns.push(
4180
4326
  new import_docx.TextRun({
4181
4327
  text: resolved.header.left.text,
@@ -4188,7 +4334,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4188
4334
  );
4189
4335
  }
4190
4336
  headerRuns.push(new import_docx.TextRun({ text: " " }));
4191
- if ((_f = resolved.header) == null ? void 0 : _f.center) {
4337
+ if ((_g = resolved.header) == null ? void 0 : _g.center) {
4192
4338
  headerRuns.push(
4193
4339
  new import_docx.TextRun({
4194
4340
  text: resolved.header.center.text,
@@ -4201,7 +4347,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4201
4347
  );
4202
4348
  }
4203
4349
  headerRuns.push(new import_docx.TextRun({ text: " " }));
4204
- if ((_g = resolved.header) == null ? void 0 : _g.right) {
4350
+ if ((_h = resolved.header) == null ? void 0 : _h.right) {
4205
4351
  headerRuns.push(
4206
4352
  new import_docx.TextRun({
4207
4353
  text: resolved.header.right.text,
@@ -4240,7 +4386,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4240
4386
  ]
4241
4387
  }) : void 0;
4242
4388
  const footerRuns = [];
4243
- if ((_h = resolved.footer) == null ? void 0 : _h.left) {
4389
+ if ((_i = resolved.footer) == null ? void 0 : _i.left) {
4244
4390
  footerRuns.push(
4245
4391
  new import_docx.TextRun({
4246
4392
  text: resolved.footer.left.text,
@@ -4253,7 +4399,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4253
4399
  );
4254
4400
  }
4255
4401
  footerRuns.push(new import_docx.TextRun({ text: " " }));
4256
- if ((_i = resolved.footer) == null ? void 0 : _i.center) {
4402
+ if ((_j = resolved.footer) == null ? void 0 : _j.center) {
4257
4403
  footerRuns.push(
4258
4404
  new import_docx.TextRun({
4259
4405
  text: resolved.footer.center.text,
@@ -4266,7 +4412,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4266
4412
  );
4267
4413
  }
4268
4414
  footerRuns.push(new import_docx.TextRun({ text: " " }));
4269
- if ((_j = resolved.footer) == null ? void 0 : _j.right) {
4415
+ if ((_k = resolved.footer) == null ? void 0 : _k.right) {
4270
4416
  const rZone = resolved.footer.right;
4271
4417
  const rColor = rZone.color.replace("#", "");
4272
4418
  const rSize = (rZone.fontSize || 9) * 2;
@@ -4386,6 +4532,10 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4386
4532
  docSections.push({
4387
4533
  properties: {
4388
4534
  page: {
4535
+ pageNumbers: {
4536
+ start: 1,
4537
+ formatType: import_docx.NumberFormat.DECIMAL
4538
+ },
4389
4539
  size: {
4390
4540
  width: resolved.paperDimensions.widthTwip,
4391
4541
  height: resolved.paperDimensions.heightTwip,
@@ -5828,10 +5978,9 @@ try {
5828
5978
  }
5829
5979
  } catch {
5830
5980
  }
5831
- var FALLBACK_VERSION = "0.4.0";
5832
5981
  function readVersionFromPackageJson(fromDir) {
5833
5982
  let currentDir = fromDir;
5834
- for (let i = 0; i < 6; i++) {
5983
+ for (let i = 0; i < 10; i++) {
5835
5984
  try {
5836
5985
  const pkgJsonPath = path8.join(currentDir, "package.json");
5837
5986
  if (fs8.existsSync(pkgJsonPath)) {
@@ -5846,7 +5995,9 @@ function readVersionFromPackageJson(fromDir) {
5846
5995
  if (parentDir === currentDir) break;
5847
5996
  currentDir = parentDir;
5848
5997
  }
5849
- return FALLBACK_VERSION;
5998
+ throw new Error(
5999
+ "Failed to resolve '@masumdev/markforge' package version: package.json was not found or is missing a valid 'version' field."
6000
+ );
5850
6001
  }
5851
6002
  function getPackageDir() {
5852
6003
  if (typeof __dirname !== "undefined") {
@@ -5878,6 +6029,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
5878
6029
  THEME_DEFAULT,
5879
6030
  Theme,
5880
6031
  WatermarkPosition,
6032
+ applyHeadingNumbering,
5881
6033
  buildDocxDocument,
5882
6034
  buildHtmlDocument,
5883
6035
  buildPdfDocument,