@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.mjs CHANGED
@@ -189,7 +189,16 @@ function parseInlineSpans(text) {
189
189
  remaining = remaining.slice(nextSpecial);
190
190
  }
191
191
  }
192
- return spans;
192
+ const merged = [];
193
+ for (const s of spans) {
194
+ const prev = merged[merged.length - 1];
195
+ if (prev && prev.type === "text" && s.type === "text") {
196
+ prev.content += s.content;
197
+ } else {
198
+ merged.push(s);
199
+ }
200
+ }
201
+ return merged;
193
202
  }
194
203
  function slugify(text) {
195
204
  return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
@@ -201,6 +210,7 @@ function applyHeadingNumbering(nodes, tocEntries, options) {
201
210
  const counters = [0, 0, 0, 0, 0, 0];
202
211
  for (const node of nodes) {
203
212
  if (node.type === "heading" && node.level) {
213
+ if (node._numbered) continue;
204
214
  const lvl = node.level;
205
215
  if (lvl > depth) continue;
206
216
  if (lvl === 1 && skipH1) continue;
@@ -216,6 +226,7 @@ function applyHeadingNumbering(nodes, tocEntries, options) {
216
226
  const originalText = node.text || "";
217
227
  node.text = fullPrefix + originalText;
218
228
  node.inlines = parseInlineSpans(node.text);
229
+ node._numbered = true;
219
230
  const toc = tocEntries.find((t) => t.id === node.id);
220
231
  if (toc) {
221
232
  toc.text = node.text;
@@ -578,7 +589,9 @@ import {
578
589
  convertMillimetersToTwip,
579
590
  ShadingType,
580
591
  ExternalHyperlink,
581
- TabStopType
592
+ TabStopType,
593
+ PageBreak,
594
+ NumberFormat
582
595
  } from "docx";
583
596
 
584
597
  // src/core/imageResolver.ts
@@ -1125,7 +1138,7 @@ var THEME_COMPONENTS = `
1125
1138
  .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
1126
1139
 
1127
1140
  /* Table of Contents */
1128
- .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
1141
+ .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; }
1129
1142
  .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
1130
1143
  .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
1131
1144
  .table-of-contents li { padding: 0.25rem 0; }
@@ -1198,11 +1211,13 @@ var THEME_CORPORATE = `
1198
1211
  }
1199
1212
  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; }
1200
1213
  .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1201
- 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; }
1202
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1214
+ 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; }
1215
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
1203
1216
  h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1204
- h3 { font-size: 1.3rem; }
1205
- h4 { font-size: 1.1rem; }
1217
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
1218
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
1219
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
1220
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
1206
1221
  p { margin: 0.8rem 0; }
1207
1222
  `;
1208
1223
  var THEME_DEFAULT = THEME_CORPORATE;
@@ -1244,11 +1259,13 @@ function generateThemeCss(theme) {
1244
1259
  }
1245
1260
  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; }
1246
1261
  .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1247
- 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; }
1248
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1262
+ 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; }
1263
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
1249
1264
  h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1250
- h3 { font-size: 1.3rem; }
1251
- h4 { font-size: 1.1rem; }
1265
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
1266
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
1267
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
1268
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
1252
1269
  p { margin: 0.8rem 0; }
1253
1270
  ${theme.customCss || ""}
1254
1271
  `;
@@ -1457,16 +1474,45 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
1457
1474
  if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1458
1475
  return str;
1459
1476
  }
1460
- function replaceDocumentTokens(template = "", meta) {
1461
- const currentYear = meta.year || (/* @__PURE__ */ new Date()).getFullYear().toString();
1462
- 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 || "");
1477
+ function replaceDocumentTokens(template = "", meta = {}) {
1478
+ if (!template) return "";
1479
+ const currentYear = meta.year ? String(meta.year) : (/* @__PURE__ */ new Date()).getFullYear().toString();
1480
+ const tokenMap = {
1481
+ title: meta.title ? String(meta.title) : "",
1482
+ subtitle: meta.subtitle ? String(meta.subtitle) : "",
1483
+ author: meta.author ? Array.isArray(meta.author) ? meta.author.join(", ") : String(meta.author) : "",
1484
+ version: meta.version ? String(meta.version) : "",
1485
+ date: meta.date ? String(meta.date) : "",
1486
+ company: meta.company ? String(meta.company) : "",
1487
+ year: currentYear
1488
+ };
1489
+ if (meta.metadata && typeof meta.metadata === "object") {
1490
+ for (const [key, val] of Object.entries(meta.metadata)) {
1491
+ if (val !== void 0 && val !== null) {
1492
+ tokenMap[key.toLowerCase()] = String(val);
1493
+ }
1494
+ }
1495
+ }
1496
+ for (const [key, val] of Object.entries(meta)) {
1497
+ if (val !== void 0 && val !== null && typeof val !== "object") {
1498
+ tokenMap[key.toLowerCase()] = String(val);
1499
+ }
1500
+ }
1501
+ return template.replace(/\{([a-zA-Z0-9_\-]+)\}/gi, (match, tokenKey) => {
1502
+ const lowerKey = tokenKey.toLowerCase();
1503
+ if (lowerKey in tokenMap) {
1504
+ return tokenMap[lowerKey];
1505
+ }
1506
+ return match;
1507
+ });
1463
1508
  }
1464
- function normalizeWatermark(rawWatermark) {
1509
+ function normalizeWatermark(rawWatermark, tokens) {
1465
1510
  if (!rawWatermark) {
1466
1511
  return void 0;
1467
1512
  }
1468
1513
  if (typeof rawWatermark === "string") {
1469
- const text = rawWatermark.trim();
1514
+ let text = rawWatermark.trim();
1515
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1470
1516
  if (!text) return void 0;
1471
1517
  return {
1472
1518
  text,
@@ -1479,8 +1525,10 @@ function normalizeWatermark(rawWatermark) {
1479
1525
  }
1480
1526
  if (typeof rawWatermark === "object") {
1481
1527
  if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1528
+ let text = rawWatermark.text.trim();
1529
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1482
1530
  return {
1483
- text: rawWatermark.text.trim(),
1531
+ text,
1484
1532
  color: rawWatermark.color || "#94a3b8",
1485
1533
  opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1486
1534
  fontSize: rawWatermark.fontSize || 54,
@@ -1601,27 +1649,46 @@ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1601
1649
  const cfg = typeof rawCover === "object" ? rawCover : {};
1602
1650
  if (cfg.enabled === false) return void 0;
1603
1651
  const preset = cfg.preset || "modern";
1604
- const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title || "Document Title";
1605
- const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle;
1606
- const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author;
1607
- const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1608
- const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version;
1652
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title ? String(tokenCtx.title) : "Document Title";
1653
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle ? String(tokenCtx.subtitle) : void 0;
1654
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1655
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1656
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1609
1657
  let dateStr;
1610
1658
  if (typeof cfg.date === "string") {
1611
1659
  dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
1612
1660
  } else if (cfg.date === true) {
1613
- dateStr = tokenCtx.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1661
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1614
1662
  } else {
1615
- dateStr = tokenCtx.date;
1663
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : void 0;
1616
1664
  }
1617
1665
  const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1618
1666
  const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1619
1667
  const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1620
1668
  const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1621
1669
  const logoWidth = cfg.logoWidth;
1622
- const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1670
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1671
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1623
1672
  const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1673
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1674
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1675
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1624
1676
  const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1677
+ const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1678
+ const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1679
+ const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
1680
+ const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
1681
+ let socialMap;
1682
+ if (cfg.social && typeof cfg.social === "object") {
1683
+ socialMap = {};
1684
+ for (const [k, v] of Object.entries(cfg.social)) {
1685
+ if (typeof v === "string") {
1686
+ socialMap[k] = replaceDocumentTokens(v, tokenCtx);
1687
+ }
1688
+ }
1689
+ }
1690
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1691
+ const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : void 0;
1625
1692
  return {
1626
1693
  enabled: true,
1627
1694
  preset,
@@ -1637,8 +1704,18 @@ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1637
1704
  logo,
1638
1705
  logoWidth,
1639
1706
  bgGradient,
1707
+ backgroundColor,
1640
1708
  textColor,
1641
- footerText
1709
+ titleColor,
1710
+ subtitleColor,
1711
+ accentColor,
1712
+ footerText,
1713
+ address,
1714
+ email,
1715
+ phone,
1716
+ website,
1717
+ social: socialMap,
1718
+ copyright
1642
1719
  };
1643
1720
  }
1644
1721
  function normalizeBackCover(rawBack, tokenCtx = {}) {
@@ -1648,7 +1725,10 @@ function normalizeBackCover(rawBack, tokenCtx = {}) {
1648
1725
  const preset = cfg.preset || "modern";
1649
1726
  const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
1650
1727
  const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
1651
- const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1728
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1729
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1730
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1731
+ const date = typeof cfg.date === "string" ? replaceDocumentTokens(cfg.date, tokenCtx) : tokenCtx.date ? String(tokenCtx.date) : void 0;
1652
1732
  const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1653
1733
  const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1654
1734
  const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
@@ -1664,32 +1744,45 @@ function normalizeBackCover(rawBack, tokenCtx = {}) {
1664
1744
  }
1665
1745
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1666
1746
  const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
1747
+ const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1667
1748
  const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1668
1749
  const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1669
1750
  const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1670
1751
  const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1671
1752
  const logoWidth = cfg.logoWidth;
1672
- const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1753
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1754
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1673
1755
  const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1756
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1757
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1758
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1674
1759
  return {
1675
1760
  enabled: true,
1676
1761
  preset,
1677
1762
  title,
1678
1763
  subtitle,
1764
+ author,
1679
1765
  company,
1766
+ version,
1767
+ date,
1680
1768
  address,
1681
1769
  email,
1682
1770
  phone,
1683
1771
  website,
1684
1772
  social: socialMap,
1685
1773
  copyright,
1774
+ footerText,
1686
1775
  badge,
1687
1776
  badgeColor,
1688
1777
  badgeTextColor,
1689
1778
  logo,
1690
1779
  logoWidth,
1691
1780
  bgGradient,
1692
- textColor
1781
+ backgroundColor,
1782
+ textColor,
1783
+ titleColor,
1784
+ subtitleColor,
1785
+ accentColor
1693
1786
  };
1694
1787
  }
1695
1788
  function normalizeNumberHeadings(raw) {
@@ -1729,7 +1822,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1729
1822
  const version = mergedMeta.version || void 0;
1730
1823
  const company = mergedMeta.company || void 0;
1731
1824
  const lang = mergedMeta.lang || "en";
1732
- const tokenContext = { title, subtitle, author, version, date, company };
1825
+ const tokenContext = {
1826
+ ...mergedMeta,
1827
+ title,
1828
+ subtitle,
1829
+ author,
1830
+ version,
1831
+ date,
1832
+ company
1833
+ };
1733
1834
  const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1734
1835
  const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1735
1836
  const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
@@ -1758,7 +1859,7 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1758
1859
  const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1759
1860
  const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1760
1861
  const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1761
- const watermark = normalizeWatermark(rawWatermark);
1862
+ const watermark = normalizeWatermark(rawWatermark, tokenContext);
1762
1863
  const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1763
1864
  const signatures = normalizeSignatures(rawSignatures, tokenContext);
1764
1865
  const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
@@ -1854,7 +1955,7 @@ var KATEX_INLINE_CSS = `
1854
1955
  function escapeHtml(str) {
1855
1956
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1856
1957
  }
1857
- async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1958
+ async function renderInlinesToHtml(spans = [], baseDir = process.cwd(), tokens) {
1858
1959
  let result = "";
1859
1960
  for (const span of spans) {
1860
1961
  if (span.type === "image" && span.url) {
@@ -1868,23 +1969,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1868
1969
  continue;
1869
1970
  }
1870
1971
  if (span.type === "link" && span.url) {
1871
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1972
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1872
1973
  const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
1873
1974
  result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
1874
1975
  continue;
1875
1976
  }
1876
1977
  if (span.type === "bold") {
1877
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1978
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1878
1979
  result += `<strong>${inner}</strong>`;
1879
1980
  continue;
1880
1981
  }
1881
1982
  if (span.type === "italic") {
1882
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1983
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1883
1984
  result += `<em>${inner}</em>`;
1884
1985
  continue;
1885
1986
  }
1886
1987
  if (span.type === "strikethrough") {
1887
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1988
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1888
1989
  result += `<del>${inner}</del>`;
1889
1990
  continue;
1890
1991
  }
@@ -1905,21 +2006,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1905
2006
  result += span.content;
1906
2007
  continue;
1907
2008
  }
1908
- result += escapeHtml(span.content);
2009
+ const content = tokens ? replaceDocumentTokens(span.content, tokens) : span.content;
2010
+ result += escapeHtml(content);
1909
2011
  }
1910
2012
  return result;
1911
2013
  }
1912
- async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
2014
+ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd(), tokens) {
1913
2015
  let bodyHtml = "";
2016
+ const tokenCtx = tokens || resolved;
1914
2017
  for (const node of nodes) {
1915
2018
  if (node.type === "heading") {
1916
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2019
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1917
2020
  bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
1918
2021
  `;
1919
2022
  continue;
1920
2023
  }
1921
2024
  if (node.type === "paragraph") {
1922
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2025
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1923
2026
  bodyHtml += ` <p>${inner}</p>
1924
2027
  `;
1925
2028
  continue;
@@ -1934,7 +2037,7 @@ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
1934
2037
  const gap = node.columnGap || "1.5rem";
1935
2038
  let colChildrenHtml = "";
1936
2039
  for (const col of node.children || []) {
1937
- const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir);
2040
+ const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir, tokenCtx);
1938
2041
  colChildrenHtml += ` <div class="markforge-col">
1939
2042
  ${colInner} </div>
1940
2043
  `;
@@ -1960,7 +2063,7 @@ ${escapeHtml(node.text || "")}
1960
2063
  continue;
1961
2064
  }
1962
2065
  if (node.type === "callout") {
1963
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2066
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1964
2067
  const CALLOUT_STYLES = {
1965
2068
  NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
1966
2069
  TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
@@ -1980,7 +2083,7 @@ ${escapeHtml(node.text || "")}
1980
2083
  continue;
1981
2084
  }
1982
2085
  if (node.type === "blockquote") {
1983
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
2086
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1984
2087
  bodyHtml += ` <blockquote>${inner}</blockquote>
1985
2088
  `;
1986
2089
  continue;
@@ -1994,7 +2097,7 @@ ${escapeHtml(node.text || "")}
1994
2097
  for (const cell of row.children || []) {
1995
2098
  const tag = cell.isHeader ? "th" : "td";
1996
2099
  const align = cell.align ? ` align="${cell.align}"` : "";
1997
- const inner = await renderInlinesToHtml(cell.inlines, baseDir);
2100
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir, tokenCtx);
1998
2101
  bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1999
2102
  `;
2000
2103
  }
@@ -2010,7 +2113,7 @@ ${escapeHtml(node.text || "")}
2010
2113
  bodyHtml += ` <${tag}>
2011
2114
  `;
2012
2115
  for (const item of node.children) {
2013
- const inner = await renderInlinesToHtml(item.inlines, baseDir);
2116
+ const inner = await renderInlinesToHtml(item.inlines, baseDir, tokenCtx);
2014
2117
  bodyHtml += ` <li>${inner}</li>
2015
2118
  `;
2016
2119
  }
@@ -2324,7 +2427,7 @@ async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
2324
2427
  return { html, css };
2325
2428
  }
2326
2429
  async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2327
- var _a;
2430
+ var _a, _b;
2328
2431
  const resolved = resolveDocumentConfig(doc.metadata, config);
2329
2432
  const baseThemeCss = generateThemeCss(resolved.theme);
2330
2433
  let customCss = "";
@@ -2420,6 +2523,9 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2420
2523
  bodyHtml += ` </header>
2421
2524
  `;
2422
2525
  }
2526
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
2527
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
2528
+ }
2423
2529
  if (resolved.toc && doc.tocEntries.length > 0) {
2424
2530
  bodyHtml += ` <nav class="table-of-contents">
2425
2531
  `;
@@ -2435,12 +2541,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2435
2541
  </nav>
2436
2542
  `;
2437
2543
  }
2438
- bodyHtml += await renderNodesToHtml(doc.nodes, resolved, baseDir);
2544
+ const mergedTokens = {
2545
+ ...config.metadata,
2546
+ ...doc.metadata,
2547
+ ...resolved,
2548
+ title: resolved.title,
2549
+ subtitle: resolved.subtitle,
2550
+ author: resolved.author,
2551
+ version: resolved.version,
2552
+ date: resolved.date,
2553
+ company: resolved.company
2554
+ };
2555
+ const nodesHtml = await renderNodesToHtml(doc.nodes, resolved, baseDir, mergedTokens);
2556
+ bodyHtml += ` <main class="markforge-content-body">
2557
+ ${nodesHtml} </main>
2558
+ `;
2439
2559
  let footnotesHtml = "";
2440
2560
  if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
2441
2561
  let fnListHtml = "";
2442
2562
  for (const def of doc.footnoteDefs) {
2443
- const defInner = await renderInlinesToHtml(def.inlines, baseDir);
2563
+ const defInner = await renderInlinesToHtml(def.inlines, baseDir, mergedTokens);
2444
2564
  fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">&#8617;</a></li>
2445
2565
  `;
2446
2566
  }
@@ -2478,17 +2598,7 @@ ${fnListHtml} </ol>
2478
2598
  }
2479
2599
  @media print {
2480
2600
  .document-watermark {
2481
- position: fixed;
2482
- top: 0;
2483
- left: 0;
2484
- right: 0;
2485
- bottom: 0;
2486
- width: 100vw;
2487
- height: 100vh;
2488
- pointer-events: none;
2489
- z-index: 0;
2490
- -webkit-print-color-adjust: exact;
2491
- print-color-adjust: exact;
2601
+ display: none !important;
2492
2602
  }
2493
2603
  }
2494
2604
  `;
@@ -2808,7 +2918,7 @@ function findChromeExecutable() {
2808
2918
  return null;
2809
2919
  }
2810
2920
  function injectPagedMediaStyles(html, config, metadata) {
2811
- var _a, _b, _c, _d, _e, _f, _g, _h;
2921
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2812
2922
  const resolved = resolveDocumentConfig(metadata || {}, config);
2813
2923
  const size = resolved.paperSize;
2814
2924
  const orientation = resolved.orientation;
@@ -2883,9 +2993,11 @@ function injectPagedMediaStyles(html, config, metadata) {
2883
2993
  min-height: 100vh;
2884
2994
  height: 100vh;
2885
2995
  box-sizing: border-box;
2996
+ break-before: page;
2997
+ break-after: avoid;
2886
2998
  }` : "";
2887
- const pagedCss = `
2888
- @page {
2999
+ const tocPageCss = resolved.toc ? `
3000
+ @page toc-page {
2889
3001
  size: ${size} ${orientation};
2890
3002
  margin-top: ${top};
2891
3003
  margin-bottom: ${bottom};
@@ -2896,9 +3008,36 @@ function injectPagedMediaStyles(html, config, metadata) {
2896
3008
  ${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
2897
3009
  ${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
2898
3010
  ${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
2899
- ${buildZoneCss("bottom-right", (_h = resolved.footer) == null ? void 0 : _h.right, true)}
3011
+ @bottom-right {
3012
+ content: counter(page, lower-roman);
3013
+ font-size: 9pt;
3014
+ color: #94a3b8;
3015
+ }
3016
+ }
3017
+ .table-of-contents {
3018
+ page: toc-page;
3019
+ page-break-after: always;
3020
+ break-after: page;
3021
+ }
3022
+ .markforge-content-body {
3023
+ counter-reset: page 1;
3024
+ }` : "";
3025
+ const pagedCss = `
3026
+ @page {
3027
+ size: ${size} ${orientation};
3028
+ margin-top: ${top};
3029
+ margin-bottom: ${bottom};
3030
+ margin-left: ${left};
3031
+ margin-right: ${right};
3032
+ ${buildZoneCss("top-left", (_h = resolved.header) == null ? void 0 : _h.left)}
3033
+ ${buildZoneCss("top-center", (_i = resolved.header) == null ? void 0 : _i.center)}
3034
+ ${buildZoneCss("top-right", (_j = resolved.header) == null ? void 0 : _j.right)}
3035
+ ${buildZoneCss("bottom-left", (_k = resolved.footer) == null ? void 0 : _k.left)}
3036
+ ${buildZoneCss("bottom-center", (_l = resolved.footer) == null ? void 0 : _l.center)}
3037
+ ${buildZoneCss("bottom-right", (_m = resolved.footer) == null ? void 0 : _m.right, true)}
2900
3038
  }
2901
3039
  ${coverPageCss}
3040
+ ${tocPageCss}
2902
3041
  ${backCoverCss}
2903
3042
  @media print {
2904
3043
  body { padding: 0; }
@@ -3029,9 +3168,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
3029
3168
  const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
3030
3169
  if (wmPng) {
3031
3170
  const embeddedPng = await pdfDoc.embedPng(wmPng);
3171
+ const totalPages = pdfDoc.getPageCount();
3032
3172
  const pages = pdfDoc.getPages();
3033
3173
  const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
3034
- const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? pages.length - 1 : pages.length;
3174
+ const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? totalPages - 1 : totalPages;
3035
3175
  for (let i = startPageIndex; i < endPageIndex; i++) {
3036
3176
  const page = pages[i];
3037
3177
  const { width, height } = page.getSize();
@@ -3371,7 +3511,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
3371
3511
  return runs;
3372
3512
  }
3373
3513
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3374
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
3514
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3375
3515
  const resolved = resolveDocumentConfig(doc.metadata, config);
3376
3516
  const docElements = [];
3377
3517
  const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
@@ -3443,6 +3583,9 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3443
3583
  );
3444
3584
  }
3445
3585
  }
3586
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
3587
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
3588
+ }
3446
3589
  if (resolved.toc) {
3447
3590
  const headingNodes = doc.nodes.filter(
3448
3591
  (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
@@ -3512,7 +3655,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3512
3655
  ]
3513
3656
  });
3514
3657
  docElements.push(tocCard);
3515
- docElements.push(new Paragraph({ spacing: { after: 200 } }));
3658
+ docElements.push(
3659
+ new Paragraph({
3660
+ children: [new PageBreak()]
3661
+ })
3662
+ );
3516
3663
  }
3517
3664
  }
3518
3665
  for (const node of doc.nodes) {
@@ -3522,7 +3669,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3522
3669
  font: defaultFont,
3523
3670
  size: 34,
3524
3671
  // 17pt
3525
- color: textHex,
3672
+ color: primaryDarkHex,
3526
3673
  bold: true
3527
3674
  });
3528
3675
  docElements.push(
@@ -3568,7 +3715,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3568
3715
  font: defaultFont,
3569
3716
  size: 22,
3570
3717
  // 11pt
3571
- color: textHex,
3718
+ color: primaryDarkHex,
3572
3719
  bold: true
3573
3720
  });
3574
3721
  docElements.push(
@@ -3798,7 +3945,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3798
3945
  }
3799
3946
  if (node.type === "table" && node.children) {
3800
3947
  const tableRows = [];
3801
- const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
3948
+ const numCols = ((_d = (_c = node.children[0]) == null ? void 0 : _c.children) == null ? void 0 : _d.length) || 1;
3802
3949
  const colWidth = Math.floor(9e3 / numCols);
3803
3950
  for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
3804
3951
  const rowNode = node.children[rowIdx];
@@ -3808,7 +3955,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3808
3955
  if (rowNode.children) {
3809
3956
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
3810
3957
  const cellNode = rowNode.children[colIdx];
3811
- const align = (_d = node.align) == null ? void 0 : _d[colIdx];
3958
+ const align = (_e = node.align) == null ? void 0 : _e[colIdx];
3812
3959
  let alignment = AlignmentType.LEFT;
3813
3960
  if (align === "center") alignment = AlignmentType.CENTER;
3814
3961
  if (align === "right") alignment = AlignmentType.RIGHT;
@@ -4115,7 +4262,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4115
4262
  const centerPos = Math.round(contentWidthTwip / 2);
4116
4263
  const rightPos = contentWidthTwip;
4117
4264
  const headerRuns = [];
4118
- if ((_e = resolved.header) == null ? void 0 : _e.left) {
4265
+ if ((_f = resolved.header) == null ? void 0 : _f.left) {
4119
4266
  headerRuns.push(
4120
4267
  new TextRun({
4121
4268
  text: resolved.header.left.text,
@@ -4128,7 +4275,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4128
4275
  );
4129
4276
  }
4130
4277
  headerRuns.push(new TextRun({ text: " " }));
4131
- if ((_f = resolved.header) == null ? void 0 : _f.center) {
4278
+ if ((_g = resolved.header) == null ? void 0 : _g.center) {
4132
4279
  headerRuns.push(
4133
4280
  new TextRun({
4134
4281
  text: resolved.header.center.text,
@@ -4141,7 +4288,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4141
4288
  );
4142
4289
  }
4143
4290
  headerRuns.push(new TextRun({ text: " " }));
4144
- if ((_g = resolved.header) == null ? void 0 : _g.right) {
4291
+ if ((_h = resolved.header) == null ? void 0 : _h.right) {
4145
4292
  headerRuns.push(
4146
4293
  new TextRun({
4147
4294
  text: resolved.header.right.text,
@@ -4180,7 +4327,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4180
4327
  ]
4181
4328
  }) : void 0;
4182
4329
  const footerRuns = [];
4183
- if ((_h = resolved.footer) == null ? void 0 : _h.left) {
4330
+ if ((_i = resolved.footer) == null ? void 0 : _i.left) {
4184
4331
  footerRuns.push(
4185
4332
  new TextRun({
4186
4333
  text: resolved.footer.left.text,
@@ -4193,7 +4340,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4193
4340
  );
4194
4341
  }
4195
4342
  footerRuns.push(new TextRun({ text: " " }));
4196
- if ((_i = resolved.footer) == null ? void 0 : _i.center) {
4343
+ if ((_j = resolved.footer) == null ? void 0 : _j.center) {
4197
4344
  footerRuns.push(
4198
4345
  new TextRun({
4199
4346
  text: resolved.footer.center.text,
@@ -4206,7 +4353,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4206
4353
  );
4207
4354
  }
4208
4355
  footerRuns.push(new TextRun({ text: " " }));
4209
- if ((_j = resolved.footer) == null ? void 0 : _j.right) {
4356
+ if ((_k = resolved.footer) == null ? void 0 : _k.right) {
4210
4357
  const rZone = resolved.footer.right;
4211
4358
  const rColor = rZone.color.replace("#", "");
4212
4359
  const rSize = (rZone.fontSize || 9) * 2;
@@ -4326,6 +4473,10 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4326
4473
  docSections.push({
4327
4474
  properties: {
4328
4475
  page: {
4476
+ pageNumbers: {
4477
+ start: 1,
4478
+ formatType: NumberFormat.DECIMAL
4479
+ },
4329
4480
  size: {
4330
4481
  width: resolved.paperDimensions.widthTwip,
4331
4482
  height: resolved.paperDimensions.heightTwip,
@@ -5767,10 +5918,9 @@ try {
5767
5918
  }
5768
5919
  } catch {
5769
5920
  }
5770
- var FALLBACK_VERSION = "0.4.0";
5771
5921
  function readVersionFromPackageJson(fromDir) {
5772
5922
  let currentDir = fromDir;
5773
- for (let i = 0; i < 6; i++) {
5923
+ for (let i = 0; i < 10; i++) {
5774
5924
  try {
5775
5925
  const pkgJsonPath = path8.join(currentDir, "package.json");
5776
5926
  if (fs8.existsSync(pkgJsonPath)) {
@@ -5785,7 +5935,9 @@ function readVersionFromPackageJson(fromDir) {
5785
5935
  if (parentDir === currentDir) break;
5786
5936
  currentDir = parentDir;
5787
5937
  }
5788
- return FALLBACK_VERSION;
5938
+ throw new Error(
5939
+ "Failed to resolve '@masumdev/markforge' package version: package.json was not found or is missing a valid 'version' field."
5940
+ );
5789
5941
  }
5790
5942
  function getPackageDir() {
5791
5943
  if (typeof __dirname !== "undefined") {
@@ -5816,6 +5968,7 @@ export {
5816
5968
  THEME_DEFAULT,
5817
5969
  Theme,
5818
5970
  WatermarkPosition,
5971
+ applyHeadingNumbering,
5819
5972
  buildDocxDocument,
5820
5973
  buildHtmlDocument,
5821
5974
  buildPdfDocument,