@masumdev/markforge 0.4.1 → 0.5.0

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
@@ -38,6 +38,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
38
38
  // src/index.ts
39
39
  var src_exports = {};
40
40
  __export(src_exports, {
41
+ BackCoverPreset: () => BackCoverPreset,
42
+ CoverPagePreset: () => CoverPagePreset,
41
43
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
42
44
  KATEX_INLINE_CSS: () => KATEX_INLINE_CSS,
43
45
  MARKFORGE_VERSION: () => MARKFORGE_VERSION,
@@ -46,6 +48,8 @@ __export(src_exports, {
46
48
  PAPER_DIMENSIONS_TWIP: () => PAPER_DIMENSIONS_TWIP,
47
49
  PaperSizeEnum: () => PaperSizeEnum,
48
50
  SYNTAX_COLORS: () => SYNTAX_COLORS,
51
+ SignatureAlign: () => SignatureAlign,
52
+ SignatureStyle: () => SignatureStyle,
49
53
  SyntaxTheme: () => SyntaxTheme,
50
54
  THEMES: () => THEMES,
51
55
  THEME_CORPORATE: () => THEME_CORPORATE,
@@ -56,6 +60,9 @@ __export(src_exports, {
56
60
  buildDocxDocument: () => buildDocxDocument,
57
61
  buildHtmlDocument: () => buildHtmlDocument,
58
62
  buildPdfDocument: () => buildPdfDocument,
63
+ buildPngDocument: () => buildPngDocument,
64
+ buildTextDocument: () => buildTextDocument,
65
+ buildTxtDocument: () => buildTextDocument,
59
66
  compileMarkdown: () => compileMarkdown,
60
67
  defineConfig: () => defineConfig,
61
68
  escapeHtml: () => escapeHtml,
@@ -84,6 +91,7 @@ __export(src_exports, {
84
91
  renderBackCoverHtml: () => renderBackCoverHtml,
85
92
  renderCoverPageHtml: () => renderCoverPageHtml,
86
93
  renderInlinesToHtml: () => renderInlinesToHtml,
94
+ renderInlinesToText: () => renderInlinesToText,
87
95
  renderMathToHtml: () => renderMathToHtml,
88
96
  renderMermaidToPng: () => renderMermaidToPng,
89
97
  renderNodesToHtml: () => renderNodesToHtml,
@@ -97,8 +105,8 @@ __export(src_exports, {
97
105
  module.exports = __toCommonJS(src_exports);
98
106
 
99
107
  // src/core/engine.ts
100
- var fs6 = __toESM(require("fs"));
101
- var path6 = __toESM(require("path"));
108
+ var fs7 = __toESM(require("fs"));
109
+ var path7 = __toESM(require("path"));
102
110
 
103
111
  // src/core/parser.ts
104
112
  var import_gray_matter = __toESM(require("gray-matter"));
@@ -656,6 +664,7 @@ var import_docx = require("docx");
656
664
  // src/core/imageResolver.ts
657
665
  var fs = __toESM(require("fs"));
658
666
  var path = __toESM(require("path"));
667
+ var import_node_url = require("url");
659
668
  var memoryImageCache = /* @__PURE__ */ new Map();
660
669
  function getMimeType(filePathOrUrl) {
661
670
  const clean = filePathOrUrl.split("?")[0].toLowerCase();
@@ -712,9 +721,19 @@ async function resolveImage(src, baseDir = process.cwd()) {
712
721
  memoryImageCache.set(cacheKey, resolved2);
713
722
  return resolved2;
714
723
  }
715
- let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
724
+ let localPath = src;
725
+ if (src.startsWith("file://")) {
726
+ try {
727
+ localPath = (0, import_node_url.fileURLToPath)(src);
728
+ } catch {
729
+ localPath = src;
730
+ }
731
+ }
732
+ if (!path.isAbsolute(localPath)) {
733
+ localPath = path.resolve(baseDir, localPath);
734
+ }
716
735
  if (!fs.existsSync(localPath)) {
717
- const cwdPath = path.resolve(process.cwd(), src);
736
+ const cwdPath = path.resolve(process.cwd(), src.startsWith("file://") ? localPath : src);
718
737
  if (fs.existsSync(cwdPath)) {
719
738
  localPath = cwdPath;
720
739
  } else {
@@ -1116,13 +1135,13 @@ var import_node_child_process2 = require("child_process");
1116
1135
  var fs5 = __toESM(require("fs"));
1117
1136
  var os2 = __toESM(require("os"));
1118
1137
  var path5 = __toESM(require("path"));
1119
- var import_node_url3 = require("url");
1138
+ var import_node_url4 = require("url");
1120
1139
 
1121
1140
  // src/core/pdf/pdfBuilder.ts
1122
1141
  var fs4 = __toESM(require("fs"));
1123
1142
  var path4 = __toESM(require("path"));
1124
1143
  var os = __toESM(require("os"));
1125
- var import_node_url2 = require("url");
1144
+ var import_node_url3 = require("url");
1126
1145
  var import_node_child_process = require("child_process");
1127
1146
  var import_pdf_lib = require("pdf-lib");
1128
1147
  var import_pdf_encrypt = require("@pdfsmaller/pdf-encrypt");
@@ -1137,6 +1156,7 @@ var OutputFormat = /* @__PURE__ */ ((OutputFormat2) => {
1137
1156
  OutputFormat2["PDF"] = "pdf";
1138
1157
  OutputFormat2["HTML"] = "html";
1139
1158
  OutputFormat2["PNG"] = "png";
1159
+ OutputFormat2["TXT"] = "txt";
1140
1160
  return OutputFormat2;
1141
1161
  })(OutputFormat || {});
1142
1162
  var Theme = /* @__PURE__ */ ((Theme2) => {
@@ -1171,6 +1191,33 @@ var WatermarkPosition = /* @__PURE__ */ ((WatermarkPosition2) => {
1171
1191
  WatermarkPosition2["BOTTOM_RIGHT"] = "bottom-right";
1172
1192
  return WatermarkPosition2;
1173
1193
  })(WatermarkPosition || {});
1194
+ var CoverPagePreset = /* @__PURE__ */ ((CoverPagePreset2) => {
1195
+ CoverPagePreset2["MODERN"] = "modern";
1196
+ CoverPagePreset2["CORPORATE_SPLIT"] = "corporate-split";
1197
+ CoverPagePreset2["MINIMAL"] = "minimal";
1198
+ CoverPagePreset2["CARD"] = "card";
1199
+ return CoverPagePreset2;
1200
+ })(CoverPagePreset || {});
1201
+ var BackCoverPreset = /* @__PURE__ */ ((BackCoverPreset2) => {
1202
+ BackCoverPreset2["MODERN"] = "modern";
1203
+ BackCoverPreset2["CORPORATE"] = "corporate";
1204
+ BackCoverPreset2["MINIMAL"] = "minimal";
1205
+ BackCoverPreset2["CONTACT_CARD"] = "contact-card";
1206
+ return BackCoverPreset2;
1207
+ })(BackCoverPreset || {});
1208
+ var SignatureAlign = /* @__PURE__ */ ((SignatureAlign2) => {
1209
+ SignatureAlign2["LEFT"] = "left";
1210
+ SignatureAlign2["CENTER"] = "center";
1211
+ SignatureAlign2["RIGHT"] = "right";
1212
+ SignatureAlign2["SPACE_BETWEEN"] = "space-between";
1213
+ return SignatureAlign2;
1214
+ })(SignatureAlign || {});
1215
+ var SignatureStyle = /* @__PURE__ */ ((SignatureStyle2) => {
1216
+ SignatureStyle2["LINE"] = "line";
1217
+ SignatureStyle2["BOX"] = "box";
1218
+ SignatureStyle2["CLEAN"] = "clean";
1219
+ return SignatureStyle2;
1220
+ })(SignatureStyle || {});
1174
1221
 
1175
1222
  // src/core/html/htmlThemes.ts
1176
1223
  var THEME_COMPONENTS = `
@@ -1338,7 +1385,7 @@ ${theme.customCss || ""}
1338
1385
  // src/config/loadConfig.ts
1339
1386
  var fs2 = __toESM(require("fs"));
1340
1387
  var path2 = __toESM(require("path"));
1341
- var import_node_url = require("url");
1388
+ var import_node_url2 = require("url");
1342
1389
  var YAML = __toESM(require("yaml"));
1343
1390
  var DEFAULT_CONFIG_FILENAMES = [
1344
1391
  "markforge.config.json",
@@ -1446,7 +1493,7 @@ async function loadConfig(customPath, startDir = process.cwd()) {
1446
1493
  userConfig = YAML.parse(raw) || {};
1447
1494
  } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
1448
1495
  try {
1449
- const fileUrl = `${(0, import_node_url.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
1496
+ const fileUrl = `${(0, import_node_url2.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
1450
1497
  const mod = await import(fileUrl);
1451
1498
  const rawExport = mod.default ?? mod.config ?? mod;
1452
1499
  userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
@@ -2485,7 +2532,7 @@ async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
2485
2532
  `;
2486
2533
  return { html, css };
2487
2534
  }
2488
- async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2535
+ async function buildHtmlDocument(doc, config = {}, baseDir = process.cwd()) {
2489
2536
  var _a, _b;
2490
2537
  const resolved = resolveDocumentConfig(doc.metadata, config);
2491
2538
  const baseThemeCss = generateThemeCss(resolved.theme);
@@ -2836,8 +2883,11 @@ function escapeXml(str) {
2836
2883
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2837
2884
  }
2838
2885
  function generateWatermarkPngBuffer(chromePath, wm) {
2839
- const tmpHtml = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
2840
- const tmpPng = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
2886
+ const tmpId = Math.random().toString(36).substring(2, 9);
2887
+ const tmpDir = os.tmpdir();
2888
+ const tmpHtml = path4.join(tmpDir, `markforge-wm-${tmpId}.html`);
2889
+ const tmpPng = path4.join(tmpDir, `markforge-wm-${tmpId}.png`);
2890
+ const tmpProfile = path4.join(tmpDir, `markforge-wm-prof-${tmpId}`);
2841
2891
  try {
2842
2892
  const text = escapeXml(wm.text.toUpperCase());
2843
2893
  const fontSize = (wm.fontSize || 52) * 1.5;
@@ -2882,12 +2932,15 @@ function generateWatermarkPngBuffer(chromePath, wm) {
2882
2932
  </body>
2883
2933
  </html>`;
2884
2934
  fs4.writeFileSync(tmpHtml, html, "utf8");
2885
- const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
2935
+ const fileUrl = (0, import_node_url3.pathToFileURL)(tmpHtml).href;
2886
2936
  const isWin = process.platform === "win32";
2887
2937
  (0, import_node_child_process.spawnSync)(
2888
2938
  chromePath,
2889
2939
  [
2890
2940
  "--headless=new",
2941
+ `--user-data-dir=${tmpProfile}`,
2942
+ "--no-first-run",
2943
+ "--no-default-browser-check",
2891
2944
  "--disable-gpu",
2892
2945
  "--disable-sync",
2893
2946
  "--disable-extensions",
@@ -2909,6 +2962,7 @@ function generateWatermarkPngBuffer(chromePath, wm) {
2909
2962
  try {
2910
2963
  if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
2911
2964
  if (fs4.existsSync(tmpPng)) fs4.unlinkSync(tmpPng);
2965
+ if (fs4.existsSync(tmpProfile)) fs4.rmSync(tmpProfile, { recursive: true, force: true });
2912
2966
  } catch {
2913
2967
  }
2914
2968
  }
@@ -2924,6 +2978,8 @@ function findChromeExecutable() {
2924
2978
  const winLocalAppData = process.env.LOCALAPPDATA ?? "";
2925
2979
  const winProgramFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
2926
2980
  const winProgramFilesX86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
2981
+ const winProgramW6432 = process.env.ProgramW6432 ?? "C:\\Program Files";
2982
+ const winUserProfile = process.env.USERPROFILE ?? "";
2927
2983
  const candidates = [
2928
2984
  // Linux
2929
2985
  "/usr/bin/google-chrome",
@@ -2942,17 +2998,34 @@ function findChromeExecutable() {
2942
2998
  // Windows — Microsoft Edge (Native Windows 10/11 browser, enterprise whitelist friendly)
2943
2999
  `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
2944
3000
  `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
3001
+ `${winProgramW6432}\\Microsoft\\Edge\\Application\\msedge.exe`,
2945
3002
  `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
2946
- // Windows — Google Chrome
3003
+ `${winLocalAppData}\\Microsoft\\Edge Dev\\Application\\msedge.exe`,
3004
+ `${winLocalAppData}\\Microsoft\\Edge Beta\\Application\\msedge.exe`,
3005
+ // Windows — Google Chrome & Chrome SxS (Canary)
2947
3006
  `${winProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
2948
3007
  `${winProgramFilesX86}\\Google\\Chrome\\Application\\chrome.exe`,
3008
+ `${winProgramW6432}\\Google\\Chrome\\Application\\chrome.exe`,
2949
3009
  `${winLocalAppData}\\Google\\Chrome\\Application\\chrome.exe`,
3010
+ `${winLocalAppData}\\Google\\Chrome SxS\\Application\\chrome.exe`,
2950
3011
  // Windows — Brave Browser
2951
3012
  `${winProgramFiles}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
2952
3013
  `${winProgramFilesX86}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
3014
+ `${winProgramW6432}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
2953
3015
  `${winLocalAppData}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
2954
3016
  // Windows — Chromium
2955
- `${winLocalAppData}\\Chromium\\Application\\chrome.exe`
3017
+ `${winLocalAppData}\\Chromium\\Application\\chrome.exe`,
3018
+ // Windows — Scoop Package Manager
3019
+ `${winUserProfile}\\scoop\\apps\\googlechrome\\current\\chrome.exe`,
3020
+ `${winUserProfile}\\scoop\\apps\\chromium\\current\\chrome.exe`,
3021
+ `${winUserProfile}\\scoop\\apps\\brave\\current\\brave.exe`,
3022
+ `${winUserProfile}\\scoop\\apps\\msedge\\current\\msedge.exe`,
3023
+ `${winUserProfile}\\scoop\\shims\\chrome.exe`,
3024
+ `${winUserProfile}\\scoop\\shims\\msedge.exe`,
3025
+ // Windows — Chocolatey
3026
+ "C:\\ProgramData\\chocolatey\\bin\\chrome.exe",
3027
+ "C:\\ProgramData\\chocolatey\\bin\\msedge.exe",
3028
+ "C:\\ProgramData\\chocolatey\\bin\\brave.exe"
2956
3029
  ].filter(Boolean);
2957
3030
  for (const candidate of candidates) {
2958
3031
  try {
@@ -2963,13 +3036,15 @@ function findChromeExecutable() {
2963
3036
  }
2964
3037
  }
2965
3038
  try {
2966
- const cmd = isWin ? "where" : "which";
2967
- const names = isWin ? ["chrome", "msedge", "brave", "google-chrome", "chromium", "chromium-browser"] : ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge", "brave-browser"];
3039
+ const cmd = isWin ? "where.exe" : "which";
3040
+ const names = isWin ? ["chrome.exe", "msedge.exe", "brave.exe", "chrome", "msedge", "brave", "google-chrome", "chromium", "chromium-browser"] : ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge", "brave-browser"];
2968
3041
  for (const name of names) {
2969
- const res = (0, import_node_child_process.spawnSync)(cmd, [name], { encoding: "utf-8" });
3042
+ const res = (0, import_node_child_process.spawnSync)(cmd, [name], { encoding: "utf-8", windowsHide: true });
2970
3043
  if (res.status === 0 && res.stdout.trim()) {
2971
- const binPath = res.stdout.split(/\r?\n/)[0].trim();
2972
- if (fs4.existsSync(binPath)) return binPath;
3044
+ const lines = res.stdout.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
3045
+ for (const line of lines) {
3046
+ if (fs4.existsSync(line)) return line;
3047
+ }
2973
3048
  }
2974
3049
  }
2975
3050
  } catch {
@@ -3184,7 +3259,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
3184
3259
  ];
3185
3260
  try {
3186
3261
  fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
3187
- const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
3262
+ const fileUrl = (0, import_node_url3.pathToFileURL)(tmpHtml).href;
3188
3263
  let res = (0, import_node_child_process.spawnSync)(
3189
3264
  chromePath,
3190
3265
  [
@@ -3303,6 +3378,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
3303
3378
  const tmpDir = os2.tmpdir();
3304
3379
  const tmpHtml = path5.join(tmpDir, `mermaid_${tmpId}.html`);
3305
3380
  const tmpScreenshot = path5.join(tmpDir, `mermaid_${tmpId}.png`);
3381
+ const tmpProfile = path5.join(tmpDir, `mermaid_prof_${tmpId}`);
3382
+ const isWin = process.platform === "win32";
3306
3383
  const htmlContent = `<!DOCTYPE html>
3307
3384
  <html>
3308
3385
  <head>
@@ -3342,14 +3419,18 @@ ${mermaidCode}
3342
3419
  </html>`;
3343
3420
  try {
3344
3421
  fs5.writeFileSync(tmpHtml, htmlContent, "utf-8");
3345
- const fileUrl = (0, import_node_url3.pathToFileURL)(tmpHtml).href;
3422
+ const fileUrl = (0, import_node_url4.pathToFileURL)(tmpHtml).href;
3346
3423
  const res = (0, import_node_child_process2.spawnSync)(
3347
3424
  chromePath,
3348
3425
  [
3349
- "--headless",
3426
+ "--headless=new",
3427
+ `--user-data-dir=${tmpProfile}`,
3428
+ "--no-first-run",
3429
+ "--no-default-browser-check",
3350
3430
  "--disable-gpu",
3351
- "--no-sandbox",
3352
- "--disable-setuid-sandbox",
3431
+ "--disable-sync",
3432
+ "--disable-extensions",
3433
+ ...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
3353
3434
  "--allow-file-access-from-files",
3354
3435
  "--disable-web-security",
3355
3436
  "--disable-software-rasterizer",
@@ -3358,7 +3439,7 @@ ${mermaidCode}
3358
3439
  `--screenshot=${tmpScreenshot}`,
3359
3440
  fileUrl
3360
3441
  ],
3361
- { timeout: 15e3 }
3442
+ { timeout: 15e3, windowsHide: true }
3362
3443
  );
3363
3444
  if (res.status === 0 && fs5.existsSync(tmpScreenshot)) {
3364
3445
  const buffer = fs5.readFileSync(tmpScreenshot);
@@ -3369,6 +3450,7 @@ ${mermaidCode}
3369
3450
  try {
3370
3451
  if (fs5.existsSync(tmpHtml)) fs5.unlinkSync(tmpHtml);
3371
3452
  if (fs5.existsSync(tmpScreenshot)) fs5.unlinkSync(tmpScreenshot);
3453
+ if (fs5.existsSync(tmpProfile)) fs5.rmSync(tmpProfile, { recursive: true, force: true });
3372
3454
  } catch {
3373
3455
  }
3374
3456
  }
@@ -4510,6 +4592,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4510
4592
  );
4511
4593
  docSections.push({
4512
4594
  properties: {
4595
+ type: import_docx.SectionType.NEXT_PAGE,
4513
4596
  page: {
4514
4597
  size: {
4515
4598
  width: resolved.paperDimensions.widthTwip,
@@ -4524,13 +4607,14 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4524
4607
  }
4525
4608
  }
4526
4609
  },
4527
- headers: void 0,
4528
- footers: void 0,
4610
+ headers: { default: new import_docx.Header({ children: [] }) },
4611
+ footers: { default: new import_docx.Footer({ children: [] }) },
4529
4612
  children: coverElements
4530
4613
  });
4531
4614
  }
4532
4615
  docSections.push({
4533
4616
  properties: {
4617
+ type: import_docx.SectionType.NEXT_PAGE,
4534
4618
  page: {
4535
4619
  pageNumbers: {
4536
4620
  start: 1,
@@ -4551,8 +4635,8 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4551
4635
  }
4552
4636
  }
4553
4637
  },
4554
- headers: docHeader ? { default: docHeader } : void 0,
4555
- footers: docFooter ? { default: docFooter } : void 0,
4638
+ headers: docHeader ? { default: docHeader } : { default: new import_docx.Header({ children: [] }) },
4639
+ footers: docFooter ? { default: docFooter } : { default: new import_docx.Footer({ children: [] }) },
4556
4640
  children: docElements
4557
4641
  });
4558
4642
  if (resolved.backCover && resolved.backCover.enabled) {
@@ -4567,6 +4651,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4567
4651
  );
4568
4652
  docSections.push({
4569
4653
  properties: {
4654
+ type: import_docx.SectionType.NEXT_PAGE,
4570
4655
  page: {
4571
4656
  size: {
4572
4657
  width: resolved.paperDimensions.widthTwip,
@@ -4581,8 +4666,8 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
4581
4666
  }
4582
4667
  }
4583
4668
  },
4584
- headers: void 0,
4585
- footers: void 0,
4669
+ headers: { default: new import_docx.Header({ children: [] }) },
4670
+ footers: { default: new import_docx.Footer({ children: [] }) },
4586
4671
  children: backElements
4587
4672
  });
4588
4673
  }
@@ -4985,6 +5070,432 @@ function createEmptyDocxCell(widthDxa) {
4985
5070
  });
4986
5071
  }
4987
5072
 
5073
+ // src/core/text/textBuilder.ts
5074
+ var LINE_WIDTH = 80;
5075
+ var HR_DOUBLE = "=".repeat(LINE_WIDTH);
5076
+ var HR_SINGLE = "-".repeat(LINE_WIDTH);
5077
+ function centerText(text, width = LINE_WIDTH) {
5078
+ if (text.length >= width) return text;
5079
+ const leftPad = Math.floor((width - text.length) / 2);
5080
+ return " ".repeat(leftPad) + text;
5081
+ }
5082
+ function renderInlinesToText(spans = [], meta = {}) {
5083
+ let result = "";
5084
+ for (const span of spans) {
5085
+ switch (span.type) {
5086
+ case "text": {
5087
+ const content = replaceDocumentTokens(span.content, meta);
5088
+ result += content;
5089
+ break;
5090
+ }
5091
+ case "bold": {
5092
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
5093
+ result += `**${inner}**`;
5094
+ break;
5095
+ }
5096
+ case "italic": {
5097
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
5098
+ result += `*${inner}*`;
5099
+ break;
5100
+ }
5101
+ case "code": {
5102
+ result += `\`${span.content}\``;
5103
+ break;
5104
+ }
5105
+ case "link": {
5106
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
5107
+ result += span.url && span.url !== inner ? `${inner} (${span.url})` : inner;
5108
+ break;
5109
+ }
5110
+ case "image": {
5111
+ result += `[Image: ${span.alt || "image"} (${span.url || ""})]`;
5112
+ break;
5113
+ }
5114
+ case "strikethrough": {
5115
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
5116
+ result += `~${inner}~`;
5117
+ break;
5118
+ }
5119
+ case "mathInline": {
5120
+ result += `$${span.content}$`;
5121
+ break;
5122
+ }
5123
+ case "footnoteRef": {
5124
+ const id = span.footnoteId || span.content;
5125
+ result += `[${id}]`;
5126
+ break;
5127
+ }
5128
+ case "htmlInline": {
5129
+ result += span.content.replace(/<[^>]+>/g, "");
5130
+ break;
5131
+ }
5132
+ default: {
5133
+ result += span.content || "";
5134
+ break;
5135
+ }
5136
+ }
5137
+ }
5138
+ return result;
5139
+ }
5140
+ async function buildTextDocument(doc, config = {}, _baseDir = process.cwd()) {
5141
+ const resolved = resolveDocumentConfig(doc.metadata, config);
5142
+ const meta = {
5143
+ ...doc.metadata,
5144
+ ...config.metadata,
5145
+ title: resolved.title,
5146
+ subtitle: resolved.subtitle,
5147
+ author: resolved.author,
5148
+ company: resolved.company,
5149
+ version: resolved.version,
5150
+ date: resolved.date
5151
+ };
5152
+ const nodes = JSON.parse(JSON.stringify(doc.nodes));
5153
+ const tocEntries = JSON.parse(JSON.stringify(doc.tocEntries));
5154
+ if (resolved.numberHeadings && resolved.numberHeadings.enabled) {
5155
+ applyHeadingNumbering(nodes, tocEntries, resolved.numberHeadings);
5156
+ }
5157
+ const lines = [];
5158
+ if (resolved.coverPage && resolved.coverPage.enabled) {
5159
+ const cover = resolved.coverPage;
5160
+ const coverTitle = (cover.title || meta.title || "DOCUMENT").toUpperCase();
5161
+ const coverSubtitle = cover.subtitle || meta.subtitle || "";
5162
+ const coverAuthor = cover.author || meta.author || "";
5163
+ const coverCompany = cover.company || meta.company || "";
5164
+ const coverDate = cover.date || meta.date || "";
5165
+ const coverVersion = cover.version || meta.version || "";
5166
+ const coverBadge = cover.badge || "";
5167
+ const coverFooter = cover.footerText || "";
5168
+ lines.push(HR_DOUBLE);
5169
+ lines.push(centerText(coverTitle));
5170
+ if (coverSubtitle) {
5171
+ lines.push(centerText(coverSubtitle));
5172
+ }
5173
+ lines.push(HR_DOUBLE);
5174
+ if (coverBadge) {
5175
+ lines.push(`[${coverBadge}]`);
5176
+ lines.push("");
5177
+ }
5178
+ if (coverAuthor) lines.push(`Author: ${coverAuthor}`);
5179
+ if (coverCompany) lines.push(`Company: ${coverCompany}`);
5180
+ if (coverDate) lines.push(`Date: ${coverDate}`);
5181
+ if (coverVersion) lines.push(`Version: ${coverVersion}`);
5182
+ if (coverFooter) lines.push(`Notice: ${coverFooter}`);
5183
+ lines.push(HR_DOUBLE);
5184
+ lines.push("");
5185
+ lines.push("");
5186
+ }
5187
+ if (resolved.toc) {
5188
+ const headings = nodes.filter(
5189
+ (n) => n.type === "heading" && (n.level || 1) <= 3
5190
+ );
5191
+ if (headings.length > 0) {
5192
+ const tocTitle = "TABLE OF CONTENTS";
5193
+ lines.push(HR_DOUBLE);
5194
+ lines.push(centerText(tocTitle));
5195
+ lines.push(HR_DOUBLE);
5196
+ for (const h of headings) {
5197
+ const level = h.level || 1;
5198
+ const indent = " ".repeat(level - 1);
5199
+ const headingText = renderInlinesToText(h.inlines, meta);
5200
+ const prefix = level === 1 ? "* " : "- ";
5201
+ lines.push(`${indent}${prefix}${headingText}`);
5202
+ }
5203
+ lines.push(HR_DOUBLE);
5204
+ lines.push("");
5205
+ lines.push("");
5206
+ }
5207
+ }
5208
+ function renderNode(node, listLevel = 0) {
5209
+ var _a;
5210
+ switch (node.type) {
5211
+ case "heading": {
5212
+ const level = node.level || 1;
5213
+ const text = renderInlinesToText(node.inlines, meta);
5214
+ lines.push("");
5215
+ if (level === 1) {
5216
+ lines.push(HR_DOUBLE);
5217
+ lines.push(text.toUpperCase());
5218
+ lines.push(HR_DOUBLE);
5219
+ } else if (level === 2) {
5220
+ lines.push(HR_SINGLE);
5221
+ lines.push(text);
5222
+ lines.push(HR_SINGLE);
5223
+ } else {
5224
+ const hashes = "#".repeat(level);
5225
+ lines.push(`${hashes} ${text}`);
5226
+ }
5227
+ lines.push("");
5228
+ break;
5229
+ }
5230
+ case "paragraph": {
5231
+ const text = renderInlinesToText(node.inlines, meta);
5232
+ if (text.trim()) {
5233
+ lines.push(text);
5234
+ lines.push("");
5235
+ }
5236
+ break;
5237
+ }
5238
+ case "callout": {
5239
+ const calloutType = (node.calloutType || "NOTE").toUpperCase();
5240
+ lines.push(`| [${calloutType}]`);
5241
+ if (node.inlines && node.inlines.length > 0) {
5242
+ const text = renderInlinesToText(node.inlines, meta);
5243
+ text.split("\n").forEach((l) => lines.push(`| ${l}`));
5244
+ }
5245
+ lines.push("");
5246
+ break;
5247
+ }
5248
+ case "blockquote": {
5249
+ if (node.inlines && node.inlines.length > 0) {
5250
+ const text = renderInlinesToText(node.inlines, meta);
5251
+ text.split("\n").forEach((l) => lines.push(`> ${l}`));
5252
+ lines.push("");
5253
+ }
5254
+ break;
5255
+ }
5256
+ case "codeBlock": {
5257
+ const lang = node.language ? `[Language: ${node.language}]` : "[Code]";
5258
+ lines.push(HR_SINGLE);
5259
+ lines.push(lang);
5260
+ lines.push(HR_SINGLE);
5261
+ const codeText = node.text || "";
5262
+ lines.push(codeText);
5263
+ lines.push(HR_SINGLE);
5264
+ lines.push("");
5265
+ break;
5266
+ }
5267
+ case "list": {
5268
+ if (node.children) {
5269
+ let itemIndex = 1;
5270
+ for (const item of node.children) {
5271
+ const indent = " ".repeat(listLevel);
5272
+ let prefix = node.ordered ? `${itemIndex}. ` : "* ";
5273
+ itemIndex++;
5274
+ if (item.checked !== void 0) {
5275
+ prefix = item.checked ? "[x] " : "[ ] ";
5276
+ }
5277
+ if (item.inlines && item.inlines.length > 0) {
5278
+ const text = renderInlinesToText(item.inlines, meta);
5279
+ lines.push(`${indent}${prefix}${text}`);
5280
+ }
5281
+ if (item.children && item.children.length > 0) {
5282
+ for (const subChild of item.children) {
5283
+ if (subChild.type === "list") {
5284
+ renderNode(subChild, listLevel + 1);
5285
+ } else if (subChild.type === "paragraph") {
5286
+ const text = renderInlinesToText(subChild.inlines, meta);
5287
+ lines.push(`${indent} ${text}`);
5288
+ } else {
5289
+ renderNode(subChild, listLevel + 1);
5290
+ }
5291
+ }
5292
+ }
5293
+ }
5294
+ lines.push("");
5295
+ }
5296
+ break;
5297
+ }
5298
+ case "table": {
5299
+ const rows = node.children || [];
5300
+ if (rows.length === 0) break;
5301
+ const rowCells = [];
5302
+ let maxCols = 0;
5303
+ for (const row of rows) {
5304
+ const cells = row.children || [];
5305
+ const texts = cells.map((c) => renderInlinesToText(c.inlines, meta));
5306
+ const isHeader = cells.some((c) => c.isHeader);
5307
+ maxCols = Math.max(maxCols, texts.length);
5308
+ rowCells.push({ isHeader, texts });
5309
+ }
5310
+ if (maxCols === 0) break;
5311
+ const colWidths = new Array(maxCols).fill(3);
5312
+ for (const r of rowCells) {
5313
+ for (let col = 0; col < maxCols; col++) {
5314
+ const cellText = r.texts[col] || "";
5315
+ colWidths[col] = Math.max(colWidths[col] ?? 3, cellText.length);
5316
+ }
5317
+ }
5318
+ const separatorLine = "+" + colWidths.map((w) => "-".repeat(w + 2)).join("+") + "+";
5319
+ const formatRow = (texts) => {
5320
+ const paddedCells = colWidths.map((width, colIdx) => {
5321
+ const text = texts[colIdx] || "";
5322
+ return " " + text.padEnd(width, " ") + " ";
5323
+ });
5324
+ return "|" + paddedCells.join("|") + "|";
5325
+ };
5326
+ lines.push(separatorLine);
5327
+ for (const r of rowCells) {
5328
+ lines.push(formatRow(r.texts));
5329
+ if (r.isHeader) {
5330
+ lines.push(separatorLine);
5331
+ }
5332
+ }
5333
+ if (!((_a = rowCells[rowCells.length - 1]) == null ? void 0 : _a.isHeader)) {
5334
+ lines.push(separatorLine);
5335
+ }
5336
+ lines.push("");
5337
+ break;
5338
+ }
5339
+ case "mathBlock": {
5340
+ lines.push("[Equation]");
5341
+ lines.push(` $$${node.text || ""} $$`);
5342
+ lines.push("");
5343
+ break;
5344
+ }
5345
+ case "mermaid": {
5346
+ lines.push("[Diagram: Mermaid]");
5347
+ lines.push(node.text || "");
5348
+ lines.push("");
5349
+ break;
5350
+ }
5351
+ case "thematicBreak": {
5352
+ lines.push(HR_SINGLE);
5353
+ lines.push("");
5354
+ break;
5355
+ }
5356
+ case "columns": {
5357
+ if (node.children) {
5358
+ for (const col of node.children) {
5359
+ if (col.children) {
5360
+ for (const child of col.children) {
5361
+ renderNode(child, listLevel);
5362
+ }
5363
+ }
5364
+ }
5365
+ }
5366
+ break;
5367
+ }
5368
+ default: {
5369
+ if (node.children && node.children.length > 0) {
5370
+ for (const child of node.children) {
5371
+ renderNode(child, listLevel);
5372
+ }
5373
+ }
5374
+ break;
5375
+ }
5376
+ }
5377
+ }
5378
+ for (const node of nodes) {
5379
+ renderNode(node);
5380
+ }
5381
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
5382
+ lines.push(HR_SINGLE);
5383
+ lines.push("FOOTNOTES");
5384
+ lines.push(HR_SINGLE);
5385
+ for (const fn of doc.footnoteDefs) {
5386
+ const text = renderInlinesToText(fn.inlines, meta);
5387
+ lines.push(`[${fn.id}] ${text}`);
5388
+ }
5389
+ lines.push("");
5390
+ }
5391
+ if (resolved.signatures && resolved.signatures.items && resolved.signatures.items.length > 0) {
5392
+ lines.push(HR_SINGLE);
5393
+ lines.push("SIGNATURES & APPROVALS");
5394
+ lines.push(HR_SINGLE);
5395
+ lines.push("");
5396
+ for (const item of resolved.signatures.items) {
5397
+ const title = item.title || "Signatory";
5398
+ const name = item.name ? replaceDocumentTokens(item.name, meta) : "";
5399
+ const role = item.role ? replaceDocumentTokens(item.role, meta) : "";
5400
+ const date = item.date ? replaceDocumentTokens(item.date, meta) : "";
5401
+ lines.push(`[${title}]`);
5402
+ lines.push("____________________________________");
5403
+ if (name) lines.push(`Name: ${name}`);
5404
+ if (role) lines.push(`Role: ${role}`);
5405
+ if (date) lines.push(`Date: ${date}`);
5406
+ lines.push("");
5407
+ }
5408
+ }
5409
+ if (resolved.backCover && resolved.backCover.enabled) {
5410
+ const back = resolved.backCover;
5411
+ const backTitle = (back.title || "THANK YOU").toUpperCase();
5412
+ const backSubtitle = back.subtitle || "";
5413
+ const backCompany = back.company ? replaceDocumentTokens(back.company, meta) : "";
5414
+ const backAddress = back.address ? replaceDocumentTokens(back.address, meta) : "";
5415
+ const backEmail = back.email ? replaceDocumentTokens(back.email, meta) : "";
5416
+ const backPhone = back.phone ? replaceDocumentTokens(back.phone, meta) : "";
5417
+ const backWebsite = back.website ? replaceDocumentTokens(back.website, meta) : "";
5418
+ const backCopyright = back.copyright ? replaceDocumentTokens(back.copyright, meta) : "";
5419
+ lines.push(HR_DOUBLE);
5420
+ lines.push(centerText(backTitle));
5421
+ if (backSubtitle) {
5422
+ lines.push(centerText(backSubtitle));
5423
+ }
5424
+ lines.push(HR_DOUBLE);
5425
+ if (backCompany) lines.push(`Company: ${backCompany}`);
5426
+ if (backAddress) lines.push(`Address: ${backAddress}`);
5427
+ if (backEmail) lines.push(`Email: ${backEmail}`);
5428
+ if (backPhone) lines.push(`Phone: ${backPhone}`);
5429
+ if (backWebsite) lines.push(`Website: ${backWebsite}`);
5430
+ if (back.social && back.social.github) {
5431
+ lines.push(`GitHub: ${back.social.github}`);
5432
+ }
5433
+ if (backCopyright) {
5434
+ lines.push("");
5435
+ lines.push(backCopyright);
5436
+ }
5437
+ lines.push(HR_DOUBLE);
5438
+ lines.push("");
5439
+ }
5440
+ return lines.join("\n").trim() + "\n";
5441
+ }
5442
+
5443
+ // src/core/png/pngBuilder.ts
5444
+ var fs6 = __toESM(require("fs"));
5445
+ var path6 = __toESM(require("path"));
5446
+ var os3 = __toESM(require("os"));
5447
+ var import_node_url5 = require("url");
5448
+ var import_node_child_process3 = require("child_process");
5449
+ async function buildPngDocument(doc, config, baseDir) {
5450
+ const htmlContent = await buildHtmlDocument(doc, config, baseDir);
5451
+ const chromePath = findChromeExecutable();
5452
+ if (!chromePath) {
5453
+ throw new Error(
5454
+ "Headless Chrome / Chromium / Edge executable not found for PNG document export. Please install Google Chrome, Chromium, or Microsoft Edge, or set CHROME_PATH environment variable."
5455
+ );
5456
+ }
5457
+ const tmpHtml = path6.join(
5458
+ os3.tmpdir(),
5459
+ `markforge-png-${Date.now()}-${Math.random().toString(36).slice(2)}.html`
5460
+ );
5461
+ const tmpPng = path6.join(
5462
+ os3.tmpdir(),
5463
+ `markforge-png-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
5464
+ );
5465
+ try {
5466
+ fs6.writeFileSync(tmpHtml, htmlContent, "utf-8");
5467
+ const fileUrl = (0, import_node_url5.pathToFileURL)(tmpHtml).href;
5468
+ const spawnResult = (0, import_node_child_process3.spawnSync)(
5469
+ chromePath,
5470
+ [
5471
+ "--headless=new",
5472
+ "--disable-gpu",
5473
+ "--no-sandbox",
5474
+ "--disable-setuid-sandbox",
5475
+ "--hide-scrollbars",
5476
+ "--force-device-scale-factor=2",
5477
+ "--window-size=1200,1600",
5478
+ `--screenshot=${tmpPng}`,
5479
+ fileUrl
5480
+ ],
5481
+ { timeout: 3e4, windowsHide: true }
5482
+ );
5483
+ if (spawnResult.error) {
5484
+ throw new Error(`Failed to execute Chromium for PNG export: ${spawnResult.error.message}`);
5485
+ }
5486
+ if (!fs6.existsSync(tmpPng) || fs6.statSync(tmpPng).size === 0) {
5487
+ throw new Error("Chromium PNG export failed to generate output screenshot file.");
5488
+ }
5489
+ return fs6.readFileSync(tmpPng);
5490
+ } finally {
5491
+ try {
5492
+ if (fs6.existsSync(tmpHtml)) fs6.unlinkSync(tmpHtml);
5493
+ if (fs6.existsSync(tmpPng)) fs6.unlinkSync(tmpPng);
5494
+ } catch {
5495
+ }
5496
+ }
5497
+ }
5498
+
4988
5499
  // src/core/engine.ts
4989
5500
  function formatServerTimestamp(date = /* @__PURE__ */ new Date()) {
4990
5501
  const pad = (n) => String(n).padStart(2, "0");
@@ -5008,20 +5519,20 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
5008
5519
  let baseDir = process.cwd();
5009
5520
  let inputFileName = "document.md";
5010
5521
  let isFilePath = false;
5011
- if (fs6.existsSync(inputFilePathOrContent)) {
5522
+ if (fs7.existsSync(inputFilePathOrContent)) {
5012
5523
  isFilePath = true;
5013
- rawMarkdown = fs6.readFileSync(inputFilePathOrContent, "utf-8");
5014
- baseDir = path6.dirname(path6.resolve(inputFilePathOrContent));
5015
- inputFileName = path6.basename(inputFilePathOrContent);
5524
+ rawMarkdown = fs7.readFileSync(inputFilePathOrContent, "utf-8");
5525
+ baseDir = path7.dirname(path7.resolve(inputFilePathOrContent));
5526
+ inputFileName = path7.basename(inputFilePathOrContent);
5016
5527
  } else {
5017
5528
  rawMarkdown = inputFilePathOrContent;
5018
5529
  }
5019
5530
  onProgress == null ? void 0 : onProgress(`Parsing markdown AST: ${inputFileName}...`);
5020
5531
  const parsedDoc = parseMarkdownDocument(rawMarkdown);
5021
5532
  const baseName = inputFileName.replace(/\.(md|mdx|markdown)$/i, "");
5022
- const outputDir = config.outputDir ? path6.isAbsolute(config.outputDir) ? config.outputDir : path6.resolve(process.cwd(), config.outputDir) : baseDir;
5023
- if (!fs6.existsSync(outputDir)) {
5024
- fs6.mkdirSync(outputDir, { recursive: true });
5533
+ const outputDir = config.outputDir ? path7.isAbsolute(config.outputDir) ? config.outputDir : path7.resolve(process.cwd(), config.outputDir) : baseDir;
5534
+ if (!fs7.existsSync(outputDir)) {
5535
+ fs7.mkdirSync(outputDir, { recursive: true });
5025
5536
  }
5026
5537
  const formats = Array.isArray(config.to) ? config.to : [config.to || "docx", "pdf"];
5027
5538
  const generatedFiles = [];
@@ -5031,8 +5542,8 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
5031
5542
  if (fmt === "docx") {
5032
5543
  onProgress == null ? void 0 : onProgress(`Generating DOCX document: ${baseName}.docx...`);
5033
5544
  const docxBuffer = await buildDocxDocument(parsedDoc, config, baseDir);
5034
- const docxPath = path6.join(outputDir, `${baseName}.docx`);
5035
- fs6.writeFileSync(docxPath, docxBuffer);
5545
+ const docxPath = path7.join(outputDir, `${baseName}.docx`);
5546
+ fs7.writeFileSync(docxPath, docxBuffer);
5036
5547
  generatedFiles.push({
5037
5548
  format: "docx",
5038
5549
  filePath: docxPath,
@@ -5042,8 +5553,8 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
5042
5553
  } else if (fmt === "html") {
5043
5554
  onProgress == null ? void 0 : onProgress(`Generating HTML document: ${baseName}.html...`);
5044
5555
  const htmlString = await buildHtmlDocument(parsedDoc, config, baseDir);
5045
- const htmlPath = path6.join(outputDir, `${baseName}.html`);
5046
- fs6.writeFileSync(htmlPath, htmlString, "utf-8");
5556
+ const htmlPath = path7.join(outputDir, `${baseName}.html`);
5557
+ fs7.writeFileSync(htmlPath, htmlString, "utf-8");
5047
5558
  generatedFiles.push({
5048
5559
  format: "html",
5049
5560
  filePath: htmlPath,
@@ -5053,14 +5564,36 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
5053
5564
  } else if (fmt === "pdf") {
5054
5565
  onProgress == null ? void 0 : onProgress(`Generating PDF document: ${baseName}.pdf...`);
5055
5566
  const pdfBuffer = await buildPdfDocument(parsedDoc, config, baseDir);
5056
- const pdfPath = path6.join(outputDir, `${baseName}.pdf`);
5057
- fs6.writeFileSync(pdfPath, pdfBuffer);
5567
+ const pdfPath = path7.join(outputDir, `${baseName}.pdf`);
5568
+ fs7.writeFileSync(pdfPath, pdfBuffer);
5058
5569
  generatedFiles.push({
5059
5570
  format: "pdf",
5060
5571
  filePath: pdfPath,
5061
5572
  fileName: `${baseName}.pdf`,
5062
5573
  sizeBytes: pdfBuffer.length
5063
5574
  });
5575
+ } else if (fmt === "txt") {
5576
+ onProgress == null ? void 0 : onProgress(`Generating Plain Text document: ${baseName}.txt...`);
5577
+ const textContent = await buildTextDocument(parsedDoc, config, baseDir);
5578
+ const textPath = path7.join(outputDir, `${baseName}.txt`);
5579
+ fs7.writeFileSync(textPath, textContent, "utf-8");
5580
+ generatedFiles.push({
5581
+ format: "txt",
5582
+ filePath: textPath,
5583
+ fileName: `${baseName}.txt`,
5584
+ sizeBytes: Buffer.byteLength(textContent, "utf-8")
5585
+ });
5586
+ } else if (fmt === "png") {
5587
+ onProgress == null ? void 0 : onProgress(`Generating PNG document: ${baseName}.png...`);
5588
+ const pngBuffer = await buildPngDocument(parsedDoc, config, baseDir);
5589
+ const pngPath = path7.join(outputDir, `${baseName}.png`);
5590
+ fs7.writeFileSync(pngPath, pngBuffer);
5591
+ generatedFiles.push({
5592
+ format: "png",
5593
+ filePath: pngPath,
5594
+ fileName: `${baseName}.png`,
5595
+ sizeBytes: pngBuffer.length
5596
+ });
5064
5597
  }
5065
5598
  } catch (err) {
5066
5599
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -5079,14 +5612,14 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
5079
5612
 
5080
5613
  // src/server/previewServer.ts
5081
5614
  var http = __toESM(require("http"));
5082
- var fs7 = __toESM(require("fs"));
5083
- var path7 = __toESM(require("path"));
5615
+ var fs8 = __toESM(require("fs"));
5616
+ var path8 = __toESM(require("path"));
5084
5617
  async function startPreviewServer(options) {
5085
- const absoluteFilePath = path7.resolve(process.cwd(), options.filePath);
5086
- if (!fs7.existsSync(absoluteFilePath)) {
5618
+ const absoluteFilePath = path8.resolve(process.cwd(), options.filePath);
5619
+ if (!fs8.existsSync(absoluteFilePath)) {
5087
5620
  throw new Error(`MarkForge preview error: File not found at "${absoluteFilePath}"`);
5088
5621
  }
5089
- const baseDir = path7.dirname(absoluteFilePath);
5622
+ const baseDir = path8.dirname(absoluteFilePath);
5090
5623
  const { config: fileConfig } = await loadConfig(void 0, baseDir);
5091
5624
  const baseConfig = options.config || fileConfig;
5092
5625
  const port = options.port || 3e3;
@@ -5104,9 +5637,9 @@ data: ${Date.now()}
5104
5637
  });
5105
5638
  };
5106
5639
  let debounceTimer = null;
5107
- const watcher = fs7.watch(baseDir, { recursive: false }, (_event, filename) => {
5640
+ const watcher = fs8.watch(baseDir, { recursive: false }, (_event, filename) => {
5108
5641
  if (!filename) return;
5109
- const changedPath = path7.resolve(baseDir, filename);
5642
+ const changedPath = path8.resolve(baseDir, filename);
5110
5643
  if (changedPath === absoluteFilePath || filename.includes("markforge") || filename.endsWith(".css")) {
5111
5644
  if (debounceTimer) clearTimeout(debounceTimer);
5112
5645
  debounceTimer = setTimeout(() => {
@@ -5133,12 +5666,12 @@ data: ${Date.now()}
5133
5666
  }
5134
5667
  if (url.pathname === "/api/file-content" && req.method === "GET") {
5135
5668
  try {
5136
- const content = fs7.readFileSync(absoluteFilePath, "utf-8");
5669
+ const content = fs8.readFileSync(absoluteFilePath, "utf-8");
5137
5670
  res.writeHead(200, { "Content-Type": "application/json" });
5138
5671
  res.end(
5139
5672
  JSON.stringify({
5140
5673
  content,
5141
- fileName: path7.basename(absoluteFilePath),
5674
+ fileName: path8.basename(absoluteFilePath),
5142
5675
  filePath: absoluteFilePath
5143
5676
  })
5144
5677
  );
@@ -5158,7 +5691,7 @@ data: ${Date.now()}
5158
5691
  try {
5159
5692
  const parsed = JSON.parse(body);
5160
5693
  if (typeof parsed.content === "string") {
5161
- fs7.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
5694
+ fs8.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
5162
5695
  broadcastReload();
5163
5696
  res.writeHead(200, { "Content-Type": "application/json" });
5164
5697
  res.end(JSON.stringify({ success: true, savedAt: Date.now() }));
@@ -5177,11 +5710,11 @@ data: ${Date.now()}
5177
5710
  if (url.pathname === "/api/export" && (req.method === "GET" || req.method === "POST")) {
5178
5711
  const format = url.searchParams.get("format") || "docx";
5179
5712
  try {
5180
- const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
5713
+ const mdContent = fs8.readFileSync(absoluteFilePath, "utf-8");
5181
5714
  const doc = parseMarkdownDocument(mdContent);
5182
5715
  const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
5183
5716
  const mergedConfig = { ...baseConfig, ...resolvedConfig };
5184
- const fileBase = path7.basename(absoluteFilePath, path7.extname(absoluteFilePath));
5717
+ const fileBase = path8.basename(absoluteFilePath, path8.extname(absoluteFilePath));
5185
5718
  if (format === "docx") {
5186
5719
  const buffer = await buildDocxDocument(doc, mergedConfig, baseDir);
5187
5720
  res.writeHead(200, {
@@ -5216,7 +5749,7 @@ data: ${Date.now()}
5216
5749
  }
5217
5750
  if (url.pathname === "/document-content") {
5218
5751
  try {
5219
- const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
5752
+ const mdContent = fs8.readFileSync(absoluteFilePath, "utf-8");
5220
5753
  const doc = parseMarkdownDocument(mdContent);
5221
5754
  const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
5222
5755
  const html = await buildHtmlDocument(doc, { ...baseConfig, ...resolvedConfig }, baseDir);
@@ -5249,8 +5782,8 @@ data: ${Date.now()}
5249
5782
  return;
5250
5783
  }
5251
5784
  if (url.pathname === "/" || url.pathname === "/index.html") {
5252
- const fileName = path7.basename(absoluteFilePath);
5253
- const initialContent = fs7.readFileSync(absoluteFilePath, "utf-8");
5785
+ const fileName = path8.basename(absoluteFilePath);
5786
+ const initialContent = fs8.readFileSync(absoluteFilePath, "utf-8");
5254
5787
  const appHtml = `<!DOCTYPE html>
5255
5788
  <html lang="en">
5256
5789
  <head>
@@ -5954,9 +6487,9 @@ function defineConfig(config) {
5954
6487
  }
5955
6488
 
5956
6489
  // src/version.ts
5957
- var fs8 = __toESM(require("fs"));
5958
- var path8 = __toESM(require("path"));
5959
- var import_node_url4 = require("url");
6490
+ var fs9 = __toESM(require("fs"));
6491
+ var path9 = __toESM(require("path"));
6492
+ var import_node_url6 = require("url");
5960
6493
  var import_meta = {};
5961
6494
  try {
5962
6495
  if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
@@ -5982,16 +6515,16 @@ function readVersionFromPackageJson(fromDir) {
5982
6515
  let currentDir = fromDir;
5983
6516
  for (let i = 0; i < 10; i++) {
5984
6517
  try {
5985
- const pkgJsonPath = path8.join(currentDir, "package.json");
5986
- if (fs8.existsSync(pkgJsonPath)) {
5987
- const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
6518
+ const pkgJsonPath = path9.join(currentDir, "package.json");
6519
+ if (fs9.existsSync(pkgJsonPath)) {
6520
+ const pkg = JSON.parse(fs9.readFileSync(pkgJsonPath, "utf-8"));
5988
6521
  if (pkg.name === "@masumdev/markforge" && pkg.version) {
5989
6522
  return pkg.version;
5990
6523
  }
5991
6524
  }
5992
6525
  } catch {
5993
6526
  }
5994
- const parentDir = path8.dirname(currentDir);
6527
+ const parentDir = path9.dirname(currentDir);
5995
6528
  if (parentDir === currentDir) break;
5996
6529
  currentDir = parentDir;
5997
6530
  }
@@ -6004,7 +6537,7 @@ function getPackageDir() {
6004
6537
  return __dirname;
6005
6538
  }
6006
6539
  try {
6007
- return path8.dirname((0, import_node_url4.fileURLToPath)(import_meta.url));
6540
+ return path9.dirname((0, import_node_url6.fileURLToPath)(import_meta.url));
6008
6541
  } catch {
6009
6542
  return process.cwd();
6010
6543
  }
@@ -6015,6 +6548,8 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
6015
6548
  }
6016
6549
  // Annotate the CommonJS export names for ESM import in node:
6017
6550
  0 && (module.exports = {
6551
+ BackCoverPreset,
6552
+ CoverPagePreset,
6018
6553
  DEFAULT_CONFIG,
6019
6554
  KATEX_INLINE_CSS,
6020
6555
  MARKFORGE_VERSION,
@@ -6023,6 +6558,8 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
6023
6558
  PAPER_DIMENSIONS_TWIP,
6024
6559
  PaperSizeEnum,
6025
6560
  SYNTAX_COLORS,
6561
+ SignatureAlign,
6562
+ SignatureStyle,
6026
6563
  SyntaxTheme,
6027
6564
  THEMES,
6028
6565
  THEME_CORPORATE,
@@ -6033,6 +6570,9 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
6033
6570
  buildDocxDocument,
6034
6571
  buildHtmlDocument,
6035
6572
  buildPdfDocument,
6573
+ buildPngDocument,
6574
+ buildTextDocument,
6575
+ buildTxtDocument,
6036
6576
  compileMarkdown,
6037
6577
  defineConfig,
6038
6578
  escapeHtml,
@@ -6061,6 +6601,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
6061
6601
  renderBackCoverHtml,
6062
6602
  renderCoverPageHtml,
6063
6603
  renderInlinesToHtml,
6604
+ renderInlinesToText,
6064
6605
  renderMathToHtml,
6065
6606
  renderMermaidToPng,
6066
6607
  renderNodesToHtml,